Source: src/hql/transpiler/utils/validation-helpers.ts, src/hql/transpiler/syntax/data-structure.ts, src/hql/transpiler/syntax/function.ts, src/hql/transpiler/syntax/js-interop.ts, src/hql/transpiler/pipeline/hql-ast-to-hql-ir.ts, src/hql/transpiler/pipeline/ir-to-typescript.ts
The spread operator expands arrays and objects in place. It has two syntactic forms:
...identifier -- spread a variable by name(... expression) -- spread an arbitrary expressionBoth forms work in arrays, function calls, method calls, and objects.
...identifierSpreads the value of a named variable.
(let arr [1 2])
[...arr 3] ;; => [1, 2, 3]
(fn add [x y] (+ x y))
(let args [1 2])
(add ...args) ;; => 3
(... expression)Spreads the result of any expression. This allows spreading inline literals, function call results, and computed values.
[(... [1 2]) 3] ;; => [1, 2, 3]
(fn getItems [] [1 2])
[(... (getItems)) 3] ;; => [1, 2, 3]
(let arr [1 2])
[(... (map (=> (* $0 2)) arr)) 99] ;; => [2, 4, 99]
Spread elements inside array literals ([...]). Supports start, middle, end positions and multiple spreads.
[...arr 3 4] ;; spread at start
[1 ...arr 4] ;; spread in middle
[1 2 ...arr] ;; spread at end
[0 ...a 3 4 ...b 7] ;; multiple spreads
[1 ...[] 2] ;; empty array (no effect)
Compiles to JavaScript:
[...arr, 3, 4]
Spread arguments in function calls. When spread arguments are present, arity validation is skipped (checked at runtime).
(add ...args) ;; spread all arguments
(add 1 2 ...rest) ;; mixed positional and spread
(sum ...a ...b) ;; multiple spreads
Compiles to JavaScript:
add(...args)
add(1, 2, ...rest)
sum(...a, ...b)
Spread works in both dot-notation method calls and js-call form.
;; dot notation
(arr .push ...items)
(arr .push 1 ...items 4)
;; js-call form
(js-call arr "push" ...items)
(js-call arr "push" (... [1 2 3]))
Spread inside object literals using hash-map with spread. When spread is present, the transpiler generates a native JS object expression instead of a __hql_hash_map call.
(hash-map "a" 1 ...obj "b" 2) ;; => {a: 1, ...obj, b: 2}
(hash-map (... (hash-map "a" 1)) "b" 2) ;; list form in object
Curly-brace syntax also works:
{...obj "a": 1}
{"a": 1 ...obj "d": 4}
{...a ...b "c": 3}
{...obj "a": 99} ;; literal 99 overwrites obj's a
{"a": 1 ...obj} ;; obj's a overwrites literal
Compiles to JavaScript:
{ a: 1, ...obj, b: 2 }
Two IR node types handle spread:
IRSpreadElement (type SpreadElement): Used in arrays and function/method call arguments. Has argument: IRNode.IRSpreadAssignment (type SpreadAssignment): Used in object properties. Has expression: IRNode.Both are defined in src/hql/transpiler/type/hql_ir.ts.
All spread handling is centralized in src/hql/transpiler/utils/validation-helpers.ts:
isSpreadOperator(node): Returns true for both ...identifier symbols and (... expr) lists.transformSpreadOperator(node, ...): Returns IRSpreadElement for use in arrays and function calls.transformObjectSpreadOperator(node, ...): Returns IRSpreadAssignment for use in objects.These are called from:
data-structure.ts (transformVector for array spread, transformHashMap for object spread)function.ts (transformArgsWithSpread and transformStandardFunctionCall for function call spread)js-interop.ts (for method call spread in dot-notation and js-call)hql-ast-to-hql-ir.ts (for spread in generic call expressions)In ir-to-typescript.ts:
generateSpreadElement: Emits ... followed by the argument expression.SpreadAssignment properties and emits ... followed by the expression....(expr) symbol form is not supported -- use (... (expr)) list form instead for expression spread.Test file: tests/unit/syntax-spread-operator.test.ts
.push)[(... [1 2]) 3][(... (getItems)) 3](hash-map (... (hash-map "a" 1)) "b" 2)js-call method with spread