UNPKG

@gobstones/gobstones-parser

Version:
730 lines (592 loc) 19 kB
/* eslint-disable camelcase */ /* eslint-disable no-underscore-dangle */ import { SourceReader, UnknownPosition } from './reader'; import { Token } from './token'; export const N_Main = Symbol.for('N_Main'); /* Definitions */ export const N_DefProgram = Symbol.for('N_DefProgram'); export const N_DefInteractiveProgram = Symbol.for('N_DefInteractiveProgram'); export const N_DefProcedure = Symbol.for('N_DefProcedure'); export const N_DefFunction = Symbol.for('N_DefFunction'); export const N_DefType = Symbol.for('N_DefType'); /* Statements */ export const N_StmtBlock = Symbol.for('N_StmtBlock'); export const N_StmtReturn = Symbol.for('N_StmtReturn'); export const N_StmtIf = Symbol.for('N_StmtIf'); export const N_StmtRepeat = Symbol.for('N_StmtRepeat'); export const N_StmtForeach = Symbol.for('N_StmtForeach'); export const N_StmtWhile = Symbol.for('N_StmtWhile'); export const N_StmtSwitch = Symbol.for('N_StmtSwitch'); export const N_StmtAssignVariable = Symbol.for('N_StmtAssignVariable'); export const N_StmtAssignTuple = Symbol.for('N_StmtAssignTuple'); export const N_StmtProcedureCall = Symbol.for('N_StmtProcedureCall'); /* Patterns */ export const N_PatternWildcard = Symbol.for('N_PatternWildcard'); export const N_PatternVariable = Symbol.for('N_PatternVariable'); export const N_PatternNumber = Symbol.for('N_PatternNumber'); export const N_PatternStructure = Symbol.for('N_PatternStructure'); export const N_PatternTuple = Symbol.for('N_PatternTuple'); export const N_PatternTimeout = Symbol.for('N_PatternTimeout'); /* Expressions */ export const N_ExprVariable = Symbol.for('N_ExprVariable'); export const N_ExprConstantNumber = Symbol.for('N_ExprConstantNumber'); export const N_ExprConstantString = Symbol.for('N_ExprConstantString'); export const N_ExprChoose = Symbol.for('N_ExprChoose'); export const N_ExprMatching = Symbol.for('N_ExprMatching'); export const N_ExprList = Symbol.for('N_ExprList'); export const N_ExprRange = Symbol.for('N_ExprRange'); export const N_ExprTuple = Symbol.for('N_ExprTuple'); export const N_ExprStructure = Symbol.for('N_ExprStructure'); export const N_ExprStructureUpdate = Symbol.for('N_ExprStructureUpdate'); export const N_ExprFunctionCall = Symbol.for('N_ExprFunctionCall'); /* SwitchBranch: pattern -> body */ export const N_SwitchBranch = Symbol.for('N_SwitchBranch'); /* MatchingBranch: pattern -> body */ export const N_MatchingBranch = Symbol.for('N_MatchingBranch'); /* FieldBinding: fieldName <- value */ export const N_FieldBinding = Symbol.for('N_FieldBinding'); /* ConstructorDeclaration */ export const N_ConstructorDeclaration = Symbol.for('N_ConstructorDeclaration'); /* Helper functions for the ASTNode toString method */ function indent(string: string): string { const lines = []; for (const line of string.split('\n')) { lines.push(' ' + line); } return lines.join('\n'); } function showAST(node: ASTNode | Token | ASTNode[]): string { if (node === undefined) { return 'null'; } else if (node instanceof Array) { return '[\n' + showASTs(node).join(',\n') + '\n]'; } else if (node instanceof Token) { return node.toString(); } const tag = Symbol.keyFor(node.tag).substring(2); return tag + '(\n' + showASTs(node.children).join(',\n') + '\n)'; } function showASTs(nodes: ASTNode[]): string[] { const res = []; for (const node of nodes) { res.push(indent(showAST(node))); } return res; } export type ASTDef = ASTDefType | ASTDefProgram | ASTDefInteractiveProgram | ASTDefFunction; /** An instance of ASTNode represents a node of the abstract syntax tree. * - tag should be a node tag symbol. * - children should be (recursively) a possibly empty array of ASTNode's. * - startPos and endPos represent the starting and ending * position of the code fragment in the source code, to aid error * reporting. **/ export class ASTNode { private _tag: symbol; private _children: any[]; private _startPos: SourceReader; private _endPos: SourceReader; private _attributes: Record<string, ASTDef>; public constructor(tag: symbol, children: any[]) { this._tag = tag; this._children = children; this._startPos = UnknownPosition; this._endPos = UnknownPosition; this._attributes = {}; /* Assert this invariant to protect against common mistakes. */ if (!(children instanceof Array)) { throw Error('The children of an ASTNode should be an array.'); } } public toMulangLike(): any { return { tag: this._tag.toString().replace(/(^Symbol\(|\)$)/g, ''), contents: this._children.map((node) => { if (node === undefined) { return 'null'; } else if (node instanceof Array) { return new ASTNode(Symbol('?'), node).toMulangLike().contents; } else if (node instanceof ASTNode) { return node.toMulangLike(); } else if (node instanceof Token) { return node.toString(); } else { return '?'; } }) }; } public toString(): string { return showAST(this); } public get tag(): symbol { return this._tag; } public get children(): any[] { return this._children; } public set startPos(position: SourceReader) { this._startPos = position; } public get startPos(): SourceReader { return this._startPos; } public set endPos(position: SourceReader) { this._endPos = position; } public get endPos(): SourceReader { return this._endPos; } public get attributes(): Record<string, ASTDef> { return this._attributes; } public set attributes(attributes: Record<string, ASTDef>) { this._attributes = attributes; } } /* Main */ export class ASTMain extends ASTNode { public constructor(definitions: ASTDef[]) { super(N_Main, definitions); } public get definitions(): ASTDef[] { return this.children; } } /* Definitions */ export class ASTDefProgram extends ASTNode { public constructor(body: ASTStmtBlock) { super(N_DefProgram, [body]); } public get body(): ASTStmtBlock { return this.children[0]; } } export abstract class ASTNodeWithBranches extends ASTNode { public get branches(): ASTNodeWithPattern[] { return this.children; } } export class ASTDefInteractiveProgram extends ASTNodeWithBranches { public constructor(branches: ASTNode[]) { super(N_DefInteractiveProgram, branches); } public get branches(): ASTNodeWithPattern[] { return this.children; } } export class ASTDefProcedure extends ASTNode { public constructor(name: Token, parameters: Token[], body: ASTNode) { super(N_DefProcedure, [name, parameters, body]); } public get name(): Token { return this.children[0]; } public get parameters(): Token[] { return this.children[1]; } public get body(): ASTNode { return this.children[2]; } } export class ASTDefFunction extends ASTNode { public constructor(name: Token, parameters: Token[], body: ASTNode) { super(N_DefFunction, [name, parameters, body]); } public get name(): Token { return this.children[0]; } public get parameters(): Token[] { return this.children[1]; } public get body(): ASTNode { return this.children[2]; } } export class ASTDefType extends ASTNode { public constructor(typeName: Token, constructorDeclarations: ASTConstructorDeclaration[]) { super(N_DefType, [typeName, constructorDeclarations]); } public get typeName(): Token { return this.children[0]; } public get constructorDeclarations(): ASTConstructorDeclaration[] { return this.children[1]; } } /* Statements */ export class ASTStmtBlock extends ASTNode { public constructor(statements: ASTNode[]) { super(N_StmtBlock, statements); } public get statements(): ASTNode[] { return this.children; } } export class ASTStmtReturn extends ASTNode { public constructor(result: ASTExpr) { super(N_StmtReturn, [result]); } public get result(): ASTExpr { return this.children[0]; } } export class ASTStmtIf extends ASTNode { // Note: elseBlock may be null public constructor(condition: ASTExpr, thenBlock: ASTNode, elseBlock: ASTNode) { super(N_StmtIf, [condition, thenBlock, elseBlock]); } public get condition(): ASTExpr { return this.children[0]; } public get thenBlock(): ASTNode { return this.children[1]; } public get elseBlock(): ASTNode { return this.children[2]; } } export class ASTStmtRepeat extends ASTNode { public constructor(times: ASTExpr, body: ASTNode) { super(N_StmtRepeat, [times, body]); } public get times(): ASTExpr { return this.children[0]; } public get body(): ASTNode { return this.children[1]; } } export abstract class ASTNodeWithPattern extends ASTNode { public get pattern(): ASTPattern { return this.children[0]; } } export class ASTStmtForeach extends ASTNodeWithPattern { public constructor(pattern: ASTNode, range: ASTExpr, body: ASTNode) { super(N_StmtForeach, [pattern, range, body]); } public get pattern(): ASTPattern { return this.children[0]; } public get range(): ASTExpr { return this.children[1]; } public get body(): ASTNode { return this.children[2]; } } export class ASTStmtWhile extends ASTNode { public constructor(condition: ASTExpr, body: ASTNode) { super(N_StmtWhile, [condition, body]); } public get condition(): ASTExpr { return this.children[0]; } public get body(): ASTNode { return this.children[1]; } } export class ASTStmtSwitch extends ASTNodeWithBranches { public constructor(subject: ASTExpr, branches: ASTNodeWithPattern[]) { super(N_StmtSwitch, [subject, branches]); } public get subject(): ASTExpr { return this.children[0]; } public get branches(): ASTNodeWithPattern[] { return this.children[1]; } } export class ASTSwitchBranch extends ASTNodeWithPattern { public constructor(pattern: ASTPattern, body: ASTNode) { super(N_SwitchBranch, [pattern, body]); } public get pattern(): ASTPattern { return this.children[0]; } public get body(): ASTNode { return this.children[1]; } } export class ASTMatchingBranch extends ASTNodeWithPattern { public constructor(pattern: ASTPattern, body: ASTExpr) { super(N_MatchingBranch, [pattern, body]); } public get pattern(): ASTPattern { return this.children[0]; } public get body(): ASTExpr { return this.children[1]; } } export class ASTStmtAssignVariable extends ASTNode { public constructor(variable: Token, value: ASTExpr) { super(N_StmtAssignVariable, [variable, value]); } public get variable(): Token { return this.children[0]; } public get value(): ASTExpr { return this.children[1]; } } export class ASTStmtAssignTuple extends ASTNode { public constructor(variables: Token[], value: ASTExpr) { super(N_StmtAssignTuple, [variables, value]); } public get variables(): Token[] { return this.children[0]; } public get value(): ASTExpr { return this.children[1]; } } export class ASTStmtProcedureCall extends ASTNode { public constructor(procedureName: Token, args: ASTExpr[]) { super(N_StmtProcedureCall, [procedureName, args]); } public get procedureName(): Token { return this.children[0]; } public get args(): ASTExpr[] { return this.children[1]; } } /* Patterns */ export type ASTPattern = | ASTPatternWildcard | ASTPatternVariable | ASTPatternNumber | ASTPatternStructure | ASTPatternTuple | ASTPatternTimeout; export class ASTPatternWildcard extends ASTNode { public constructor(statement?: ASTStmtBlock) { super(N_PatternWildcard, []); } public get boundVariables(): ASTNode[] { return []; } } export class ASTPatternVariable extends ASTNode { public constructor(variableName: Token) { super(N_PatternVariable, [variableName]); } public get variableName(): Token { return this.children[0]; } public get boundVariables(): Token[] { return [this.children[0]]; } } export class ASTPatternNumber extends ASTNode { public constructor(number: Token) { super(N_PatternNumber, [number]); } public get number(): Token { return this.children[0]; } public get boundVariables(): ASTNode[] { return []; } } export class ASTPatternStructure extends ASTNode { public constructor(constructorName: Token, parameters: Token[]) { super(N_PatternStructure, [constructorName, parameters]); } public get constructorName(): Token { return this.children[0]; } public get boundVariables(): Token[] { return this.children[1]; } } export class ASTPatternTuple extends ASTNode { public constructor(parameters: Token[]) { super(N_PatternTuple, parameters); } public get boundVariables(): Token[] { return this.children; } } export class ASTPatternTimeout extends ASTNode { public constructor(timeout: Token) { super(N_PatternTimeout, [timeout]); } public get boundVariables(): ASTNode[] { return []; } public get timeout(): number { return parseInt(this.children[0].value, 10); } } /* Expressions */ export type ASTExpr = | ASTExprVariable | ASTExprConstantNumber | ASTExprConstantString | ASTExprChoose | ASTExprMatching | ASTExprList | ASTExprRange | ASTExprTuple | ASTExprStructure | ASTExprStructureUpdate | ASTExprFunctionCall; export class ASTExprVariable extends ASTNode { public constructor(variableName: Token) { super(N_ExprVariable, [variableName]); } public get variableName(): Token { return this.children[0]; } } export class ASTExprConstantNumber extends ASTNode { public constructor(number: Token) { super(N_ExprConstantNumber, [number]); } public get number(): Token { return this.children[0]; } } export class ASTExprConstantString extends ASTNode { public constructor(string: Token) { super(N_ExprConstantString, [string]); } public get string(): Token { return this.children[0]; } } export class ASTExprChoose extends ASTNode { public constructor(condition: ASTExpr, trueExpr: ASTExpr, falseExpr: ASTExpr) { super(N_ExprChoose, [condition, trueExpr, falseExpr]); } public get condition(): ASTExpr { return this.children[0]; } public get trueExpr(): ASTExpr { return this.children[1]; } public get falseExpr(): ASTExpr { return this.children[2]; } } export class ASTExprMatching extends ASTNodeWithBranches { public constructor(subject: ASTExpr, branches: ASTNode[]) { super(N_ExprMatching, [subject, branches]); } public get subject(): ASTExpr { return this.children[0]; } public get branches(): ASTNodeWithPattern[] { return this.children[1]; } } export class ASTExprList extends ASTNode { public constructor(elements: ASTExpr[]) { super(N_ExprList, elements); } public get elements(): ASTExpr[] { return this.children; } } export class ASTExprRange extends ASTNode { // Note: second may be null public constructor(first: ASTExpr, second: ASTExpr, last: ASTExpr) { super(N_ExprRange, [first, second, last]); } public get first(): ASTExpr { return this.children[0]; } public get second(): ASTExpr { return this.children[1]; } public get last(): ASTExpr { return this.children[2]; } } export class ASTExprTuple extends ASTNode { public constructor(elements: ASTExpr[]) { super(N_ExprTuple, elements); } public get elements(): ASTExpr[] { return this.children; } } export class ASTExprStructure extends ASTNode { public constructor(constructorName: Token, fieldBindings: ASTFieldBinding[]) { super(N_ExprStructure, [constructorName, fieldBindings]); } public get constructorName(): Token { return this.children[0]; } public get fieldBindings(): ASTFieldBinding[] { return this.children[1]; } public fieldNames(): ASTNode[] { const names = []; for (const fieldBinding of this.fieldBindings) { names.push(fieldBinding.fieldName.value); } return names; } } export class ASTExprStructureUpdate extends ASTNode { public constructor( constructorName: Token, original: ASTExpr, fieldBindings: ASTFieldBinding[] ) { super(N_ExprStructureUpdate, [constructorName, original, fieldBindings]); } public get constructorName(): Token { return this.children[0]; } public get original(): ASTExpr { return this.children[1]; } public get fieldBindings(): ASTFieldBinding[] { return this.children[2]; } public fieldNames(): Token[] { const names = []; for (const fieldBinding of this.fieldBindings) { names.push(fieldBinding.fieldName.value); } return names; } } export class ASTExprFunctionCall extends ASTNode { public constructor(functionName: Token, args: ASTExpr[]) { super(N_ExprFunctionCall, [functionName, args]); } public get functionName(): Token { return this.children[0]; } public get args(): ASTExpr[] { return this.children[1]; } } export class ASTFieldBinding extends ASTNode { public constructor(fieldName: Token, value: ASTExpr) { super(N_FieldBinding, [fieldName, value]); } public get fieldName(): Token { return this.children[0]; } public get value(): ASTExpr { return this.children[1]; } } export class ASTConstructorDeclaration extends ASTNode { public constructor(constructorName: Token, fieldNames: Token[]) { super(N_ConstructorDeclaration, [constructorName, fieldNames]); } public get constructorName(): Token { return this.children[0]; } public get fieldNames(): Token[] { return this.children[1]; } }