UNPKG

microvium

Version:

A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.

141 lines 6.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.traverseChildren = void 0; const utils_1 = require("../utils"); const common_1 = require("./common"); /* * I tried using `@babel/traverse` but I find that the type signatures are not * strong enough to do what I want to do, and it seemed not to give much control * over whether to iterate deeper or not at any particular node. This * `traverseChildren` function is my solution. It's a function which simply * calls the callback for each child of the given node. It's not recursive -- it * requires that the callback call traverseAST if it wishes to traverse deeper. * This gives full control to the callback about when to traverse vs when to * override the traversal with custom behavior. * * The intended way to use this is for the callback to be a function with a * switch statement to define special handling for chosen node types, and then a * `default` path that calls traverseAST. * * The cursor is just used for reporting errors. * * Note: In the case of identifiers, this function only calls `f` if the * identifier is a variable reference. For example, in the member expression * `o.p`, `o` is a variable reference, but `p` is not. In `var v`, `v` is not a * variable reference -- it is considered part of the variable declaration. The * reason for this is so that the tag `Identifier` does not need context to * understand. */ function traverseChildren(cur, node, callback, context) { (0, common_1.visitingNode)(cur, node); const f = (n) => { (0, common_1.visitingNode)(cur, n); callback(n, context); (0, common_1.visitingNode)(cur, node); // Back to parent }; const n = node; switch (n.type) { case 'ArrayExpression': return n.elements.forEach(e => e && f(e)); case 'AssignmentExpression': return f(n.left), f(n.right); case 'BinaryExpression': return f(n.left), f(n.right); case 'BlockStatement': return n.body.forEach(f); case 'CallExpression': return f(n.callee), n.arguments.forEach(f); case 'ConditionalExpression': return f(n.test), f(n.consequent), f(n.alternate); case 'DoWhileStatement': return f(n.test), f(n.body); case 'ExpressionStatement': return f(n.expression); case 'NewExpression': return f(n.callee), n.arguments.forEach(f); case 'ForStatement': return n.init && f(n.init), n.test && f(n.test), n.update && f(n.update), f(n.body); case 'IfStatement': return f(n.test), f(n.consequent), n.alternate && f(n.alternate); case 'LogicalExpression': return f(n.left), f(n.right); case 'ObjectExpression': return n.properties.forEach(f); case 'Program': return n.body.forEach(f); case 'ReturnStatement': return n.argument && f(n.argument); case 'ThrowStatement': return n.argument && f(n.argument); case 'UnaryExpression': return f(n.argument); case 'UpdateExpression': return f(n.argument); case 'VariableDeclaration': return n.declarations.forEach(f); case 'WhileStatement': return f(n.test), f(n.body); case 'ExportNamedDeclaration': return f(n.declaration ?? (0, utils_1.unexpected)()); case 'ObjectProperty': return (n.computed ? f(n.key) : undefined), f(n.value); case 'TemplateLiteral': return n.expressions.forEach(f); case 'TryStatement': return f(n.block), n.handler && f(n.handler), n.finalizer && f(n.finalizer); case 'CatchClause': return f(n.body); case 'AwaitExpression': return f(n.argument); case 'ImportDeclaration': return; case 'Identifier': return; case 'StringLiteral': return; case 'ThisExpression': return; case 'BooleanLiteral': return; case 'NullLiteral': return; case 'NumericLiteral': return; case 'BreakStatement': return; case 'SwitchStatement': { f(n.discriminant); for (const { test, consequent } of n.cases) { test && f(test); consequent.forEach(f); } break; } case 'MemberExpression': { f(n.object); // Note: if the member access is of the form `o.p` then `p` here is not // iterated because the identifier `p` is in the scope of `o`. In the case // of `o[p]`, `p` is a variable reference to the corresponding variable in // the scope that the expression is executing. if (n.computed) { f(n.property); } return; } case 'VariableDeclarator': { // Note: variable IDs are intentionally not iterated, because contexts that // use the ID will not be looking to visit "Identifier" nodes but rather // just "VariableDeclarator" nodes. n.init && f(n.init); return; } case 'ArrowFunctionExpression': case 'FunctionExpression': case 'FunctionDeclaration': { for (const param of n.params) { if (param.type !== 'Identifier') { // Note: for non-identifier parameters, we would need to recurse on // the initializers, but no the identifiers (for the same reason as noted above for VariableDeclarator) return (0, common_1.compileError)(cur, 'Not supported'); } } return f(n.body); } case 'ClassExpression': case 'ClassDeclaration': { n.superClass && f(n.superClass); n.body.body.forEach(f); return; } case 'ClassMethod': { if (n.computed) f(n.key); for (const param of n.params) { if (param.type !== 'Identifier') { // Note: for non-identifier parameters, we would need to recurse on // the initializers, but no the identifiers (for the same reason as noted above for VariableDeclarator) return (0, common_1.compileError)(cur, 'Not supported'); } } f(n.body); break; } case 'ClassProperty': { if (n.computed) f(n.key); if (n.value) f(n.value); break; } default: (0, common_1.compileErrorIfReachable)(cur, n); } } exports.traverseChildren = traverseChildren; //# sourceMappingURL=traverse-ast.js.map