js-slang
Version:
Javascript-based implementations of Source, written in Typescript
473 lines • 14.3 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.tokenize = exports.parse = void 0;
const parser_1 = require("../parser/parser");
const source_1 = require("../parser/source");
const syntax_1 = require("../parser/source/syntax");
const formatters_1 = require("../utils/formatters");
const list_1 = require("./list");
class ParseError extends Error {
constructor(message) {
super(message);
this.name = 'ParseError';
}
}
function unreachable() {
// tslint:disable-next-line:no-console
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.reduce((b, ex) => b || ex.type === 'VariableDeclaration' || ex.type === 'FunctionDeclaration', false);
}
const transformers = new Map([
[
'Program',
(node) => {
node = node;
return makeSequenceIfNeeded(node.body);
}
],
[
'BlockStatement',
(node) => {
return makeBlockIfNeeded(node.body);
}
],
[
'StatementSequence',
(node) => {
return makeSequenceIfNeeded(node.body);
}
],
[
'ExpressionStatement',
(node) => {
return transform(node.expression);
}
],
[
'IfStatement',
(node) => {
return (0, list_1.vector_to_list)([
'conditional_statement',
transform(node.test),
transform(node.consequent),
node.alternate === null
? makeSequenceIfNeeded([])
: transform(node.alternate)
]);
}
],
[
'FunctionDeclaration',
(node) => {
return (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)
]);
}
],
[
'VariableDeclaration',
(node) => {
if (node.kind === 'let') {
return (0, list_1.vector_to_list)([
'variable_declaration',
transform(node.declarations[0].id),
transform(node.declarations[0].init)
]);
}
else if (node.kind === 'const') {
return (0, list_1.vector_to_list)([
'constant_declaration',
transform(node.declarations[0].id),
transform(node.declarations[0].init)
]);
}
else {
unreachable();
throw new ParseError('Invalid declaration kind');
}
}
],
[
'ReturnStatement',
(node) => {
return (0, list_1.vector_to_list)(['return_statement', transform(node.argument)]);
}
],
[
'CallExpression',
(node) => {
return (0, list_1.vector_to_list)([
'application',
transform(node.callee),
(0, list_1.vector_to_list)(node.arguments.map(transform))
]);
}
],
[
'UnaryExpression',
(node) => {
return (0, list_1.vector_to_list)([
'unary_operator_combination',
node.operator === '-' ? '-unary' : node.operator,
transform(node.argument)
]);
}
],
[
'BinaryExpression',
(node) => {
return (0, list_1.vector_to_list)([
'binary_operator_combination',
node.operator,
transform(node.left),
transform(node.right)
]);
}
],
[
'LogicalExpression',
(node) => {
return (0, list_1.vector_to_list)([
'logical_composition',
node.operator,
transform(node.left),
transform(node.right)
]);
}
],
[
'ConditionalExpression',
(node) => {
return (0, list_1.vector_to_list)([
'conditional_expression',
transform(node.test),
transform(node.consequent),
transform(node.alternate)
]);
}
],
[
'ArrowFunctionExpression',
(node) => {
return (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)])
]);
}
],
[
'Identifier',
(node) => {
return (0, list_1.vector_to_list)(['name', node.name]);
}
],
[
'Literal',
(node) => {
return (0, list_1.vector_to_list)(['literal', node.value]);
}
],
[
'ArrayExpression',
(node) => {
return (0, list_1.vector_to_list)([
'array_expression',
(0, list_1.vector_to_list)(node.elements.map(transform))
]);
}
],
[
'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');
}
}
],
[
'ForStatement',
(node) => {
return (0, list_1.vector_to_list)([
'for_loop',
transform(node.init),
transform(node.test),
transform(node.update),
transform(node.body)
]);
}
],
[
'WhileStatement',
(node) => {
return (0, list_1.vector_to_list)(['while_loop', transform(node.test), transform(node.body)]);
}
],
[
'BreakStatement',
(_node) => {
return (0, list_1.vector_to_list)(['break_statement']);
}
],
[
'ContinueStatement',
(_node) => {
return (0, list_1.vector_to_list)(['continue_statement']);
}
],
[
'ObjectExpression',
(node) => {
return (0, list_1.vector_to_list)(['object_expression', (0, list_1.vector_to_list)(node.properties.map(transform))]);
}
],
[
'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)
]);
}
],
[
'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)
]);
}
],
[
'ImportDeclaration',
(node) => {
return (0, list_1.vector_to_list)([
'import_declaration',
(0, list_1.vector_to_list)(node.specifiers.map(transform)),
node.source.value
]);
}
],
[
'ImportSpecifier',
(node) => {
return (0, list_1.vector_to_list)(['name', node.imported.name]);
}
],
[
'ImportDefaultSpecifier',
(_node) => {
return (0, list_1.vector_to_list)(['default']);
}
],
[
'ExportNamedDeclaration',
(node) => {
return (0, list_1.vector_to_list)([
'export_named_declaration',
node.declaration ? transform(node.declaration) : node.specifiers.map(transform)
]);
}
],
[
'ExportDefaultDeclaration',
(node) => {
return (0, list_1.vector_to_list)(['export_default_declaration', transform(node.declaration)]);
}
],
[
'ExportSpecifier',
(node) => {
return (0, list_1.vector_to_list)(['name', node.exported.name]);
}
],
[
'ClassDeclaration',
(node) => {
return (0, list_1.vector_to_list)([
'class_declaration',
(0, list_1.vector_to_list)([
'name',
node.id === null ? null : node.id.name,
node.superClass === null || node.superClass === undefined
? null
: transform(node.superClass),
node.body.body.map(transform)
])
]);
}
],
[
'NewExpression',
(node) => {
return (0, list_1.vector_to_list)([
'new_expression',
transform(node.callee),
(0, list_1.vector_to_list)(node.arguments.map(transform))
]);
}
],
[
'MethodDefinition',
(node) => {
return (0, list_1.vector_to_list)([
'method_definition',
node.kind,
node.static,
transform(node.key),
transform(node.value)
]);
}
],
[
'FunctionExpression',
(node) => {
return (0, list_1.vector_to_list)([
'lambda_expression',
(0, list_1.vector_to_list)(node.params.map(transform)),
makeBlockIfNeeded(node.body.body)
]);
}
],
[
'ThisExpression',
(_node) => {
return (0, list_1.vector_to_list)(['this_expression']);
}
],
[
'Super',
(_node) => {
return (0, list_1.vector_to_list)(['super_expression']);
}
],
[
'TryStatement',
(node) => {
return (0, list_1.vector_to_list)([
'try_statement',
transform(node.block),
node.handler === null || node.handler === undefined
? null
: (0, list_1.vector_to_list)(['name', node.handler.param.name]),
node.handler === null || node.handler === undefined ? null : transform(node.handler.body)
]);
}
],
[
'ThrowStatement',
(node) => {
return (0, list_1.vector_to_list)(['throw_statement', transform(node.argument)]);
}
],
[
'SpreadElement',
(node) => {
return (0, list_1.vector_to_list)(['spread_element', transform(node.argument)]);
}
],
[
'RestElement',
(node) => {
return (0, list_1.vector_to_list)(['rest_element', transform(node.argument)]);
}
]
]);
function transform(node) {
if (transformers.has(node.type)) {
const transformer = transformers.get(node.type);
const transformed = transformer(node);
// Attach location information
if (transformed !== null &&
transformed !== undefined &&
typeof transformed === 'object' &&
transformed.tag !== undefined) {
transformed.loc = node.loc;
}
return transformed;
}
else {
unreachable();
throw new ParseError('Cannot transform unknown type: ' + node.type);
}
}
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');
}
}
exports.parse = 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);
}
exports.tokenize = tokenize;
//# sourceMappingURL=parser.js.map
;