antlr-ng
Version:
Next generation ANTLR Tool
124 lines (123 loc) • 4.25 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { existsSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { Token } from "antlr4ng";
import { Constants } from "../Constants.js";
import { IssueCode } from "../tool/Issues.js";
const linePattern = /\s*(?<tokenID>\w+|'(\\'|[^'])*')\s*=\s*(?<tokenTypeS>\d*)/;
class TokenVocabParser {
constructor(g, outputDirectory, libDirectory) {
this.outputDirectory = outputDirectory;
this.libDirectory = libDirectory;
this.g = g;
}
static {
__name(this, "TokenVocabParser");
}
g;
/**
* Loads a vocab file `<vocabName>.tokens` and return mapping.
*
* @returns A map of token names to token types.
*/
load() {
const tokens = /* @__PURE__ */ new Map();
let maxTokenType = -1;
const tool = this.g.tool;
const vocabName = this.g.getOptionString("tokenVocab");
const content = this.getImportedVocabFile();
const lines = content.split("\n");
let lineNum = 1;
for (const tokenDef of lines) {
++lineNum;
if (tokenDef.length === 0) {
continue;
}
const match = linePattern.exec(tokenDef);
if (match) {
const { tokenID, tokenTypeS } = match.groups;
let tokenType;
try {
tokenType = Number.parseInt(tokenTypeS);
} catch {
this.g.tool.errorManager.toolError(
IssueCode.TokensFileSyntaxError,
vocabName + Constants.VocabFileExtension,
" bad token type: " + tokenTypeS,
lineNum
);
tokenType = Token.INVALID_TYPE;
}
tool.logInfo({ component: "grammar", msg: "import " + tokenID + "=" + tokenType });
tokens.set(tokenID, tokenType);
maxTokenType = Math.max(maxTokenType, tokenType);
} else if (tokenDef.length > 0) {
this.g.tool.errorManager.toolError(
IssueCode.TokensFileSyntaxError,
vocabName + Constants.VocabFileExtension,
" bad token def: " + tokenDef,
lineNum
);
}
}
return tokens;
}
/**
* Return the content of the vocab file. Look in library or in -o output path. antlr -o foo T.g4 U.g4 where U
* needs T.tokens won't work unless we look in foo too. If we do not find the file in the lib directory then must
* assume that the .tokens file is going to be generated as part of this build and we have defined .tokens files
* so that they ALWAYS are generated in the base output directory, which means the current directory for the
* command line tool if there was no output directory specified.
*
* @returns The content of the vocab file.
*/
getImportedVocabFile() {
const vocabName = this.g.getOptionString("tokenVocab");
if (!vocabName) {
return "";
}
try {
let name = join(this.libDirectory ?? ".", vocabName + Constants.VocabFileExtension);
if (existsSync(name)) {
return readFileSync(name, "utf8");
}
if (this.outputDirectory) {
name = join(this.outputDirectory, vocabName + Constants.VocabFileExtension);
if (existsSync(name)) {
return readFileSync(name, "utf8");
}
}
name = dirname(this.g.fileName);
if (name) {
name = join(name, vocabName + Constants.VocabFileExtension);
if (existsSync(name)) {
return readFileSync(name, "utf8");
}
}
const inTree = this.g.ast.getOptionAST("tokenVocab");
const inTreeValue = inTree?.token?.text;
if (vocabName === inTreeValue) {
this.g.tool.errorManager.grammarError(
IssueCode.CannotFindTokensFileRefdInGrammar,
this.g.fileName,
inTree?.token ?? null,
inTreeValue
);
} else {
this.g.tool.errorManager.toolError(
IssueCode.CannotFindTokensFile,
vocabName,
this.g.name
);
}
} catch (e) {
const message = e instanceof Error ? e.message : String(e);
this.g.tool.errorManager.toolError(IssueCode.ErrorReadingTokensFile, e, vocabName, message);
}
return "";
}
}
export {
TokenVocabParser
};