@abaplint/core
Version:
abaplint - Core API
339 lines • 10 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.seq = seq;
exports.alt = alt;
exports.beginEnd = beginEnd;
exports.opt = opt;
exports.star = star;
exports.sta = sta;
exports.sub = sub;
const nodes_1 = require("../../nodes");
const _statement_1 = require("../../2_statements/statements/_statement");
class Sequence {
constructor(list) {
if (list.length < 2) {
throw new Error("Sequence, length error");
}
this.list = list;
}
toRailroad() {
const children = this.list.map((e) => { return e.toRailroad(); });
return "Railroad.Sequence(" + children.join() + ")";
}
getUsing() {
return this.list.reduce((a, c) => { return a.concat(c.getUsing()); }, []);
}
first() {
return this.list[0].first();
}
run(statements, parent) {
let inn = statements;
let out = [];
for (const i of this.list) {
const match = i.run(inn, parent);
if (match.error) {
return {
matched: [],
unmatched: statements,
error: true,
errorDescription: match.errorDescription,
errorMatched: out.length,
};
}
if (match.matched.length < 100) {
out.push(...match.matched);
}
else {
// avoid using the spread operator, it might trigger "Maximum call stack size exceeded"
// when the number of matched elements is very large
out = out.concat(match.matched);
}
inn = match.unmatched;
}
return {
matched: out,
unmatched: inn,
error: false,
errorDescription: "",
errorMatched: 0,
};
}
}
// Note that the Alternative does not nessesarily return the first in the alternative
// as a map is used for better performance
class Alternative {
constructor(list) {
if (list.length < 2) {
throw new Error("Alternative, length error");
}
this.list = list;
}
setupMap() {
// dont call from constructor, it will cause infinite loop
if (this.map === undefined) {
this.map = {};
for (const i of this.list) {
for (const first of i.first()) {
if (this.map[first]) {
this.map[first].push(i);
}
else {
this.map[first] = [i];
}
}
}
}
}
first() {
return [""];
}
toRailroad() {
const children = this.list.map((e) => { return e.toRailroad(); });
return "Railroad.Choice(0, " + children.join() + ")";
}
getUsing() {
return this.list.reduce((a, c) => { return a.concat(c.getUsing()); }, []);
}
run(statements, parent) {
this.setupMap();
let count = 0;
let countError = "";
if (statements.length === 0) {
return {
matched: [],
unmatched: statements,
error: true,
errorDescription: countError,
errorMatched: count,
};
}
const token = statements[0].getFirstToken().getStr().toUpperCase();
for (const i of this.map[token] || []) {
const match = i.run(statements, parent);
if (match.error === false) {
return match;
}
if (match.errorMatched > count) {
countError = match.errorDescription;
count = match.errorMatched;
}
}
for (const i of this.map[""] || []) {
const match = i.run(statements, parent);
if (match.error === false) {
return match;
}
if (match.errorMatched > count) {
countError = match.errorDescription;
count = match.errorMatched;
}
}
if (count === 0) {
return {
matched: [],
unmatched: statements,
error: true,
errorDescription: "Unexpected code structure",
errorMatched: count,
};
}
else {
return {
matched: [],
unmatched: statements,
error: true,
errorDescription: countError,
errorMatched: count,
};
}
}
}
class Optional {
constructor(obj) {
this.obj = obj;
}
toRailroad() {
return "Railroad.Optional(" + this.obj.toRailroad() + ")";
}
getUsing() {
return this.obj.getUsing();
}
run(statements, parent) {
const ret = this.obj.run(statements, parent);
ret.error = false;
return ret;
}
first() {
return [""];
}
}
class Star {
constructor(obj) {
this.obj = obj;
}
toRailroad() {
return "Railroad.ZeroOrMore(" + this.obj.toRailroad() + ")";
}
getUsing() {
return this.obj.getUsing();
}
run(statements, parent) {
let inn = statements;
let out = [];
while (true) {
if (inn.length === 0) {
return {
matched: out,
unmatched: inn,
error: false,
errorDescription: "",
errorMatched: 0,
};
}
const match = this.obj.run(inn, parent);
if (match.error === true) {
if (match.errorMatched > 0) {
return {
matched: out,
unmatched: inn,
error: true,
errorDescription: match.errorDescription,
errorMatched: match.errorMatched,
};
}
else {
return {
matched: out,
unmatched: inn,
error: false,
errorDescription: "",
errorMatched: 0,
};
}
}
if (match.matched.length < 100) {
out.push(...match.matched);
}
else {
// avoid using the spread operator, it might trigger "Maximum call stack size exceeded"
// when the number of matched elements is very large
out = out.concat(match.matched);
}
inn = match.unmatched;
}
}
first() {
return [""];
}
}
class SubStructure {
constructor(s) {
this.s = s;
}
toRailroad() {
return "Railroad.NonTerminal('" + this.s.constructor.name + "', {href: '#/structure/" + this.s.constructor.name + "'})";
}
getUsing() {
return ["structure/" + this.s.constructor.name];
}
first() {
this.setupMatcher();
return this.matcher.first();
}
setupMatcher() {
if (this.matcher === undefined) {
// SubStructures are singletons, so the getMatcher can be saved, its expensive to create
// dont move this to the constructor, as it might trigger infinite recursion
this.matcher = this.s.getMatcher();
}
}
run(statements, parent) {
const nparent = new nodes_1.StructureNode(this.s);
this.setupMatcher();
const ret = this.matcher.run(statements, nparent);
if (ret.matched.length === 0) {
ret.error = true;
}
else {
parent.addChild(nparent);
}
return ret;
}
}
class SubStatement {
constructor(obj) {
this.obj = obj;
}
first() {
const o = new this.obj();
if (o instanceof _statement_1.MacroCall || o instanceof _statement_1.NativeSQL) {
return [""];
}
return o.getMatcher().first();
}
toRailroad() {
return "Railroad.Terminal('" + this.className() + "', {href: '#/statement/" + this.className() + "'})";
}
getUsing() {
return ["statement/" + this.className()];
}
className() {
return this.obj.name;
}
run(statements, parent) {
if (statements.length === 0) {
return {
matched: [],
unmatched: [],
error: true,
errorDescription: "Expected " + this.className().toUpperCase(),
errorMatched: 0,
};
}
else if (statements[0].get() instanceof this.obj) {
parent.addChild(statements[0]);
return {
matched: [statements[0]],
unmatched: statements.splice(1),
error: false,
errorDescription: "",
errorMatched: 0,
};
}
else {
return {
matched: [],
unmatched: statements,
error: true,
errorDescription: "Expected " + this.className().toUpperCase(),
errorMatched: 0,
};
}
}
}
function seq(first, ...rest) {
return new Sequence([first].concat(rest));
}
function alt(first, ...rest) {
return new Alternative([first].concat(rest));
}
function beginEnd(begin, body, end) {
return new Sequence([begin, body, end]);
}
function opt(o) {
return new Optional(o);
}
function star(s) {
return new Star(s);
}
function sta(s) {
return new SubStatement(s);
}
const singletons = {};
function sub(s) {
if (singletons[s.name] === undefined) {
singletons[s.name] = new SubStructure(new s());
}
return singletons[s.name];
}
//# sourceMappingURL=_combi.js.map