UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

312 lines (311 loc) 9.34 kB
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 };