UNPKG

@jplorg/jpl

Version:
2,377 lines (2,344 loc) 45.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.opAnd = opAnd; exports.opArrayConstructor = opArrayConstructor; exports.opComparison = opComparison; exports.opConstant = opConstant; exports.opDifference = opDifference; exports.opEquality = opEquality; exports.opErrorSuppression = opErrorSuppression; exports.opFunctionDefinition = opFunctionDefinition; exports.opGroup = opGroup; exports.opIf = opIf; exports.opMultiplication = opMultiplication; exports.opNamedFunctionDefinition = opNamedFunctionDefinition; exports.opNegation = opNegation; exports.opNot = opNot; exports.opNullCoalescence = opNullCoalescence; exports.opNumber = opNumber; exports.opObjectConstructor = opObjectConstructor; exports.opOr = opOr; exports.opOutputConcat = opOutputConcat; exports.opPipe = opPipe; exports.opStringLiteral = opStringLiteral; exports.opSubPipe = opSubPipe; exports.opSubRoute = opSubRoute; exports.opTry = opTry; exports.opValueAccess = opValueAccess; exports.opVariableAccess = opVariableAccess; exports.parseAccess = parseAccess; exports.parseAssignment = parseAssignment; exports.parseEntrypoint = parseEntrypoint; exports.parseFunctionHeader = parseFunctionHeader; exports.parseNumber = parseNumber; exports.parseProgram = parseProgram; exports.parseString = parseString; var _library = require("../library"); var _util = require("./util"); /** * Parse a single program at i. * Throws an error if src contains additional content. */ async function parseEntrypoint(src, i, c) { let n = i; const result = await parseProgram(src, n, c); ({ i: n } = result); if (!(0, _util.eot)(src, n, c).is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'program', message: 'expected EOT' }); return result; } /** Parse program at i */ function parseProgram(src, i, c) { let n = i; ({ i: n } = (0, _util.walkWhitespace)(src, n, c)); return opPipe(src, n, c); } /** Parse function header at i */ function parseFunctionHeader(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: '(' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'function definition', message: "expected '('" }); const argNames = []; m = (0, _util.matchWord)(src, n, c, { phrase: ')' }); if (m.is) ({ i: n } = m);else for (;;) { const v = (0, _util.safeVariable)(src, n, c); if (!v.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'function definition', message: 'expected argument name' }); let name; ({ i: n, value: name } = v); argNames.push(name); m = (0, _util.matchWord)(src, n, c, { phrase: ')' }); if (m.is) { ({ i: n } = m); break; } m = (0, _util.matchWord)(src, n, c, { phrase: ',' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'function definition', message: "expected ',' or ')'" }); } m = (0, _util.matchWord)(src, n, c, { phrase: ':' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'function definition', message: "expected ':'" }); return { i: n, argNames }; } /** Parse access at i */ async function parseAccess(src, i, c, { identity } = {}) { let n = i; const selectors = []; let canAssign = true; for (;;) { let m = (0, _util.matchWord)(src, n, c, { phrase: '.' }); const isIdentity = identity && selectors.length === 0; if (!isIdentity && m.is) { ({ i: n } = m); const v = (0, _util.safeVariable)(src, n, c); if (!v.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'field access operator', message: 'expected field name' }); let name; ({ i: n, value: name } = v); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_FIELD, params: { pipe: [{ op: _library.OP_STRING, params: { string: name } }], optional } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '[' }); if (m.is) { ({ i: n } = m); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); if (m.is) { ({ i: n } = m); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_ITER, params: { optional } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: ':' }); if (m.is) { ({ i: n } = m); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); if (m.is) { ({ i: n } = m); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_SLICE, params: { from: [{ op: _library.OP_CONSTANT_NULL }], to: [{ op: _library.OP_CONSTANT_NULL }], optional } }); continue; } let opsRight; ({ i: n, ops: opsRight } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'array slice operator', message: "expected ']'" }); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_SLICE, params: { from: [{ op: _library.OP_CONSTANT_NULL }], to: opsRight, optional } }); continue; } let opsLeft; ({ i: n, ops: opsLeft } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); if (m.is) { ({ i: n } = m); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_FIELD, params: { pipe: opsLeft, optional } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: ':' }); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'variable access operator', message: "expected ':' or ']'" }); ({ i: n } = m); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); if (m.is) { ({ i: n } = m); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_SLICE, params: { from: opsLeft, to: [{ op: _library.OP_CONSTANT_NULL }], optional } }); continue; } let opsRight; ({ i: n, ops: opsRight } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'array slice operator', message: "expected ']'" }); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_SLICE, params: { from: opsLeft, to: opsRight, optional } }); continue; } let bound; m = (0, _util.matchWord)(src, n, c, { phrase: '->' }); if (m.is) ({ i: n, is: bound } = m); m = (0, _util.matchWord)(src, n, c, { phrase: '(' }); if (!m.is && bound) { return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'bound function call', message: "expected '('" }); } if (m.is) { ({ i: n } = m); const args = []; m = (0, _util.matchWord)(src, n, c, { phrase: ')' }); if (m.is) ({ i: n } = m);else for (;;) { let opsArg; ({ i: n, ops: opsArg } = await opSubPipe(src, n, c)); args.push(opsArg); m = (0, _util.matchWord)(src, n, c, { phrase: ')' }); if (m.is) { ({ i: n } = m); break; } m = (0, _util.matchWord)(src, n, c, { phrase: ',' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'function call', message: "expected ',' or ')'" }); } let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_FUNCTION, params: { args, bound, optional } }); canAssign = false; continue; } break; } return { i: n, is: selectors.length > 0, selectors, canAssign }; } /** Parse assignment at i */ async function parseAssignment(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: '=', notBeforeSet: '=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_SET, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '|=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_UPDATE, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '+=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_ADDITION, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '-=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_SUBTRACTION, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '*=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_MULTIPLICATION, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '/=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_DIVISION, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '%=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_REMAINDER, params: { pipe: ops } } }; } m = (0, _util.matchWord)(src, n, c, { phrase: '?=' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, is: true, assignment: { op: _library.OPU_NULL_COALESCENCE, params: { pipe: ops } } }; } return { i: n, is: false }; } /** Parse number at i */ function parseNumber(src, i, c) { let n = i; let is = false; let value = ''; for (;;) { const set = (0, _util.matchSet)(src, n, c, { set: '0123456789' }); if (!set.is) break; ({ i: n } = set); is = true; value += set.value; } if (!is) return { i: n, is: false }; const m = (0, _util.match)(src, n, c, { phrase: '.' }); if (m.is) { ({ i: n } = m); value += '.'; for (;;) { const set = (0, _util.matchSet)(src, n, c, { set: '0123456789' }); if (!set.is) break; ({ i: n } = set); value += set.value; } } let set = (0, _util.matchSet)(src, n, c, { set: 'eE' }); if (set.is) { ({ i: n } = set); value += set.value; set = (0, _util.matchSet)(src, n, c, { set: '+-' }); if (set.is) { ({ i: n } = set); value += set.value; } let isE; for (;;) { set = (0, _util.matchSet)(src, n, c, { set: '0123456789' }); if (!set.is) break; ({ i: n } = set); isE = true; value += set.value; } if (!isE) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'number', message: 'expected digit' }); } const number = +value; if (!Number.isFinite(number)) { return (0, _util.errorGeneric)(src, n, c, { operator: 'number', message: `invalid number ${number}` }); } ({ i: n } = (0, _util.walkWhitespace)(src, n, c)); return { i: n, is: true, ops: [{ op: _library.OP_NUMBER, params: { number } }] }; } /** Parse string at i */ async function parseString(src, i, c) { let n = i; let value = ''; const set = (0, _util.matchSet)(src, n, c, { set: '"\'`' }); if (!set.is) return { i: n, is: false }; let boundary; ({ i: n, value: boundary } = set); const multilineString = boundary === '`'; const interpolations = []; for (;;) { let m = (0, _util.match)(src, n, c, { phrase: boundary }); if (m.is) { ({ i: n } = m); break; } const end = (0, _util.eot)(src, n, c); if (end.is) { ({ i: n } = end); return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'string', message: 'incomplete string literal' }); } m = (0, _util.match)(src, n, c, { phrase: '\\' }); if (m.is) { ({ i: n } = m); m = (0, _util.matchWord)(src, n, c, { phrase: '(' }); if (m.is) { ({ i: n } = m); let ops; ({ i: n, ops } = await opPipe(src, n, c)); m = (0, _util.match)(src, n, c, { phrase: ')' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'string interpolation', message: "expected ')'" }); interpolations.push({ before: value, pipe: ops }); value = ''; continue; } if (multilineString) { switch (src[n]) { case '\n': n += 1; continue; case '\r': n += 1; if (!(0, _util.eot)(src, n, c).is && src[n] === '\n') n += 1; continue; default: } } switch (src[n]) { case '"': value += '"'; n += 1; continue; case "'": value += "'"; n += 1; continue; case '`': value += '`'; n += 1; continue; case '\\': value += '\\'; n += 1; continue; case '/': value += '/'; n += 1; continue; case 'b': value += '\b'; n += 1; continue; case 'f': value += '\f'; n += 1; continue; case 'n': value += '\n'; n += 1; continue; case 'r': value += '\r'; n += 1; continue; case 't': value += '\t'; n += 1; continue; case 'u': { n += 1; let hexVal = ''; for (let j = 0; j < 4; j += 1) { m = (0, _util.hex)(src, n, c); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'string', message: 'incomplete unicode escape sequence: expected hex digit' }); hexVal += m.value; } value += String.fromCodePoint(parseInt(hexVal, 16)); continue; } default: return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'string', message: 'invalid escape sequence' }); } } if (multilineString) { switch (src[n]) { case '\n': case '\r': case '\t': value += src[n]; n += 1; continue; default: } } if (src.charCodeAt(n) < 0x20) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'string' }); value += src[n]; n += 1; } ({ i: n } = (0, _util.walkWhitespace)(src, n, c)); if (interpolations.length === 0) return { i: n, is: true, ops: [{ op: _library.OP_STRING, params: { string: value } }] }; return { i: n, is: true, ops: [{ op: _library.OP_INTERPOLATED_STRING, params: { interpolations, after: value } }] }; } /** Parse pipe at i */ async function opPipe(src, i, c) { // Call stack decoupling - This is necessary as some browsers (i.e. Safari) have very limited call stack sizes which result in stack overflow exceptions in certain situations. await undefined; let n = i; const pipe = []; for (;;) { let ops; ({ i: n, ops } = await opOutputConcat(src, n, c)); pipe.push(...ops); const m = (0, _util.matchWord)(src, n, c, { phrase: '|', notBeforeSet: '=' }); if (!m.is) break; ({ i: n } = m); } return { i: n, ops: pipe }; } /** Parse subpipe at i */ async function opSubPipe(src, i, c) { // Call stack decoupling - This is necessary as some browsers (i.e. Safari) have very limited call stack sizes which result in stack overflow exceptions in certain situations. await undefined; let n = i; const pipe = []; for (;;) { let ops; ({ i: n, ops } = await opTry(src, n, c)); pipe.push(...ops); const m = (0, _util.matchWord)(src, n, c, { phrase: '|', notBeforeSet: '=' }); if (!m.is) break; ({ i: n } = m); } return { i: n, ops: pipe }; } /** Parse subroute at i */ async function opSubRoute(src, i, c) { // Call stack decoupling - This is necessary as some browsers (i.e. Safari) have very limited call stack sizes which result in stack overflow exceptions in certain situations. await undefined; return opTry(src, i, c); } /** Parse output concat at i */ async function opOutputConcat(src, i, c) { let n = i; const pipes = []; for (;;) { let ops; ({ i: n, ops } = await opTry(src, n, c)); pipes.push(ops); const m = (0, _util.matchWord)(src, n, c, { phrase: ',' }); if (!m.is) break; ({ i: n } = m); } if (pipes.length === 1) return { i: n, ops: pipes[0] }; return { i: n, ops: [{ op: _library.OP_OUTPUT_CONCAT, params: { pipes } }] }; } /** Parse try at i */ async function opTry(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: 'try', spaceAfter: true }); if (!m.is) return opOr(src, n, c); ({ i: n } = m); let opsTry; ({ i: n, ops: opsTry } = await opOr(src, n, c)); let opsCatch; m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'catch', spaceAfter: true }); if (m.is) { ({ i: n } = m); ({ i: n, ops: opsCatch } = await opOr(src, n, c)); } else opsCatch = [{ op: _library.OP_VOID }]; return { i: n, ops: [{ op: _library.OP_TRY, params: { try: opsTry, catch: opsCatch } }] }; } /** Parse or at i */ async function opOr(src, i, c) { let n = i; const pipes = []; for (;;) { let ops; ({ i: n, ops } = await opAnd(src, n, c)); pipes.push(ops); const m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'or', spaceAfter: true }); if (!m.is) break; ({ i: n } = m); } if (pipes.length === 1) return { i: n, ops: pipes[0] }; return { i: n, ops: [{ op: _library.OP_OR, params: { pipes } }] }; } /** Parse and at i */ async function opAnd(src, i, c) { let n = i; const pipes = []; for (;;) { let ops; ({ i: n, ops } = await opEquality(src, n, c)); pipes.push(ops); const m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'and', spaceAfter: true }); if (!m.is) break; ({ i: n } = m); } if (pipes.length === 1) return { i: n, ops: pipes[0] }; return { i: n, ops: [{ op: _library.OP_AND, params: { pipes } }] }; } /** Parse equality at i */ async function opEquality(src, i, c) { let n = i; let ops; ({ i: n, ops } = await opComparison(src, n, c)); const comparisons = []; for (;;) { let m = (0, _util.matchWord)(src, n, c, { phrase: '==' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opComparison(src, n, c)); comparisons.push({ op: _library.OPC_EQUAL, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '!=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opComparison(src, n, c)); comparisons.push({ op: _library.OPC_UNEQUAL, params: { by: opsBy } }); continue; } break; } if (comparisons.length === 0) return { i: n, ops }; return { i: n, ops: [{ op: _library.OP_COMPARISON, params: { pipe: ops, comparisons } }] }; } /** Parse comparison at i */ async function opComparison(src, i, c) { let n = i; let ops; ({ i: n, ops } = await opNot(src, n, c)); const comparisons = []; for (;;) { let m = (0, _util.matchWord)(src, n, c, { phrase: '<=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNot(src, n, c)); comparisons.push({ op: _library.OPC_LESSEQUAL, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '<' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNot(src, n, c)); comparisons.push({ op: _library.OPC_LESS, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '>=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNot(src, n, c)); comparisons.push({ op: _library.OPC_GREATEREQUAL, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '>' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNot(src, n, c)); comparisons.push({ op: _library.OPC_GREATER, params: { by: opsBy } }); continue; } break; } if (comparisons.length === 0) return { i: n, ops }; return { i: n, ops: [{ op: _library.OP_COMPARISON, params: { pipe: ops, comparisons } }] }; } /** Parse not at i */ async function opNot(src, i, c) { let n = i; const m = (0, _util.matchWord)(src, n, c, { phrase: 'not', spaceAfter: true }); if (!m.is) return opErrorSuppression(src, n, c); ({ i: n } = m); let ops; ({ i: n, ops } = await opErrorSuppression(src, n, c)); return { i: n, ops: [...ops, { op: _library.OP_NOT }] }; } /** Parse error suppression at i */ async function opErrorSuppression(src, i, c) { let n = i; const result = await opDifference(src, n, c); ({ i: n } = result); const m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (!m.is) return result; ({ i: n } = m); return { i: n, ops: [{ op: _library.OP_TRY, params: { try: result.ops, catch: [{ op: _library.OP_VOID }] } }] }; } /** Parse difference at i */ async function opDifference(src, i, c) { let n = i; let ops; ({ i: n, ops } = await opMultiplication(src, n, c)); const operations = []; for (;;) { let m = (0, _util.matchWord)(src, n, c, { phrase: '+', notBeforeSet: '=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opMultiplication(src, n, c)); operations.push({ op: _library.OPM_ADDITION, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '-', notBeforeSet: '=>' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opMultiplication(src, n, c)); operations.push({ op: _library.OPM_SUBTRACTION, params: { by: opsBy } }); continue; } break; } if (operations.length === 0) return { i: n, ops }; return { i: n, ops: [{ op: _library.OP_CALCULATION, params: { pipe: ops, operations } }] }; } /** Parse multiplication at i */ async function opMultiplication(src, i, c) { let n = i; let ops; ({ i: n, ops } = await opNullCoalescence(src, n, c)); const operations = []; for (;;) { let m = (0, _util.matchWord)(src, n, c, { phrase: '*', notBeforeSet: '=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNullCoalescence(src, n, c)); operations.push({ op: _library.OPM_MULTIPLICATION, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '/', notBeforeSet: '=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNullCoalescence(src, n, c)); operations.push({ op: _library.OPM_DIVISION, params: { by: opsBy } }); continue; } m = (0, _util.matchWord)(src, n, c, { phrase: '%', notBeforeSet: '=' }); if (m.is) { ({ i: n } = m); let opsBy; ({ i: n, ops: opsBy } = await opNullCoalescence(src, n, c)); operations.push({ op: _library.OPM_REMAINDER, params: { by: opsBy } }); continue; } break; } if (operations.length === 0) return { i: n, ops }; return { i: n, ops: [{ op: _library.OP_CALCULATION, params: { pipe: ops, operations } }] }; } /** Parse null coalescence at i */ async function opNullCoalescence(src, i, c) { let n = i; const pipes = []; for (;;) { let ops; ({ i: n, ops } = await opNegation(src, n, c)); pipes.push(ops); const m = (0, _util.matchWord)(src, n, c, { phrase: '??' }); if (!m.is) break; ({ i: n } = m); } if (pipes.length === 1) return { i: n, ops: pipes[0] }; return { i: n, ops: [{ op: _library.OP_NULL_COALESCENCE, params: { pipes } }] }; } /** Parse negation at i */ async function opNegation(src, i, c) { let n = i; const m = (0, _util.matchWord)(src, n, c, { phrase: '-', notBeforeSet: '=>' }); if (!m.is) return opIf(src, n, c); ({ i: n } = m); let ops; ({ i: n, ops } = await opIf(src, n, c)); return { i: n, ops: [...ops, { op: _library.OP_NEGATION }] }; } /** Parse if at i */ async function opIf(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: 'if', spaceAfter: true }); if (!m.is) return opConstant(src, n, c); ({ i: n } = m); const ifs = []; for (;;) { let opsIf; ({ i: n, ops: opsIf } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'then', spaceAfter: true }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'if statement', message: "expected 'then'" }); let opsThen; ({ i: n, ops: opsThen } = await opPipe(src, n, c)); ifs.push({ if: opsIf, then: opsThen }); m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'elif', spaceAfter: true }); if (!m.is) break; ({ i: n } = m); } let opsElse; m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'else', spaceAfter: true }); if (m.is) { ({ i: n } = m); ({ i: n, ops: opsElse } = await opPipe(src, n, c)); } else opsElse = []; m = (0, _util.matchWord)(src, n, c, { spaceBefore: true, phrase: 'end' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'if statement', message: "expected 'end'" }); return { i: n, ops: [{ op: _library.OP_IF, params: { ifs, else: opsElse } }] }; } /** Parse constant at i */ function opConstant(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: 'true', spaceAfter: true }); if (m.is) { ({ i: n } = m); return { i: n, ops: [{ op: _library.OP_CONSTANT_TRUE }] }; } m = (0, _util.matchWord)(src, n, c, { phrase: 'false', spaceAfter: true }); if (m.is) { ({ i: n } = m); return { i: n, ops: [{ op: _library.OP_CONSTANT_FALSE }] }; } m = (0, _util.matchWord)(src, n, c, { phrase: 'null', spaceAfter: true }); if (m.is) { ({ i: n } = m); return { i: n, ops: [{ op: _library.OP_CONSTANT_NULL }] }; } return opNumber(src, n, c); } /** Parse number at i */ async function opNumber(src, i, c) { let n = i; const result = await parseNumber(src, n, c); if (!result.is) return opNamedFunctionDefinition(src, n, c); ({ i: n } = result); return { i: n, ops: result.ops }; } /** Parse named function definition at i */ async function opNamedFunctionDefinition(src, i, c) { let n = i; const m = (0, _util.matchWord)(src, n, c, { phrase: 'func', spaceAfter: true }); if (!m.is) return opFunctionDefinition(src, n, c); ({ i: n } = m); const v = (0, _util.safeVariable)(src, n, c); if (!v.is) return opFunctionDefinition(src, i, c); let name; ({ i: n, value: name } = v); let argNames; ({ i: n, argNames } = await parseFunctionHeader(src, n, c)); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, ops: [{ op: _library.OP_VARIABLE_DEFINITION, params: { name, pipe: [{ op: _library.OP_FUNCTION_DEFINITION, params: { argNames, pipe: ops } }] } }] }; } /** Parse function definition at i */ async function opFunctionDefinition(src, i, c) { let n = i; const m = (0, _util.matchWord)(src, n, c, { phrase: 'func', spaceAfter: true }); if (!m.is) return opVariableAccess(src, n, c); ({ i: n } = m); let argNames; ({ i: n, argNames } = await parseFunctionHeader(src, n, c)); let ops; ({ i: n, ops } = await opSubRoute(src, n, c)); return { i: n, ops: [{ op: _library.OP_FUNCTION_DEFINITION, params: { argNames, pipe: ops } }] }; } /** Parse variable definition at i */ async function opVariableAccess(src, i, c) { let n = i; const v = (0, _util.safeVariable)(src, n, c); if (!v.is) return opValueAccess(src, n, c); let name; ({ i: n, value: name } = v); let selectors; let canAssign; const ac = await parseAccess(src, n, c); if (ac.is) ({ i: n, selectors, canAssign } = ac);else { selectors = []; canAssign = true; } if (!canAssign) { const ops = [{ op: _library.OP_VARIABLE, params: { name } }]; if (selectors.length === 0) return { i: n, ops }; return { i: n, ops: [{ op: _library.OP_ACCESS, params: { pipe: ops, selectors } }] }; } const as = await parseAssignment(src, n, c); if (!as.is) { const ops = [{ op: _library.OP_VARIABLE, params: { name } }]; if (selectors.length === 0) return { i: n, ops }; return { i: n, ops: [{ op: _library.OP_ACCESS, params: { pipe: ops, selectors } }] }; } let opAssignment; ({ i: n, assignment: opAssignment } = as); if (selectors.length === 0 && opAssignment.op === _library.OPU_SET) { return { i: n, ops: [{ op: _library.OP_VARIABLE_DEFINITION, params: { name, pipe: opAssignment.params.pipe } }] }; } return { i: n, ops: [{ op: _library.OP_VARIABLE_DEFINITION, params: { name, pipe: [{ op: _library.OP_ASSIGNMENT, params: { pipe: [{ op: _library.OP_VARIABLE, params: { name } }], selectors, assignment: opAssignment } }] } }] }; } /** Parse variable access at i */ async function opValueAccess(src, i, c) { let n = i; const selectors = []; let ops; let m = (0, _util.matchWord)(src, n, c, { phrase: '.' }); if (!m.is) ({ i: n, ops } = await opObjectConstructor(src, n, c));else { ({ i: n } = m); ops = []; const v = (0, _util.safeVariable)(src, n, c); if (v.is) { let name; ({ i: n, value: name } = v); let optional; m = (0, _util.matchWord)(src, n, c, { phrase: '?', notBeforeSet: '?=' }); if (m.is) ({ i: n, is: optional } = m); selectors.push({ op: _library.OPA_FIELD, params: { pipe: [{ op: _library.OP_STRING, params: { string: name } }], optional } }); } } const ac = await parseAccess(src, n, c, { identity: ops.length === 0 && selectors.length === 0 }); let canAssign; if (ac.is) { ({ i: n, canAssign } = ac); selectors.push(...ac.selectors); } else canAssign = selectors.length > 0; if (selectors.length === 0) return { i: n, ops }; if (!canAssign) return { i: n, ops: [{ op: _library.OP_ACCESS, params: { pipe: ops, selectors } }] }; const as = await parseAssignment(src, n, c); if (!as.is) return { i: n, ops: [{ op: _library.OP_ACCESS, params: { pipe: ops, selectors } }] }; let opAssignment; ({ i: n, assignment: opAssignment } = as); return { i: n, ops: [{ op: _library.OP_ASSIGNMENT, params: { pipe: ops, selectors, assignment: opAssignment } }] }; } /** Parse object constructor at i */ async function opObjectConstructor(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: '{' }); if (!m.is) return opArrayConstructor(src, n, c); ({ i: n } = m); const fields = []; m = (0, _util.matchWord)(src, n, c, { phrase: '}' }); if (m.is) ({ i: n } = m);else for (;;) { m = (0, _util.matchWord)(src, n, c, { phrase: '(' }); if (m.is) { ({ i: n } = m); let opsKey; ({ i: n, ops: opsKey } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { phrase: ')' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: "expected ')'" }); let optional; ({ i: n, is: optional } = (0, _util.matchWord)(src, n, c, { phrase: '?' })); m = (0, _util.matchWord)(src, n, c, { phrase: ':' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: "expected ':'" }); let opsValue; ({ i: n, ops: opsValue } = await opSubPipe(src, n, c)); fields.push({ key: opsKey, value: opsValue, optional }); m = (0, _util.matchWord)(src, n, c, { phrase: '}' }); if (m.is) { ({ i: n } = m); break; } m = (0, _util.matchWord)(src, n, c, { phrase: ',' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: "expected ',' or '}'" }); continue; } const s = await parseString(src, n, c); if (s.is) { let opsKey; ({ i: n, ops: opsKey } = s); m = (0, _util.matchWord)(src, n, c, { phrase: ':' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: "expected ':'" }); let opsValue; ({ i: n, ops: opsValue } = await opSubPipe(src, n, c)); fields.push({ key: opsKey, value: opsValue, optional: false }); m = (0, _util.matchWord)(src, n, c, { phrase: '}' }); if (m.is) { ({ i: n } = m); break; } m = (0, _util.matchWord)(src, n, c, { phrase: ',' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: "expected ',' or '}'" }); continue; } const v = (0, _util.safeVariable)(src, n, c); if (v.is) { let name; ({ i: n, value: name } = v); let opsValue; m = (0, _util.matchWord)(src, n, c, { phrase: ':' }); if (m.is) { ({ i: n } = m); ({ i: n, ops: opsValue } = await opSubPipe(src, n, c)); } else { let optional; ({ i: n, is: optional } = (0, _util.matchWord)(src, n, c, { phrase: '?' })); opsValue = optional ? [{ op: _library.OP_TRY, params: { try: [{ op: _library.OP_VARIABLE, params: { name } }], catch: [{ op: _library.OP_VOID }] } }] : [{ op: _library.OP_VARIABLE, params: { name } }]; } fields.push({ key: [{ op: _library.OP_STRING, params: { string: name } }], value: opsValue, optional: false }); m = (0, _util.matchWord)(src, n, c, { phrase: '}' }); if (m.is) { ({ i: n } = m); break; } m = (0, _util.matchWord)(src, n, c, { phrase: ',' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: "expected ',' or '}'" }); continue; } return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'object', message: 'expected field declaration' }); } return { i: n, ops: [{ op: _library.OP_OBJECT_CONSTRUCTOR, params: { fields } }] }; } /** Parse array constructor at i */ async function opArrayConstructor(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: '[' }); ({ i: n } = m); if (!m.is) return opStringLiteral(src, n, c); let ops; m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); if (m.is) { ({ i: n } = m); ops = [{ op: _library.OP_VOID }]; } else { ({ i: n, ops } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { phrase: ']' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'array', message: "expected ']'" }); } return { i: n, ops: [{ op: _library.OP_ARRAY_CONSTRUCTOR, params: { pipe: ops } }] }; } /** Parse string literal at i */ async function opStringLiteral(src, i, c) { let n = i; const s = await parseString(src, n, c); if (!s.is) return opGroup(src, n, c); ({ i: n } = s); return { i: n, ops: s.ops }; } /** Parse group at i */ async function opGroup(src, i, c) { let n = i; let m = (0, _util.matchWord)(src, n, c, { phrase: '(' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c); let ops; ({ i: n, ops } = await opPipe(src, n, c)); m = (0, _util.matchWord)(src, n, c, { phrase: ')' }); ({ i: n } = m); if (!m.is) return (0, _util.errorUnexpectedToken)(src, n, c, { operator: 'group', message: "expected ')'" }); return { i: n, ops }; }