@aire-ux/aire-condensation
Version:
Client-side serialization library for Aire-UX
159 lines (158 loc) • 5.06 kB
JavaScript
/**
* 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
};
};