UNPKG

@gobstones/gobstones-lang

Version:
404 lines 12 kB
/* eslint-disable no-underscore-dangle */ import { UnknownPosition } from '@gobstones/gobstones-parser'; /* Opcodes are constant symbols */ export const I_PushInteger = Symbol.for('I_PushInteger'); export const I_PushString = Symbol.for('I_PushString'); export const I_PushVariable = Symbol.for('I_PushVariable'); export const I_SetVariable = Symbol.for('I_SetVariable'); export const I_UnsetVariable = Symbol.for('I_UnsetVariable'); export const I_Label = Symbol.for('I_Label'); export const I_Jump = Symbol.for('I_Jump'); export const I_JumpIfFalse = Symbol.for('I_JumpIfFalse'); export const I_JumpIfStructure = Symbol.for('I_JumpIfStructure'); export const I_JumpIfTuple = Symbol.for('I_JumpIfTuple'); export const I_Call = Symbol.for('I_Call'); export const I_Return = Symbol.for('I_Return'); export const I_MakeTuple = Symbol.for('I_MakeTuple'); export const I_MakeList = Symbol.for('I_MakeList'); export const I_MakeStructure = Symbol.for('I_MakeStructure'); export const I_UpdateStructure = Symbol.for('I_UpdateStructure'); export const I_ReadTupleComponent = Symbol.for('I_ReadTupleComponent'); export const I_ReadStructureField = Symbol.for('I_ReadStructureField'); export const I_ReadStructureFieldPop = Symbol.for('I_ReadStructureFieldPop'); export const I_Add = Symbol.for('I_Add'); export const I_Dup = Symbol.for('I_Dup'); export const I_Pop = Symbol.for('I_Pop'); export const I_PrimitiveCall = Symbol.for('I_PrimitiveCall'); export const I_SaveState = Symbol.for('I_SaveState'); export const I_RestoreState = Symbol.for('I_RestoreState'); export const I_TypeCheck = Symbol.for('I_TypeCheck'); export class Code { constructor(instructions) { this._instructions = instructions; } toString() { const res = []; for (const instruction of this._instructions) { res.push(instruction.toString()); } return res.join('\n'); } produce(instruction) { this._instructions.push(instruction); } /* Return the instruction at the given location */ at(ip) { if (ip >= 0 && ip < this._instructions.length) { return this._instructions[ip]; } else { throw Error('Code: instruction pointer out of range.'); } } /* Return a dictionary mapping label names to their corresponding * instruction pointers. */ labelTargets() { const labelTargets = {}; for (let i = 0; i < this._instructions.length; i++) { if (this._instructions[i].opcode === I_Label) { const label = this._instructions[i].label; if (label in labelTargets) { throw Error('Code: label "' + label + '" is repeated.'); } labelTargets[label] = i; } } return labelTargets; } } function argToString(arg) { if (arg instanceof Array) { const res = []; for (const elem of arg) { res.push(argToString(elem)); } return '[' + res.join(', ') + ']'; } else { return arg.toString(); } } export class Instruction { constructor(opcode, args) { this._opcode = opcode; this._args = args; this._startPos = UnknownPosition; this._endPos = UnknownPosition; } toString() { const opcode = Symbol.keyFor(this._opcode).substring(2); const sargs = []; for (const arg of this._args) { sargs.push(argToString(arg)); } return ' ' + opcode + ' ' + sargs.join(', '); } get opcode() { return this._opcode; } get args() { return this._args; } set startPos(position) { this._startPos = position; } get startPos() { return this._startPos; } set endPos(position) { this._endPos = position; } get endPos() { return this._endPos; } } /* Push a constant on the stack. */ export class IPushInteger extends Instruction { constructor(number) { super(I_PushInteger, [number]); } get number() { return this._args[0]; } } export class IPushString extends Instruction { constructor(string) { super(I_PushString, [string]); } get string() { return this._args[0]; } } /* Push a local index/variable/parameter on the stack. */ export class IPushVariable extends Instruction { constructor(variableName) { super(I_PushVariable, [variableName]); } get variableName() { return this._args[0]; } } /* Set a local index/variable/parameter to the value on the top of the stack. */ export class ISetVariable extends Instruction { constructor(variableName) { super(I_SetVariable, [variableName]); } get variableName() { return this._args[0]; } } /* Unset a local index/variable/parameter. * This should be used to avoid the variable being used after the end * of its scope. * * E.g. "i" should have no value after the end of the foreach: * * foreach i in [1,2,3] { * } * x := i */ export class IUnsetVariable extends Instruction { constructor(variableName) { super(I_UnsetVariable, [variableName]); } get variableName() { return this._args[0]; } } /* Pseudo-instruction to mark the target of a jump. */ export class ILabel extends Instruction { constructor(label) { super(I_Label, [label]); } toString() { return this.label + ':'; } get label() { return this._args[0]; } } /* Unconditional jump. */ export class IJump extends Instruction { constructor(targetLabel) { super(I_Jump, [targetLabel]); } get targetLabel() { return this._args[0]; } } /* Jump if the top of the stack is False. * Pops the top of the stack. */ export class IJumpIfFalse extends Instruction { constructor(targetLabel) { super(I_JumpIfFalse, [targetLabel]); } get targetLabel() { return this._args[0]; } } /* Jump if the top of the stack is a structure built using the given * constructor. Does NOT pop the top of the stack. */ export class IJumpIfStructure extends Instruction { constructor(constructorName, targetLabel) { super(I_JumpIfStructure, [constructorName, targetLabel]); } get constructorName() { return this._args[0]; } get targetLabel() { return this._args[1]; } } /* Jump if the top of the stack is an n-tuple of the given size. * Does NOT pop the top of the stack. */ export class IJumpIfTuple extends Instruction { constructor(size, targetLabel) { super(I_JumpIfTuple, [size, targetLabel]); } get size() { return this._args[0]; } get targetLabel() { return this._args[1]; } } /* Call a subroutine (procedure or function). * The arguments are expected to be located in the stack * with the last one at the top. * * The arguments are popped from the current frame and pushed * onto the new frame. */ export class ICall extends Instruction { constructor(targetLabel, nargs) { super(I_Call, [targetLabel, nargs]); } get targetLabel() { return this._args[0]; } get nargs() { return this._args[1]; } } /* Return from a routine to the caller. * If returning a value (from a function or program), * it must be on the top of the stack. */ export class IReturn extends Instruction { constructor() { super(I_Return, []); } } /* Make a tuple of the given size. * The components are expected to be located in the stack * with the last one at the top. */ export class IMakeTuple extends Instruction { constructor(size) { super(I_MakeTuple, [size]); } get size() { return this._args[0]; } } /* Make a list of the given size. * The elements are expected to be located in the stack * with the last one at the top. */ export class IMakeList extends Instruction { constructor(size) { super(I_MakeList, [size]); } get size() { return this._args[0]; } } /* Make a structure using the given constructor and the given fields. * The values of the fields are expected to be located in the stack * with the last one at the top. */ export class IMakeStructure extends Instruction { constructor(typeName, constructorName, fieldNames) { super(I_MakeStructure, [typeName, constructorName, fieldNames]); } get typeName() { return this._args[0]; } get constructorName() { return this._args[1]; } get fieldNames() { return this._args[2]; } } /* Update a structure built using the given constructor with the given * fields. * The stack should have a structure built using the given constructor, * followed by the values of the fields that are expected. * The last field should be at the top. */ export class IUpdateStructure extends Instruction { constructor(typeName, constructorName, fieldNames) { super(I_UpdateStructure, [typeName, constructorName, fieldNames]); } get typeName() { return this._args[0]; } get constructorName() { return this._args[1]; } get fieldNames() { return this._args[2]; } } /* Read the n-th component from the tuple at the top of the stack. * Does not pop the tuple. */ export class IReadTupleComponent extends Instruction { constructor(index) { super(I_ReadTupleComponent, [index]); } get index() { return this._args[0]; } } /* Read the given field from the structure at the top of the stack. * Does not pop the structure. */ export class IReadStructureField extends Instruction { constructor(fieldName) { super(I_ReadStructureField, [fieldName]); } get fieldName() { return this._args[0]; } } /* Read the given field from the structure at the top of the stack. * Pop the structure. */ export class IReadStructureFieldPop extends Instruction { constructor(fieldName) { super(I_ReadStructureFieldPop, [fieldName]); } get fieldName() { return this._args[0]; } } /* Add the topmost elements of the stack (used mostly for testing/debugging) */ export class IAdd extends Instruction { constructor() { super(I_Add, []); } } /* Duplicate the top of the stack (there should be at least one element) */ export class IDup extends Instruction { constructor() { super(I_Dup, []); } } /* Pop the top of the stack (there should be at least one element) */ export class IPop extends Instruction { constructor() { super(I_Pop, []); } } /* Call a primitive function. * * The arguments are expected to be located in the stack * with the last one at the top. * * Note: the compiler relies on various primitive functions. * For example, the operation to make a range is a primitive * function: * * function _makeRange(start, end) * * So is the function that checks whether the top of the stack is a list, * etc. (required to compile a "foreach"), and so on. */ export class IPrimitiveCall extends Instruction { constructor(primitiveName, nargs) { super(I_PrimitiveCall, [primitiveName, nargs]); } get primitiveName() { return this._args[0]; } get nargs() { return this._args[1]; } } /* Save the global state (when entering a function) */ export class ISaveState extends Instruction { constructor() { super(I_SaveState, []); } } /* Restore the global state (when leaving a function) */ export class IRestoreState extends Instruction { constructor() { super(I_RestoreState, []); } } /* Check that the top of the stack has the given type. * Does not pop the top of the stack. */ export class ITypeCheck extends Instruction { constructor(type) { super(I_TypeCheck, [type]); } get type() { return this._args[0]; } } //# sourceMappingURL=instruction.js.map