UNPKG

forth

Version:

Forth programming environment

1,314 lines (1,198 loc) 56.7 kB
'use strict'; var def = require('./def'), // jsof = require('jsof'), // generate = require('escodegen').generate, ast = require('./ast'), estraverse = require('estraverse'); var Syntax = estraverse.Syntax; function main (cxt) { // : : CREATE ] DOES> doLIST ; def(':', function () { var w = this.io.word('\\s'); this.setState(1); this.cfs = []; this.lastw = w; this.pos = []; this.label = 1; this.orig = []; this.ast = ast.program(this.pos); }, cxt); // : ; next [ ; IMMEDIATE // : ; POSTPONE EXIT REVEAL POSTPONE [ ; IMMEDIATE def(';', function () { this.setState(0); def(this.lastw, this.ast, this); }, cxt, { immediate: true }); // : [ -1 STATE ! ; IMMEDIATE def('[', function () { this.setState(0); }, cxt, { immediate: true }); // : ] 0 STATE ! ; def(']', function () { this.setState(1); }, cxt); // 6.1.2250 STATE (CORE) // ( -- a-addr ) // a-addr is the address of a cell containing the compilation-state flag. // STATE is true when in compilation state, false otherwise. The true value in // STATE is non-zero, but is otherwise implementation-defined. Only the // following standard words alter the value in STATE: : (colon), ; (semicolon), // ABORT, QUIT, :NONAME, [ (left-bracket), and ] (right-bracket). // Note: A program shall not directly alter the contents of STATE. def('state', function () { this.dpush(this.stateAddr()); // TODO should be real state memory cell }, cxt); // 6.1.2033 POSTPONE (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( “<spaces>name” -- ) // Skip leading space delimiters. Parse name delimited by a space. // Find name. Append the compilation semantics of name to the current // definition. An ambiguous condition exists if name is not found. def('postpone', function () { var w = this.io.word('\\s'); var wordLink = this.O[w.toLowerCase()]; if (wordLink) { this.pos.push({ "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "MemberExpression", "computed": false, "object": { "type": "MemberExpression", "computed": false, "object": { "type": "ThisExpression" }, "property": { "type": "Identifier", "name": "pos" } }, "property": { "type": "Identifier", "name": "push" } }, "arguments": [ { "type": "ObjectExpression", "properties": [ { "type": "Property", "key": { "type": "Literal", "value": "type", "raw": "\"type\"" }, "computed": false, "value": { "type": "Literal", "value": "ExpressionStatement", "raw": "\"ExpressionStatement\"" }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "expression", "raw": "\"expression\"" }, "computed": false, "value": { "type": "ObjectExpression", "properties": [ { "type": "Property", "key": { "type": "Literal", "value": "type", "raw": "\"type\"" }, "computed": false, "value": { "type": "Literal", "value": "CallExpression", "raw": "\"CallExpression\"" }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "callee", "raw": "\"callee\"" }, "computed": false, "value": { "type": "ObjectExpression", "properties": [ { "type": "Property", "key": { "type": "Literal", "value": "type", "raw": "\"type\"" }, "computed": false, "value": { "type": "Literal", "value": "MemberExpression", "raw": "\"MemberExpression\"" }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "computed", "raw": "\"computed\"" }, "computed": false, "value": { "type": "Literal", "value": true, "raw": "true" }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "object", "raw": "\"object\"" }, "computed": false, "value": { "type": "ObjectExpression", "properties": [ { "type": "Property", "key": { "type": "Literal", "value": "type", "raw": "\"type\"" }, "computed": false, "value": { "type": "Literal", "value": "ThisExpression", "raw": "\"ThisExpression\"" }, "kind": "init", "method": false, "shorthand": false } ] }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "property", "raw": "\"property\"" }, "computed": false, "value": { "type": "ObjectExpression", "properties": [ { "type": "Property", "key": { "type": "Literal", "value": "type", "raw": "\"type\"" }, "computed": false, "value": { "type": "Literal", "value": "Literal", "raw": "\"Literal\"" }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "value", "raw": "\"value\"" }, "computed": false, "value": { "type": "Literal", "value": wordLink.index, "raw": wordLink + '' }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "raw", "raw": "\"raw\"" }, "computed": false, "value": { "type": "Literal", "value": wordLink.index, "raw": wordLink.index + '' }, "kind": "init", "method": false, "shorthand": false } ] }, "kind": "init", "method": false, "shorthand": false } ] }, "kind": "init", "method": false, "shorthand": false }, { "type": "Property", "key": { "type": "Literal", "value": "arguments", "raw": "\"arguments\"" }, "computed": false, "value": { "type": "ArrayExpression", "elements": [] }, "kind": "init", "method": false, "shorthand": false } ] }, "kind": "init", "method": false, "shorthand": false } ] } ] } }); } else { cxt.log('word: ' + w + ' is not defined'); throw new Error; } }, cxt, { immediate: true }); // 6.1.1380 EXIT (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Execution: ( -- ) ( R: nest-sys -- ) // Return control to the calling definition specified by nest-sys. // Before executing EXIT within a do-loop, a program shall discard // the loop-control parameters by executing UNLOOP. def('exit', function () { this.pos.push({ type: Syntax.ReturnStatement, argument: null }); }, cxt, { immediate: true }); /* if (dpop()) { // IF ; } // THEN ; L1: { if (dpop()) { // IF ; break L1; // ELSE } ; } // THEN ; L2: do { // BEGIN ; } while(true) // AGAIN ; L3: do { // BEGIN ; } while(!dpop()) // UNTIL ; L5: do { // BEGIN ; if (!dpop) { break L5; }// WHILE ; } while(true) // REPEAT ; L9: { L7: { L8: do { // BEGIN ; if(!dpop) { break L7; } // WHILE ; if(!dpop) { break L8; } // WHILE ; } while(true) // REPEAT ; break L9; // ELSE } ; } // THEN ; */ // 6.1.1700 IF (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: -- orig ) // Put the location of a new unresolved forward reference orig onto // the control flow stack. Append the run-time semantics given below // to the current definition. The semantics are incomplete until orig // is resolved, e.g., by THEN or ELSE. // Run-time: ( x -- ) // If all bits of x are zero, continue execution at the location // specified by the resolution of orig. def('if', function () { var oldpos = this.pos; this.cfs.push(oldpos); this.orig.push(this.label); this.pos = [ { type: Syntax.IfStatement, test: ast.notdpop(), consequent: { type: Syntax.BreakStatement, label: { type: Syntax.Identifier, name: 'L' + this.label } }, alternate: null } ]; oldpos.push({ type: Syntax.LabeledStatement, label: { type: Syntax.Identifier, name: 'L' + this.label }, body: { type: Syntax.BlockStatement, body: this.pos } }); this.label++; // cxt.log(jsof.s(this.ast, {ansi: true})); // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // def('if', function () { // var oldpos = this.pos; // this.pos = []; // oldpos.push({ // type: Syntax.IfStatement, // test: ast.dpop(), // consequent: { // type: Syntax.BlockStatement, // body: this.pos // }, // alternate: null // }); // this.cfs.push(oldpos); // }, cxt, { immediate: true }); // 6.1.2270 THEN (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: orig -- ) // Append the run-time semantics given below to the current definition. // Resolve the forward reference orig using the location of the appended // run-time semantics. // Run-time: ( -- ) // Continue execution. def('then', function () { this.pos = this.cfs.pop(); this.orig.pop(); // cxt.log(jsof.s(this.ast, {ansi: true})); // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 6.1.1310 ELSE (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: orig1 -- orig2 ) // Put the location of a new unresolved forward reference orig2 onto // the control flow stack. Append the run-time semantics given below // to the current definition. The semantics will be incomplete until // orig2 is resolved (e.g., by THEN). Resolve the forward reference // orig1 using the location following the appended run-time semantics. // Run-time: ( -- ) // Continue execution at the location given by the resolution of orig2. // : ELSE ( orig1 -- orig2 / -- ) // \ resolve IF supplying alternate execution // POSTPONE AHEAD \ unconditional forward branch orig2 // 1 CS-ROLL \ put orig1 back on top // POSTPONE THEN \ resolve forward branch from orig1 // ; IMMEDIATE def('else', function () { var newlabel = this.label; this.label++; this.pos.push({ type: Syntax.BreakStatement, label: { type: Syntax.Identifier, name: 'L' + newlabel } }); var oldlabel = this.orig.pop(); this.orig.push(newlabel); var oldlimbs = this.cfs[this.cfs.length - 1]; var lastoldlimb = oldlimbs.pop(); lastoldlimb.label.name = 'L' + oldlabel; this.pos = [lastoldlimb]; oldlimbs.push({ type: Syntax.LabeledStatement, label: { type: Syntax.Identifier, name: 'L' + newlabel }, body: { type: Syntax.BlockStatement, body: this.pos } }); // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 6.1.0760 BEGIN (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: -- dest ) // Put the next location for a transfer of control, dest, onto the control // flow stack. Append the run-time semantics given below to the current // definition. // Run-time: ( -- ) // Continue execution. def('begin', function () { var oldpos; oldpos = this.pos; this.cfs.push(oldpos); this.pos = []; oldpos.push({ type: Syntax.LabeledStatement, label: { type: Syntax.Identifier, name: 'L' // + this.label }, body: { type: Syntax.DoWhileStatement, body: { type: Syntax.BlockStatement, body: this.pos }, test: { type: Syntax.Literal, value: true, raw: 'true' } } }); this.label++; // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 6.1.2390 UNTIL (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: dest -- ) // Append the run-time semantics given below to the current definition, // resolving the backward reference dest. // Run-time: ( x -- ) // If all bits of x are zero, continue execution at the location // specified by dest. def('until', function () { this.pos = this.cfs.pop(); this.pos[this.pos.length - 1].body.test = ast.notdpop(); // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 6.1.2430 WHILE (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: dest -- orig dest ) // Put the location of a new unresolved forward reference orig onto the // control flow stack, under the existing dest. Append the run-time // semantics given below to the current definition. The semantics are // incomplete until orig and dest are resolved (e.g., by REPEAT). // Run-time: ( x -- ) // If all bits of x are zero, continue execution at the location // specified by the resolution of orig. // : WHILE ( dest -- orig dest / flag -- ) // \ conditional exit from loops // POSTPONE IF \ conditional forward brach // 1 CS-ROLL \ keep dest on top // ; IMMEDIATE def('while', function () { var oldpos = this.pos; this.cfs.push(oldpos); this.orig.push(this.label); this.pos = [ { type: Syntax.IfStatement, test: ast.notdpop(), consequent: { type: Syntax.BreakStatement, label: { type: Syntax.Identifier, name: 'L' + this.label } }, alternate: null } ]; oldpos.push({ type: Syntax.LabeledStatement, label: { type: Syntax.Identifier, name: 'L' // + this.label }, body: { type: Syntax.BlockStatement, body: this.pos } }); this.label++; // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 6.2.0700 AGAIN (CORE EXT) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: dest -- ) // Append the run-time semantics given below to the current definition, // resolving the backward reference dest. // Run-time: ( -- ) // Continue execution at the location specified by dest. // If no other control flow words are used, any program // code after AGAIN will not be executed. def('again', function () { this.pos = this.cfs.pop(); // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 6.1.2140 REPEAT (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: orig dest -- ) // Append the run-time semantics given below to the current definition, // resolving the backward reference dest. Resolve the forward reference // orig using the location following the appended run-time semantics. // Run-time: ( -- ) // Continue execution at the location given by dest. // : REPEAT ( orig dest -- / -- ) // \ resolve a single WHILE and return to BEGIN // POSTPONE AGAIN \ uncond. backward branch to dest // POSTPONE THEN \ resolve forward branch from orig // ; IMMEDIATE def('repeat', function () { this.pos = this.cfs.pop(); var last = this.pos[this.pos.length - 1]; last.label.name = 'L' + this.label; this.label++; this.pos = this.cfs.pop(); var last = this.pos[this.pos.length - 1]; last.label.name = 'L' + this.orig.pop(); // cxt.log(generate(this.ast)); }, cxt, { immediate: true }); // 15.6.2.0702 AHEAD (TOOLS EXT) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( C: -- orig ) // Put the location of a new unresolved forward reference orig onto the // control flow stack. Append the run-time semantics given below to the // current definition. The semantics are incomplete until orig is resolved // (e.g., by THEN). // Run-time: ( -- ) // Continue execution at the location specified by the resolution of orig. // 6.1.1780 LITERAL (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( x -- ) // Append the run-time semantics given below to the current definition. // Run-time: ( -- x ) // Place x on the stack. def('literal', function () { this.pos.push(ast.dpush(this.dpop())); }, cxt, { immediate: true }); // 6.1.2520 [CHAR] “bracket-char” (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( “<spaces>name” -- ) // Skip leading space delimiters. Parse name delimited by a space. // Append the run-time semantics given below to the current definition. // Run-time: ( -- char ) // Place char, the value of the first character of name, on the stack. def('[char]', function () { var w = this.io.word('\\s'); this.pos.push(ast.dpush(w.charCodeAt(0))); }, cxt, { immediate: true }); // 6.1.2510 ['] “bracket-tick” (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( “<spaces>name” -- ) // Skip leading space delimiters. Parse name delimited by a space. Find name. // Append the run-time semantics given below to the current definition. // An ambiguous condition exists if name is not found. // Run-time: ( -- xt ) // Place name’s execution token xt on the stack. The execution token returned // by the compiled phrase “['] X ” is the same value returned by “' X ” outside // of compilation state. def('[\']', function () { var w = this.io.word('\\s'); var wordLink = this.O[w.toLowerCase()]; if (wordLink) { this.pos.push(ast.dpush(wordLink.index)); } else { cxt.log('word: ' + w + ' is not defined'); throw new Error; } }, cxt, { immediate: true }); // 6.1.2165 S" “s-quote” (CORE) // Interpretation: Interpretation semantics for this word are undefined. // Compilation: ( “ccc<quote>” -- ) // Parse ccc delimited by " (double-quote). Append the run-time semantics // given below to the current definition. // Run-time: ( -- c-addr u ) // Return c-addr and u describing a string consisting of the characters // ccc. A program shall not alter the returned string. def('s"', function () { var w = this.io.parse('"'); var i, ilen, here; here = this.here; for (i = 0, ilen = w.length; i < ilen; i++) { this.MEMi8[here + i] = w.charCodeAt(i); } this.here += ilen; this.pos.push(ast.dpush(here)); // TODO addr this.pos.push(ast.dpush(ilen)); }, cxt, { immediate: true }); // : DO POSTPONE 2>R HERE ; IMMEDIATE def('do', function () { var oldpos; oldpos = this.pos; this.cfs.push(oldpos); this.pos = []; oldpos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpush' } }, arguments: [ { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'dnext' } }, arguments: [] } ] } }); // limit oldpos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpush' } }, arguments: [ { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'dpop' } }, arguments: [] } ] } }); oldpos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'dpop' } }, arguments: [] } }); oldpos.push({ type: Syntax.LabeledStatement, label: { type: Syntax.Identifier, name: 'L' + this.label }, body: { type: Syntax.DoWhileStatement, body: { type: Syntax.BlockStatement, body: this.pos }, test: { type: Syntax.Literal, value: true, raw: 'true' } } }); this.label++; }, cxt, { immediate: true }); def('?do', function () { var oldpos; oldpos = this.pos; this.cfs.push(oldpos); this.pos = []; oldpos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpush' } }, arguments: [ { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'dnext' } }, arguments: [] } ] } }); // limit oldpos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpush' } }, arguments: [ { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'dpop' } }, arguments: [] } ] } }); oldpos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'dpop' } }, arguments: [] } }); oldpos.push({ type: Syntax.LabeledStatement, label: { type: Syntax.Identifier, name: 'L' + this.label }, body: { type: Syntax.DoWhileStatement, body: { type: Syntax.BlockStatement, body: this.pos }, test: { type: Syntax.Literal, value: true, raw: 'true' } } }); this.pos.push({ type: Syntax.IfStatement, test: { type: Syntax.BinaryExpression, operator: '===', left: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rtop' } }, arguments: [] }, right: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rnext' } }, arguments: [] } }, consequent: { type: Syntax.BlockStatement, body: [ { type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpop' } }, arguments: [] } }, { type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpop' } }, arguments: [] } }, { type: Syntax.BreakStatement, label: null } ] }, alternate: null }); this.label++; }, cxt, { immediate: true }); def('loop', function () { this.pos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpush' } }, arguments: [ { type: Syntax.BinaryExpression, operator: '+', left: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpop' } }, arguments: [] }, right: { type: Syntax.Literal, value: 1, raw: '1' } } ] } }); this.pos = this.cfs.pop(); this.pos[this.pos.length - 1].body.test = { type: Syntax.BinaryExpression, operator: '!==', left: { type: Syntax.BinaryExpression, operator: '>>>', left: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rtop' } }, arguments: [] }, right: { type: Syntax.Literal, value: 0, raw: '0' } }, right: { type: Syntax.BinaryExpression, operator: '>>>', left: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rnext' } }, arguments: [] }, right: { type: Syntax.Literal, value: 0, raw: '0' } } }; this.pos.push({ type: Syntax.ExpressionStatement, expression: ast.rpop() }); this.pos.push({ type: Syntax.ExpressionStatement, expression: ast.rpop() }); }, cxt, { immediate: true }); def('+loop', function () { this.pos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rpush' } }, arguments: [ { type: Syntax.BinaryExpression, operator: '+', left: ast.rpop(), right: ast.dpop() } ] } }); this.pos = this.cfs.pop(); this.pos[this.pos.length - 1].body.test = { type: Syntax.BinaryExpression, operator: '!==', left: { type: Syntax.BinaryExpression, operator: '>>>', left: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rtop' } }, arguments: [] }, right: { type: Syntax.Literal, value: 0, raw: '0' } }, right: { type: Syntax.BinaryExpression, operator: '>>>', left: { type: Syntax.CallExpression, callee: { type: Syntax.MemberExpression, computed: false, object: { type: Syntax.ThisExpression }, property: { type: Syntax.Identifier, name: 'rnext' } }, arguments: [] }, right: { type: Syntax.Literal, value: 0, raw: '0' } } }; this.pos.push({ type: Syntax.ExpressionStatement, expression: ast.rpop() }); this.pos.push({ type: Syntax.ExpressionStatement, expression: ast.rpop() }); }, cxt, { immediate: true }); def('unloop', function () { this.pos.push({ type: Syntax.ExpressionStatement, expression: ast.rpop() }); this.pos.push({ type: Syntax.ExpressionStatement, expression: ast.rpop() }); }, cxt, { immediate: true }); def('leave', function () { this.pos.push({ type: Syntax.ExpressionStatement, expression: { type: Syntax.CallExpression,