antlr-ng
Version:
Next generation ANTLR Tool
312 lines (311 loc) • 9.34 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { CommonToken, Token } from "antlr4ng";
import { ANTLRv4Parser } from "../generated/ANTLRv4Parser.js";
import { Character } from "../support/Character.js";
import { AttributeDict } from "../tool/AttributeDict.js";
import { IssueCode } from "../tool/Issues.js";
class ScopeParser {
static {
__name(this, "ScopeParser");
}
/**
* Given an arg or retval scope definition list like
* ```
* Map<String, String>, int[] j3, char *foo32[3]
* ```
* or
* ```
* int i=3, j=a[34]+20
* ```
* convert to an attribute scope.
*
* @param action The action node that contains the decl.
* @param s The decl to parse.
* @param g The grammar.
*
* @returns The attribute scope.
*/
static parseTypedArgList(action, s, g) {
return this.parse(action, s, ",", g);
}
static parse(action, s, separator, g) {
const dict = new AttributeDict();
const decls = this.splitDecls(s, separator);
for (const decl of decls) {
if (decl[0].trim().length > 0) {
const a = this.parseAttributeDef(action, decl, g);
dict.add(a);
}
}
return dict;
}
/**
* For decls like "String foo" or "char *foo32[]" compute the ID and type declarations. Also handle "int x=3"
* and 'T t = new T("foo")' but if the separator is ',' you cannot use ',' in the init value
* unless you escape use "\," escape.
*
* @param action The action node that contains the decl.
* @param decl The decl to parse.
* @param g The grammar.
*
* @returns The attribute definition.
*/
static parseAttributeDef(action, decl, g) {
if (decl[0] === null) {
return null;
}
const attr = { name: "" };
let rightEdgeOfDeclarator = decl[0].length - 1;
const equalsIndex = decl[0].indexOf("=");
if (equalsIndex > 0) {
attr.initValue = decl[0].substring(equalsIndex + 1, decl[0].length).trim();
rightEdgeOfDeclarator = equalsIndex - 1;
}
const declarator = decl[0].substring(0, rightEdgeOfDeclarator + 1);
let p;
let text = decl[0];
text = text.replaceAll("::", "");
if (text.includes(":")) {
p = ScopeParser.parsePostfixDecl(attr, declarator, action, g);
} else {
p = ScopeParser.parsePrefixDecl(attr, declarator, action, g);
}
const [idStart, idStop] = p;
attr.decl = decl[0];
if (action !== null) {
const actionText = action.getText();
const lines = new Array(actionText.length);
const charPositionInLines = new Array(actionText.length);
for (let i = 0, line2 = 0, col = 0; i < actionText.length; i++, col++) {
lines[i] = line2;
charPositionInLines[i] = col;
if (actionText[i] === "\n") {
line2++;
col = -1;
}
}
const charIndexes = new Array(actionText.length);
for (let i = 0, j = 0; i < actionText.length; i++, j++) {
charIndexes[j] = i;
if (i < actionText.length - 1 && actionText[i] === "/" && actionText[i + 1] === "/") {
while (i < actionText.length && actionText[i] !== "\n") {
i++;
}
}
}
const declOffset = charIndexes[decl[1]];
const declLine = lines[declOffset + idStart];
const line = action.token.line + declLine;
let charPositionInLine = charPositionInLines[declOffset + idStart];
if (declLine === 0) {
charPositionInLine += action.token.column + 1;
}
const offset = action.token.start;
attr.token = CommonToken.fromSource(
[null, action.token.inputStream],
ANTLRv4Parser.ID,
Token.DEFAULT_CHANNEL,
offset + declOffset + idStart + 1,
offset + declOffset + idStop
);
attr.token.line = line;
attr.token.column = charPositionInLine;
}
return attr;
}
static parsePrefixDecl(attr, decl, a, g) {
let inID = false;
let start = -1;
for (let i = decl.length - 1; i >= 0; i--) {
const ch = decl.codePointAt(i);
if (!inID && Character.isLetterOrDigit(ch)) {
inID = true;
} else if (inID && !(Character.isLetterOrDigit(ch) || ch === 95)) {
start = i + 1;
break;
}
}
if (start < 0 && inID) {
start = 0;
}
if (start < 0) {
g.tool.errorManager.grammarError(
IssueCode.CannotFindAttributeNameInDecl,
g.fileName,
a?.token ?? null,
decl
);
}
let stop = -1;
for (let i = start; i < decl.length; i++) {
const ch = decl.codePointAt(i);
if (!(Character.isLetterOrDigit(ch) || ch === 95)) {
stop = i;
break;
}
if (i === decl.length - 1) {
stop = i + 1;
}
}
attr.name = decl.substring(start, stop);
attr.type = decl.substring(0, start);
if (stop <= decl.length - 1) {
attr.type += decl.substring(stop, decl.length);
}
attr.type = attr.type.trim();
if (attr.type.length === 0) {
attr.type = void 0;
}
return [start, stop];
}
static parsePostfixDecl(attr, decl, a, g) {
let start = -1;
let stop = -1;
const colon = decl.indexOf(":");
const namePartEnd = colon === -1 ? decl.length : colon;
for (let i = 0; i < namePartEnd; ++i) {
const ch = decl.codePointAt(i);
if (Character.isLetterOrDigit(ch) || ch === 95) {
start = i;
break;
}
}
if (start === -1) {
start = 0;
g.tool.errorManager.grammarError(
IssueCode.CannotFindAttributeNameInDecl,
g.fileName,
a?.token ?? null,
decl
);
}
for (let i = start; i < namePartEnd; ++i) {
const ch = decl.codePointAt(i);
if (!(Character.isLetterOrDigit(ch) || ch === 95)) {
stop = i;
break;
}
if (i === namePartEnd - 1) {
stop = namePartEnd;
}
}
if (stop === -1) {
stop = start;
}
attr.name = decl.substring(start, stop);
if (colon === -1) {
attr.type = "";
} else {
attr.type = decl.substring(colon + 1, decl.length);
}
attr.type = attr.type.trim();
if (attr.type.length === 0) {
attr.type = void 0;
}
return [start, stop];
}
/**
* Given an argument list like
* ```
* x, (*a).foo(21,33), 3.2+1, '\n',
* "a,oo\nick", {bl, "abc"eck}, ["cat\n,", x, 43]
* ```
* convert to a list of attributes. Allow nested square brackets etc...
* Set separatorChar to ';' or ',' or whatever you want.
*
* @param s The argument list to parse.
* @param separatorChar The separator character.
*
* @returns The list of attributes.
*/
static splitDecls(s, separatorChar) {
const args = new Array();
ScopeParser.splitArgumentList(s, 0, -1, separatorChar.codePointAt(0), args);
return args;
}
static splitArgumentList(actionText, start, targetChar, separatorChar, args) {
if (actionText === null) {
return -1;
}
actionText = actionText.replaceAll(/\/\/[^\n]*/g, "");
const n = actionText.length;
let p = start;
let last = p;
while (p < n && actionText.codePointAt(p) !== targetChar) {
const c = actionText.codePointAt(p);
switch (c) {
case 39: {
++p;
while (p < n && actionText.codePointAt(p) !== 39) {
if (actionText.codePointAt(p) === 92 && p + 1 < n && actionText.codePointAt(p + 1) === 39) {
++p;
}
++p;
}
++p;
break;
}
case 34: {
++p;
while (p < n && actionText.codePointAt(p) !== 34) {
if (actionText.codePointAt(p) === 92 && p + 1 < n && actionText.codePointAt(p + 1) === 34) {
++p;
}
++p;
}
++p;
break;
}
case 40: {
p = ScopeParser.splitArgumentList(actionText, p + 1, 35, separatorChar, args);
break;
}
case 123: {
p = ScopeParser.splitArgumentList(actionText, p + 1, 125, separatorChar, args);
break;
}
case 60: {
if (actionText.indexOf(">", p + 1) >= p) {
p = ScopeParser.splitArgumentList(actionText, p + 1, 62, separatorChar, args);
} else {
++p;
}
break;
}
case 91: {
p = ScopeParser.splitArgumentList(actionText, p + 1, 93, separatorChar, args);
break;
}
default: {
if (c === separatorChar && targetChar === -1) {
const arg = actionText.substring(last, p);
let index = last;
while (index < p && Character.isWhitespace(actionText.codePointAt(index))) {
index++;
}
args.push([arg.trim(), index]);
last = p + 1;
}
++p;
break;
}
}
}
if (targetChar === -1) {
const arg = actionText.substring(last, p).trim();
let index = last;
while (index < p && Character.isWhitespace(actionText.codePointAt(index))) {
index++;
}
if (arg.length > 0) {
args.push([arg.trim(), index]);
}
}
++p;
return p;
}
}
export {
ScopeParser
};