UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

124 lines (123 loc) 4.25 kB
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 };