Skip to content

Commit 1ef286a

Browse files
committed
rest params
1 parent 7d0f43c commit 1ef286a

4 files changed

Lines changed: 63 additions & 5 deletions

File tree

src/arc/compiler/emit.gleam

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import arc/vm/opcode.{
99
IrArraySpread, IrAsyncYieldStarNext, IrAsyncYieldStarResume, IrAwait, IrBinOp,
1010
IrCallApply, IrCallConstructor, IrCallConstructorApply, IrCallMethod,
1111
IrCallMethodApply, IrCallSuper, IrCallSuperApply, IrCreateArguments,
12-
IrDeclareGlobalLex, IrDeclareGlobalVar, IrDefineAccessor,
12+
IrCreateRestArray, IrDeclareGlobalLex, IrDeclareGlobalVar, IrDefineAccessor,
1313
IrDefineAccessorComputed, IrDefineField, IrDefineFieldComputed, IrDefineMethod,
1414
IrDefineMethodComputed, IrDeleteElem, IrDeleteField, IrDup, IrForInNext,
1515
IrForInStart, IrGetAsyncIterator, IrGetElem, IrGetElem2, IrGetField,
@@ -1300,6 +1300,18 @@ fn class_body_references_arguments(body: List(ast.ClassElement)) -> Bool {
13001300
})
13011301
}
13021302

1303+
/// Split off a trailing rest parameter. Returns the fixed params (in order)
1304+
/// and the rest target pattern (the binding inside `...`), if present. A rest
1305+
/// element is only valid as the last parameter, so we only check the tail.
1306+
fn split_trailing_rest(
1307+
params: List(ast.Pattern),
1308+
) -> #(List(ast.Pattern), Option(ast.Pattern)) {
1309+
case list.reverse(params) {
1310+
[ast.RestElement(inner), ..rev_fixed] -> #(list.reverse(rev_fixed), Some(inner))
1311+
_ -> #(params, None)
1312+
}
1313+
}
1314+
13031315
/// Compile a function body into a CompiledChild.
13041316
fn compile_function_body(
13051317
parent: Emitter,
@@ -1342,9 +1354,17 @@ fn compile_function_body(
13421354
False -> emit_op(e, DeclareThis)
13431355
}
13441356

1357+
// A trailing rest parameter (`...rest`) is bound separately from the fixed
1358+
// params: the fixed ones bind positionally (arity counts only them, so
1359+
// build_locals leaves the rest slot undefined), then IrCreateRestArray
1360+
// collects the leftover args into an Array. `arity` excludes the rest param,
1361+
// which also gives the correct `fn.length` (§15.1.5).
1362+
let #(fixed_params, rest_param) = split_trailing_rest(params)
1363+
let arity = list.length(fixed_params)
1364+
13451365
// Phase 1: Declare parameters (identifier or synthetic for destructuring)
13461366
let #(e, destructured_params_rev) =
1347-
list.index_fold(params, #(e, []), fn(acc, param, idx) {
1367+
list.index_fold(fixed_params, #(e, []), fn(acc, param, idx) {
13481368
let #(e, destr) = acc
13491369
case param {
13501370
ast.IdentifierPattern(pname) -> #(
@@ -1393,6 +1413,17 @@ fn compile_function_body(
13931413
emit_destructuring_bind(e, pattern, LetBinding) |> result.unwrap(e)
13941414
})
13951415

1416+
// Phase 2b: Bind the trailing rest parameter, if any. Build the array from
1417+
// the args at `arity` and beyond, then bind it (an identifier, or a nested
1418+
// destructuring target like `...[a, b]`). ParamBinding declares the slot.
1419+
let e = case rest_param {
1420+
None -> e
1421+
Some(rest_target) -> {
1422+
let e = emit_ir(e, IrCreateRestArray(arity))
1423+
emit_destructuring_bind(e, rest_target, ParamBinding) |> result.unwrap(e)
1424+
}
1425+
}
1426+
13961427
// Hoisting for the function body
13971428
let hoisted_vars = collect_hoisted_vars(stmts)
13981429
let lex_names = collect_top_lex_names(stmts)
@@ -1442,7 +1473,7 @@ fn compile_function_body(
14421473

14431474
CompiledChild(
14441475
name:,
1445-
arity: list.length(params),
1476+
arity:,
14461477
code:,
14471478
constants:,
14481479
constants_map:,

src/arc/compiler/resolve.gleam

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import arc/vm/opcode.{
1010
IrArrayPushHole, IrArraySpread, IrAsyncYieldStarNext, IrAsyncYieldStarResume,
1111
IrAwait, IrBinOp, IrBoxLocal, IrCall, IrCallApply, IrCallConstructor,
1212
IrCallConstructorApply, IrCallEval, IrCallMethod, IrCallMethodApply,
13-
IrCallSuper, IrCallSuperApply, IrCreateArguments, IrDeclareEvalVar,
13+
IrCallSuper, IrCallSuperApply, IrCreateArguments, IrCreateRestArray,
14+
IrDeclareEvalVar,
1415
IrDeclareGlobalLex, IrDeclareGlobalVar, IrDefineAccessor,
1516
IrDefineAccessorComputed, IrDefineField, IrDefineFieldComputed, IrDefineMethod,
1617
IrDefineMethodComputed, IrDeleteElem, IrDeleteField, IrDup, IrForInNext,
@@ -310,6 +311,9 @@ fn resolve_ops(
310311
[IrCreateArguments, ..rest] ->
311312
resolve_ops(rest, labels, [opcode.CreateArguments, ..acc])
312313

314+
[IrCreateRestArray(from_index), ..rest] ->
315+
resolve_ops(rest, labels, [opcode.CreateRestArray(from_index), ..acc])
316+
313317
// RegExp
314318
[IrNewRegExp, ..rest] ->
315319
resolve_ops(rest, labels, [opcode.NewRegExp, ..acc])

src/arc/vm/exec/interpreter.gleam

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import arc/vm/opcode.{
1717
type Op, Add, ArrayFrom, ArrayFromWithHoles, ArrayPush, ArrayPushHole,
1818
ArraySpread, AsyncYieldStarNext, AsyncYieldStarResume, Await, BinOp, BoxLocal,
1919
Call, CallApply, CallConstructor, CallConstructorApply, CallEval, CallMethod,
20-
CallMethodApply, CallSuper, CallSuperApply, CreateArguments, DeclareEvalVar,
20+
CallMethodApply, CallSuper, CallSuperApply, CreateArguments, CreateRestArray,
21+
DeclareEvalVar,
2122
DeclareGlobalLex, DeclareGlobalVar, DefineAccessor, DefineAccessorComputed,
2223
DefineField, DefineFieldComputed, DefineMethod, DefineMethodComputed,
2324
DeleteElem, DeleteField, Dup, ForInNext, ForInStart, GetAsyncIterator,
@@ -2738,6 +2739,22 @@ fn step(state: State, op: Op) -> Result(State, #(StepResult, JsValue, State)) {
27382739
)
27392740
}
27402741

2742+
CreateRestArray(from_index) -> {
2743+
// §10.4.4 rest parameter: a plain Array of the call args from
2744+
// `from_index` onward (the params before the rest are bound positionally).
2745+
let rest_args = list.drop(state.call_args, from_index)
2746+
let #(heap, ref) =
2747+
common.alloc_array(state.heap, rest_args, state.builtins.array.prototype)
2748+
Ok(
2749+
State(
2750+
..state,
2751+
heap:,
2752+
stack: [JsObject(ref), ..state.stack],
2753+
pc: state.pc + 1,
2754+
),
2755+
)
2756+
}
2757+
27412758
// -- RegExp literal --
27422759
NewRegExp -> {
27432760
case state.stack {

src/arc/vm/opcode.gleam

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ pub type Op {
204204
/// Reads state.call_args, allocates ArgumentsObject, pushes ref onto stack.
205205
CreateArguments
206206

207+
/// Create a rest-parameter array from the current call's args, taking those
208+
/// at index `from_index` and beyond. Reads state.call_args, allocates a plain
209+
/// Array, pushes ref onto stack.
210+
CreateRestArray(from_index: Int)
211+
207212
// -- RegExp --
208213
/// Pop flags string, pop pattern string -> push new RegExp object.
209214
NewRegExp
@@ -371,6 +376,7 @@ pub type IrOp {
371376
IrAsyncYieldStarResume(next_label: Int)
372377
IrAwait
373378
IrCreateArguments
379+
IrCreateRestArray(from_index: Int)
374380
IrNewRegExp
375381

376382
// -- Global Environment Record --

0 commit comments

Comments
 (0)