UNPKG

@gobstones/gobstones-lang

Version:
876 lines 34.6 kB
/* eslint-disable no-underscore-dangle */ import { i18n } from './i18n'; import { ValueInteger, ValueList, ValueStructure, TypeAny, TypeInteger, TypeString, TypeStructure, TypeList, joinTypes } from './value'; import { GbsRuntimeError } from '@gobstones/gobstones-parser'; /* * This module provides the runtime support for the execution of a program. * * The runtime support includes: * * - A definition of a class RuntimeState, representing the global state * of a program. * * - A definition of a class RuntimePrimitives, representing the available * primitive functions. * * This file is a particular implementation, in which RuntimeState * represents a Gobstones board, and RuntimePrimitives are the primitives * functions and procedures available in Gobstones. * * Potential variants of the language might have a different notion of * global state, and different available primitives. */ function fail(startPos, endPos, reason, args) { throw new GbsRuntimeError(startPos, endPos, reason, args); } const boolEnum = () => [i18n('CONS:False'), i18n('CONS:True')]; const colorEnum = () => [ i18n('CONS:Color0'), i18n('CONS:Color1'), i18n('CONS:Color2'), i18n('CONS:Color3') ]; const dirEnum = () => [ i18n('CONS:Dir0'), i18n('CONS:Dir1'), i18n('CONS:Dir2'), i18n('CONS:Dir3') ]; /* Enumeration of all the constructors of the Event type, including * INIT and TIMEOUT. */ function keyEventEnum() { const modifiers = [ '', 'CTRL_', 'ALT_', 'SHIFT_', 'CTRL_ALT_', 'CTRL_SHIFT_', 'ALT_SHIFT_', 'CTRL_ALT_SHIFT_' ]; const charKeys = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]; const specialKeys = [ 'SPACE', 'RETURN', 'TAB', 'BACKSPACE', 'ESCAPE', 'INSERT', 'DELETE', 'HOME', 'END', 'PAGEUP', 'PAGEDOWN', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12' ]; const symbolKeys = [ 'AMPERSAND', 'ASTERISK', 'AT', 'BACKSLASH', 'CARET', 'COLON', 'DOLLAR', 'EQUALS', 'EXCLAIM', 'GREATER', 'HASH', 'LESS', 'PERCENT', 'PLUS', 'SEMICOLON', 'SLASH', 'QUESTION', 'QUOTE', 'QUOTEDBL', 'UNDERSCORE', 'LEFTPAREN', 'RIGHTPAREN', 'LEFTBRACKET', 'RIGHTBRACKET', 'LEFTBRACE', 'RIGHTBRACE' ]; const arrowKeys = ['LEFT', 'RIGHT', 'UP', 'DOWN']; const keys = charKeys.concat(specialKeys).concat(symbolKeys).concat(arrowKeys); const eventNames = []; for (const modifier of modifiers) { for (const key of keys) { eventNames.push('K_' + modifier + key); } } return eventNames; } const KEY_EVENT_ENUM = keyEventEnum(); const eventEnum = () => [i18n('CONS:INIT'), i18n('CONS:TIMEOUT')].concat(KEY_EVENT_ENUM); const toEnum = (enumeration, name) => enumeration.indexOf(name); const fromEnum = (enumeration, index) => enumeration[index]; const dirOpposite = (dirName) => fromEnum(dirEnum(), (toEnum(dirEnum(), dirName) + 2) % 4); const dirNext = (dirName) => fromEnum(dirEnum(), (toEnum(dirEnum(), dirName) + 1) % 4); const dirPrev = (dirName) => fromEnum(dirEnum(), (toEnum(dirEnum(), dirName) + 3) % 4); const colorNext = (colorName) => fromEnum(colorEnum(), (toEnum(colorEnum(), colorName) + 1) % 4); const colorPrev = (colorName) => fromEnum(colorEnum(), (toEnum(colorEnum(), colorName) + 3) % 4); /* * An instance of RuntimeState represents the current global state of * a program. In the case of Gobstones, it is a Gobstones board. * * It MUST implement the following methods: * * this.clone() ~~> returns a copy of the state * */ export class RuntimeState { constructor() { /* * The board is represented as a list of columns, so that board[x] is the * x-th column and board[x][y] is the cell at (x, y). * * By default, create an empty 9x9 board. */ this._width = 11; this._height = 7; this._board = []; for (let x = 0; x < this._width; x++) { const column = []; for (let y = 0; y < this._height; y++) { column.push(this._emptyCell()); } this._board.push(column); } this._head = { x: 0, y: 0 }; } clone() { const newState = new RuntimeState(); newState._width = this._width; newState._height = this._height; newState._board = []; for (let x = 0; x < this._width; x++) { const column = []; for (let y = 0; y < this._height; y++) { const cell = {}; for (const colorName of colorEnum()) { cell[colorName] = this._board[x][y][colorName]; } column.push(cell); } newState._board.push(column); } newState._head = { x: this._head.x, y: this._head.y }; return newState; } /* Dump the state to a Jboard data structure */ dump() { const jboard = { head: [], width: 0, height: 0, board: [] }; jboard.width = this._width; jboard.height = this._height; jboard.head = [this._head.x, this._head.y]; jboard.board = []; for (let x = 0; x < this._width; x++) { const column = []; for (let y = 0; y < this._height; y++) { const cell = {}; cell['a'] = this._board[x][y][i18n('CONS:Color0')].asNumber(); cell['n'] = this._board[x][y][i18n('CONS:Color1')].asNumber(); cell['r'] = this._board[x][y][i18n('CONS:Color2')].asNumber(); cell['v'] = this._board[x][y][i18n('CONS:Color3')].asNumber(); column.push(cell); } jboard.board.push(column); } return jboard; } /* Load the state from a Jboard data structure */ load(jboard) { this._width = jboard.width; this._height = jboard.height; this._head.x = jboard.head[0]; this._head.y = jboard.head[1]; this._board = []; for (let x = 0; x < this._width; x++) { const row = []; for (let y = 0; y < this._height; y++) { const cell = jboard.board[x][y]; const newCell = {}; newCell[i18n('CONS:Color0')] = new ValueInteger(cell['a']); newCell[i18n('CONS:Color1')] = new ValueInteger(cell['n']); newCell[i18n('CONS:Color2')] = new ValueInteger(cell['r']); newCell[i18n('CONS:Color3')] = new ValueInteger(cell['v']); row.push(newCell); } this._board.push(row); } } /* Gobstones specific methods */ putStone(colorName) { let n = this._board[this._head.x][this._head.y][colorName]; n = n.add(new ValueInteger(1)); this._board[this._head.x][this._head.y][colorName] = n; } removeStone(colorName) { let n = this._board[this._head.x][this._head.y][colorName]; if (n.le(new ValueInteger(0))) { throw Error('Cannot remove stone.'); } n = n.sub(new ValueInteger(1)); this._board[this._head.x][this._head.y][colorName] = n; } numStones(colorName) { return this._board[this._head.x][this._head.y][colorName]; } move(dirName) { if (!this.canMove(dirName)) { throw Error('Cannot move.'); } const delta = this._deltaForDirection(dirName); this._head.x += delta[0]; this._head.y += delta[1]; } goToEdge(dirName) { if (dirName === i18n('CONS:Dir0')) { this._head.y = this._height - 1; } else if (dirName === i18n('CONS:Dir1')) { this._head.x = this._width - 1; } else if (dirName === i18n('CONS:Dir2')) { this._head.y = 0; } else if (dirName === i18n('CONS:Dir3')) { this._head.x = 0; } else { throw Error('Invalid direction: ' + dirName); } } emptyBoardContents() { for (let x = 0; x < this._width; x++) { for (let y = 0; y < this._height; y++) { this._board[x][y] = this._emptyCell(); } } } canMove(dirName) { const delta = this._deltaForDirection(dirName); const x = this._head.x + delta[0]; const y = this._head.y + delta[1]; return x >= 0 && x < this._width && y >= 0 && y < this._height; } _deltaForDirection(dirName) { let delta; if (dirName === i18n('CONS:Dir0')) { delta = [0, 1]; } else if (dirName === i18n('CONS:Dir1')) { delta = [1, 0]; } else if (dirName === i18n('CONS:Dir2')) { delta = [0, -1]; } else if (dirName === i18n('CONS:Dir3')) { delta = [-1, 0]; } else { throw Error('Invalid direction: ' + dirName); } return delta; } _emptyCell() { const cell = {}; for (const colorName of colorEnum()) { cell[colorName] = new ValueInteger(0); } return cell; } } class PrimitiveOperation { constructor(argumentTypes, argumentValidator, implementation) { this._argumentTypes = argumentTypes; this._argumentValidator = argumentValidator; this._implementation = implementation; } get argumentTypes() { return this._argumentTypes; } nargs() { return this._argumentTypes.length; } call(globalState, args) { return this._implementation.apply(undefined, [globalState].concat(args)); } /* Check that the arguments are valid according to the validator. * The validator should be a function receiving a start and end * positions, and a list of arguments. * It should throw a GbsRuntimeError if the arguments are invalid. */ validateArguments(startPos, endPos, globalState, args) { this._argumentValidator(startPos, endPos, globalState, args); } } /* Casting Gobstones values to JavaScript values and vice-versa */ const typeAny = new TypeAny(); const typeInteger = new TypeInteger(); const typeString = new TypeString(); const typeBool = () => new TypeStructure(i18n('TYPE:Bool'), {}); const typeListAny = new TypeList(new TypeAny()); function valueFromBool(bool) { if (bool) { return new ValueStructure(i18n('TYPE:Bool'), i18n('CONS:True'), {}); } else { return new ValueStructure(i18n('TYPE:Bool'), i18n('CONS:False'), {}); } } export const boolFromValue = (value) => value.constructorName === i18n('CONS:True'); const typeColor = () => new TypeStructure(i18n('TYPE:Color'), {}); const valueFromColor = (colorName) => new ValueStructure(i18n('TYPE:Color'), colorName, {}); const colorFromValue = (value) => value.constructorName; const typeDir = () => new TypeStructure(i18n('TYPE:Dir'), {}); const valueFromDir = (dirName) => new ValueStructure(i18n('TYPE:Dir'), dirName, {}); const dirFromValue = (value) => value.constructorName; /* Argument validators */ function noValidation() { /* No validation */ } const isInteger = (x) => joinTypes(x.type(), typeInteger) !== undefined; const isBool = (x) => joinTypes(x.type(), typeBool()) !== undefined; const isColor = (x) => joinTypes(x.type(), typeColor()) !== undefined; const isDir = (x) => joinTypes(x.type(), typeDir()) !== undefined; export const typesWithOpposite = () => [typeInteger, typeBool(), typeDir()]; export const typesWithOrder = () => [typeInteger, typeBool(), typeColor(), typeDir()]; /* Generic operations */ function enumIndex(value) { if (isBool(value)) { if (boolFromValue(value)) { return 1; } else { return 0; } } else if (isColor(value)) { return toEnum(colorEnum(), colorFromValue(value)); } else if (isDir(value)) { return toEnum(dirEnum(), dirFromValue(value)); } else { throw Error('Value should be Bool, Color or Dir.'); } } function genericLE(a, b) { if (isInteger(a)) { return valueFromBool(a.le(b)); } else { const indexA = enumIndex(a); const indexB = enumIndex(b); return valueFromBool(indexA <= indexB); } } function genericGE(a, b) { if (isInteger(a)) { return valueFromBool(a.ge(b)); } else { const indexA = enumIndex(a); const indexB = enumIndex(b); return valueFromBool(indexA >= indexB); } } function genericLT(a, b) { if (isInteger(a)) { return valueFromBool(a.lt(b)); } else { const indexA = enumIndex(a); const indexB = enumIndex(b); return valueFromBool(indexA < indexB); } } function genericGT(a, b) { if (isInteger(a)) { return valueFromBool(a.gt(b)); } else { const indexA = enumIndex(a); const indexB = enumIndex(b); return valueFromBool(indexA > indexB); } } function genericNext(a) { if (isInteger(a)) { return a.add(new ValueInteger(1)); } else if (isBool(a)) { if (boolFromValue(a)) { return valueFromBool(false); } else { return valueFromBool(true); } } else if (isColor(a)) { return valueFromColor(colorNext(colorFromValue(a))); } else if (isDir(a)) { return valueFromDir(dirNext(dirFromValue(a))); } else { throw Error('genericNext: value has no next.'); } } function genericPrev(a) { if (isInteger(a)) { return a.sub(new ValueInteger(1)); } else if (isBool(a)) { if (boolFromValue(a)) { return valueFromBool(false); } else { return valueFromBool(true); } } else if (isColor(a)) { return valueFromColor(colorPrev(colorFromValue(a))); } else if (isDir(a)) { return valueFromDir(dirPrev(dirFromValue(a))); } else { throw Error('genericPrev: value has no prev.'); } } function genericOpposite(a) { if (isInteger(a)) { return a.negate(); } else if (isBool(a)) { return valueFromBool(!boolFromValue(a)); } else if (isDir(a)) { return valueFromDir(dirOpposite(dirFromValue(a))); } else { throw Error('genericOpposite: value has no opposite.'); } } /* Validate that the type of 'x' is among the given list of types */ function validateTypeAmong(startPos, endPos, x, types) { /* Succeed if the type of x is in the list 'types' */ for (const type of types) { if (joinTypes(x.type(), type) !== undefined) { return; } } /* Report error */ fail(startPos, endPos, 'expected-value-of-some-type-but-got', [types, x.type()]); } /* Validate that the types of 'x' and 'y' are compatible */ function validateCompatibleTypes(startPos, endPos, x, y) { if (joinTypes(x.type(), y.type()) === undefined) { fail(startPos, endPos, 'expected-values-to-have-compatible-types', [x.type(), y.type()]); } } /* Runtime primitives */ export class RuntimePrimitives { constructor() { /* this._primitiveTypes is a dictionary indexed by type names. * * this._primitiveTypes[typeName] is a dictionary indexed by * the constructor names of the given type. * * this._primitiveTypes[typeName][constructorName] * is a list of field names. */ this._primitiveTypes = {}; /* this._primitiveProcedures and this._primitiveFunctions * are dictionaries indexed by the name of the primitive operation * (procedure or function). Their value is an instance of * PrimitiveOperation. */ this._primitiveProcedures = {}; this._primitiveFunctions = {}; /* --Primitive types-- */ /* Booleans */ this._primitiveTypes[i18n('TYPE:Bool')] = {}; for (const boolName of boolEnum()) { this._primitiveTypes[i18n('TYPE:Bool')][boolName] = []; } /* Colors */ this._primitiveTypes[i18n('TYPE:Color')] = {}; for (const colorName of colorEnum()) { this._primitiveTypes[i18n('TYPE:Color')][colorName] = []; } /* Directions */ this._primitiveTypes[i18n('TYPE:Dir')] = {}; for (const dirName of dirEnum()) { this._primitiveTypes[i18n('TYPE:Dir')][dirName] = []; } /* Events */ this._primitiveTypes[i18n('TYPE:Event')] = {}; for (const eventName of eventEnum()) { this._primitiveTypes[i18n('TYPE:Event')][eventName] = []; } /* --Primitive procedures-- */ this._primitiveProcedures[i18n('PRIM:TypeCheck')] = new PrimitiveOperation([typeAny, typeAny, typeString], (startPos, endPos, globalState, args) => { const v1 = args[0]; const v2 = args[1]; const errorMessage = args[2]; if (joinTypes(v1.type(), v2.type()) === undefined) { fail(startPos, endPos, 'typecheck-failed', [ errorMessage.string, v1.type(), v2.type() ]); } }, (globalState, color) => undefined); this._primitiveProcedures[i18n('PRIM:PutStone')] = new PrimitiveOperation([typeColor()], noValidation, (globalState, color) => { globalState.putStone(colorFromValue(color)); return undefined; }); this._primitiveProcedures[i18n('PRIM:RemoveStone')] = new PrimitiveOperation([typeColor()], (startPos, endPos, globalState, args) => { const colorName = colorFromValue(args[0]); if (globalState.numStones(colorName).le(new ValueInteger(0))) { fail(startPos, endPos, 'cannot-remove-stone', [colorName]); } }, (globalState, color) => { globalState.removeStone(colorFromValue(color)); return undefined; }); this._primitiveProcedures[i18n('PRIM:Move')] = new PrimitiveOperation([typeDir()], (startPos, endPos, globalState, args) => { const dirName = dirFromValue(args[0]); if (!globalState.canMove(dirName)) { fail(startPos, endPos, 'cannot-move-to', [dirName]); } }, (globalState, dir) => { globalState.move(dirFromValue(dir)); return undefined; }); this._primitiveProcedures[i18n('PRIM:GoToEdge')] = new PrimitiveOperation([typeDir()], noValidation, (globalState, dir) => { globalState.goToEdge(dirFromValue(dir)); return undefined; }); this._primitiveProcedures[i18n('PRIM:EmptyBoardContents')] = new PrimitiveOperation([], noValidation, (globalState, dir) => { globalState.emptyBoardContents(); return undefined; }); this._primitiveProcedures['_FAIL'] = /* Procedure that always fails */ new PrimitiveOperation([typeString], (startPos, endPos, globalState, args) => { fail(startPos, endPos, args[0].string, []); }, (globalState, errMsg /* Unreachable */) => undefined); /* --Primitive functions-- */ this._primitiveFunctions['_makeRange'] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const first = args[0]; const last = args[1]; validateCompatibleTypes(startPos, endPos, first, last); validateTypeAmong(startPos, endPos, first, typesWithOrder()); validateTypeAmong(startPos, endPos, last, typesWithOrder()); }, (globalState, first, last) => { let current = first; if (boolFromValue(genericGT(current, last))) { return new ValueList([]); } const result = []; while (boolFromValue(genericLT(current, last))) { result.push(current); current = genericNext(current); } result.push(current); return new ValueList(result); }); this._primitiveFunctions['not'] = new PrimitiveOperation([typeBool()], noValidation, (globalState, x) => valueFromBool(!boolFromValue(x))); this._primitiveFunctions['&&'] = new PrimitiveOperation([typeAny, typeAny], noValidation, /* * This function is a stub so the linter recognizes '&&' * as a defined primitive function of arity 2. * * The implementation of '&&' is treated specially by the * compiler to account for short-circuiting. */ (globalState, x, y) => { throw Error('The function "&&" should never be called'); }); this._primitiveFunctions['||'] = new PrimitiveOperation([typeAny, typeAny], noValidation, /* * This function is a stub so the linter recognizes '||' * as a defined primitive function of arity 2. * * The implementation of '||' is treated specially by the * compiler to account for short-circuiting. */ (globalState, x, y) => { throw Error('The function "||" should never be called'); }); this._primitiveFunctions['_makeRangeWithSecond'] = new PrimitiveOperation([typeAny, typeAny, typeAny], (startPos, endPos, globalState, args) => { const first = args[0]; const last = args[1]; const second = args[2]; validateTypeAmong(startPos, endPos, first, [typeInteger]); validateTypeAmong(startPos, endPos, last, [typeInteger]); validateTypeAmong(startPos, endPos, second, [typeInteger]); }, (globalState, first, last, second) => { const delta = second.sub(first); if (delta.lt(new ValueInteger(1))) { return new ValueList([]); } let current = first; const result = []; while (current.le(last)) { result.push(current); current = current.add(delta); } return new ValueList(result); }); this._primitiveFunctions['_unsafeListLength'] = new PrimitiveOperation([typeAny], noValidation, (globalState, list) => new ValueInteger(list.length())); this._primitiveFunctions['_unsafeListNth'] = new PrimitiveOperation([typeAny, typeAny], noValidation, (globalState, list, index) => list.elements[index.asNumber()]); this._primitiveFunctions[i18n('PRIM:numStones')] = new PrimitiveOperation([typeColor()], noValidation, (globalState, color) => globalState.numStones(colorFromValue(color))); this._primitiveFunctions[i18n('PRIM:anyStones')] = new PrimitiveOperation([typeColor()], noValidation, (globalState, color) => { const num = globalState.numStones(colorFromValue(color)); return valueFromBool(num.gt(new ValueInteger(0))); }); this._primitiveFunctions[i18n('PRIM:canMove')] = new PrimitiveOperation([typeDir()], noValidation, (globalState, dir) => valueFromBool(globalState.canMove(dirFromValue(dir)))); this._primitiveFunctions[i18n('PRIM:next')] = new PrimitiveOperation([typeAny], (startPos, endPos, globalState, args) => { const value = args[0]; validateTypeAmong(startPos, endPos, value, typesWithOrder()); }, (globalState, value) => genericNext(value)); this._primitiveFunctions[i18n('PRIM:prev')] = new PrimitiveOperation([typeAny], (startPos, endPos, globalState, args) => { const value = args[0]; validateTypeAmong(startPos, endPos, value, typesWithOrder()); }, (globalState, value) => genericPrev(value)); this._primitiveFunctions[i18n('PRIM:opposite')] = new PrimitiveOperation([typeAny], (startPos, endPos, globalState, args) => { const value = args[0]; validateTypeAmong(startPos, endPos, value, typesWithOpposite()); }, (globalState, value) => genericOpposite(value)); this._primitiveFunctions[i18n('PRIM:minBool')] = new PrimitiveOperation([], noValidation, (globalState) => valueFromBool(false)); this._primitiveFunctions[i18n('PRIM:maxBool')] = new PrimitiveOperation([], noValidation, (globalState) => valueFromBool(true)); this._primitiveFunctions[i18n('PRIM:minColor')] = new PrimitiveOperation([], noValidation, (globalState) => valueFromColor(colorEnum()[0])); this._primitiveFunctions[i18n('PRIM:maxColor')] = new PrimitiveOperation([], noValidation, (globalState) => valueFromColor(colorEnum()[colorEnum().length - 1])); this._primitiveFunctions[i18n('PRIM:minDir')] = new PrimitiveOperation([], noValidation, (globalState) => valueFromDir(dirEnum()[0])); this._primitiveFunctions[i18n('PRIM:maxDir')] = new PrimitiveOperation([], noValidation, (globalState) => valueFromDir(dirEnum()[dirEnum().length - 1])); /* Arithmetic operators */ this._primitiveFunctions['+'] = new PrimitiveOperation([typeInteger, typeInteger], noValidation, (globalState, a, b) => a.add(b)); this._primitiveFunctions['-'] = new PrimitiveOperation([typeInteger, typeInteger], noValidation, (globalState, a, b) => a.sub(b)); this._primitiveFunctions['*'] = new PrimitiveOperation([typeInteger, typeInteger], noValidation, (globalState, a, b) => a.mul(b)); this._primitiveFunctions['div'] = new PrimitiveOperation([typeInteger, typeInteger], (startPos, endPos, globalState, args) => { const b = args[1]; if (b.eq(new ValueInteger(0))) { fail(startPos, endPos, 'cannot-divide-by-zero', []); } }, (globalState, a, b) => a.div(b)); this._primitiveFunctions['mod'] = new PrimitiveOperation([typeInteger, typeInteger], (startPos, endPos, globalState, args) => { const b = args[1]; if (b.eq(new ValueInteger(0))) { fail(startPos, endPos, 'cannot-divide-by-zero', []); } }, (globalState, a, b) => a.mod(b)); this._primitiveFunctions['^'] = new PrimitiveOperation([typeInteger, typeInteger], (startPos, endPos, globalState, args) => { const b = args[1]; if (b.lt(new ValueInteger(0))) { fail(startPos, endPos, 'negative-exponent', []); } }, (globalState, a, b) => a.pow(b)); this._primitiveFunctions['-(unary)'] = new PrimitiveOperation([typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; validateTypeAmong(startPos, endPos, a, typesWithOpposite()); }, (globalState, a) => genericOpposite(a)); /* Relational operators */ this._primitiveFunctions['=='] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); }, (globalState, a, b) => valueFromBool(a.equal(b))); this._primitiveFunctions['/='] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); }, (globalState, a, b) => valueFromBool(!a.equal(b))); this._primitiveFunctions['<='] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); validateTypeAmong(startPos, endPos, a, typesWithOrder()); validateTypeAmong(startPos, endPos, b, typesWithOrder()); }, (globalState, a, b) => genericLE(a, b)); this._primitiveFunctions['>='] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); validateTypeAmong(startPos, endPos, a, typesWithOrder()); validateTypeAmong(startPos, endPos, b, typesWithOrder()); }, (globalState, a, b) => genericGE(a, b)); this._primitiveFunctions['<'] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); validateTypeAmong(startPos, endPos, a, typesWithOrder()); validateTypeAmong(startPos, endPos, b, typesWithOrder()); }, (globalState, a, b) => genericLT(a, b)); this._primitiveFunctions['>'] = new PrimitiveOperation([typeAny, typeAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); validateTypeAmong(startPos, endPos, a, typesWithOrder()); validateTypeAmong(startPos, endPos, b, typesWithOrder()); }, (globalState, a, b) => genericGT(a, b)); /* User-triggered failure */ this._primitiveProcedures[i18n('PRIM:BOOM')] = new PrimitiveOperation([typeString], (startPos, endPos, globalState, args) => { fail(startPos, endPos, 'boom-called', [args[0].string]); }, (globalState, msg) => { throw Error('Should not be reachable.'); }); this._primitiveFunctions[i18n('PRIM:boom')] = this._primitiveProcedures[i18n('PRIM:BOOM')]; /* List operators */ this._primitiveFunctions['++'] = new PrimitiveOperation([typeListAny, typeListAny], (startPos, endPos, globalState, args) => { const a = args[0]; const b = args[1]; validateCompatibleTypes(startPos, endPos, a, b); }, (globalState, a, b) => a.append(b)); this._primitiveFunctions[i18n('PRIM:isEmpty')] = new PrimitiveOperation([typeListAny], noValidation, (globalState, a) => valueFromBool(a.length() === 0)); this._primitiveFunctions[i18n('PRIM:head')] = new PrimitiveOperation([typeListAny], (startPos, endPos, globalState, args) => { const a = args[0]; if (a.length() === 0) { fail(startPos, endPos, 'list-cannot-be-empty', []); } }, (globalState, a) => a.head()); this._primitiveFunctions[i18n('PRIM:tail')] = new PrimitiveOperation([typeListAny], (startPos, endPos, globalState, args) => { const a = args[0]; if (a.length() === 0) { fail(startPos, endPos, 'list-cannot-be-empty', []); } }, (globalState, a) => a.tail()); this._primitiveFunctions[i18n('PRIM:oldTail')] = this._primitiveFunctions[i18n('PRIM:tail')]; this._primitiveFunctions[i18n('PRIM:init')] = new PrimitiveOperation([typeListAny], (startPos, endPos, globalState, args) => { const a = args[0]; if (a.length() === 0) { fail(startPos, endPos, 'list-cannot-be-empty', []); } }, (globalState, a) => a.init()); this._primitiveFunctions[i18n('PRIM:last')] = new PrimitiveOperation([typeListAny], (startPos, endPos, globalState, args) => { const a = args[0]; if (a.length() === 0) { fail(startPos, endPos, 'list-cannot-be-empty', []); } }, (globalState, a) => a.last()); } /* Types */ types() { const typeNames = []; for (const typeName in this._primitiveTypes) { typeNames.push(typeName); } return typeNames; } typeConstructors(typeName) { if (!(typeName in this._primitiveTypes)) { throw Error('Not a primitive type: ' + typeName); } const constructorNames = []; for (const constructorName in this._primitiveTypes[typeName]) { constructorNames.push(constructorName); } return constructorNames; } constructorFields(typeName, constructorName) { if (!(typeName in this._primitiveTypes)) { throw Error('Not a primitive type: ' + typeName); } if (!(constructorName in this._primitiveTypes[typeName])) { throw Error('Not a primitive constructor: ' + constructorName); } return this._primitiveTypes[typeName][constructorName]; } /* Operations */ isOperation(primitiveName) { return (primitiveName in this._primitiveProcedures || primitiveName in this._primitiveFunctions); } getOperation(primitiveName) { if (primitiveName in this._primitiveProcedures) { return this._primitiveProcedures[primitiveName]; } else if (primitiveName in this._primitiveFunctions) { return this._primitiveFunctions[primitiveName]; } else { throw Error(primitiveName + ' is not a primitive.'); } } /* Procedures */ procedures() { const procedureNames = []; for (const procedureName in this._primitiveProcedures) { procedureNames.push(procedureName); } return procedureNames; } isProcedure(primitiveName) { return primitiveName in this._primitiveProcedures; } /* Functions */ functions() { const functionNames = []; for (const functionName in this._primitiveFunctions) { functionNames.push(functionName); } return functionNames; } isFunction(primitiveName) { return primitiveName in this._primitiveFunctions; } } //# sourceMappingURL=runtime.js.map