UNPKG

tenko

Version:

A "pixel perfect" 100% spec compliant ES2021 JavaScript parser written in JS.

968 lines (929 loc) 39.2 kB
// This is a fork of src/tools/printer.mjs // See it as a rudimentary transformation script of a system that has no plugin. // This is Babel at its core. You just also see the ugly parts :) // The objective of this script is to strip dev artifacts, like all ASSERT expressions, and optimize a few things // that were coded in a very particular pattern. // The script is obviously overkill for most of what it does but that's fine. I may refine and abstract this. Or not. import { $$A_61, $$A_UC_41, $$B_62, $$B_UC_42, $$C_UC_43, $$D_64, $$D_UC_44, $$E_65, $$E_UC_45, $$G_67, $$I_69, $$I_UC_49, $$L_6C, $$L_UC_4C, $$M_6D, $$N_UC_4E, $$O_6F, $$O_UC_4F, $$P_70, $$R_72, $$S_73, $$S_UC_53, $$T_74, $$U_75, $$W_77, $$X_78 } from "../src/charcodes.mjs"; function assert(a, b, desc) { // This is an assert that can be dropped for a build... It confirms hashing assumptions // (Will also be an invaluable tool when adding a new node type ;) if (a !== b) throw new Error('Expected `' + b + '`, got `' + a + '`; ' + (desc || '')); } const SCRUB_OTHERS = process.argv.includes('--no-compat'); // force all occurrences of compatAcorn and compatBabel to false const SCRUB_ERRORS = process.argv.includes('--strip-errors'); // strip error message contents (wip) const NATIVE_SYMBOLS = process.argv.includes('--native-symbols'); // Replace `PERF_$` with `%`? const NO_AST = process.argv.includes('--no-ast'); // drop ast related code from the parser (`AST_*`) function ArrayExpression(node) { assert(node.type, 'ArrayExpression'); return '[' + node.elements.map((n, i) => n === null ? ',' : ($(n) + ((n.type === 'RestElement' || i === node.elements.length - 1) ? '' : ','))).join(' ') + ']' } function ArrayPattern(node) { assert(node.type, 'ArrayPattern'); return '[' + node.elements.map((n, i) => n === null ? ',' : ($(n) + ((n.type === 'RestElement' || i === node.elements.length - 1) ? '' : ','))).join(' ') + ']' } function ArrowFunctionExpression(node) { assert(node.type, 'ArrowFunctionExpression'); let body = $(node.body); if (node.expression || (node.expression === undefined && node.body.type !== 'BlockStatement') && ( node.expression.type !== 'Identifier' // x=>x && node.expression.type !== 'Import' // x=>import() && node.expression.type !== 'Super' // x=>super && node.expression.type !== 'MemberExpression' // x=>x.y && node.expression.type !== 'Literal' // x=>5 && node.expression.type !== 'CallExpression' // x=>x() && node.expression.type !== 'ArrayExpression' // x=>[x] // && node.expression.type !== 'ObjectExpression' // {x} Can not because x=>{} is a block, not objlit && node.expression.type !== 'MetaProperty' // x=>new.target && node.expression.type !== 'TaggedTemplateExpression' // x=>foo`` && node.expression.type !== 'TemplateLiteral' // x=>`foo` && node.expression.type !== 'ThisExpression' // x=>this )) { body = '(' + body + ')'; } if ( node.params.length === 1 && node.params[0].type !== 'AssignmentPattern' && node.params[0].type !== 'ArrayPattern' && node.params[0].type !== 'ObjectPattern' && node.params[0].type !== 'RestElement' ) { return (node.async ? 'async ' : '') + $(node.params[0]) + ' => ' + body; } return (node.async ? 'async ' : '') + '(' + node.params.map($).join(', ') + ') => ' + body; } function AssignmentExpression(node) { assert(node.type, 'AssignmentExpression'); if (node.left.type === 'ObjectPattern') { return '(' + $(node.left) + ' ' + node.operator + ' ' + $(node.right) + ')'; } return $(node.left) + ' ' + node.operator + ' ' + $(node.right); } function AssignmentPattern(node) { assert(node.type, 'AssignmentPattern'); return $(node.left) + ' = ' + $(node.right); } function AwaitExpression(node) { assert(node.type, 'AwaitExpression'); return 'await (' + $(node.argument) + ')'; } function BigIntLiteral(node) { assert(node.type, 'BigIntLiteral'); return node.bigint + 'n'; } function BinaryExpression(node) { assert(node.type, 'BinaryExpression'); let left = $(node.left); switch (node.left.type) { case 'Identifier': case 'Literal': case 'MemberExpression': case 'CallExpression': case 'ArrayExpression': case 'MetaProperty': case 'TaggedTemplateExpression': case 'TemplateLiteral': case 'ThisExpression': break; default: left = '(' + left + ')'; } let right = $(node.right); switch (node.right.type) { case 'Identifier': case 'Literal': case 'MemberExpression': case 'CallExpression': case 'ArrayExpression': case 'ObjectExpression': case 'MetaProperty': case 'TaggedTemplateExpression': case 'TemplateLiteral': case 'ThisExpression': break; default: right = '(' + right + ')'; } return left + ' ' + node.operator + ' ' + right; } function BlockStatement(node) { assert(node.type, 'BlockStatement'); return '{\n' + node.body.map($).join('\n') + '\n}'; } function BooleanLiteral(node) { assert(node.type, 'BooleanLiteral'); return node.value; } function BreakStatement(node) { assert(node.type, 'BreakStatement'); return 'break' + (node.label ? ' ' + $(node.label) : '') + ';'; } function CallExpression(node) { assert(node.type, 'CallExpression'); if (node.callee.type === 'Identifier') { if (node.callee.name.startsWith('PERF_')) { if (NATIVE_SYMBOLS && node.callee.name.startsWith('PERF_$')) { return '%' + node.callee.name.slice('PERF_$'.length) + '(' + node.arguments.map($).join(', ') + ')'; } return '0'; } // The lexer `peek()` is really just a return of a closured variable, and in dev mode some assertions. // So for a build, just replace it with the closured variable instead... if (node.callee.name === 'peek' || node.callee.name === '_readCache') { return 'cache'; // This is the local closure } // The lexer `peeky()` is really just a local comparison. similar to the `peek()` optimization above if (node.callee.name === 'peeky') { // Note: allow `!peeky(x)` return '(cache === ' + $(node.arguments[0]) + ')'; } // `neof()` is really just `pointer < len` if (node.callee.name === 'neof') { // Do not care about !neof() because that's what eof() is for return 'pointer < len'; } // `eof()` is really just `pointer >= len` if (node.callee.name === 'eof') { // Do not care about !eof() because that's what neof() is for return 'pointer >= len'; } if (node.callee.name === 'sansFlag') { // Basically an alias for `(a | b) ^ b`, to unset all bits in a that are set in b return '((' + $w(node.arguments[0]) + ' | ' + $w(node.arguments[1]) + ') ^ ' + $w(node.arguments[1]) + ')'; } if (node.callee.name === 'hasAllFlags') { // Basically an alias for `(flags1 & flags2) === flags2`, to check whether at least all bits in b are set in a return '((' + $w(node.arguments[0]) + ' & ' + $w(node.arguments[1]) + ') === ' + $w(node.arguments[1]) + ')'; } if (node.callee.name === 'hasAnyFlag') { // Basically an alias for `(flags1 & flags2) === flags2`, to check whether at least one bit in b is set in a return '((' + $w(node.arguments[0]) + ' & ' + $w(node.arguments[1]) + ') !== 0)'; } if (node.callee.name === 'hasNoFlag') { // Basically an alias for `(flags1 & flags2) === 0`, to check whether none of the bits in b is set in a return '((' + $w(node.arguments[0]) + ' & ' + $w(node.arguments[1]) + ') === 0)'; } } // Drop error messages // TODO: symbolize them, store them in a local lookup file, build a mechanism to make that all work smoothly if (SCRUB_ERRORS && node.callee.type === 'Identifier') { if (node.callee.name === 'THROW') { return 'THROW(1)'; } if (node.callee.name === 'THROW_TOKEN') { return 'THROW_TOKEN(1)'; } if (node.callee.name === 'regexSyntaxError') { return 'regexSyntaxError(1)'; } if (node.callee.name === 'updateRegexPotentialError') { return 'updateRegexPotentialError(1)'; } if (node.callee.name === 'updateRegexUflagIsIllegal') { return 'updateRegexUflagIsIllegal(' + $(node.arguments[0]) + ', 1)'; } if (node.callee.name === 'updateRegexUflagIsMandatory') { return 'updateRegexUflagIsMandatory(' + $(node.arguments[0]) + ', 1)'; } } if ( node.callee.type === 'Identifier' // prevents `x()` becoming `(x)()`, but also `async({__proto__: 1, __proto__: 2})` || node.callee.type === 'Import' // import() || node.callee.type === 'Super' // super() || node.callee.type === 'MemberExpression' // x.y() || node.callee.type === 'Literal' // 5() Probably an error || node.callee.type === 'CallExpression' // x()() || node.callee.type === 'ArrayExpression' // [x]() Probably an error // || node.callee.type === 'ObjectExpression' // {x}() Probably an error, but unsafe for block anyways || node.callee.type === 'MetaProperty' // new.target() || node.callee.type === 'TaggedTemplateExpression' // foo``() Probably an error || node.callee.type === 'TemplateLiteral' // `foo`() Probably an error || node.callee.type === 'ThisExpression' // this() Could technically work ) { return $(node.callee) + '(' + node.arguments.map($).join(', ') + ')'; } return $w(node.callee) + '(' + node.arguments.map($).join(', ') + ')'; } function CatchClause(node) { assert(node.type, 'CatchClause'); return 'catch ' + (node.param ? $w(node.param) + ' ' : '') + $(node.body); } function ClassBody(node) { assert(node.type, 'ClassBody'); return '{\n' + node.body.map($).join('\n') + '\n}'; } function ClassDeclaration(node) { assert(node.type, 'ClassDeclaration'); return 'class' + (node.id ? ' ' + $(node.id) : '') + (node.superClass ? ' extends (' + $(node.superClass) + ') ' : '') + $(node.body); } function ClassExpression(node) { assert(node.type, 'ClassExpression'); return 'class' + (node.id ? ' ' + $(node.id) : '') + (node.superClass ? ' extends (' + $(node.superClass) + ') ' : '') + $(node.body); } function ClassMethod(node) { assert(node.type, 'ClassMethod'); assert('value' in node, false); // The `ClassMethod` type is only used for babelCompat // Babel does not have .value and merges the method node with the function node, different from the estree spec return ( (node.static ? 'static ' : '') + (node.kind === 'get' ? 'get ' : '') + (node.kind === 'set' ? 'set ' : '') + (node.async ? 'async ' : '') + (node.generator ? '* ' : '') + (node.computed ? '[' + $(node.key) + ']' : $(node.key)) + '(' + node.params.map($).join(', ') + ')' + $(node.body) + ';' ); } function CommentBlock(node) { assert(node.type, 'CommentBlock'); return '/*' + node.value + '*/'; } function CommentLine(node) { assert(node.type, 'CommentLine'); return '//' + node.value + '\n'; } function ConditionalExpression(node) { assert(node.type, 'ConditionalExpression'); let a = $(node.test); if ( node.test.type !== 'Super' // super ? b : c (Error since super is always call or member) && node.test.type !== 'Import' // import() ? b : c && node.test.type !== 'Identifier' // y ? b : c && node.test.type !== 'Literal' // 5 ? b : c && node.test.type !== 'MemberExpression' // x.y ? b : c && node.test.type !== 'CallExpression' // x()() ? b : c && node.test.type !== 'ArrayExpression' // [] ? b : c // && node.test.type !== 'ObjectExpression' // {} ? b : c Unsafe if exprstmt && node.test.type !== 'MetaProperty' // new.target ? b : c && node.test.type !== 'TaggedTemplateExpression' // foo``() ? b : c && node.test.type !== 'TemplateLiteral' // `foo` ? b : c && node.test.type !== 'ThisExpression' // this ? b : c // && node.test.type !== 'BinaryExpression' // a ? x + y : c TODO: verify whether this is safe // && node.test.type !== 'AssignmentExpression' // x = y ? b : c -> x = (y ? b : c) Unsafe ) { a = '(' + a + ')'; } let b = $(node.consequent); if ( node.consequent.type !== 'Super' // a ? super : c (Error since super is always call or member) && node.consequent.type !== 'Import' // a ? import() : c && node.consequent.type !== 'Identifier' // a ? y : c && node.consequent.type !== 'Literal' // a ? 5 : c && node.consequent.type !== 'MemberExpression' // a ? x.y : c && node.consequent.type !== 'CallExpression' // a ? x()() : c && node.consequent.type !== 'ArrayExpression' // a ? [] : c && node.consequent.type !== 'ObjectExpression' // a ? {} : c && node.consequent.type !== 'MetaProperty' // a ? new.target : c && node.consequent.type !== 'TaggedTemplateExpression' // a ? foo``() : c && node.consequent.type !== 'TemplateLiteral' // a ? `foo` : c && node.consequent.type !== 'ThisExpression' // a ? this : c && node.consequent.type !== 'BinaryExpression' // a ? x + y : c && node.consequent.type !== 'AssignmentExpression' // a ? x = y : c ) { b = '(' + b + ')'; } let c = $(node.alternate); if ( node.alternate.type !== 'Super' // a ? b : super (Error since super is always call or member) && node.alternate.type !== 'Import' // a ? b : import() && node.alternate.type !== 'Identifier' // a ? b : y && node.alternate.type !== 'Literal' // a ? b : 5 && node.alternate.type !== 'MemberExpression' // a ? b : x.y && node.alternate.type !== 'CallExpression' // a ? b : x()() && node.alternate.type !== 'ArrayExpression' // a ? b : [] && node.alternate.type !== 'ObjectExpression' // a ? b : {} && node.alternate.type !== 'MetaProperty' // a ? b : new.target && node.alternate.type !== 'TaggedTemplateExpression' // a ? b : foo``() && node.alternate.type !== 'TemplateLiteral' // a ? b : `foo` && node.alternate.type !== 'ThisExpression' // a ? b : this // && node.alternate.type !== 'BinaryExpression' // a ? x + y : c TODO: verify whether this is safe // && node.alternate.type !== 'AssignmentExpression' // a ? b : (x = y) Unsafe ) { c = '(' + c + ')'; } return '(' + a + '? ' + b + ' : ' + c + ')'; } function ContinueStatement(node) { assert(node.type, 'ContinueStatement'); return 'continue' + (node.label ? ' ' + $(node.label) : '') + ';'; } function DebuggerStatement(node) { assert(node.type, 'DebuggerStatement'); return 'debugger;'; } function Directive(node) { assert(node.type, 'Directive'); return $(node.value); } function DirectiveLiteral(node) { assert(node.type, 'DirectiveLiteral'); return "'" + node.value + "';"; } function DoWhileStatement(node) { assert(node.type, 'DoWhileStatement'); return 'do ' + $(node.body) + ' while ' + $w(node.test) + ';'; } function EmptyStatement(node) { assert(node.type, 'EmptyStatement'); return ';'; } function ExportAllDeclaration(node) { assert(node.type, 'ExportAllDeclaration'); assert(0, 1, 'should not exist anymore'); } function ExportDefaultDeclaration(node) { assert(node.type, 'ExportDefaultDeclaration'); assert(0, 1, 'should not exist anymore'); } function ExportNamespaceSpecifier(node) { assert(node.type, 'ExportNamespaceSpecifier'); assert(0, 1, 'should not exist anymore'); } function ExportNamedDeclaration(node) { assert(node.type, 'ExportNamedDeclaration'); assert(0, 1, 'should not exist anymore'); } function ExportSpecifier(node) { assert(node.type, 'ExportSpecifier'); assert(0, 1, 'should not exist anymore'); } function ExpressionStatement(node) { assert(node.type, 'ExpressionStatement'); if (node.directive === undefined && ( // Protect directives from demotion node.expression.type === 'ObjectExpression' || // {} would be a block if not paren wrapped // node.expression.type === 'ArrayExpression' || // [{__proto__: 1, __proto__: 2}] Should be fine with semis // node.expression.type === 'SequenceExpression' || // a,b node.expression.type === 'FunctionExpression' || // function(){} -> (function(){}) Else it's a decl or illegal node.expression.type === 'ClassExpression' || // class{} -> (class{}) Else it's a decl or illegal // node.expression.type === 'BinaryExpression' || // a+b // node.expression.type === 'MemberExpression' || // a.b // node.expression.type === 'Identifier' || // foo // node.expression.type === 'UnaryExpression' || // ~foo // node.expression.type === 'CallExpression' || // foo() // node.expression.type === 'AssignmentExpression' // a=b // node.expression.type === 'MetaExpression' // new.target // TaggedTemplate // TemplateLiteral // ThisExpression (!node.directive && node.expression.type === 'Literal' && typeof node.expression.value === 'string') // Prevent grouped strings of being promoted to directives )) { // :'( let stmt = $w(node.expression) + ';'; if (stmt === '(1001);') return ';'; return stmt; } // if (node.directive === undefined && node.expression.type === 'Literal' && typeof node.expression.value === 'string') { // return ''; // } let stmt = $(node.expression) + ';'; if (stmt === '1001;') return ';'; return stmt; } function ForInStatement(node) { assert(node.type, 'ForInStatement'); return 'for (' + (node.left.type === 'VariableDeclaration' || node.left.type === 'ObjectPattern' || node.left.type === 'ArrayPattern' ? $(node.left, undefined, undefined, true) : $w(node.left)) + ' in ' + $(node.right) + ') ' + $(node.body); } function ForOfStatement(node) { assert(node.type, 'ForOfStatement'); return 'for ' + (node.await ? 'await ' : '') + '(' + (node.left.type === 'VariableDeclaration' || node.left.type === 'ObjectPattern' || node.left.type === 'ArrayPattern' ? $(node.left, undefined, undefined, true) : $w(node.left)) + ' of ' + $(node.right) + ') ' + $(node.body); } function ForStatement(node) { assert(node.type, 'ForStatement'); return 'for (' + (node.init ? (node.init.type === 'VariableDeclaration' ? $(node.init, undefined, undefined, true) : $w(node.init)) : '') + ';' + (node.test ? $(node.test) : '') + ';' + (node.update ? $(node.update) : '') + ') ' + $(node.body); } function FunctionDeclaration(node) { assert(node.type, 'FunctionDeclaration'); let suffix = (NATIVE_SYMBOLS && node.id ? ';allFuncs.push('+node.id.name+');' : ''); return ( (node.async ? 'async ' : '') + 'function' + (node.generator ? '*' : '') + (node.id ? ' ' + $(node.id) : '') + '(' + node.params.map($).join(', ') + ') {\n' + node.body.body.map($).join('\n') + '\n}' ) + suffix; } function FunctionExpression(node) { assert(node.type, 'FunctionExpression'); return (node.async ? 'async ' : '') + 'function' + (node.generator ? '*' : '') + (node.id ? ' ' + $(node.id) : '') + '(' + node.params.map($).join(', ') + ') {\n' + node.body.body.map($).join('\n') + '\n}'; } function Identifier(node) { assert(node.type, 'Identifier'); let name = node.name; switch (name) { case '__$lf_flag': case '__$start': case '__$leaf': case '__$group': throw new Error('The ident `' + name + '` should only be used as part of an update expression (++x), the build script assumes this'); } return name; } function IfStatement(node) { assert(node.type, 'IfStatement'); return 'if ' + $w(node.test) + ' ' + $(node.consequent) + (node.alternate ? ' else ' + $(node.alternate) : ''); } function Import(node) { assert(node.type, 'Import'); assert(0, 1, 'imports should have been scrubbed'); } function ImportDeclaration(node) { assert(node.type, 'ImportDeclaration'); assert(0, 1, 'imports should have been scrubbed'); } function ImportDefaultSpecifier(node) { assert(node.type, 'ImportDefaultSpecifier'); assert(0, 1, 'imports should have been scrubbed'); } function ImportExpression(node) { assert(node.type, 'ImportExpression'); assert(0, 1, 'imports should have been scrubbed'); } function ImportNamespaceSpecifier(node) { assert(node.type, 'ImportNamespaceSpecifier'); assert(0, 1, 'imports should have been scrubbed'); } function ImportSpecifier(node) { assert(node.type, 'ImportSpecifier'); assert(0, 1, 'imports should have been scrubbed'); } function LabeledStatement(node) { assert(node.type, 'LabeledStatement'); return $(node.label) + ': ' + $(node.body); } function Literal(node) { assert(node.type, 'Literal'); switch (typeof node.value) { case 'boolean': return node.raw; case 'number': return node.raw; case 'string': return node.raw; case 'object': // regex return node.raw; } throw new Error('fixme; literal type'); } function LogicalExpression(node) { assert(node.type, 'LogicalExpression'); let left = $(node.left); if ( node.left.type !== 'Super' // super && x (Error since super is always call or member) && node.left.type !== 'Import' // import() && x && node.left.type !== 'Identifier' // x && y && node.left.type !== 'Literal' // 5 && x && node.left.type !== 'MemberExpression' // x.y && z && node.left.type !== 'CallExpression' // x()() && x && node.left.type !== 'ArrayExpression' // [] && x // && node.left.type !== 'ObjectExpression' // {} && x Block if statement expression so, meh && node.left.type !== 'MetaProperty' // new.target && x && node.left.type !== 'TaggedTemplateExpression' // foo``() && x && node.left.type !== 'TemplateLiteral' // `foo` && x && node.left.type !== 'ThisExpression' // this && x ) { left = '(' + left + ')'; } let right = $(node.right); if ( node.right.type !== 'Super' // x && super (Error since super is always call or member) && node.right.type !== 'Import' // x && import() && node.right.type !== 'Identifier' // x && y && node.right.type !== 'Literal' // x && 5 && node.right.type !== 'MemberExpression' // x && x.y && node.right.type !== 'CallExpression' // x && x()() && node.right.type !== 'ArrayExpression' // x && [] && node.right.type !== 'ObjectExpression' // x && {} && node.right.type !== 'MetaProperty' // x && new.target && node.right.type !== 'TaggedTemplateExpression' // x && foo``() && node.right.type !== 'TemplateLiteral' // x && `foo` && node.right.type !== 'ThisExpression' // x && this ) { right = '(' + right + ')'; } return '(' + left + ' ' + node.operator + ' ' + right + ')'; } function MemberExpression(node) { assert(node.type, 'MemberExpression'); if ( node.object.type === 'ObjectExpression' || // {}.c -> ({}).c node.object.type === 'SequenceExpression' || // a,b.c -> (a,b).c node.object.type === 'FunctionExpression' || // function(){}.c -> (function(){}).c node.object.type === 'ClassExpression' || // class x{}.b -> (class x{}).b node.object.type === 'BinaryExpression' || // a+b.c -> (a+b).c // node.object.type === 'MemberExpression' || // a.b.c -> (a.b).c // node.object.type === 'Identifier' || // a.b -> (a).b // node.object.type === 'CallExpression' || // -> a().b -> (a()).b node.object.type === 'UnaryExpression' || // `(!t).y` node.object.type === 'ArrowFunctionExpression' || // ()=>x.y -> (()=>x).y node.object.type === 'UpdateExpression' || // `(++x)[x]` (node.object.type === 'Literal' && typeof node.object.value === 'number') || // `4 .p` (node.object.type === 'Identifier' && node.object.name === 'let') || // `(let)[x]` node.object.type === 'AssignmentExpression' // a=b.c -> (a=b).c ) { return $w(node.object) + (node.computed ? '[' + $(node.property) + ']' : ('.' + $(node.property))); } else { return $(node.object) + (node.computed ? '[' + $(node.property) + ']' : ('.' + $(node.property))); } } function MetaProperty(node) { assert(node.type, 'MetaProperty'); return $(node.meta) + '.' + $(node.property); } function MethodDefinition(node) { assert(node.type, 'MethodDefinition'); return ( (node.static ? 'static ' : '') + (node.kind === 'get' ? 'get ' : '') + (node.kind === 'set' ? 'set ' : '') + (node.value.async ? 'async ' : '') + (node.value.generator ? '* ' : '') + (node.computed ? '[' + $(node.key) + ']' : $(node.key)) + '(' + node.value.params.map($).join(', ') + ')' + $(node.value.body) + ';' ); } function NewExpression(node) { assert(node.type, 'NewExpression'); if ( node.callee.type === 'Super' || node.callee.type === 'Import' || node.callee.type === 'Identifier' || node.callee.type === 'Literal' || node.callee.type === 'MemberExpression' // || node.callee.type === 'CallExpression' // new x()() -> new (x())() || node.callee.type === 'ArrayExpression' // new [] Runtime error...? || node.callee.type === 'ObjectExpression' // new {} Runtime error...? || node.callee.type === 'MetaProperty' // new new.target // || node.callee.type === 'TaggedTemplateExpression' // new foo``() -> new (foo``) || node.callee.type === 'TemplateLiteral' // new `foo` Runtime error? || node.callee.type === 'ThisExpression' // new this (Could be made to work) ) { return 'new ' + $(node.callee) + '(' + node.arguments.map($).join(', ') + ')'; } return 'new ' + $w(node.callee) + '(' + node.arguments.map($).join(', ') + ')'; } function NullLiteral(node) { assert(node.type, 'NullLiteral'); return 'null'; } function NumericLiteral(node) { assert(node.type, 'NumericLiteral'); return node.raw; } let x = 0 function ObjectExpression(node) { assert(node.type, 'ObjectExpression'); let arr = node.properties.map((e, i) => $(e, i)); return '{' + arr.join(', ') + '}'; } function ObjectMethod(node) { assert(node.type, 'ObjectMethod'); return ( (node.static ? 'static ' : '') + (node.kind === 'get' ? 'get ' : '') + (node.kind === 'set' ? 'set ' : '') + (node.value.async ? 'async ' : '') + (node.value.generator ? '* ' : '') + (node.computed ? '[' + $(node.value.id) + ']' : $(node.value.id)) + '(' + $(node.value.params).join(', ') + ')' + $(node.value.body) + ',' ); } function ObjectPattern(node) { assert(node.type, 'ObjectPattern'); return '{' + node.properties.map($).join(', ') + '}'; } function ObjectProperty(node) { assert(node.type, 'ObjectProperty'); return node.body.map($).join('\n'); } function Program(node) { assert(node.type, 'Program'); return node.body.map($).join('\n'); } function Property(node) { assert(node.type, 'Property'); if (SCRUB_OTHERS) { // Do not visit the ident otherwise you change `{acornCompat: false}` to `{false: false}` if (node.key.type === 'Identifier' && (node.key.name === 'acornCompat' || node.key.name === 'babelCompat')) { return node.key.name; } } return ( (node.kind === 'get' || node.kind === 'set' || node.method) ? ( (node.static ? 'static ' : '') + (node.kind === 'get' ? 'get ' : '') + (node.kind === 'set' ? 'set ' : '') + (node.value.async ? 'async ' : '') + (node.value.generator ? '* ' : '') + (node.computed ? '[' : '') + $(node.key) + (node.computed ? ']' : '') + '(' + node.value.params.map($).join(', ') + ')' + $(node.value.body) + '' ) : ( (node.shorthand ? '' : ((node.computed ? '[' : '') + $(node.key) + (node.computed ? ']' : '') + ':')) + $(node.value) ) ); } function RegExpLiteral(node) { assert(node.type, 'RegExpLiteral'); return node.raw; } function RestElement(node) { assert(node.type, 'RestElement'); return '...' + $(node.argument); } function ReturnStatement(node) { assert(node.type, 'ReturnStatement'); return 'return' + (node.argument ? ' ' + $(node.argument) : '') + ';'; } function SequenceExpression(node) { assert(node.type, 'SequenceExpression'); return '(' + node.expressions.map($).join(', ') + ')'; } function SpreadElement(node) { assert(node.type, 'SpreadElement'); return '...' + $(node.argument); } function StringLiteral(node) { assert(node.type, 'StringLiteral'); return node.raw; } function Super(node) { assert(node.type, 'Super'); return 'super'; } function SwitchCase(node) { assert(node.type, 'SwitchCase'); return (node.test ? 'case ' + $(node.test) : 'default') + ':\n' + node.consequent.map($).join('\n'); } function SwitchStatement(node) { assert(node.type, 'SwitchStatement'); return 'switch ' + $w(node.discriminant) + ' {\n' + node.cases.map($).join('\n') + '\n}'; } function TaggedTemplateExpression(node) { assert(node.type, 'TaggedTemplateExpression'); return $w(node.tag) + $(node.quasi); } function TemplateElement(node) { assert(node.type, 'TemplateElement'); return node.value.raw; } function TemplateLiteral(node) { assert(node.type, 'TemplateLiteral'); return '`' + $(node.quasis[0]) + (node.expressions.length ? '${' : '') + node.expressions.map((e, i) => $(e) + '}' + $(node.quasis[i+1])).join('${') + '`'; } function ThisExpression(node) { assert(node.type, 'ThisExpression'); return 'this'; } function ThrowStatement(node) { assert(node.type, 'ThrowStatement'); return 'throw ' + $(node.argument) + ';'; } function TryStatement(node) { assert(node.type, 'TryStatement'); return 'try ' + $(node.block) + (node.handler ? ' ' + $(node.handler) : '') + (node.finalizer ? ' finally ' + $(node.finalizer) : ''); } function UnaryExpression(node) { assert(node.type, 'UnaryExpression'); if ( node.argument.type === 'Identifier' // !foo || node.argument.type === 'Import' // !import() || node.argument.type === 'Super' // !super || node.argument.type === 'MemberExpression' // !x.y || node.argument.type === 'Literal' // !5 || node.argument.type === 'CallExpression' // !x() || node.argument.type === 'ArrayExpression' // ![x] || node.argument.type === 'ObjectExpression' // !{x} || node.argument.type === 'MetaProperty' // !new.target || node.argument.type === 'TaggedTemplateExpression' // ! foo`x` || node.argument.type === 'TemplateLiteral' // !`foo` || node.argument.type === 'ThisExpression' // !this ) { return node.operator + ('!+-~'.includes(node.operator)?'':' ') + $(node.argument); } return node.operator + ('!+-~'.includes(node.operator)?'':' ') + $w(node.argument); } function UpdateExpression(node) { assert(node.type, 'UpdateExpression'); return (node.prefix ? node.operator : '') + $(node.argument) + (node.prefix ? '' : node.operator); } function VariableDeclaration(node, fromFor) { assert(node.type, 'VariableDeclaration'); assert(node.declarations.length, 1, 'coding style uses only one binding per declaration (counting a whole destructuring as one) `' + (node.declarations.length !== 1 && (node.kind + ' ' + node.declarations.map($).join(', '))) + '`'); return node.kind + ' ' + node.declarations.map($).join(', ') + (fromFor ? '' : ';'); // no semi inside `for` } function VariableDeclarator(node) { assert(node.type, 'VariableDeclarator'); return $(node.id) + (node.init ? ' = ' + $(node.init) : ''); } function WhileStatement(node) { assert(node.type, 'WhileStatement'); return 'while ' + $w(node.test) + ' ' + $(node.body); } function WithStatement(node) { assert(node.type, 'WithStatement'); return 'with ' + $w(node.object) + ' ' + $(node.body); } function YieldExpression(node) { assert(node.type, 'YieldExpression'); return '(yield' + (node.delegate ? ' *' : '') + (node.argument ? ' ' + $w(node.argument) : '') + ')'; } function $w(node) { return '(' + $(node) + ')'; } let jumpTable = [ (node, fromFor, type, c) => { if (c === $$I_69) return Directive(node); if (c === $$X_78) return ExportDefaultDeclaration(node); return UpdateExpression(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(3); if (c === $$L_6C) return BooleanLiteral(node); if (c === $$I_UC_49) return ForInStatement(node); if (c === $$O_UC_4F) return ForOfStatement(node); return UnaryExpression(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(0); if (c === $$A_UC_41) return AssignmentPattern(node); if (c === $$B_UC_42) return BlockStatement(node); return ImportSpecifier(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(2); if (c === $$A_61) return ClassExpression(node); if (c === $$M_6D) return CommentBlock(node); if (c === $$P_70) return EmptyStatement(node); return ForStatement(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(2); if (c === $$G_67) return BigIntLiteral(node); if (c === $$M_6D) return CommentLine(node); return WithStatement(node); }, (node, fromFor, type, c) => { if (c === $$R_72) return ArrowFunctionExpression(node); return ClassBody(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(4); if (c === $$T_74) return FunctionDeclaration(node); if (c === $$E_UC_45) return ThisExpression(node); if (c === $$W_77) return ThrowStatement(node); if (c === $$E_65) return WhileStatement(node); return YieldExpression(node); }, (node, fromFor, type, c) => { if (c === $$S_73) return AssignmentExpression(node); if (c === $$L_6C) return ClassMethod(node); return FunctionExpression(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(0); if (c === $$N_UC_4E) return NewExpression(node); return RegExpLiteral(node); }, (node, fromFor, type, c) => { return MetaProperty(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(8); if (c === $$U_75) return CatchClause(node); if (c === $$A_61) return ReturnStatement(node); if (c === $$E_UC_45) return TemplateElement(node); return TemplateLiteral(node); }, (node, fromFor, type, c) => { if (c === $$X_78) return ExpressionStatement(node); return Import(node); }, (node, fromFor, type, c) => { if (c === $$X_78) return ExportAllDeclaration(node); return ObjectProperty(node); }, (node, fromFor, type, c) => { return IfStatement(node); }, (node, fromFor, type, c) => { if (c === $$X_78) return ExportNamedDeclaration(node); if (c === $$D_64) return Identifier(node); if (c === $$I_69) return Literal(node); if (c === $$B_62) return ObjectMethod(node); return RestElement(node); }, (node, fromFor, type, c) => { if (c === $$A_61) return CallExpression(node); return ObjectPattern(node); }, (node, fromFor, type, c) => { return Super(node); }, (node, fromFor, type, c) => { return LabeledStatement(node); }, (node, fromFor, type, c) => { if (c === $$B_62) return ObjectExpression(node); return VariableDeclaration(node, fromFor); }, (node, fromFor, type, c) => { return VariableDeclarator(node); }, (node, fromFor, type, c) => { return DebuggerStatement(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(2); if (c === $$P_70) return ImportNamespaceSpecifier(node); if (c === $$M_6D) return MemberExpression(node); if (c === $$T_74) return MethodDefinition(node); return Program(node); }, (node, fromFor, type, c) => { // (nothing) }, (node, fromFor, type, c) => { if (c === $$X_78) return ExportSpecifier(node); return SequenceExpression(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(0); if (c === $$A_UC_41) return AwaitExpression(node); return SwitchStatement(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(0); if (c === $$B_UC_42) return BinaryExpression(node); if (c === $$D_UC_44) return DirectiveLiteral(node); if (c === $$S_UC_53) return StringLiteral(node); return TaggedTemplateExpression(node); }, (node, fromFor, type, c) => { if (c === $$O_6F) return ConditionalExpression(node); return Property(node); }, (node, fromFor, type, c) => { if (c === $$M_6D) return ImportDefaultSpecifier(node); return NumericLiteral(node); }, (node, fromFor, type, c) => { if (c === $$R_72) return BreakStatement(node); if (c === $$L_6C) return ClassDeclaration(node); return ImportDeclaration(node); }, (node, fromFor, type, c) => { if (c === $$R_72) return ArrayExpression(node); if (c === $$M_6D) return ImportExpression(node); if (c === $$P_70) return SpreadElement(node); return SwitchCase(node); }, (node, fromFor, type, c) => { c = type.charCodeAt(0); if (c === $$A_UC_41) return ArrayPattern(node); if (c === $$C_UC_43) return ContinueStatement(node); if (c === $$L_UC_4C) return LogicalExpression(node); if (c === $$N_UC_4E) return NullLiteral(node); return TryStatement(node); }, (node, fromFor, type, c) => { return DoWhileStatement(node); }, ]; function $(node, _, __, fromFor) { // This is a walker that was (manually) built using a simple hash, as follows: // Input file is a text file with every node name, one per line // Get distribution // var x=1; require('fs').readFileSync('nodes.txt', 'utf8').split('\n').filter(Boolean).reduce((map, s) => { let p = s.length^s.charCodeAt(x)-96; if (!map[p]) map[p] = 0; ++map[p]; return map }, {}) // Get node names per bucket // var x=6; var obj=require('fs').readFileSync('nodes.txt', 'utf8').split('\n').filter(Boolean).reduce((map, s) => { let p = s.length^s.charCodeAt(s.length-x)-96; if (!map[p]) map[p] = []; map[p].push(s); return map }, {}); // This breaks down the 83 node names into an almost perfect 31 case hash, within 5bit, where each bucket has at most 5 elements let type = node.type; let c = type.charCodeAt(1); // We can use the second character as a second hash in some of these :) let hash = type.length ^ c - 96; return jumpTable[hash](node, fromFor, type, c); } function scrub(node) { return $(node); } export { scrub, };