Java中的ANTLR基本示例
在过去的几个小时里,我一直在网上搜索,试图学习一个使用ANTLR的简单示例。但是我很难理解这些示例。是否有任何主体有简单的示例可以用Java输出: 如果我的输入是Java中的ANTLR基本示例,java,compiler-construction,antlr,Java,Compiler Construction,Antlr,在过去的几个小时里,我一直在网上搜索,试图学习一个使用ANTLR的简单示例。但是我很难理解这些示例。是否有任何主体有简单的示例可以用Java输出: 如果我的输入是 printf(“你好世界”) 输出应为: 你好,世界 如果我的输入是 inx=1 它应该给出一个错误消息 我试图用java创建C++编译器(从词汇开始直到语义部分),我很想知道我应该做什么。 grammar Expr; @header { package test; import java.util.HashMap; } @lex
printf(“你好世界”)代码>
输出应为:
你好,世界
如果我的输入是
inx=1
它应该给出一个错误消息
<>我试图用java创建C++编译器(从词汇开始直到语义部分),我很想知道我应该做什么。
grammar Expr;
@header {
package test;
import java.util.HashMap;
}
@lexer::header {package test;}
@members {
/** Map variable name to Integer object holding value */
HashMap memory = new HashMap();
}
prog: stat+ ;
stat: expr NEWLINE {System.out.println($expr.value);}
| ID '=' expr NEWLINE
{memory.put($ID.text, new Integer($expr.value));}
| NEWLINE
;
expr returns [int value]
: e=multExpr {$value = $e.value;}
( '+' e=multExpr {$value += $e.value;}
| '-' e=multExpr {$value -= $e.value;}
)*
;
multExpr returns [int value]
: e=atom {$value = $e.value;} ('*' e=atom {$value *= $e.value;})*
;
atom returns [int value]
: INT {$value = Integer.parseInt($INT.text);}
| ID
{
Integer v = (Integer)memory.get($ID.text);
if ( v!=null ) $value = v.intValue();
else System.err.println("undefined variable "+$ID.text);
}
| '(' e=expr ')' {$value = $e.value;}
;
ID : ('a'..'z'|'A'..'Z')+ ;
INT : '0'..'9'+ ;
NEWLINE:'\r'? '\n' ;
WS : (' '|'\t')+ {skip();} ;
但是正如我在我的评论中提到的,C++很难正确解析。有很多不明确之处,需要一定的前瞻性(ANTLR确实提供了这一点)。因此,以任何有效的形式进行这项工作都是复杂的。这就是为什么我建议实现类似的东西,它是为学生编写第一个编译器而设计的。这也是一个良好的开端。这两种方法都可以实现,而无需使用ANTLR之类的工具。我在1000行中实现了这两种方法(分别在C++和C语言中)。
不过,ANTLR是一个很棒的工具,尤其是当您开始关注递归下降时,您可能希望升级到一个更强大的解析器。我推荐特伦斯·帕尔的两本书,还有。ANTLR的书会告诉你关于ANTLR的一切(还有一些)你想知道的。第二本书将教你所有关于解析器和编译器的知识,从递归下降到黑魔法回溯
可以找到类似问题的更多资源。如果您喜欢Lisp或Scheme,您可以查看,它是用Java编写的(我相信不到1000行)。这里有一个语法,它几乎可以满足您的需要:
grammar PrintLang;
sentence
: statement
;
statement
: functionCall '(' argument ')' ';'
{
if ($functionCall.funName.equals("printf")) {
System.out.println($argument.arg);
}
}
;
functionCall returns [String funName]
: ID
{ $funName = $ID.text; }
;
argument returns [String arg]
: STRING
{ $arg = $STRING.text; }
;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
;
WS : ( ' '
| '\t'
| '\r'
| '\n'
) {$channel=HIDDEN;}
;
STRING
: '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
;
fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
fragment
ESC_SEQ
: '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
| UNICODE_ESC
| OCTAL_ESC
;
fragment
OCTAL_ESC
: '\\' ('0'..'3') ('0'..'7') ('0'..'7')
| '\\' ('0'..'7') ('0'..'7')
| '\\' ('0'..'7')
;
fragment
UNICODE_ESC
: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
;
我在AntlWorks中生成了这个。所有的令牌规则都是为我生成的
下面是测试它的java文件
import org.antlr.runtime.*;
public class PrintIt {
public static void main(String args[]) {
String inputString = "printf(\"HelloWorld\");";
// Create an input character stream from standard in
ANTLRStringStream input = new ANTLRStringStream(inputString);
// Create an ExprLexer that feeds from that stream
PrintLangLexer lexer = new PrintLangLexer(input);
// Create a stream of tokens fed by the lexer
CommonTokenStream tokens = new CommonTokenStream(lexer);
// Create a parser that feeds off the token stream
PrintLangParser plParser = new PrintLangParser(tokens);
try {
plParser.sentence();
} catch (Exception e) {
e.printStackTrace();
}
}
}
您会注意到,这段java代码几乎是Antlr网站示例的逐字复制/粘贴(我甚至不相信我更改了注释,这就是注释在中引用标准的原因,但代码实际上使用了字符串)。这是我用来做这件事的命令行
bash$ java -cp ./antlr-3.4-complete.jar org.antlr.Tool PrintLang.g
bash$ javac -cp ./:./antlr-3.4-complete.jar PrintIt.java
bash$ java -cp antlr-3.4-complete.jar:. PrintIt
"HelloWorld"
哎呀,我忘了我要打印的字符串不是匹配的标记(“HelloWorld”,包括引号),而是引号中的字符串
另外,您会注意到我将printf的查找硬编码为字符串比较。实际上,您需要一个包含在给定范围内可访问的符号的环境(相关的,请参阅antlr的“范围”构造。更困难的,尽管有时有用:创建一个传递给每个解析规则的环境)
最重要的是:通过搜索更多的antlr问题来找到巴特·基尔斯的答案。他举了很好的例子。你输入的语法是什么?你的输入都是一个标记吗??如果是这样,那很容易!如果你想把它像C一样解析,那么它离“简单”很远。对于记录来说,C++是很难正确解析的。它是上下文敏感的。你提到你的输出。。。那么你是在写解释器,而不是编译器?@Austinenley是这样吗?那么你能推荐一种替代编程语言,让我的语法更简单吗?Python是一种好的编程语言吗?@Austinenley我实际上计划创建一个解释器,如果我的输入通过了所有的分析部分,我将把它移植到一个现成的输出生成器,如C:%gcc-C中所示