js-slang
Version:
Javascript-based implementations of Source, written in Typescript
244 lines • 10.3 kB
JavaScript
;
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