I want to generate an antlr lexer at runtime -- that is, generate the grammar and from the grammar generate the lexer class, and its supporting bits at runtime. I am happy to feed it into the the java compiler, which is accessible at runtime.
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
Here's a quick and dirty way to:
- generate a combined (!) ANTLR grammar
.g
file given a String as grammar-source, - and create a Parser & Lexer from this
.g
file, - compile the these Parser & Lexer
.java
files, - create instances of the Parser & Lexer classes and invoke the entry point of the parser.
Main.java
import java.io.*; import javax.tools.*; import java.lang.reflect.*; import org.antlr.runtime.*; import org.antlr.Tool; public class Main { public static void main(String[] args) throws Exception { // The grammar which echos the parsed characters to theconsole, // skipping any white space chars. final String grammar = "grammar T; \n" + " \n" + "parse \n" + " : (ANY {System.out.println(\"ANY=\" + $ANY.text);})* EOF \n" + " ; \n" + " \n" + "SPACE \n" + " : (' ' | '\\t' | '\\r' | '\\n') {skip();} \n" + " ; \n" + " \n" + "ANY \n" + " : . \n" + " ; "; final String grammarName = "T"; final String entryPoint = "parse"; // 1 - Write the `.g` grammar file to disk. Writer out = new BufferedWriter(new FileWriter(new File(grammarName + ".g"))); out.write(grammar); out.close(); // 2 - Generate the lexer and parser. Tool tool = new Tool(new String[]{grammarName + ".g"}); tool.process(); // 3 - Compile the lexer and parser. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); compiler.run(null, System.out, System.err, "-sourcepath", "", grammarName + "Lexer.java"); compiler.run(null, System.out, System.err, "-sourcepath", "", grammarName + "Parser.java"); // 4 - Parse the command line parameter using the dynamically created lexer and // parser with a bit of reflection Voodoo :) Lexer lexer = (Lexer)Class.forName(grammarName + "Lexer").newInstance(); lexer.setCharStream(new ANTLRStringStream(args[0])); CommonTokenStream tokens = new CommonTokenStream(lexer); Class<?> parserClass = Class.forName(grammarName + "Parser"); Constructor parserCTor = parserClass.getConstructor(TokenStream.class); Parser parser = (Parser)parserCTor.newInstance(tokens); Method entryPointMethod = parserClass.getMethod(entryPoint); entryPointMethod.invoke(parser); } }
Which, after compiling and running it like this (on *nix):
java -cp .:antlr-3.2.jar Main "a b c"
or on Windows
java -cp .;antlr-3.2.jar Main "a b c"
, produces the following output:
ANY=a ANY=b ANY=c
回答2:
You'll have to use org.antlr.Tool()
class to get it working.
You can check ANTLRWorks source code on github to have an idea how to use it, specifically the generate()
method here:
ErrorListener el = ErrorListener.getThreadInstance(); ErrorManager.setErrorListener(el); String[] params; if(debug) params = new String[] { "-debug", "-o", getOutputPath(), "-lib", window.getFileFolder(), window.getFilePath() }; else params = new String[] { "-o", getOutputPath(), "-lib", window.getFileFolder(), window.getFilePath() }; new File(getOutputPath()).mkdirs(); Tool antlr = new Tool(Utils.concat(params, AWPrefs.getANTLR3Options())); antlr.process(); boolean success = !el.hasErrors(); if(success) { dateOfModificationOnDisk = window.getDocument().getDateOfModificationOnDisk(); } lastError = el.getFirstErrorMessage(); el.clear(); ErrorManager.removeErrorListener(); return success;
回答3:
Have you tried calling org.antlr.Tool.main(String[])
with an appropriate String[] argument?
If that's too cumbersome, you could reverse engineer the Tool
class (source code) to figure out how it works, and how to do the specific tasks you need to do.