module.exports = function api(src) {
const { Buffer } = require('buffer');
const thisFileName = 'api.js: ';
const thisObject = this;
const apglib = require('../apg-lib/node-exports');
const converter = require('../apg-conv-api/converter');
const scanner = require('./scanner');
const parser = new (require('./parser'))();
const { attributes, showAttributes, showAttributeErrors, showRuleDependencies } = require('./attributes');
const showRules = require('./show-rules');
const abnfToHtml = function abnfToHtml(chars, beg, len) {
const NORMAL = 0;
const CONTROL = 1;
const INVALID = 2;
const CONTROL_BEG = `<span class="${apglib.style.CLASS_CTRLCHAR}">`;
const CONTROL_END = '</span>';
const INVALID_BEG = `<span class="${apglib.style.CLASS_NOMATCH}">`;
const INVALID_END = '</span>';
let end;
let html = '';
const TRUE = true;
while (TRUE) {
if (!Array.isArray(chars) || chars.length === 0) {
break;
}
if (typeof beg !== 'number') {
throw new Error('abnfToHtml: beg must be type number');
}
if (beg >= chars.length) {
break;
}
if (typeof len !== 'number' || beg + len >= chars.length) {
end = chars.length;
} else {
end = beg + len;
}
let state = NORMAL;
for (let i = beg; i < end; i += 1) {
const ch = chars[i];
if (ch >= 32 && ch <= 126) {
if (state === CONTROL) {
html += CONTROL_END;
state = NORMAL;
} else if (state === INVALID) {
html += INVALID_END;
state = NORMAL;
}
switch (ch) {
case 32:
html += ' ';
break;
case 60:
html += '<';
break;
case 62:
html += '>';
break;
case 38:
html += '&';
break;
case 34:
html += '"';
break;
case 39:
html += ''';
break;
case 92:
html += '\';
break;
default:
html += String.fromCharCode(ch);
break;
}
} else if (ch === 9 || ch === 10 || ch === 13) {
if (state === NORMAL) {
html += CONTROL_BEG;
state = CONTROL;
} else if (state === INVALID) {
html += INVALID_END + CONTROL_BEG;
state = CONTROL;
}
if (ch === 9) {
html += 'TAB';
}
if (ch === 10) {
html += 'LF';
}
if (ch === 13) {
html += 'CR';
}
} else {
if (state === NORMAL) {
html += INVALID_BEG;
state = INVALID;
} else if (state === CONTROL) {
html += CONTROL_END + INVALID_BEG;
state = INVALID;
}
html += `\\x${apglib.utils.charToHex(ch)}`;
}
}
if (state === INVALID) {
html += INVALID_END;
}
if (state === CONTROL) {
html += CONTROL_END;
}
break;
}
return html;
};
const abnfToAscii = function abnfToAscii(chars, beg, len) {
let str = '';
for (let i = beg; i < beg + len; i += 1) {
const ch = chars[i];
if (ch >= 32 && ch <= 126) {
str += String.fromCharCode(ch);
} else {
switch (ch) {
case 9:
str += '\\t';
break;
case 10:
str += '\\n';
break;
case 13:
str += '\\r';
break;
default:
str += '\\unknown';
break;
}
}
}
return str;
};
const linesToAscii = function linesToAscii(lines) {
let str = 'Annotated Input Grammar';
lines.forEach((val) => {
str += '\n';
str += `line no: ${val.lineNo}`;
str += ` : char index: ${val.beginChar}`;
str += ` : length: ${val.length}`;
str += ` : abnf: ${abnfToAscii(thisObject.chars, val.beginChar, val.length)}`;
});
str += '\n';
return str;
};
const linesToHtml = function linesToHtml(lines) {
let html = '';
html += `<table class="${apglib.style.CLASS_GRAMMAR}">\n`;
const title = 'Annotated Input Grammar';
html += `<caption>${title}</caption>\n`;
html += '<tr>';
html += '<th>line<br>no.</th><th>first<br>char</th><th><br>length</th><th><br>text</th>';
html += '</tr>\n';
lines.forEach((val) => {
html += '<tr>';
html += `<td>${val.lineNo}`;
html += `</td><td>${val.beginChar}`;
html += `</td><td>${val.length}`;
html += `</td><td>${abnfToHtml(thisObject.chars, val.beginChar, val.length)}`;
html += '</td>';
html += '</tr>\n';
});
html += '</table>\n';
return html;
};
const errorsToHtml = function errorsToHtml(errors, lines, chars, title) {
const [style] = apglib;
let html = '';
const errorArrow = `<span class="${style.CLASS_NOMATCH}">»</span>`;
html += `<p><table class="${style.CLASS_GRAMMAR}">\n`;
if (title && typeof title === 'string') {
html += `<caption>${title}</caption>\n`;
}
html += '<tr><th>line<br>no.</th><th>line<br>offset</th><th>error<br>offset</th><th><br>text</th></tr>\n';
errors.forEach((val) => {
let line;
let relchar;
let beg;
let end;
let text;
let prefix = '';
let suffix = '';
if (lines.length === 0) {
text = errorArrow;
relchar = 0;
} else {
line = lines[val.line];
beg = line.beginChar;
if (val.char > beg) {
prefix = abnfToHtml(chars, beg, val.char - beg);
}
beg = val.char;
end = line.beginChar + line.length;
if (beg < end) {
suffix = abnfToHtml(chars, beg, end - beg);
}
text = prefix + errorArrow + suffix;
relchar = val.char - line.beginChar;
html += '<tr>';
html += `<td>${val.line}</td><td>${line.beginChar}</td><td>${relchar}</td><td>${text}</td>`;
html += '</tr>\n';
html += '<tr>';
html += `<td colspan="3"></td><td>↑: ${apglib.utils.stringToAsciiHtml(val.msg)}</td>`;
html += '</tr>\n';
}
});
html += '</table></p>\n';
return html;
};
const errorsToAscii = function errorsToAscii(errors, lines, chars) {
let str;
let line;
let beg;
let len;
str = '';
errors.forEach((error) => {
line = lines[error.line];
str += `${line.lineNo}: `;
str += `${line.beginChar}: `;
str += `${error.char - line.beginChar}: `;
beg = line.beginChar;
len = error.char - line.beginChar;
str += abnfToAscii(chars, beg, len);
str += ' >> ';
beg = error.char;
len = line.beginChar + line.length - error.char;
str += abnfToAscii(chars, beg, len);
str += '\n';
str += `${line.lineNo}: `;
str += `${line.beginChar}: `;
str += `${error.char - line.beginChar}: `;
str += 'error: ';
str += error.msg;
str += '\n';
});
return str;
};
let isScanned = false;
let isParsed = false;
let isTranslated = false;
let haveAttributes = false;
let attributeErrors = 0;
let lineMap;