@abaplint/core
Version:
abaplint - Core API
912 lines • 23.6 kB
JavaScript
"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