NR is primarily about inter-Entity links that build a Web of Entities. NR talks hyphenated extensions and uids and fixed graph structures unification, references, logic, AND/OR/NOT, rewrites, wildcards. Domain-independent logic, unaware of NT domains
JSON unification and production rules; schema language
Entities' behaviour can be defined by JSON-based rules over their own state and the visible state of other Entities
Psi Logic but 2-way links and Entity-centric
Entity Rule (NR) notation applies only to the "content": part of the Entity, not the "headers": part. All NR JSON extensions are flagged by having hyphens either side. NR is fully-compliant JSON, extended in these ways:
{ "-id-": "-14-", "tag": "value" } == -14-{ "tag": "value" }
[ "-id-:-14-", "value" ] == -14-[ "value" ]
[ "-id-", "-14-", "value" ] == -14-[ "value" ]
Using it:
"a": { "-id-":"-14-", "b": "c" }
"d": { "e": "-14-" }
Now "d" looks like:
"d": { "e": { "b": "c" } }
UID: "sub-json": "-123-456-789-" URL: "sub-json": "-http://host/path/u/123-456-789.js-"This is semantically equivalent to transparently including the sub-JSON "content": in place of the link. URLs are distinguished from UIDs/ids by having any slash.
A whole JSON structure with a uid can have an optional =-uid-= in a similar way to =-id-=:
{ "-uid-":"-1d7-a14-", "tag": "value" } == -1d7-a14-{ "tag": "value" }
The following are all equivalent as far as NR is concerned (this
shows how individual Entities and their header/content pair are
effectively invisible to the NR graph; content parts are sewn together):
{ "headers": { "UID": "9a1c3340-fe3328" },
"content": { "x": "-13cea-0aa281-" }
}
{ "headers": { "UID": "13cea-0aa281" },
"content": { "a", "b" }
}
==
{ "headers": { "UID": "9a1c3340-fe3328" },
"content": { "x": { "headers": { "UID": "13cea-0aa281" },
"content": { "a", "b" }
}
}
}
==
{ "headers": { "UID": "9a1c3340-fe3328" },
"content": { "x": { "-uid-":"-13cea-0aa281-", "a", "b" } }
}
==
{ "headers": { "UID": "9a1c3340-fe3328" },
"content": { "x": { "-id-":"-1-", "a", "b" } }
}
==
{ "headers": { "UID": "9a1c3340-fe3328" },
"content": { "x": { "a", "b" } }
}
==
{ "x": { "a", "b" } }
If a sub-JSON needs to be transparently included 'higher up', use "-etc-":
"a": 1, "b": 2, "-etc-": "-123-456-"if 123-456 is:
"b": 3, "c": 4results in:
"a": 1 "b": 3, "c": 4.. the 'up'ed JSON overwrites any pre-existing elements.
Again, "-etc-" links are to "content": parts. Merging a non-hash into a hash is meaningless.
Lists can also be up'd:
111-111-111: [ "-123-321-", "-4114-414-", "-4577-242-" , "-772-4542-" ]Can be split like this:
111-111-111: [ "-123-321-", "-4114-414-", "-etc-:-111-111-222-" ] 111-111-222: [ "-4577-242-" , "-772-4542-" ]If a list is needed explicitly, put it immediately after the '-etc-' string:
[ "-123-321-", "-4114-414-", "-etc-", [ "-4577-242-" , "-772-4542-" ] ]Now sub-lists can be 'pulled out' and referred to locally inside the structure:
"a": [ "-123-321-", "-4114-414-", "-etc-", [ "-id-:-x-", "-4577-242-" , "-772-4542-" ] ], "b": "-x-"Looks like:
"a": [ "-123-321-", "-4114-414-", "-4577-242-" , "-772-4542-" ] "b": [ "-4577-242-" , "-772-4542-" ]
Merging a non-list into a list is meaningless, unless it's an ordered hash/object, when the tags are dropped, and only the values inserted.
When something overlays another JSON, or an edit suggestion is sent, it uses the JSON unification system described here. This syntax is used to edit, decorate or 'subclass' another JSON resource.
In general, when two JSONs are paired for unification, the match succeeds if they don't differ, only add:
{ "x": [ 1, 2, 3, 4 ] } <=> { "x": [ 2, 4 ], "y": 3 }
= { "x": [ 1, 2, 3, 4 ], "y": 3 }
So in lists, the elements of the smaller side must appear in the
larger, in the right order. In objects/hashes, the side with fewer
tags has to have corresponding, recursively unifying tags in the
side with more tags.
There is a wildcard syntax for cases where the whole set of values is unknown but needs to be represented to allow extension. Here, '<=>' means 'try to match or unify':
{ "x": [ 1, 2, 3, 4 ], "v": 3 } <=> { "*":"*", "y": 3 }
= { "x": [ 1, 2, 3, 4 ], "v": 3, "y": 3 }
{ "x": [ 1, 2, 3, 4 ], "v": "*" } <=> { "x": [ "*", 2, "*:*", 5 ], "y": 3, "v": 3 }
= { "x": [ 1, 2, 3, 4, 5 ], "y": 3, "v": 3 }
So ="*":"*"= and ="*:*"= match any number (including zero) of
unknown tags and values in hashes/objects and lists respectively.
Note that ="*"= means something has to be there but it only 'consumes' one, it doesn't account for all like ="*:*"= does. If more than one exists, the match fails and the list can't extend:
[ 0, 1, 2, 3, 4 ] <=> [ "*", 2, "*" ] matches - at least one for each "*"
[ 0, 1, 2, 3, 4 ] <=> [ "*", 2, "*", 5 ] fails to match or extend
[ 0, 1, 2, 3, 4 ] <=> [ "*:*", 2, "*", "*", 5 ] matches and extends
As a value, ="*"= matches hashes, lists, strings, numbers and booleans.
Use ={}= or =[]= to wildcard just hashes and lists, respectively.
Use "/../" to match strings using a regular expression (Perl /
Java / Javascript style).
So just adding values is easy in lists and objects using the wildcard:
{ "*":"*", "y": 23 } - add y: 23
[ 23, "*:*" ] - prepend 23
[ "*:*", 23 ] - append 23
Filling in a placeholder:
{ "-order-": [ "a", "b", "c" ],
"a": { "x": "y" },
"b": { },
"c": "w"
}
<=>
{ "-order-": [ "b" ],
"b": { "q": "r" }
}
=
{ "-order-": [ "a", "b", "c" ],
"a": { "x": "y" },
"b": { "q": "r" },
"c": "w"
}
Find all matching in a list and add something:
[ { "name": "/.*smith/", "*":"*", "ref": 23 } ]
[ .., { "name": "joe smith", "age": 34 }, .., { "name": "sam smith", "age": 17 }, .. ]
[ .., { "name": "joe smith", "age": 34, "ref": 23 }, .., { "name": "sam smith", "age": 17, "ref": 23 }, .. ]
Find all matching in a hash and add something:
{ "*": { "name": "/.*smith/", "*":"*", "ref": 23 } }
{ "abc": { "name": "joe smith", "age": 34 },
"ghi": { "name": "joe brown", "age": 55 },
"def": { "name": "sam smith", "age": 17 } }
{ "abc": { "name": "joe smith", "age": 34, "ref": 23 },
"ghi": { "name": "joe brown", "age": 55 },
"def": { "name": "sam smith", "age": 17, "ref": 23 } }
A unification extension will fail if there is any mismatch, or if
the value being extended is read-only and doesn't already have the
extension.
A more powerful way to edit JSON resources is the generic edit syntax, which is a rewrite or 'production' system based on the above unification syntax.
Generic edit operator: =[ lhs, "->", rhs ]=. Operator is a two- or three-element list with =list[1]= = ="->"=; lhs must match or be null to match the start of a list or a non-existent hash element; rhs gives replacement; rhs may be empty or null, meaning delete.
Here are generic edit forms of some examples:
[ [ "*", "->", 23 ] ]
- change all in list to 23
[ [ 1, "->", 0 ] ]
- set all 1s to 0s
[ [ "*", "->", 23 ], "*:*" ]
- change first in list to 23, leave rest of list unaltered
[ "*:*", [ "*", "->", 23 ] ]
- change last in list to 23, leave rest of list unaltered
[ [ "*", "->", 45 ], "*", "*", [ "*:*", "->" ] ]
- replace 45 in first place, keep second/third (must be present), drop any fourth onwards
[ "*", [ "*", "->", 45 ] ]
- replace with 45 everywhere except first value
[ [ 45, "->", "*"] ]
- no-op: matches 45s and leaves them alone
[ { "name": "mo", "age": [ "*", "->", 23 ] } ]
- find mo, set her age to 23, leave other values alone
[ [ { "name": "mo", "age": "*" }, "->", { "name": "mo", "age": 23 } ] ]
- same thing, showing how edit operator can be 'refactored'
[ { "name": "bo" }, { "name": "mo", "age": [ "*", "->", 23 ] } ]
- find and update mo, but only if she follows bo anywhere in the list
[ 0, "*", 22, [ 3, "->", 33 ], [ 3, "->" ], "*", 55 ]
- then [ 1, 3, 3, 4 ] -> [ 0, 1, 22, 33, 4, 55 ]:
Clearly, a rewrite will fail if an update to any read-only data is
attempted that changes their value.
[2278]