2019-01-20 16:22:32 -06:00
|
|
|
// The list of base types for a UCG value.
|
|
|
|
let base_types = [
|
|
|
|
"int",
|
|
|
|
"float",
|
|
|
|
"str",
|
|
|
|
"bool",
|
|
|
|
"null",
|
|
|
|
"tuple",
|
|
|
|
"list",
|
2019-01-24 20:04:40 -06:00
|
|
|
"func",
|
2019-01-20 16:22:32 -06:00
|
|
|
"module",
|
|
|
|
];
|
|
|
|
|
2019-02-04 20:30:06 -06:00
|
|
|
// Computes the base type of a value.
|
2019-11-02 11:01:47 -05:00
|
|
|
let base_type_of = func (val) => reduce(func (acc, f) => select ((acc.val is f), f) => {
|
2019-01-20 16:22:32 -06:00
|
|
|
true = acc{typ = f},
|
|
|
|
false = acc,
|
2019-02-04 20:30:06 -06:00
|
|
|
}, {val=val, typ="null"}, base_types).typ;
|
2019-01-20 16:22:32 -06:00
|
|
|
|
|
|
|
// Turns any schema check module into a compile failure.
|
|
|
|
// The module must export the computed value as the result field.
|
2019-11-02 11:01:47 -05:00
|
|
|
let must = func (m, msg) => select (m, fail msg) => {
|
2019-02-24 08:16:07 -06:00
|
|
|
true = m,
|
2019-01-20 16:22:32 -06:00
|
|
|
};
|
|
|
|
|
2019-04-17 22:17:06 -05:00
|
|
|
// All does a boolean match against all of set of allowed shapes for a type.
|
|
|
|
// This module uses the shape module below so the same rules for checking
|
|
|
|
// the types against the source value apply for each example in the list.
|
|
|
|
//
|
|
|
|
// This module does partial matches for the shapes.
|
|
|
|
let all = module {
|
|
|
|
// The source value to check.
|
|
|
|
val=NULL,
|
|
|
|
// The set of allowed type shapes it can be.
|
|
|
|
types=[],
|
|
|
|
} => (result) {
|
|
|
|
let schema = mod.pkg();
|
|
|
|
|
|
|
|
let reducer = func (acc, t) => acc{
|
|
|
|
ok = acc.ok && (schema.shaped{val=acc.val, shape=t, partial=true}),
|
|
|
|
};
|
|
|
|
let any = func (val, types) => reduce(reducer, {ok=true, val=val}, types);
|
|
|
|
|
|
|
|
let result = any(mod.val, mod.types).ok;
|
|
|
|
};
|
|
|
|
|
2019-01-20 16:22:32 -06:00
|
|
|
// Any does a boolean match against a set of allowed shapes for a type.
|
|
|
|
// This module uses the shape module below so the same rules for checking
|
|
|
|
// the types against the source value apply for each example in the list.
|
2019-04-17 21:02:09 -05:00
|
|
|
//
|
2019-04-17 22:17:06 -05:00
|
|
|
// This module does not do partial matches for the shapes by default.
|
2019-01-20 16:22:32 -06:00
|
|
|
let any = module {
|
|
|
|
// The source value to check.
|
2019-01-20 08:32:05 -06:00
|
|
|
val=NULL,
|
2019-01-20 16:22:32 -06:00
|
|
|
// The set of allowed type shapes it can be.
|
2019-01-20 08:32:05 -06:00
|
|
|
types=[],
|
2019-04-17 22:17:06 -05:00
|
|
|
// Whether to do partial matches for the shapes
|
|
|
|
partial=false
|
2019-02-24 08:16:07 -06:00
|
|
|
} => (result) {
|
2019-04-16 20:09:27 -05:00
|
|
|
let schema = mod.pkg();
|
2019-01-20 16:22:32 -06:00
|
|
|
|
2019-01-24 20:04:40 -06:00
|
|
|
let reducer = func (acc, t) => acc{
|
2019-04-17 22:17:06 -05:00
|
|
|
ok = acc.ok || (schema.shaped{val=acc.val, shape=t, partial=mod.partial}),
|
2019-01-20 08:32:05 -06:00
|
|
|
};
|
2019-01-31 16:33:12 -06:00
|
|
|
let any = func (val, types) => reduce(reducer, {ok=false, val=val}, types);
|
2019-01-20 08:32:05 -06:00
|
|
|
|
|
|
|
let result = any(mod.val, mod.types).ok;
|
|
|
|
};
|
|
|
|
|
2019-01-20 16:22:32 -06:00
|
|
|
// Compares a value against an example schema value. compares the "shape" of the
|
|
|
|
// value to see if it matches. The base type must be the same as the base type
|
|
|
|
// of the shape. For tuples any field in the shape tuple must be present in the
|
|
|
|
// source value and must be of the same base type and shape. This module will
|
|
|
|
// recurse into nested tuples.
|
|
|
|
//
|
2019-02-18 20:15:54 -06:00
|
|
|
// Lists are must contain types from the list shape they are compared against.
|
|
|
|
// and empty list shape means the list val can have any types it wants inside.
|
|
|
|
//
|
2019-01-24 20:04:40 -06:00
|
|
|
// We do not check that functions or modules have the same argument lengths or types
|
2019-01-20 16:22:32 -06:00
|
|
|
// nor we check that they output the same types.
|
|
|
|
let shaped = module {
|
|
|
|
// The source value to validate
|
|
|
|
val = NULL,
|
|
|
|
|
|
|
|
// The shape to validate it against.
|
|
|
|
shape = NULL,
|
|
|
|
|
|
|
|
// Whether partial matches are accepted.
|
|
|
|
// When set to true then the source value can have
|
|
|
|
// fields in tuples that are not present in the
|
|
|
|
// shape it is compared with.
|
|
|
|
partial = true,
|
2019-02-24 08:16:07 -06:00
|
|
|
} => (result) {
|
2019-04-16 20:09:27 -05:00
|
|
|
let schema = mod.pkg();
|
2019-04-24 19:30:10 -05:00
|
|
|
let this = mod.this;
|
2019-01-20 16:22:32 -06:00
|
|
|
|
2019-02-18 20:15:54 -06:00
|
|
|
let simple_handler = func (val, shape) => val is schema.base_type_of(shape);
|
2019-01-20 16:22:32 -06:00
|
|
|
|
2019-01-24 20:04:40 -06:00
|
|
|
let tuple_handler = func (acc, name, value) => acc{
|
2019-11-02 11:01:47 -05:00
|
|
|
ok = select ((name) in acc.shape, mod.partial) => {
|
2019-04-24 19:30:10 -05:00
|
|
|
true = this{val=value, shape=acc.shape.(name), partial=mod.partial},
|
2019-01-20 16:22:32 -06:00
|
|
|
},
|
|
|
|
};
|
2019-02-18 18:47:52 -06:00
|
|
|
|
2019-04-17 23:02:18 -05:00
|
|
|
let shape_tuple_handler = func (acc, field_name, value) => acc{
|
2019-11-02 11:01:47 -05:00
|
|
|
ok = select ((field_name) in acc.val, false) => {
|
2019-04-24 19:30:10 -05:00
|
|
|
true = this{val=value, shape=acc.val.(field_name), partial=false},
|
2019-04-17 22:17:06 -05:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let match_shape_fields = func(val, shape) => reduce(
|
|
|
|
shape_tuple_handler, {val=val, ok=false}, shape).ok;
|
|
|
|
|
|
|
|
let match_tuple_fields = func(val, shape) =>
|
|
|
|
reduce(tuple_handler, {shape=shape, ok=false}, val).ok;
|
|
|
|
|
2019-02-18 18:47:52 -06:00
|
|
|
let list_handler = func(acc, value) => acc{
|
2019-04-17 22:17:06 -05:00
|
|
|
ok = acc.ok && schema.any{val=value, types=acc.shape},
|
2019-02-18 18:47:52 -06:00
|
|
|
};
|
2019-04-17 22:17:06 -05:00
|
|
|
|
2019-11-02 11:01:47 -05:00
|
|
|
let result = select (schema.base_type_of(mod.val), false) => {
|
2019-01-20 16:22:32 -06:00
|
|
|
str = simple_handler(mod.val, mod.shape),
|
|
|
|
int = simple_handler(mod.val, mod.shape),
|
|
|
|
float = simple_handler(mod.val, mod.shape),
|
|
|
|
bool = simple_handler(mod.val, mod.shape),
|
|
|
|
null = simple_handler(mod.val, mod.shape),
|
2019-04-17 22:17:06 -05:00
|
|
|
tuple = (mod.shape is "tuple") &&
|
|
|
|
match_shape_fields(mod.val, mod.shape) &&
|
|
|
|
match_tuple_fields(mod.val, mod.shape),
|
2019-11-02 11:01:47 -05:00
|
|
|
list = (mod.shape is "list") && select (mod.shape == [], true) => {
|
2019-04-17 22:17:06 -05:00
|
|
|
false = reduce(list_handler, {shape=mod.shape, ok=true}, mod.val).ok,
|
2019-02-18 18:47:52 -06:00
|
|
|
},
|
2019-01-24 20:04:40 -06:00
|
|
|
func = simple_handler(mod.val, mod.shape),
|
2019-01-20 16:22:32 -06:00
|
|
|
module = simple_handler(mod.val, mod.shape),
|
|
|
|
};
|
|
|
|
};
|