UNPKG

@abaplint/core

Version:
912 lines • 23.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Combi = exports.Expression = void 0; exports.str = str; exports.regex = regex; exports.tok = tok; exports.seq = seq; exports.alt = alt; exports.altPrio = altPrio; exports.opt = opt; exports.optPrio = optPrio; exports.per = per; exports.star = star; exports.starPrio = starPrio; exports.plus = plus; exports.plusPrio = plusPrio; exports.ver = ver; exports.verNot = verNot; exports.failCombinator = failCombinator; exports.failStar = failStar; const Tokens = require("../1_lexer/tokens"); const nodes_1 = require("../nodes"); const version_1 = require("../../version"); const result_1 = require("./result"); class Regex { constructor(r) { this.regexp = r; } listKeywords() { return []; } getUsing() { return []; } run(r) { const result = []; for (const input of r) { if (input.remainingLength() === 0) { continue; } const token = input.peek(); if (this.regexp.test(token.getStr()) === true) { result.push(input.shift(new nodes_1.TokenNodeRegex(token))); } } return result; } railroad() { return "Railroad.Terminal(\"" + this.regexp.source.replace(/\\/g, "\\\\") + "\")"; } toStr() { return this.regexp.toString(); } first() { return [""]; } } class Word { constructor(s) { this.s = s.toUpperCase(); } listKeywords() { return [this.s]; } getUsing() { return []; } run(r) { const result = []; for (const input of r) { if (input.remainingLength() !== 0 && input.peek().getStr().toUpperCase() === this.s) { // console.log("match, " + this.s + result.length); result.push(input.shift(new nodes_1.TokenNode(input.peek()))); } } return result; } railroad() { return "Railroad.Terminal('\"" + this.s + "\"')"; } toStr() { return "\"" + this.s + "\""; } first() { return [this.s]; } } class Token { constructor(s) { this.name = s.toUpperCase(); } listKeywords() { return []; } getUsing() { return []; } run(r) { const result = []; for (const input of r) { if (input.remainingLength() !== 0 && input.peek().constructor.name.toUpperCase() === this.name) { result.push(input.shift(new nodes_1.TokenNode(input.peek()))); } } return result; } railroad() { let text = this.name; const toke = Tokens; for (const token in Tokens) { if (token.toUpperCase() === this.name && toke[token].railroad) { text = toke[token].railroad(); break; } } return "Railroad.Terminal('!\"" + text + "\"')"; } toStr() { return "Token \"" + this.name + "\""; } first() { return [""]; } } class Vers { constructor(version, runnable, or) { this.version = version; this.runnable = runnable; this.or = or; } listKeywords() { return this.runnable.listKeywords(); } run(r) { const targetVersion = Combi.getVersion(); if (this.or && targetVersion === this.or) { return this.runnable.run(r); } else if (targetVersion === version_1.Version.OpenABAP) { if (this.version > version_1.Version.v702) { return []; } else { return this.runnable.run(r); } } else if (targetVersion >= this.version || targetVersion === version_1.Version.Cloud) { return this.runnable.run(r); } else { return []; } } getUsing() { return this.runnable.getUsing(); } railroad() { let text = this.version; if (this.or) { text += " or " + this.or; } return "Railroad.Sequence(Railroad.Comment(\"" + text + "\", {}), " + this.runnable.railroad() + ")"; } toStr() { return "Version(" + this.runnable.toStr() + ")"; } first() { return this.runnable.first(); } } class VersNot { constructor(version, runnable) { this.version = version; this.runnable = runnable; } listKeywords() { return this.runnable.listKeywords(); } getUsing() { return this.runnable.getUsing(); } run(r) { if (Combi.getVersion() !== this.version) { return this.runnable.run(r); } else { return []; } } railroad() { return "Railroad.Sequence(Railroad.Comment(\"not " + this.version + "\", {}), " + this.runnable.railroad() + ")"; } toStr() { return "VersionNot(" + this.runnable.toStr() + ")"; } first() { return this.runnable.first(); } } class OptionalPriority { constructor(optional) { this.optional = optional; } listKeywords() { return this.optional.listKeywords(); } getUsing() { return this.optional.getUsing(); } run(r) { const result = []; for (const input of r) { const res = this.optional.run([input]); if (res.length > 1) { result.push(...res); } else if (res.length === 0) { result.push(input); } else if (res[0].remainingLength() < input.remainingLength()) { result.push(...res); } else { result.push(input); } } return result; } railroad() { return "Railroad.Optional(" + this.optional.railroad() + ")"; } toStr() { return "opt(" + this.optional.toStr() + ")"; } first() { return [""]; } } class Optional { constructor(optional) { this.optional = optional; } listKeywords() { return this.optional.listKeywords(); } getUsing() { return this.optional.getUsing(); } run(r) { const result = []; for (const input of r) { result.push(input); const res = this.optional.run([input]); result.push(...res); } return result; } railroad() { return "Railroad.Optional(" + this.optional.railroad() + ")"; } toStr() { return "opt(" + this.optional.toStr() + ")"; } first() { return [""]; } } class Star { constructor(sta) { this.sta = sta; } listKeywords() { return this.sta.listKeywords(); } getUsing() { return this.sta.getUsing(); } run(r) { let result = r; try { let res = r; let input = []; for (;;) { input = res; res = this.sta.run(input); if (res.length === 0) { break; } if (res.length > 1000) { // avoid stack overflow result = result.concat(res); } else { result.push(...res); } } } catch (err) { if (err instanceof FailStarError) { return result; } throw err; } // console.dir(result); return result; } railroad() { return "Railroad.ZeroOrMore(" + this.sta.railroad() + ")"; } toStr() { return "star(" + this.sta.toStr() + ")"; } first() { return [""]; } } class StarPriority { constructor(sta) { this.sta = sta; } listKeywords() { return this.sta.listKeywords(); } getUsing() { return this.sta.getUsing(); } run(r) { let result = r; let res = r; // let input: Result[] = []; let prev; for (;;) { // input = res; res = this.sta.run(res); if (res.length === 0) { if (prev !== undefined) { // console.log("star length: " + prev.length); let best = Number.MAX_SAFE_INTEGER; for (const p of prev) { if (p.remainingLength() < best) { result = [p]; best = p.remainingLength(); } } } break; } prev = res; } return result; } railroad() { return "Railroad.ZeroOrMore(" + this.sta.railroad() + ")"; } toStr() { return "star(" + this.sta.toStr() + ")"; } first() { return [""]; } } class Plus { constructor(plu) { this.plu = plu; this.sub = new Sequence([this.plu, new Star(this.plu)]); } listKeywords() { return this.plu.listKeywords(); } getUsing() { return this.plu.getUsing(); } run(r) { return this.sub.run(r); } railroad() { return "Railroad.OneOrMore(" + this.plu.railroad() + ")"; } toStr() { return "plus(" + this.plu.toStr() + ")"; } first() { return this.plu.first(); } } class PlusPriority { constructor(plu) { this.plu = plu; this.sub = new Sequence([this.plu, new StarPriority(this.plu)]); } listKeywords() { return this.plu.listKeywords(); } getUsing() { return this.plu.getUsing(); } run(r) { return this.sub.run(r); } railroad() { return "Railroad.OneOrMore(" + this.plu.railroad() + ")"; } toStr() { return "plus(" + this.plu.toStr() + ")"; } first() { return this.plu.first(); } } class Sequence { constructor(list) { if (list.length < 2) { throw new Error("Sequence, length error"); } this.list = list; } listKeywords() { const ret = []; for (const i of this.list) { ret.push(...i.listKeywords()); } return ret; } getUsing() { return this.list.reduce((a, c) => { return a.concat(c.getUsing()); }, []); } run(r) { let result = []; for (const input of r) { let temp = [input]; let match = true; for (const sequence of this.list) { temp = sequence.run(temp); if (temp.length === 0) { match = false; break; } } if (match === true) { if (temp.length > 1000) { // avoid stack overflow result = result.concat(temp); } else { result.push(...temp); } } } return result; } railroad() { const children = this.list.map((e) => { return e.railroad(); }); return "Railroad.Sequence(" + children.join() + ")"; } toStr() { let ret = ""; for (const i of this.list) { ret = ret + i.toStr() + ","; } return "seq(" + ret + ")"; } first() { return this.list[0].first(); } } class WordSequence { constructor(stri) { this.words = []; this.stri = stri; const foo = this.stri.replace(/-/g, " - "); const split = foo.split(" "); for (const st of split) { // todo, use Dash token this.words.push(new Word(st)); } this.seq = new Sequence(this.words); } listKeywords() { return [this.stri.toString()]; } getUsing() { return []; } run(r) { return this.seq.run(r); } railroad() { return "Railroad.Terminal('\"" + this.stri + "\"')"; } toStr() { return "str(" + this.stri + ")"; } first() { return this.words[0].first(); } } class Expression { constructor() { this.runnable = undefined; } run(r) { const results = []; if (this.runnable === undefined) { this.runnable = this.getRunnable(); } for (const input of r) { const temp = this.runnable.run([input]); for (const t of temp) { let consumed = input.remainingLength() - t.remainingLength(); if (consumed > 0) { const originalLength = t.getNodes().length; const children = []; while (consumed > 0) { const sub = t.popNode(); if (sub) { children.push(sub); consumed = consumed - sub.countTokens(); } } const re = new nodes_1.ExpressionNode(this); re.setChildren(children.reverse()); const n = t.getNodes().slice(0, originalLength - consumed); n.push(re); t.setNodes(n); } results.push(t); } } // console.dir(results); return results; } listKeywords() { // do not recurse, all Expressions are evaluated only on first level return []; } getUsing() { return ["expression/" + this.getName()]; } getName() { return this.constructor.name; } railroad() { return "Railroad.NonTerminal('" + this.getName() + "', {href: '#/expression/" + this.getName() + "'})"; } toStr() { return "expression(" + this.getName() + ")"; } first() { return this.getRunnable().first(); } } exports.Expression = Expression; class Permutation { constructor(list) { if (list.length < 2) { throw new Error("Permutation, length error, got " + list.length); } this.list = list; } listKeywords() { const ret = []; for (const i of this.list) { ret.push(...i.listKeywords()); } return ret; } getUsing() { return this.list.reduce((a, c) => { return a.concat(c.getUsing()); }, []); } run(r) { const result = []; const copy = this.list.slice(); for (let index = 0; index < this.list.length; index++) { const temp = this.list[index].run(r); if (temp.length !== 0) { // match result.push(...temp); const left = copy; left.splice(index, 1); if (left.length === 1) { result.push(...left[0].run(temp)); } else { result.push(...new Permutation(left).run(temp)); } } } return result; } railroad() { const children = this.list.map((e) => { return e.railroad(); }); return "Railroad.MultipleChoice(0, 'any'," + children.join() + ")"; } toStr() { const children = this.list.map((e) => { return e.toStr(); }); return "per(" + children.join() + ")"; } first() { return [""]; } } class FailCombinatorError extends Error { } class FailStarError extends Error { } class FailCombinator { listKeywords() { return []; } getUsing() { return []; } run(_r) { throw new FailCombinatorError(); } railroad() { return "Railroad.Terminal('!FailCombinator')"; } toStr() { return "fail()"; } first() { return []; } } // Note that Plus is implemented with Star class FailStar { listKeywords() { return []; } getUsing() { return []; } run(_r) { throw new FailStarError(); } railroad() { return "Railroad.Terminal('!FailStar')"; } toStr() { return "fail()"; } first() { return []; } } class Alternative { constructor(list) { if (list.length < 2) { throw new Error("Alternative, length error"); } this.list = list; } listKeywords() { const ret = []; for (const i of this.list) { ret.push(...i.listKeywords()); } return ret; } getUsing() { return this.list.reduce((a, c) => { return a.concat(c.getUsing()); }, []); } run(r) { const result = []; for (const sequ of this.list) { const temp = sequ.run(r); result.push(...temp); } return result; } railroad() { const children = this.list.map((e) => { return e.railroad(); }); return "Railroad.Choice(0, " + children.join() + ")"; } toStr() { let ret = ""; for (const i of this.list) { ret = ret + i.toStr() + ","; } return "alt(" + ret + ")"; } first() { if (this.list.length !== 2) { return [""]; } const f1 = this.list[0].first(); const f2 = this.list[1].first(); if (f1.length === 1 && f1[0] === "") { return f1; } if (f2.length === 1 && f2[0] === "") { return f2; } if (f1.length === 1 && f2.length === 1 && f1[0] === f2[0]) { return f1; } f1.push(...f2); return f1; } } // prioritized alternative, skip others if match found class AlternativePriority { constructor(list) { if (list.length < 2) { throw new Error("Alternative, length error"); } this.list = list; } listKeywords() { const ret = []; for (const i of this.list) { ret.push(...i.listKeywords()); } return ret; } getUsing() { return this.list.reduce((a, c) => { return a.concat(c.getUsing()); }, []); } run(r) { const result = []; for (const sequ of this.list) { // console.log(seq.toStr()); const temp = sequ.run(r); if (temp.length > 0) { result.push(...temp); break; } } return result; } railroad() { const children = this.list.map((e) => { return e.railroad(); }); return "Railroad.Choice(0, " + children.join() + ")"; } toStr() { let ret = ""; for (const i of this.list) { ret = ret + i.toStr() + ","; } return "alt(" + ret + ")"; } first() { if (this.list.length !== 2) { return [""]; } const f1 = this.list[0].first(); const f2 = this.list[1].first(); if (f1.length === 1 && f1[0] === "") { return f1; } if (f2.length === 1 && f2[0] === "") { return f2; } if (f1.length === 1 && f2.length === 1 && f1[0] === f2[0]) { return f1; } f1.push(...f2); return f1; } } class Combi { static railroad(runnable, complex = false) { // todo, move method to graph.js? let type = "Railroad.Diagram("; if (complex === true) { type = "Railroad.ComplexDiagram("; } const result = "Railroad.Diagram.INTERNAL_ALIGNMENT = 'left';\n" + type + runnable.railroad() + ").toString();"; return result; } static listKeywords(runnable) { // todo, move these walkers of the syntax tree to some abstraction? let res = runnable.listKeywords(); // remove duplicates res = res.filter((x, i, a) => { return a.indexOf(x) === i; }); return res; } // assumption: no pragmas supplied in tokens input static run(runnable, tokens, version) { this.ver = version; const input = new result_1.Result(tokens, 0); try { const result = runnable.run([input]); /* console.log("res: " + result.length); for (const res of result) { console.dir(res.getNodes().map(n => n.get().constructor.name)); console.dir(res.getNodes().map(n => n.concatTokens())); } */ for (const res of result) { if (res.remainingLength() === 0) { return res.getNodes(); } } } catch (err) { if (err instanceof FailCombinatorError) { return undefined; } throw err; } return undefined; } static getVersion() { return this.ver; } } exports.Combi = Combi; // ----------------------------------------------------------------------------- function str(s) { if (s.indexOf(" ") > 0 || s.indexOf("-") > 0) { return new WordSequence(s); } else { return new Word(s); } } function regex(r) { return new Regex(r); } function tok(t) { return new Token(t.name); } const expressionSingletons = {}; const stringSingletons = {}; function map(s) { const type = typeof s; if (type === "string") { if (stringSingletons[s] === undefined) { stringSingletons[s] = str(s); } return stringSingletons[s]; } else if (type === "function") { // @ts-ignore const name = s.name; if (expressionSingletons[name] === undefined) { // @ts-ignore expressionSingletons[name] = new s(); } return expressionSingletons[name]; } else { return s; } } function seq(first, second, ...rest) { const list = [map(first), map(second)]; list.push(...rest.map(map)); return new Sequence(list); } function alt(first, second, ...rest) { const list = [map(first), map(second)]; list.push(...rest.map(map)); return new Alternative(list); } function altPrio(first, second, ...rest) { const list = [map(first), map(second)]; list.push(...rest.map(map)); return new AlternativePriority(list); } function opt(first) { return new Optional(map(first)); } function optPrio(first) { return new OptionalPriority(map(first)); } function per(first, second, ...rest) { const list = [map(first), map(second)]; list.push(...rest.map(map)); return new Permutation(list); } function star(first) { return new Star(map(first)); } function starPrio(first) { return new StarPriority(map(first)); } function plus(first) { return new Plus(map(first)); } function plusPrio(first) { return new PlusPriority(map(first)); } function ver(version, first, or) { return new Vers(version, map(first), or); } function verNot(version, first) { return new VersNot(version, map(first)); } function failCombinator() { return new FailCombinator(); } function failStar() { return new FailStar(); } //# sourceMappingURL=combi.js.map