cbon
Version:
Common Bracket Object Notation
245 lines (244 loc) • 8.54 kB
JavaScript
import { Block, KeyVal, Arr, Key, Num, Str, Bool, Null, Docs, Comma, Split, LineComment, BlockComment } from "./ast";
import { State, Context, ReDo } from "./state_machine";
import { TEOF, TStr, TWord, TSObjStart, TSArrStart, TSComma, TSObjEnd, TSArrEnd, TSSplit, TLineComment, TBlockComment } from "./token";
import { getIterator, next_micro_tick } from "./utils";
import { _continue, _break } from "./loop";
import { AlwaysFalse } from "./canceller";
export function parser(code, config = { show_all_err: false, async: false, cancel: AlwaysFalse }) {
var _a, _b, _c, _d, _e, _f;
const cancel = (_b = (_a = config) === null || _a === void 0 ? void 0 : _a.cancel, (_b !== null && _b !== void 0 ? _b : AlwaysFalse));
const state = new State((_d = (_c = config) === null || _c === void 0 ? void 0 : _c.show_all_err, (_d !== null && _d !== void 0 ? _d : false)));
let rootAst;
state.push(root(new Context(state), (d) => {
rootAst = d;
}));
let finish = false;
const iter = getIterator(code);
function* main() {
if (state.queue.length != 0) {
state.queue.pop()();
return _continue;
}
if (finish)
return _break;
const c = yield iter.next();
if (c.done === true) {
finish = true;
return _continue;
}
state.call(c.value);
}
const loop = (_f = (_e = config) === null || _e === void 0 ? void 0 : _e.async, (_f !== null && _f !== void 0 ? _f : false)) ? async function () {
while (true) {
if (await cancel())
break;
const g = main();
let y = g.next();
if (!y.done) {
y = g.next(await y.value);
}
const s = y.value;
if (s === _continue) {
await next_micro_tick();
continue;
}
if (s === _break)
break;
await next_micro_tick();
}
return state.errors.length !== 0 ? { err: state.errors, val: rootAst } : { err: state.errors, val: rootAst };
} : function () {
while (true) {
if (cancel())
break;
const g = main();
let y = g.next();
if (!y.done) {
y = g.next(y.value);
}
const s = y.value;
if (s === _continue)
continue;
if (s === _break)
break;
}
return state.errors.length !== 0 ? { err: state.errors, val: rootAst } : { err: state.errors, val: rootAst };
};
return loop();
}
function root(ctx, finish) {
const items = [];
return (t) => {
if (t instanceof TEOF) {
finish(new Docs(items));
}
else if (t instanceof TSObjStart) {
return ctx.callNoFirst(block, t, b => items.push(b));
}
else if (t instanceof TSArrStart) {
return ctx.callNoFirst(arr, t, a => items.push(a));
}
else if (t instanceof TLineComment) {
items.push(new LineComment(t.range, t.items));
}
else if (t instanceof TBlockComment) {
items.push(new BlockComment(t.range, t.items));
}
else {
ctx.error(t.range, 'File root must have no content other than a document');
}
};
}
function block(ctx, begin, finish) {
const items = [];
return (t) => {
if (t instanceof TEOF) {
ctx.error(t.range, 'Block is not closed');
ctx.end();
finish(new Block(begin.range, t.range, items));
return ReDo;
}
else if (t instanceof TSComma) {
items.push(new Comma(t.range));
}
else if (t instanceof TSObjEnd) {
ctx.end();
finish(new Block(begin.range, t.range, items));
}
else if (t instanceof TSObjStart) {
ctx.error(t.range, 'Block content must start with a key');
return ctx.callNoFirst(block, t, _ => { });
}
else if (t instanceof TSArrStart) {
ctx.error(t.range, 'Block content must start with a key');
return ctx.callNoFirst(arr, t, _ => { });
}
else if (t instanceof TStr || t instanceof TWord) {
return ctx.callNoFirst(key, t, kv => items.push(kv));
}
else if (t instanceof TSArrEnd) {
ctx.error(t.range, 'No Array here');
}
else {
ctx.error(t.range, 'Block content must start with a key');
}
};
}
function arr(ctx, begin, finish) {
const items = [];
return (t) => {
if (t instanceof TEOF) {
ctx.error(t.range, 'Array is not closed');
ctx.end();
finish(new Arr(begin.range, t.range, items));
return ReDo;
}
else if (t instanceof TSComma) {
items.push(new Comma(t.range));
}
else if (t instanceof TSArrEnd) {
ctx.end();
finish(new Arr(begin.range, t.range, items));
}
else if (t instanceof TSObjEnd) {
ctx.error(t.range, 'No Block here');
}
else if (t instanceof TSSplit) {
ctx.error(t.range, 'Array cant have key');
}
else {
return ctx.call(val, u => items.push(u));
}
};
}
function key(ctx, k, finish) {
return (t) => {
if (t instanceof TEOF) {
ctx.error(t.range, 'There should be a key value here');
ctx.end();
return ReDo;
}
else if (t instanceof TSComma) {
ctx.error(t.range, 'There should be a key value here');
ctx.end();
return ReDo;
}
else if (t instanceof TSSplit) {
ctx.end();
return ctx.callNoFirst(val, u => {
const sp = new Split(t.range, t.val);
const kv = new KeyVal(new Key(k.range, k instanceof TStr ? new Str(k.range, k.val, k.col) : k.val), u, sp);
finish(kv);
});
}
else {
ctx.end();
return ctx.call(val, u => {
const kv = new KeyVal(new Key(k.range, k instanceof TStr ? new Str(k.range, k.val, k.col) : k.val), u);
finish(kv);
});
}
};
}
function val(ctx, finish) {
return (t) => {
if (t instanceof TEOF) {
ctx.error(t.range, 'There should be a value here');
ctx.end();
return ReDo;
}
else if (t instanceof TStr) {
ctx.end();
finish(new Str(t.range, t.val, t.col));
}
else if (t instanceof TWord) {
ctx.end();
return ctx.call(word, t, u => {
finish(u);
});
}
else if (t instanceof TSComma || t instanceof TSSplit || t instanceof TSArrEnd || t instanceof TSObjEnd) {
ctx.error(t.range, 'There should be a value here');
ctx.end();
return ReDo;
}
else if (t instanceof TSObjStart) {
ctx.end();
return ctx.callNoFirst(block, t, b => {
finish(b);
});
}
else if (t instanceof TSArrStart) {
ctx.end();
return ctx.callNoFirst(arr, t, a => {
finish(a);
});
}
else {
t;
//todo
ctx.end();
}
};
}
const reg_Num = /(0x[\da-fA-F_]+)|(([\-]?([\d\_])+)\.([\-]?([\d\_])+([eE]([\-]?)\d+)?))|(([\-]?([\d\_])+)\.([eE]([\-]?)\d+)?)|([\-]?\.(([\d\_])+([eE]([\-]?)\d+)?))|(([\-]?([\d\_])+([eE]([\-]?)\d+)?))/i;
function word(ctx, w, finish) {
return (t) => {
ctx.end();
if (w.val === 'true') {
finish(new Bool(w.range, true));
}
else if (w.val === 'false') {
finish(new Bool(w.range, false));
}
else if (w.val === 'null') {
finish(new Null(w.range));
}
else if (reg_Num.test(w.val)) {
finish(new Num(w.range, Number(w.val.replace('_', ''))));
}
else {
finish(new Str(w.range, w.val, null));
}
};
}