mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DOCS: Add documentation on our stdlib.
* Adds schema.ucg to our reference site. * Adds comments to the std libs themselves.
This commit is contained in:
parent
cccef4554d
commit
b571ead599
65
docsite/site/content/stdlib/schema.md
Normal file
65
docsite/site/content/stdlib/schema.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
+++
|
||||||
|
title = "UCG Schema Modules"
|
||||||
|
weight = 4
|
||||||
|
sort_by = "weight"
|
||||||
|
in_search_index = true
|
||||||
|
+++
|
||||||
|
|
||||||
|
UCG's std lib has a selection of functions and modules to help assert a base type and shape for a value. They are located in the `std/schema.ucg` import.
|
||||||
|
|
||||||
|
## Schema shapes
|
||||||
|
|
||||||
|
The primary tool in this import is the shaped module. This module allows you to match a value against a stereotype value. If the provided value is the same base type and has the same shape of that base type as the provided stereotype then the
|
||||||
|
result field of the computed tuple will be true.
|
||||||
|
|
||||||
|
```
|
||||||
|
let fits = schema.shaped{
|
||||||
|
val={foo="bar", inner=[1, 2]},
|
||||||
|
shape={foo="", inner=[]}
|
||||||
|
}.result;
|
||||||
|
|
||||||
|
fits == true;
|
||||||
|
```
|
||||||
|
|
||||||
|
By default the matching allows the shape to specify a partial or minimum shape. But you can specify exact matching if desired by setting the partial paramater to false..
|
||||||
|
|
||||||
|
```
|
||||||
|
let exact_fit = schema.shaped{
|
||||||
|
partial=false,
|
||||||
|
val={foo="bar", count=1},
|
||||||
|
shape={foo=""},
|
||||||
|
}.result;
|
||||||
|
|
||||||
|
exact_fit == false;
|
||||||
|
```
|
||||||
|
|
||||||
|
The shape algorithm does not enforce a length or set of contained types for lists at this time.
|
||||||
|
|
||||||
|
Besides the shaped module there are also some useful utility modules and functions to assist in some addtional type checking logic.
|
||||||
|
## any
|
||||||
|
|
||||||
|
The `any` module tests a value against a list of possible shapes. If the value
|
||||||
|
fits any of the candidate shapes then it stores true in the result field. If it does not then it returns false in that result field.
|
||||||
|
|
||||||
|
```
|
||||||
|
let fits_one_of = any{val={foo="bar"}, types=[1, {foo=""}]}.result;
|
||||||
|
fits_one_of == true;
|
||||||
|
```
|
||||||
|
|
||||||
|
## base_type_of function.
|
||||||
|
|
||||||
|
The `base_type_of` function computes the base type of a value.
|
||||||
|
|
||||||
|
```
|
||||||
|
let foo = 1;
|
||||||
|
base_type_of(foo) == "int";
|
||||||
|
```
|
||||||
|
|
||||||
|
## must
|
||||||
|
|
||||||
|
The `must` function turns any schema check module failure into a compile failure. It composes well with the modules in the `std/schema.ucg` import and automatically checks the result field they produce. If that field is true then it returns that true value. If the field is false they produce a compile failure.
|
||||||
|
|
||||||
|
```
|
||||||
|
// results in a compile failure "Must be a string"
|
||||||
|
must(shaped{val="foo", shape=1}, "Must be a string");
|
||||||
|
```
|
@ -1,7 +1,14 @@
|
|||||||
|
// Joins a list into a string with an optional provided separator.
|
||||||
|
// The string will be in the result field of the generated tuple
|
||||||
|
// from the module.
|
||||||
let str_join = module{
|
let str_join = module{
|
||||||
|
// The default separator is a single space. You can override
|
||||||
|
// this when you call the module if desired.
|
||||||
sep=" ",
|
sep=" ",
|
||||||
|
// The list is a required parameter.
|
||||||
list=NULL,
|
list=NULL,
|
||||||
} => {
|
} => {
|
||||||
|
// The function used by reduce to join the list into a string.
|
||||||
let joiner = func (acc, item) => select (acc.out == ""), NULL, {
|
let joiner = func (acc, item) => select (acc.out == ""), NULL, {
|
||||||
true = acc{
|
true = acc{
|
||||||
out="@@" % (acc.out,item),
|
out="@@" % (acc.out,item),
|
||||||
@ -11,16 +18,27 @@ let str_join = module{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The resulting joined string. Reference the result field from the
|
||||||
|
// tuple the module produces to get the joined string.
|
||||||
let result = reduce(joiner, {sep=mod.sep, out=""}, (mod.list)).out;
|
let result = reduce(joiner, {sep=mod.sep, out=""}, (mod.list)).out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Computes the length of the provided list.
|
||||||
let len = func(list) => reduce(func(acc, item) => acc + 1, 0, list);
|
let len = func(list) => reduce(func(acc, item) => acc + 1, 0, list);
|
||||||
|
|
||||||
|
// Reverses the provided list.
|
||||||
let reverse = func(list) => reduce(func (acc, item) => [item] + acc, [], list);
|
let reverse = func(list) => reduce(func (acc, item) => [item] + acc, [], list);
|
||||||
|
|
||||||
|
// Enumerates the provided list with optional start and step parameters for the
|
||||||
|
// enumeration. Prodices a list of pairs with the numeration and the list item.
|
||||||
|
//
|
||||||
|
// enumerate{list=["a","b","c"]}.result == [[0, "a"], [1, "b"], [2, "c"]]
|
||||||
let enumerate = module{
|
let enumerate = module{
|
||||||
|
// Where to start the enumeration.
|
||||||
start = 0,
|
start = 0,
|
||||||
|
// The step amount for each enumeration.
|
||||||
step = 1,
|
step = 1,
|
||||||
|
// The list to enumerate.
|
||||||
list = NULL,
|
list = NULL,
|
||||||
} => {
|
} => {
|
||||||
let reducer = func (acc, item) => acc{
|
let reducer = func (acc, item) => acc{
|
||||||
@ -34,21 +52,26 @@ let enumerate = module{
|
|||||||
let result = enumerated.list;
|
let result = enumerated.list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// zips two lists together.
|
||||||
|
//
|
||||||
|
// zip{list1=[0,2,4],list2=[1,3,5]}.result == [[0, 1], [2, 3], [4, 5]]
|
||||||
let zip = module{
|
let zip = module{
|
||||||
list1 = NULL,
|
list1 = NULL,
|
||||||
list2 = NULL,
|
list2 = NULL,
|
||||||
} => {
|
} => {
|
||||||
let len = import "std/lists.ucg".len;
|
let len = import "std/lists.ucg".len;
|
||||||
|
|
||||||
|
// Compute the length of each list.
|
||||||
let len1 = len(mod.list1);
|
let len1 = len(mod.list1);
|
||||||
let len2 = len(mod.list2);
|
let len2 = len(mod.list2);
|
||||||
|
|
||||||
|
// Compute the min range of the two lists.
|
||||||
let rng = select (len1 >= len2), NULL, {
|
let rng = select (len1 >= len2), NULL, {
|
||||||
true = 0:(len1 - 1),
|
true = 0:(len1 - 1),
|
||||||
false = 0:(len2 - 1),
|
false = 0:(len2 - 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The reducer function for the zip operation.
|
||||||
let reducer = func (acc, item) => acc{
|
let reducer = func (acc, item) => acc{
|
||||||
result = acc.result + [[acc.list1.(item), acc.list2.(item)]],
|
result = acc.result + [[acc.list1.(item), acc.list2.(item)]],
|
||||||
idxs = acc.idxs + [item]
|
idxs = acc.idxs + [item]
|
||||||
@ -61,5 +84,6 @@ let zip = module{
|
|||||||
idxs = [],
|
idxs = [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The resulting zipped list.
|
||||||
let result = reduce(reducer, acc, rng).result;
|
let result = reduce(reducer, acc, rng).result;
|
||||||
};
|
};
|
@ -1,8 +1,9 @@
|
|||||||
|
// Assert that a value is a tuple. Generate a compile failure if it is not.
|
||||||
let assert_tuple = func (tpl) => select tpl is "tuple", NULL, {
|
let assert_tuple = func (tpl) => select tpl is "tuple", NULL, {
|
||||||
false = fail "@ is not a tuple" % (tpl),
|
false = fail "@ is not a tuple" % (tpl),
|
||||||
};
|
};
|
||||||
|
|
||||||
// return a list of the fields in a tuple.
|
// Return a list of the fields in a tuple.
|
||||||
let fields = module{
|
let fields = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
@ -13,7 +14,7 @@ let fields = module{
|
|||||||
let result = reduce(func (acc, field, value) => acc + [field], [], (mod.tpl));
|
let result = reduce(func (acc, field, value) => acc + [field], [], (mod.tpl));
|
||||||
};
|
};
|
||||||
|
|
||||||
// return a list of the values in a tuple.
|
// Return a list of the values in a tuple.
|
||||||
let values = module{
|
let values = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
@ -24,6 +25,7 @@ let values = module{
|
|||||||
let result = reduce(func (acc, field, value) => acc + [value], [], (mod.tpl));
|
let result = reduce(func (acc, field, value) => acc + [value], [], (mod.tpl));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Return a list of the key value pairs in the tuple.
|
||||||
let iter = module{
|
let iter = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
@ -34,6 +36,7 @@ let iter = module{
|
|||||||
let result = reduce(func (acc, field, value) => acc + [[field, value]], [], (mod.tpl));
|
let result = reduce(func (acc, field, value) => acc + [[field, value]], [], (mod.tpl));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Strip all the null fields from a tuple.
|
||||||
let strip_nulls = module{
|
let strip_nulls = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
@ -44,6 +47,7 @@ let strip_nulls = module{
|
|||||||
let result = filter(func (name, value) => value != NULL, (mod.tpl));
|
let result = filter(func (name, value) => value != NULL, (mod.tpl));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if a tuple has all the fields in a given list.
|
||||||
let has_fields = module{
|
let has_fields = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
fields = [],
|
fields = [],
|
||||||
@ -57,6 +61,7 @@ let has_fields = module{
|
|||||||
let result = reduce(reducer = func (acc, f) => acc && (f in fs), true, (mod.fields));
|
let result = reduce(reducer = func (acc, f) => acc && (f in fs), true, (mod.fields));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if a field in a tuple is a given type.
|
||||||
let field_type = module{
|
let field_type = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
field = NULL,
|
field = NULL,
|
||||||
@ -76,12 +81,15 @@ let field_type = module{
|
|||||||
false = fail "@ is not a string" % (mod.type),
|
false = fail "@ is not a string" % (mod.type),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get the list of field value pairs.
|
||||||
let it = lib.iter{tpl=mod.tpl}.result;
|
let it = lib.iter{tpl=mod.tpl}.result;
|
||||||
|
|
||||||
|
// The reducer function used to check the field's types if it exists.
|
||||||
let reducer = func (acc, l) => acc && (select l.0 == mod.field, NULL, {
|
let reducer = func (acc, l) => acc && (select l.0 == mod.field, NULL, {
|
||||||
true = l.1 is mod.type, // if this is the field then we propagate if it's the right type.
|
true = l.1 is mod.type, // if this is the field then we propagate if it's the right type.
|
||||||
false = true, // if this isn't the field then we propagate true
|
false = true, // if this isn't the field then we propagate true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The computed answer true or false.
|
||||||
let result = lib.has_fields{tpl=mod.tpl, fields=[mod.field]}.result && reduce(reducer, true, it);
|
let result = lib.has_fields{tpl=mod.tpl, fields=[mod.field]}.result && reduce(reducer, true, it);
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user