enum - May be one of several values
values: %{string => atom} | [string]
static - Will be static value, regardless of what it is in the JSON
value - JSON value without validation or transformation
monomorphic_map - All keys map to one value type
map - Key to value mappings
array - All indices map to one value type
tuple - Fixed length, indices map to individual types
All composing types takes matchers as subtypes.
none - Performs no matching, all values map to a single node type
tagged_map - Subtype must be map. Matches on key in said map
default: :error | :ignore | {:node, n}
tag_array - Subtype must be array of [tag, value]
default: :error | :ignore | {:node, n}
type - Matches on the type of the key
type_mappings: %{string: string_matcher, number: number_matcher, …}
Done with a simple bytecode
{:type, type_mappings: %{string: [child: {:value, []}], null: [child: {:static, [value: “”]}}}
Everything above is done with primitive operations
Operations can operate on the value stack
Certain invariants must be enforced to keep stack sane
Bytecode written on a node-by-node basis
Easy way to preserve topology, what if we want to change it?
We get bytecode-soup, maximally flexible
Less compile-time verification
parse_value - parses a full standard json value and pushes it to stack