this.generateSource = function generateSource(chars, lines, rules, udts, lite, name) {
let source = '';
let i;
let bkrname;
let bkrlower;
let opcodeCount = 0;
let charCodeMin = Infinity;
let charCodeMax = 0;
const ruleNames = [];
const udtNames = [];
let alt = 0;
let cat = 0;
let rnm = 0;
let udt = 0;
let rep = 0;
let and = 0;
let not = 0;
let tls = 0;
let tbs = 0;
let trg = 0;
let bkr = 0;
let bka = 0;
let bkn = 0;
let abg = 0;
let aen = 0;
rules.forEach((rule) => {
ruleNames.push(rule.lower);
opcodeCount += rule.opcodes.length;
rule.opcodes.forEach((op) => {
switch (op.type) {
case id.ALT:
alt += 1;
break;
case id.CAT:
cat += 1;
break;
case id.RNM:
rnm += 1;
break;
case id.UDT:
udt += 1;
break;
case id.REP:
rep += 1;
break;
case id.AND:
and += 1;
break;
case id.NOT:
not += 1;
break;
case id.BKA:
bka += 1;
break;
case id.BKN:
bkn += 1;
break;
case id.BKR:
bkr += 1;
break;
case id.ABG:
abg += 1;
break;
case id.AEN:
aen += 1;
break;
case id.TLS:
tls += 1;
for (i = 0; i < op.string.length; i += 1) {
if (op.string[i] < charCodeMin) {
charCodeMin = op.string[i];
}
if (op.string[i] > charCodeMax) {
charCodeMax = op.string[i];
}
}
break;
case id.TBS:
tbs += 1;
for (i = 0; i < op.string.length; i += 1) {
if (op.string[i] < charCodeMin) {
charCodeMin = op.string[i];
}
if (op.string[i] > charCodeMax) {
charCodeMax = op.string[i];
}
}
break;
case id.TRG:
trg += 1;
if (op.min < charCodeMin) {
charCodeMin = op.min;
}
if (op.max > charCodeMax) {
charCodeMax = op.max;
}
break;
default:
throw new Error('generateSource: unrecognized opcode');
}
});
});
ruleNames.sort();
if (udts.length > 0) {
udts.forEach((udtFunc) => {
udtNames.push(udtFunc.lower);
});
udtNames.sort();
}
source += '// copyright: Copyright (c) 2023 Lowell D. Thomas, all rights reserved<br>\n';
source += '// license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)<br>\n';
source += '//\n';
source += '// Generated by apg-js, Version 4.3.0 [apg-js](https://github.com/ldthomas/apg-js)\n';
if (name && typeof name === 'string') {
source += `const ${name} = function grammar(){\n`;
} else if (lite) {
source += 'export default function grammar(){\n';
} else {
source += `module.exports = function grammar(){\n`;
}
source += ' // ```\n';
source += ' // SUMMARY\n';
source += ` // rules = ${rules.length}\n`;
source += ` // udts = ${udts.length}\n`;
source += ` // opcodes = ${opcodeCount}\n`;
source += ' // --- ABNF original opcodes\n';
source += ` // ALT = ${alt}\n`;
source += ` // CAT = ${cat}\n`;
source += ` // REP = ${rep}\n`;
source += ` // RNM = ${rnm}\n`;
source += ` // TLS = ${tls}\n`;
source += ` // TBS = ${tbs}\n`;
source += ` // TRG = ${trg}\n`;
source += ' // --- SABNF superset opcodes\n';
source += ` // UDT = ${udt}\n`;
source += ` // AND = ${and}\n`;
source += ` // NOT = ${not}\n`;
if (!lite) {
source += ` // BKA = ${bka}\n`;
source += ` // BKN = ${bkn}\n`;
source += ` // BKR = ${bkr}\n`;
source += ` // ABG = ${abg}\n`;
source += ` // AEN = ${aen}\n`;
}
source += ' // characters = [';
if (tls + tbs + trg === 0) {
source += ' none defined ]';
} else {
source += `${charCodeMin} - ${charCodeMax}]`;
}
if (udt > 0) {
source += ' + user defined';
}
source += '\n';
source += ' // ```\n';
if (!lite) {
source += ' /* OBJECT IDENTIFIER (for internal parser use) */\n';
source += " this.grammarObject = 'grammarObject';\n";
}
source += '\n';
source += ' /* RULES */\n';
source += ' this.rules = [];\n';
rules.forEach((rule, ii) => {
let thisRule = ' this.rules[';
thisRule += ii;
thisRule += "] = {name: '";
thisRule += rule.name;
thisRule += "', lower: '";
thisRule += rule.lower;
thisRule += "', index: ";
thisRule += rule.index;
thisRule += ', isBkr: ';
thisRule += rule.isBkr;
thisRule += '};\n';
source += thisRule;
});
source += '\n';
source += ' /* UDTS */\n';
source += ' this.udts = [];\n';
if (udts.length > 0) {
udts.forEach((udtFunc, ii) => {
let thisUdt = ' this.udts[';
thisUdt += ii;
thisUdt += "] = {name: '";
thisUdt += udtFunc.name;
thisUdt += "', lower: '";
thisUdt += udtFunc.lower;
thisUdt += "', index: ";
thisUdt += udtFunc.index;
thisUdt += ', empty: ';
thisUdt += udtFunc.empty;
thisUdt += ', isBkr: ';
thisUdt += udtFunc.isBkr;
thisUdt += '};\n';
source += thisUdt;
});
}
source += '\n';
source += ' /* OPCODES */\n';
rules.forEach((rule, ruleIndex) => {
if (ruleIndex > 0) {
source += '\n';
}
source += ` /* ${rule.name} */\n`;
source += ` this.rules[${ruleIndex}].opcodes = [];\n`;
rule.opcodes.forEach((op, opIndex) => {
let prefix;
switch (op.type) {
case id.ALT:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${
op.type
}, children: [${op.children.toString()}]};// ALT\n`;
break;
case id.CAT:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${
op.type
}, children: [${op.children.toString()}]};// CAT\n`;
break;
case id.RNM:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}, index: ${op.index}};// RNM(${
rules[op.index].name
})\n`;
break;
case id.BKR:
if (op.index >= rules.length) {
bkrname = udts[op.index - rules.length].name;
bkrlower = udts[op.index - rules.length].lower;
} else {
bkrname = rules[op.index].name;
bkrlower = rules[op.index].lower;
}
prefix = '%i';
if (op.bkrCase === id.BKR_MODE_CS) {
prefix = '%s';
}
if (op.bkrMode === id.BKR_MODE_UM) {
prefix += '%u';
} else {
prefix += '%p';
}
bkrname = prefix + bkrname;
source +=
` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}, index: ${op.index}, lower: '${bkrlower}'` +
`, bkrCase: ${op.bkrCase}, bkrMode: ${op.bkrMode}};// BKR(\\${bkrname})\n`;
break;
case id.UDT:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}, empty: ${op.empty}, index: ${
op.index
}};// UDT(${udts[op.index].name})\n`;
break;
case id.REP:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}, min: ${op.min}, max: ${op.max}};// REP\n`;
break;
case id.AND:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}};// AND\n`;
break;
case id.NOT:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}};// NOT\n`;
break;
case id.ABG:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}};// ABG(%^)\n`;
break;
case id.AEN:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}};// AEN(%$)\n`;
break;
case id.BKA:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}};// BKA\n`;
break;
case id.BKN:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}};// BKN\n`;
break;
case id.TLS:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${
op.type
}, string: [${op.string.toString()}]};// TLS\n`;
break;
case id.TBS:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${
op.type
}, string: [${op.string.toString()}]};// TBS\n`;
break;
case id.TRG:
source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = {type: ${op.type}, min: ${op.min}, max: ${op.max}};// TRG\n`;
break;
default:
throw new Error('parser.js: ~143: unrecognized opcode');
}
});
});
source += '\n';
source += ' // The `toString()` function will display the original grammar file(s) that produced these opcodes.\n';
source += ' this.toString = function toString(){\n';
source += ' let str = "";\n';
let str;
lines.forEach((line) => {
const end = line.beginChar + line.length;
str = '';
source += ' str += "';
for (let ii = line.beginChar; ii < end; ii += 1) {
switch (chars[ii]) {
case 9:
str = ' ';
break;
case 10:
str = '\\n';
break;
case 13:
str = '\\r';
break;
case 34:
str = '\\"';
break;
case 92:
str = '\\\\';
break;
default:
str = String.fromCharCode(chars[ii]);
break;
}
source += str;
}
source += '";\n';
});
source += ' return str;\n';
source += ' }\n';
source += '}\n';
return source;
};