@gobstones/gobstones-parser
Version:
Gobstones parser
730 lines (592 loc) • 19 kB
text/typescript
/* 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];
}
}