UNPKG

@aire-ux/aire-condensation

Version:

Client-side serialization library for Aire-UX

159 lines (158 loc) 5.06 kB
/** * parse an invocation into a readable format * * invocation: (<namespace> '::')? <invocationPath> * invocationPath: <ws>? (<invocationOrProperty><separator>)+ * invocationOrProperty: invocation | property * invocation: <openParen>(ws?) paramList (ws?) <closeParam> * openParen: '(' * paramList: (<parameter>',')?<parameter> * closeParam: ')' * property: <identifier> */ import { stream, TokenType, } from "./lexer"; const expectToken = (ctx, token, ...expected) => { if (expected.indexOf(token.type) === -1) { throw new Error(`Expected one of [${[...expected] .map((e) => TokenType[e]) .join(",")}]. Got ${TokenType[token.type]} at (${(token.start, token.end)}) '${ctx.substr(token.start, token.end)}'`); } }; const expect = (ctx, iter, ...expected) => { if (!iter.hasNext()) { throw new Error(`Unexpected end of token stream. Expected one of [${[...expected] .map((e) => TokenType[e]) .join(",")}]`); } const next = iter.next(); expectToken(ctx, next, ...expected); return next; }; export var SegmentType; (function (SegmentType) { SegmentType[SegmentType["Property"] = 0] = "Property"; SegmentType[SegmentType["Invocation"] = 1] = "Invocation"; SegmentType[SegmentType["Reference"] = 2] = "Reference"; })(SegmentType || (SegmentType = {})); const ws = (iter) => { let token; do { token = iter.next(); } while (token.type === TokenType.Whitespace); if (token.type !== TokenType.EOF) { iter.unread(token); } }; const readParameterNames = (seq, iter) => { let result = []; for (;;) { ws(iter); if (iter.peek().type === TokenType.CloseParenthesis) { break; } expect(seq, iter, TokenType.ParameterOpen); ws(iter); result.push(expect(seq, iter, TokenType.Identifier).value); ws(iter); expect(seq, iter, TokenType.ParameterClose); ws(iter); const next = iter.peek(); if (next.type === TokenType.ParameterSeparator) { expect(seq, iter, TokenType.ParameterSeparator); continue; } if (next.type === TokenType.CloseParenthesis) { break; } } ws(iter); return result; }; const readPath = (seq, iter) => { let root = null, current = null; while (iter.hasNext()) { ws(iter); const id = expect(seq, iter, TokenType.Identifier, TokenType.Number); ws(iter); let peek = iter.peek(); if (peek.type === TokenType.PropertySeparator) { expect(seq, iter, TokenType.PropertySeparator); const value = { name: id.value, next: undefined, formalParameterNames: undefined, segmentType: id.type == TokenType.Identifier ? SegmentType.Property : SegmentType.Reference, }; if (!root) { root = value; current = value; } else { current.next = value; current = current.next; } } else if (peek.type == TokenType.OpenParenthesis) { expect(seq, iter, TokenType.OpenParenthesis); ws(iter); const paramNames = readParameterNames(seq, iter); const value = { name: id.value, next: undefined, formalParameterNames: paramNames, segmentType: SegmentType.Invocation, }; if (!root) { root = value; current = value; } else { current.next = value; current = current.next; } ws(iter); expect(seq, iter, TokenType.CloseParenthesis); ws(iter); if (iter.peek().type === TokenType.PropertySeparator) { expect(seq, iter, TokenType.PropertySeparator); } } else if (peek.type == TokenType.EOF) { const value = { name: id.value, next: undefined, formalParameterNames: undefined, segmentType: SegmentType.Property, }; if (!root) { root = value; current = value; } else { current.next = value; current = current.next; } break; } } return { segment: root, }; }; export const parse = (seq) => { const iter = stream(seq); ws(iter); const namespace = expect(seq, iter, TokenType.Identifier, TokenType.Number); ws(iter); const next = iter.peek(); if (next.type === TokenType.NamespaceSeparator) { expect(seq, iter, TokenType.NamespaceSeparator); } const path = readPath(seq, iter); return { path: path, namespace: namespace.value }; };