UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

244 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; exports.tokenize = tokenize; const parser_1 = require("../parser/parser"); const source_1 = require("../parser/source"); const syntax_1 = require("../parser/source/syntax"); const helpers_1 = require("../utils/ast/helpers"); const typeGuards_1 = require("../utils/ast/typeGuards"); const formatters_1 = require("../utils/formatters"); const list_1 = require("./list"); class ParseError extends Error { constructor(message) { super(message); this.name = 'ParseError'; } } function unreachable() { console.error((0, formatters_1.oneLine) ` UNREACHABLE CODE REACHED! Please file an issue at https://github.com/source-academy/js-slang/issues if you see this. `); } // sequences of expressions of length 1 // can be represented by the element itself, // instead of constructing a sequence function makeSequenceIfNeeded(exs) { return exs.length === 1 ? transform(exs[0]) : (0, list_1.vector_to_list)(['sequence', (0, list_1.vector_to_list)(exs.map(transform))]); } function makeBlockIfNeeded(exs) { return hasDeclarationAtToplevel(exs) ? (0, list_1.vector_to_list)(['block', makeSequenceIfNeeded(exs)]) : makeSequenceIfNeeded(exs); } // checks if sequence has declaration at toplevel // (outside of any block) function hasDeclarationAtToplevel(exs) { return exs.some(typeGuards_1.isDeclaration); } const transformers = { ArrayExpression: ({ elements }) => (0, list_1.vector_to_list)([ 'array_expression', (0, list_1.vector_to_list)(elements.map(transform)) ]), ArrowFunctionExpression: node => (0, list_1.vector_to_list)([ 'lambda_expression', (0, list_1.vector_to_list)(node.params.map(transform)), node.body.type === 'BlockStatement' ? // body.body: strip away one layer of block: // The body of a function is the statement // inside the curly braces. makeBlockIfNeeded(node.body.body) : (0, list_1.vector_to_list)(['return_statement', transform(node.body)]) ]), AssignmentExpression: node => { if (node.left.type === 'Identifier') { return (0, list_1.vector_to_list)(['assignment', transform(node.left), transform(node.right)]); } else if (node.left.type === 'MemberExpression') { return (0, list_1.vector_to_list)(['object_assignment', transform(node.left), transform(node.right)]); } else { unreachable(); throw new ParseError('Invalid assignment'); } }, BinaryExpression: node => (0, list_1.vector_to_list)([ 'binary_operator_combination', node.operator, transform(node.left), transform(node.right) ]), BlockStatement: ({ body }) => makeBlockIfNeeded(body), BreakStatement: () => (0, list_1.vector_to_list)(['break_statement']), CallExpression: ({ callee, arguments: args }) => (0, list_1.vector_to_list)(['application', transform(callee), (0, list_1.vector_to_list)(args.map(transform))]), ClassDeclaration: node => { return (0, list_1.vector_to_list)([ 'class_declaration', (0, list_1.vector_to_list)([ 'name', node.id?.name, !node.superClass ? null : transform(node.superClass), node.body.body.map(transform) ]) ]); }, ConditionalExpression: node => (0, list_1.vector_to_list)([ 'conditional_expression', transform(node.test), transform(node.consequent), transform(node.alternate) ]), ContinueStatement: () => (0, list_1.vector_to_list)(['continue_statement']), ExportDefaultDeclaration: node => (0, list_1.vector_to_list)(['export_default_declaration', transform(node.declaration)]), ExportNamedDeclaration: ({ declaration, specifiers }) => (0, list_1.vector_to_list)([ 'export_named_declaration', declaration ? transform(declaration) : specifiers.map(transform) ]), ExportSpecifier: node => (0, list_1.vector_to_list)(['name', node.exported.name]), ExpressionStatement: ({ expression }) => transform(expression), ForStatement: node => (0, list_1.vector_to_list)([ 'for_loop', transform(node.init), transform(node.test), transform(node.update), transform(node.body) ]), FunctionDeclaration: node => (0, list_1.vector_to_list)([ 'function_declaration', transform(node.id), (0, list_1.vector_to_list)(node.params.map(transform)), makeBlockIfNeeded(node.body.body) ]), FunctionExpression: ({ body: { body }, params }) => (0, list_1.vector_to_list)([ 'lambda_expression', (0, list_1.vector_to_list)(params.map(transform)), makeBlockIfNeeded(body) ]), Identifier: ({ name }) => (0, list_1.vector_to_list)(['name', name]), IfStatement: node => (0, list_1.vector_to_list)([ 'conditional_statement', transform(node.test), transform(node.consequent), node.alternate == null ? makeSequenceIfNeeded([]) : transform(node.alternate) ]), ImportDeclaration: node => (0, list_1.vector_to_list)([ 'import_declaration', (0, list_1.vector_to_list)(node.specifiers.map(transform)), node.source.value ]), ImportDefaultSpecifier: () => (0, list_1.vector_to_list)(['default']), ImportSpecifier: node => (0, list_1.vector_to_list)(['name', node.imported.name]), Literal: ({ value }) => (0, list_1.vector_to_list)(['literal', value]), LogicalExpression: node => (0, list_1.vector_to_list)([ 'logical_composition', node.operator, transform(node.left), transform(node.right) ]), MemberExpression: node => { // "computed" property of MemberExpression distinguishes // between dot access (not computed) and // a[...] (computed) // the key in dot access is meant as string, and // represented by a "property" node in parse result return (0, list_1.vector_to_list)([ 'object_access', transform(node.object), !node.computed && node.property.type === 'Identifier' ? (0, list_1.vector_to_list)(['property', node.property.name]) : transform(node.property) ]); }, MethodDefinition: node => (0, list_1.vector_to_list)([ 'method_definition', node.kind, node.static, transform(node.key), transform(node.value) ]), NewExpression: ({ callee, arguments: args }) => (0, list_1.vector_to_list)(['new_expression', transform(callee), (0, list_1.vector_to_list)(args.map(transform))]), ObjectExpression: ({ properties }) => (0, list_1.vector_to_list)(['object_expression', (0, list_1.vector_to_list)(properties.map(transform))]), Program: ({ body }) => makeSequenceIfNeeded(body), Property: node => { // identifiers before the ":" in literal objects are meant // as string, and represented by a "property" node in parse result return (0, list_1.vector_to_list)([ 'key_value_pair', node.key.type === 'Identifier' ? (0, list_1.vector_to_list)(['property', node.key.name]) : transform(node.key), transform(node.value) ]); }, RestElement: ({ argument }) => (0, list_1.vector_to_list)(['rest_element', transform(argument)]), ReturnStatement: node => (0, list_1.vector_to_list)(['return_statement', transform(node.argument)]), SpreadElement: ({ argument }) => (0, list_1.vector_to_list)(['spread_element', transform(argument)]), StatementSequence: ({ body }) => makeSequenceIfNeeded(body), Super: () => (0, list_1.vector_to_list)(['super_expression']), ThisExpression: () => (0, list_1.vector_to_list)(['this_expression']), ThrowStatement: ({ argument }) => (0, list_1.vector_to_list)(['throw_statement', transform(argument)]), TryStatement: node => { return (0, list_1.vector_to_list)([ 'try_statement', transform(node.block), !node.handler ? null : (0, list_1.vector_to_list)(['name', node.handler.param.name]), !node.handler ? null : transform(node.handler.body) ]); }, UnaryExpression: ({ operator, argument }) => (0, list_1.vector_to_list)([ 'unary_operator_combination', operator === '-' ? '-unary' : operator, transform(argument) ]), VariableDeclaration: node => { const { id, init } = (0, helpers_1.getSourceVariableDeclaration)(node); if (node.kind === 'let') { return (0, list_1.vector_to_list)(['variable_declaration', transform(id), transform(init)]); } else if (node.kind === 'const') { return (0, list_1.vector_to_list)(['constant_declaration', transform(id), transform(init)]); } else { unreachable(); throw new ParseError('Invalid declaration kind'); } }, WhileStatement: ({ test, body }) => (0, list_1.vector_to_list)(['while_loop', transform(test), transform(body)]) }; /** * Converts the given Node to a Source Value (which will be a list * consisting of a string description followed by that node's components) */ function transform(node) { if (!(node.type in transformers)) { unreachable(); throw new ParseError('Cannot transform unknown type: ' + node.type); } const transformer = transformers[node.type]; return transformer(node); } function parse(x, context) { context.chapter = syntax_1.libraryParserLanguage; const program = (0, parser_1.parse)(x, context); if (context.errors.length > 0) { throw new ParseError(context.errors[0].explain()); } if (program) { return transform(program); } else { unreachable(); throw new ParseError('Invalid parse'); } } function tokenize(x, context) { const tokensArr = source_1.SourceParser.tokenize(x, context).map(tok => x.substring(tok.start, tok.end)); return (0, list_1.vector_to_list)(tokensArr); } //# sourceMappingURL=parser.js.map