mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FEATURE: More robust schema matching.
* Fixed some weirdness with partial matching in shaped. * Added an all matcher for bundling multiple partial matches together.
This commit is contained in:
parent
643b597e35
commit
524f85102f
@ -23,12 +23,12 @@ let must = func (m, msg) => select m, fail msg, {
|
|||||||
true = m,
|
true = m,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Any does a boolean match against a set of allowed shapes for a type.
|
// 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
|
// 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.
|
// the types against the source value apply for each example in the list.
|
||||||
//
|
//
|
||||||
// This module does not do partial matches for the shapes.
|
// This module does partial matches for the shapes.
|
||||||
let any = module {
|
let all = module {
|
||||||
// The source value to check.
|
// The source value to check.
|
||||||
val=NULL,
|
val=NULL,
|
||||||
// The set of allowed type shapes it can be.
|
// The set of allowed type shapes it can be.
|
||||||
@ -37,7 +37,30 @@ let any = module {
|
|||||||
let schema = mod.pkg();
|
let schema = mod.pkg();
|
||||||
|
|
||||||
let reducer = func (acc, t) => acc{
|
let reducer = func (acc, t) => acc{
|
||||||
ok = acc.ok || (schema.shaped{val=acc.val, shape=t, partial=false}),
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// This module does not do partial matches for the shapes by default.
|
||||||
|
let any = module {
|
||||||
|
// The source value to check.
|
||||||
|
val=NULL,
|
||||||
|
// The set of allowed type shapes it can be.
|
||||||
|
types=[],
|
||||||
|
// Whether to do partial matches for the shapes
|
||||||
|
partial=false
|
||||||
|
} => (result) {
|
||||||
|
let schema = mod.pkg();
|
||||||
|
|
||||||
|
let reducer = func (acc, t) => acc{
|
||||||
|
ok = acc.ok || (schema.shaped{val=acc.val, shape=t, partial=mod.partial}),
|
||||||
};
|
};
|
||||||
let any = func (val, types) => reduce(reducer, {ok=false, val=val}, types);
|
let any = func (val, types) => reduce(reducer, {ok=false, val=val}, types);
|
||||||
|
|
||||||
@ -74,23 +97,37 @@ let shaped = module {
|
|||||||
|
|
||||||
let tuple_handler = func (acc, name, value) => acc{
|
let tuple_handler = func (acc, name, value) => acc{
|
||||||
ok = select (name) in acc.shape, mod.partial, {
|
ok = select (name) in acc.shape, mod.partial, {
|
||||||
true = schema.shaped{val=value, shape=acc.shape.(name)},
|
true = schema.shaped{val=value, shape=acc.shape.(name), partial=mod.partial},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let list_handler = func(acc, value) => acc{
|
let shape_tuple_handler = func (acc, name, value) => acc{
|
||||||
ok = false || schema.any{val=value, types=acc.shape},
|
ok = select (name) in acc.val, false, {
|
||||||
|
true = schema.shaped{val=value, shape=acc.val.(name), partial=false},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
let list_handler = func(acc, value) => acc{
|
||||||
|
ok = acc.ok && schema.any{val=value, types=acc.shape},
|
||||||
|
};
|
||||||
|
|
||||||
let result =select schema.base_type_of(mod.val), false, {
|
let result =select schema.base_type_of(mod.val), false, {
|
||||||
str = simple_handler(mod.val, mod.shape),
|
str = simple_handler(mod.val, mod.shape),
|
||||||
int = simple_handler(mod.val, mod.shape),
|
int = simple_handler(mod.val, mod.shape),
|
||||||
float = simple_handler(mod.val, mod.shape),
|
float = simple_handler(mod.val, mod.shape),
|
||||||
bool = simple_handler(mod.val, mod.shape),
|
bool = simple_handler(mod.val, mod.shape),
|
||||||
null = simple_handler(mod.val, mod.shape),
|
null = simple_handler(mod.val, mod.shape),
|
||||||
tuple = (mod.shape is "tuple") && reduce(tuple_handler, {shape=mod.shape, ok=false}, mod.val).ok,
|
tuple = (mod.shape is "tuple") &&
|
||||||
list = select mod.shape == [], true, {
|
match_shape_fields(mod.val, mod.shape) &&
|
||||||
false = reduce(list_handler, {shape=mod.shape, ok=false}, mod.val).ok,
|
match_tuple_fields(mod.val, mod.shape),
|
||||||
|
list = (mod.shape is "list") && select mod.shape == [], true, {
|
||||||
|
false = reduce(list_handler, {shape=mod.shape, ok=true}, mod.val).ok,
|
||||||
},
|
},
|
||||||
func = simple_handler(mod.val, mod.shape),
|
func = simple_handler(mod.val, mod.shape),
|
||||||
module = simple_handler(mod.val, mod.shape),
|
module = simple_handler(mod.val, mod.shape),
|
||||||
|
@ -131,12 +131,42 @@ assert t.ok{
|
|||||||
desc="inner list with valid types matches empty list shape",
|
desc="inner list with valid types matches empty list shape",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert t.not_ok{
|
||||||
|
test = schema.shaped{val={list=[1, "foo"]}, shape={list="foo"}},
|
||||||
|
desc="inner list with with non list shape does not match",
|
||||||
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = schema.shaped{val={foo="bar"}, shaped=1.0},
|
test = schema.shaped{val={foo="bar"}, shaped=1.0},
|
||||||
desc = "a tuple is not a float",
|
desc = "a tuple is not a float",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = schema.any{val={foo="bar", quux="baz"}, types=[1.0, {foo="bar", qux="baz"}]},
|
test = schema.any{val={foo="bar", quux="baz"}, types=[1.0, {foo="bar"}]},
|
||||||
desc = "any doesn't match against missing fields for tuples.",
|
desc = "any doesn't match against missing fields for tuples for value tuple.",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert t.ok{
|
||||||
|
test = schema.any{val={foo="bar", quux="baz"}, types=[1.0, {foo="bar"}], partial=true},
|
||||||
|
desc = "any can do partial matching against missing fields for tuples shapes.",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert t.not_ok{
|
||||||
|
test = schema.any{val={foo="bar"}, types=[1.0, {foo="", quux=""}]},
|
||||||
|
desc = "any does match against missing fields for tuples shape tuple.",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert t.not_ok{
|
||||||
|
test = schema.shaped{val={foo="bar", baz="quux"}, shape={bear=""}, partial=true},
|
||||||
|
desc = "even with partial all of the shape fields must be present.",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert t.ok{
|
||||||
|
test = schema.all{val={foo="bar", baz="quux"}, types=[{foo=""}, {baz=""}]},
|
||||||
|
desc = "all enforces that all of the valid types are partial matches (success)",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert t.not_ok{
|
||||||
|
test = schema.all{val={foo="bar", baz="quux"}, types=[{foo=""}, {baz=""}, {quux=""}]},
|
||||||
|
desc = "all enforces that all of the valid shapes must be partial matches (fail)",
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user