static const char CmdType[] = "T";
static const char CmdUTC[] = "UTC";
-static const char TypeBin[] = "BIN";
-static const char TypeBlob[] = "BLOB";
-static const char TypeBool[] = "BOOL";
-static const char TypeHexlet[] = "HEXLET";
-static const char TypeInt[] = "INT";
-static const char TypeList[] = "LIST";
-static const char TypeMagic[] = "MAGIC";
-static const char TypeMap[] = "MAP";
-static const char TypeNIL[] = "NIL";
-static const char TypeStr[] = "STR";
-static const char TypeTAI[] = "TAI";
+static const char TypeBin[] = "B";
+static const char TypeBlob[] = "O";
+static const char TypeBool[] = "?";
+static const char TypeHexlet[] = "H";
+static const char TypeInt[] = "I";
+static const char TypeList[] = "L";
+static const char TypeMagic[] = "K";
+static const char TypeMap[] = "M";
+static const char TypeNIL[] = "N";
+static const char TypeStr[] = "S";
+static const char TypeTAI[] = "T";
static struct KEKSSchemaErr
keksSchemaCmd(
}
err.offSchema = schema->offsets[idxSchema];
switch (schema->list[idxSchema].atom.typ) {
+ case KEKSItemNIL:
+ (*taken) = idxData;
+ break;
case KEKSItemStr:
- if ((schema->list[idxSchema].atom.v.str.len == 1) &&
- (schema->list[idxSchema].atom.v.str.ptr[0] == '.')) {
- (*taken) = idxData;
- } else {
- if (data->list[idxData].atom.typ != KEKSItemMap) {
- err.code = KEKSSchemaErrUnexpectedState;
- err.msg = "non-map TAKE target";
- return err;
- }
- (*taken) = KEKSItemsGetByKeyLen(
- data,
- idxData,
- (const char *)schema->list[idxSchema].atom.v.str.ptr,
- schema->list[idxSchema].atom.v.str.len);
- if ((*taken) == 0) {
- (*taken) = SIZE_MAX;
- }
+ if (data->list[idxData].atom.typ != KEKSItemMap) {
+ err.code = KEKSSchemaErrUnexpectedState;
+ err.msg = "non-map TAKE target";
+ return err;
+ }
+ (*taken) = KEKSItemsGetByKeyLen(
+ data,
+ idxData,
+ (const char *)schema->list[idxSchema].atom.v.str.ptr,
+ schema->list[idxSchema].atom.v.str.len);
+ if ((*taken) == 0) {
+ (*taken) = SIZE_MAX;
}
break;
case KEKSItemPint:
break;
case KEKSItemInvalid:
case KEKSItemEOC:
- case KEKSItemNIL:
case KEKSItemFalse:
case KEKSItemTrue:
case KEKSItemHexlet:
}}
}
switch k := cmd[1].(type) {
+ case nil:
+ taken = "."
+ vs = []any{data}
case string:
- taken = k
- if k == "." {
- vs = []any{data}
- } else {
- var m map[string]any
- m, ok = data.(map[string]any)
- if !ok {
- return &SchemaErr{BaseErr: BaseErr{
- SchemaName: schemaName,
- CmdIdx: cmdIdx,
- CmdName: cmdName,
- Msg: "non map",
- Data: data,
- }}
- }
- v, exists := m[k]
- if !exists {
- continue
- }
- vs = []any{v}
+ taken = ":" + k
+ var m map[string]any
+ m, ok = data.(map[string]any)
+ if !ok {
+ return &SchemaErr{BaseErr: BaseErr{
+ SchemaName: schemaName,
+ CmdIdx: cmdIdx,
+ CmdName: cmdName,
+ Msg: "non map",
+ Data: data,
+ }}
+ }
+ v, exists := m[k]
+ if !exists {
+ continue
}
+ vs = []any{v}
case uint64:
taken = strconv.FormatUint(k, 10)
var l []any
}}
}
switch t {
- case "NIL":
+ case "N":
expected = append(expected, types.NIL)
- case "BOOL":
+ case "?":
expected = append(expected, types.Bool)
- case "HEXLET":
+ case "H":
expected = append(expected, types.Hexlet)
- case "INT":
+ case "I":
expected = append(expected, types.UInt, types.Int)
- case "LIST":
+ case "L":
expected = append(expected, types.List)
- case "MAP":
+ case "M":
expected = append(expected, types.Map)
- case "BLOB":
+ case "O":
expected = append(expected, types.Blob)
- case "TAI":
+ case "T":
expected = append(expected, types.TAI64, types.TAI64N, types.TAI64NA)
- case "MAGIC":
+ case "K":
expected = append(expected, types.Magic)
- case "BIN":
+ case "B":
expected = append(expected, types.Bin)
- case "STR":
+ case "S":
expected = append(expected, types.Str)
default:
return &SchemaErr{BaseErr: BaseErr{
generated from higher level schema descriptions.
TAKE | [".", k]
- Take/choose the value of the "k" key in the map, if "k" is a string.
- If "k" is integer, then choose the k-th value in a list.
- If "k" equals to ".", then choose the element you are currently in.
- Command never fails, but key can be non-existent.
- All following commands will be applied to the taken value.
- By default analogue of [".", "."] command is executed when schema
+ Take/choose/consider element "k". All following commands will be
+ applied to the taken value.
+ If "k" is NIL, then choose the element your scheme is currently in.
+ If "k" is an integer, then choose the k-th value in a list, starting
+ from the zero.
+ If "k" is a string, then choose the value of the "k" key in the map.
+ By default analogue of [".", NIL] command is executed when schema
check is called.
EXISTS | ["E"]
TYPE | ["T", T0 [, T1, ...]]
Check that chosen (if it exists) element's type is in (T0, T1 ...)
- set. Possible types: "BIN", "BLOB", "BOOL", "HEXLET", "INT", "LIST",
- "MAGIC", "MAP", "NIL", "STR", "TAI".
+ set. Possible single-character types are:
+
+ "B"(IN)
+ (BL)"O"(B)
+ "?" for BOOL
+ "H"(EXLET)
+ "I"(NT)
+ "L"(IST)
+ "K"(EKS) MAGIC
+ "M"(AP)
+ "N"(IL)
+ "S"(TR)
+ "T"(AI)
GT | [">", n]
Check that chosen (if it exists) integer value is greater than "n".
{"our": [
[".", "a"],
["E"],
- ["T", "STR"],
+ ["T", "S"],
[">", 0],
[".", "v"],
["E"],
- ["T", "BIN", "STR"],
+ ["T", "B", "S"],
[".", "fpr"],
["E"],
- ["T", "BIN"],
+ ["T", "B"],
[">", 31],
["<", 33],
[".", "comment"],
- ["T", "STR"],
+ ["T", "S"],
]}
Here is an example with multiple schemas:
{
"where": [
- ["T", "LIST"],
+ ["T", "L"],
[">", 1],
["<", 3],
["*"],
- ["T", "INT"],
+ ["T", "I"],
[".", 0],
[">", -91],
["<", 181],
],
"wheres": [
- ["T", "LIST"],
+ ["T", "L"],
[">", 0],
["*"],
["S", "where"],
"field" command helps creation of commands related to the field.
-Its first argument is either field's name in the map, or list's index or
-dot, meaning the self-structure itself.
-
-Second argument is a list of allowable types, written in lowercase.
-If that list equals to {with S}, then {SCHEMA s} command will be called
-instead of TYPE checking. If list equals to {set}, then it is checked
-to be a MAP with EACH value of NIL.
+ {field N {T} [optional] [!exists] [{of type T}] [{of S}]
+ [>n] [<n] [len=n] [=v] [prec=p] [utc]}
+
+"N" required argument is the name of the field to consider. Either it is
+".", meaning the current taken element by schema. Or it is a digit,
+meaning the n-th element of the list. Otherwise it is a name of the key
+in map. If N starts with ":", then its remaining part is a string
+anyway, giving you ability to specify ":2" for choosing the "2" key of
+map for example.
+
+"T" required argument is:
+* either a list of whitespace-separated allowable types
+ (bin, blob, bool, hexlet, int, list, magic, map, nil, str, tai)
+* or {set}, that will assure the field is a map with NIL values
+* or {with S} string, that will issue the {SCHEMA S} command instead of
+ type checking the field
All other arguments are optional.
specified, then it is explicitly checked to be non-existent and
you can specify empty list of types in second argument.
-">n" and "<n" arguments allow checking of the integer value or
-the lengths. ">0" assures that either list/map or strings are not
-empty.
+">n" and "<n" arguments allow checking of the integer's value or
+the bin/str/list/map lengths. ">0" assures that either list/map or
+strings are not empty.
-"len=n" checks the exact length.
+"len=n" checks the exact length of bin/str/list/map, or integer's value.
-"=v" checks that given element has specified string/binary value
-(use "len=" for integers).
+"=v" checks that given bin/str/hexlet/magic has specified binary value.
"prec=p" issues TIMEPREC command, but instead of specifying the raw
integer values, you choose one of: s, ms, us, ns, ps, fs.
"utc" issues UTC command.
-{of s} argument issues checking of EACH element of the list or map
-against the specified schema "s".
+{of S} argument issues checking of EACH element of the list or map
+against the specified schema "S".
-{of type t} argument issues checking of EACH element of the list or map
-against the specified type "t".
+{of type T [T ...]} argument issues checking of EACH element of the list
+or map against the specified types.
"schema-include filename.tcl" command used instead of "field" allows
inclusion of the specified file with the path relative to given schema
$root/keks.tcl >schema.keks.hex <<EOF
MAGIC schema
MAP {e {LIST {
- {LIST {{STR T} {STR LIST}}}
+ {LIST {{STR T} {STR L}}}
{LIST {{STR *}}}
- {LIST {{STR T} {STR INT}}}
+ {LIST {{STR T} {STR I}}}
{LIST {{STR >} {INT 100}}}
{LIST {{STR <} {INT 300}}}
}}}
set version 0.1.0
+proc EXISTS {} {return {LIST {{STR E}}}}
+proc !EXISTS {} {return {LIST {{STR !E}}}}
+proc EACH {} {return {LIST {{STR *}}}}
+proc EQ {v} {subst {LIST {{STR =} {BIN $v}}}}
+proc GT {n} {subst {LIST {{STR >} {INT $n}}}}
+proc LT {n} {subst {LIST {{STR <} {INT $n}}}}
+proc SCHEMA {s} {subst {LIST {{STR S} {STR $s}}}}
+proc TIMEPREC {p} {subst {LIST {{STR TP} {INT $p}}}}
+proc UTC {} {return {LIST {{STR UTC}}}}
+
proc TAKE {k} {
- if {[string is digit $k]} {
+ if {$k == "."} {
+ set v [list NIL]
+ } elseif {[string first ":" $k] == 0} {
+ set v [list STR [string range $k 1 end]]
+ } elseif {[string is digit $k]} {
set v [list INT $k]
- } {
+ } else {
set v [list STR $k]
}
subst {LIST {{STR .} {$v}}}
}
-proc EXISTS {} {return {LIST {{STR E}}}}
-proc !EXISTS {} {return {LIST {{STR !E}}}}
-proc EACH {} {return {LIST {{STR *}}}}
-proc EQ {v} {subst {LIST {{STR =} {BIN $v}}}}
+
+set knownTypes [dict create \
+ bin B blob O bool ? hexlet H int I list L magic K map M nil N str S tai T]
+
proc TYPE {types} {
+ variable knownTypes
set l {{STR T}}
foreach t $types {
- lappend l [list STR [string toupper $t]]
+ lappend l [list STR [dict get $knownTypes [string tolower $t]]]
}
subst {LIST {$l}}
}
-proc GT {n} {subst {LIST {{STR >} {INT $n}}}}
-proc LT {n} {subst {LIST {{STR <} {INT $n}}}}
-proc SCHEMA {s} {subst {LIST {{STR S} {STR $s}}}}
-proc TIMEPREC {p} {subst {LIST {{STR TP} {INT $p}}}}
-proc UTC {} {return {LIST {{STR UTC}}}}
set timeprecArgs [dict create s 0 ms 3 us 6 ns 9 ps 12 fs 15]
-set knownTypes {bin blob bool hexlet int list magic map nil set str tai}
proc field {k types args} {
upvar _cmds _cmds buf buf
lappend _cmds [EACH]
set s [lindex $args $i]
if {[llength $s] > 2} {
- lappend _cmds [TYPE [lindex $s 2]]
+ lappend _cmds [TYPE [lrange $s 2 end]]
} else {
lappend _cmds [SCHEMA [lindex $s 1]]
}