Implementation: Parser literal syntax + transpiler (data-structure.ts, get.ts)
HQL provides syntactic sugar for common data structures:
[...] syntax{...} syntax#[...] syntaxArrays and objects support both Lisp style and JavaScript/JSON style. Sets use
HQL-specific #[...] syntax.
// Empty vector
[]
// Vector with elements (Lisp style, space-separated)
[1 2 3 4]
// Vector with elements (JavaScript/JSON style, comma-separated)
[1, 2, 3, 4]
["apple", "banana", "cherry"]
// Mixed types
[1, "hello", true, null]
// Nested vectors
[[1, 2], [3, 4]]
// Spread operator in vectors
(var arr [1, 2])
[0 ...arr 3] // => [0, 1, 2, 3]
// Access by index
(var v ["a", "b", "c"])
(get v 1) // => "b"
// Property access
v.length // => 3
// Empty map
{}
// Map with key-value pairs (Lisp style, symbol keys with colon)
{name: "Alice" age: 30}
{host: "localhost" port: 8080}
// Map with key-value pairs (JavaScript/JSON style)
{"name": "Alice", "age": 30}
{"host": "localhost", "port": 8080}
// Access by key
(var m {"name": "Alice"})
(get m "name") // => "Alice"
// Nested maps
{"user": {"name": "Bob", "id": 123}}
// Mutation
(var m {"count": 10})
(= m.newProp "added")
// Spread operator in hash maps
(var obj {"a": 1, "b": 2})
{...obj, "c": 3} // => {"a": 1, "b": 2, "c": 3}
{"x": 10, ...obj, "y": 20} // => {"x": 10, "a": 1, "b": 2, "y": 20}
// Empty set (HQL-specific syntax)
#[]
// Set with elements
#[1 2 3]
#["red" "green" "blue"]
// Automatic deduplication
(var s #[1, 2, 2, 3, 3, 3])
s.size // => 3
// Membership check
(var colors #["red", "green", "blue"])
(colors.has "green") // => true
// new expression
(new Set [1, 2, 3])
(new Date "2024-01-01")
(new Map)
HQL's parser transforms literal syntax into S-expressions:
[1, 2, 3](vector 1 2 3)[] → (empty-array)parseVector in src/hql/transpiler/pipeline/parser.tstransformVector in data-structure.ts → JS array literal [1, 2, 3][1 ...arr 2] → [1, ...arr, 2] in JS{"x": 10, "y": 20}(hash-map "x" 10 "y" 20){} → (empty-map) macro → (hash-map) → __hql_hash_map()parseMap in src/hql/transpiler/pipeline/parser.tstransformHashMap → __hql_hash_map("x", 10, "y", 20) runtime helper calltransformHashMap → JS object literal {...obj, x: 10}#[1, 2, 3](hash-set 1 2 3)#[] → (empty-set) macro → (hash-set) → new Set([])parseSet in src/hql/transpiler/pipeline/parser.tstransformHashSet → new Set([1, 2, 3]) in JS(new Constructor arg1 arg2)transformNew in data-structure.ts → new Constructor(arg1, arg2) in JS(hash-map ...) macro expands to (__hql_hash_map ...) runtime helper call(empty-map) macro expands to (hash-map) (zero-arg hash-map)(empty-set) macro expands to (hash-set) (zero-arg hash-set)#[...] notationThe get function provides uniform access across collections:
(get collection key) // basic access
(get collection key default) // with default value for missing keys
transformGet in get.ts → __hql_get(collection, key, default?) runtime helper.has.map JS method).filter JS method).reduce JS method)[...arr 3 4], [1 ...arr 4], [1 2 ...arr][...a ...b]{...obj, "a": 1}, {"a": 1, ...obj, "d": 4}{...a, ...b, ...c}spec.mdsrc/hql/transpiler/pipeline/parser.tssrc/hql/transpiler/syntax/data-structure.tssrc/hql/transpiler/syntax/get.tssrc/common/runtime-helper-impl.ts (__hql_hash_map, __hql_get)src/hql/lib/macro/core.hql (hash-map, empty-map, empty-set)See examples.hql for executable examples demonstrating access patterns and disambiguation between property access and function calls.
HQL Source
|
Tokenizer (identifies [, {, #[ tokens)
|
Parser (parseVector, parseMap, parseSet)
|
S-expression: (vector ...), (hash-map ...), (hash-set ...)
|
Macro expansion: empty-map -> (hash-map), empty-set -> (hash-set)
|
Transpiler (transforms to JS)
|
JavaScript:
- vector -> Array literal [...]
- hash-map (no spread) -> __hql_hash_map(...) call
- hash-map (with spread) -> Object literal {...}
- hash-set -> new Set([...])
- new -> new Constructor(...)