使用antlr非常新。我正在制作一个计算器,我已经完成了我的语法。如果我使用听众而不是访客,是否有可能获得结果?我不知道这是否有意义,但我正在使用监听器,我唯一能做的就是让它打印输入而不是输入的计算结果。我在网上查了一下,每个例子都使用了访客。所以我应该使用访客还是在那里你可以在使用听众时获得结果?我希望这是有道理的。
这是我的g4文件
grammar calc;
TYPE_INT: 'int';
TYPE_FLOAT: 'float';
EQU: '=';
MUL: '*';
DIV: '/';
PLU: '+';
MIN: '-';
L_BKT: '(';
R_BKT: ')';
WRITE: 'write';
END_MARK: ';';
ID: ([A-Z]|[a-z]) ([A-Z]|[a-z]|[0-9])*;
INT: ('0'|([1-9]([0-9])*));
FLOAT: ('0'|([1-9]([0-9])*))'.'([0-9]+)(('e'|'E')('+'|'-')?('0'|([1-9]([0-
9])*)))? ;
WS : [ \t\r\n]+ -> skip ;
declare: (typeInt=TYPE_INT|typeFloat=TYPE_FLOAT) id=ID;
assign: id=ID equ=EQU expr;
expr: expr (plu=PLU|min=MIN) expr2|expr2;
expr2: expr2 (mul=MUL|div=DIV) expr3 |expr3;
expr3: floatNumber=FLOAT| intNumber=INT| id=ID| lBkt=L_BKT expr rBkt=R_BKT;
writeFunction: write=WRITE lBkt=L_BKT id=ID rBkt=R_BKT;
stmt: declare| assign| writeFunction;
stmts: (stmt endMark=END_MARK)*;
r: stmts;
这是我在calcbaseListener
中使用的变量和variableList类public class Variable {
public String name="";
public String type="int";
public float value=(float) 0.0;
}
public class VariableList {
List<Variable> list=new ArrayList();
private static VariableList self=new VariableList();
public static VariableList getList(){
return self;
}
public Variable find(String name){
Variable v=null;
for(int i=0;i<list.size();i++){
Variable tmp=list.get(i);
if(tmp.name.equals(name)){
v=tmp;
}
}
return v;
}
public void add(Variable v){
list.add(v);
}
}
这是我在calcBaseListener中放入的代码
public void exitDeclare(calcParser.DeclareContext ctx) {
String name=ctx.id.getText();
Variable v=new Variable();
v.name = name;
if(ctx.typeFloat != null) {
v.type = "float";
} else {
v.type = "int";
}
VariableList list = VariableList.getList();
if(list.find(v.name) == null) {
list.add(v);
} else {
System.out.println("The id has already been declared");
System.exit(0);
}
}
public void exitAssign(calcParser.AssignContext ctx) {
String name = ctx.id.getText();
VariableList list = VariableList.getList();
if(list.find(name) == null) {
System.out.println("Undeclared id");
System.exit(0);
} else {
Variable v = list.find(name);
if(v.type.equals(ctx.expr().v.type) || v.type.equals("float") &&
ctx.expr().v.type.equals("int")) {
v.value = ctx.expr().v.value;
}
else {
System.out.println("The type of int assigned to the type of type
of float error");
System.exit(0);
}
}
}
public void exitExpr(calcParser.ExprContext ctx) {
if(ctx.expr()==null){
if(ctx.v.type.equals(ctx.expr2().v.type)||
ctx.v.type.equals("float")&&ctx.expr2().v.type.equals("int")){
ctx.v.value=ctx.expr().v.value;
}else{
System.out.println("The type of int assign to the tpye of float
error");
System.exit(0);
}
ctx.v.value=ctx.expr().v.value;
return;
}
if(ctx.plu!=null){
String type="";
if(ctx.expr().v.type.equals("float")
||ctx.expr2().v.type.equals("float")){
type="float";
}else{
type="int";
}
if(ctx.v.type.equals(type)||
ctx.v.type.equals("float")&&type.equals("int")){
ctx.v.value=ctx.expr().v.value;
}else{
System.out.println("The type of int assign to the tpye of float
error");
System.exit(0);
}
ctx.v.value=ctx.expr().v.value+ctx.expr2().v.value;
return;
}
if(ctx.min!=null){
String type="";
if(ctx.expr().v.type.equals("float")
||ctx.expr2().v.type.equals("float")){
type="float";
}else{
type="int";
}
if(ctx.v.type.equals(type)||
ctx.v.type.equals("float")&&type.equals("int")){
ctx.v.value=ctx.expr().v.value;
}else{
System.out.println("The type of int assign to the tpye of float
error");
System.exit(0);
}
ctx.v.value=ctx.expr().v.value-ctx.expr2().v.value;
return;
}
}
public void exitExpr2(calcParser.Expr2Context ctx) {
if(ctx.expr2()==null){
if(ctx.v.type.equals(ctx.expr3().v.type)||
ctx.v.type.equals("float")&&ctx.expr3().v.type.equals("int")){
ctx.v.value=ctx.expr2().v.value;
}else{
System.out.println("The type of int assign to the tpye of float error");
System.exit(0);
}
ctx.v.value=ctx.expr2().v.value;
return;
}
if(ctx.mul!=null){
String type="";
if(ctx.expr2().v.type.equals("float")
||ctx.expr3().v.type.equals("float")){
type="float";
}else{
type="int";
}
if(ctx.v.type.equals(type)||
ctx.v.type.equals("float")&&type.equals("int")){
ctx.v.value=ctx.expr2().v.value;
}else{
System.out.println("The type of int assign to the tpye of float error");
System.exit(0);
}
ctx.v.value=ctx.expr2().v.value*ctx.expr3().v.value;
return;
}
if(ctx.div!=null){
String type="";
if(ctx.expr2().v.type.equals("float")
||ctx.expr3().v.type.equals("float")){
type="float";
}else{
type="int";
}
if(ctx.v.type.equals(type)||
ctx.v.type.equals("float")&&type.equals("int")){
ctx.v.value=ctx.expr2().v.value;
}else{
System.out.println("The type of int assign to the tpye of float error");
System.exit(0);
}
ctx.v.value=ctx.expr2().v.value/ctx.expr3().v.value;
return;
}
}
public void exitExpr3(calcParser.Expr3Context ctx) {
if(ctx.floatNumber!=null){
ctx.v.type="float";
ctx.v.value=Float.valueOf(ctx.floatNumber.getText());
}
if(ctx.intNumber!=null){
ctx.v.type="int";
ctx.v.value=Float.valueOf(ctx.intNumber.getText());
}
if(ctx.id!=null){
VariableList list=VariableList.getList();
String name=ctx.id.getText();
ctx.v.type=list.find(name).type;
ctx.v.value=list.find(name).value;
}
if(ctx.expr()!=null){
ctx.v.type=ctx.expr().v.type;
ctx.v.value=ctx.expr().v.value;
}
}
public void exitWriteFunction(calcParser.WriteFunctionContext ctx) {
VariableList list=VariableList.getList();
String name=ctx.id.getText();
if(list.find(name)!=null){
Variable v=list.find(name);
System.out.println(v.value);
}else{
System.out.println("Undecleared id");
System.exit(0);
}
}
如果我像这样编写代码,我是否有可能获得计算的价值?
答案 0 :(得分:0)
为了回答您的原始问题:是的,可以使用监听器实现计算器。
此时将简单地引用提供两者的this GitHub repository:一个使用侦听器的实现和一个使用访问者的实现。
由于上面提到的语法不能处理赋值,我建议看看this repo,它确实提供了纯算术解析旁边的赋值功能。请注意,虽然这个没有利用ANTLR处理直接左递归的能力,这导致了相同任务的更多代码。
一般来说,我建议你利用直接的左递归规则,因为tehy使你的语法更容易阅读。在替代规则标签的组合中,它是一个真正强大的功能。
答案 1 :(得分:0)
请查找基于antlr4 FreeBSD documentation的示例。
语法非常相似。但是我会严格遵循Raven所表达的建议并将所有&#34;代数优先级的东西组合在一起#34;在一个antlr4规则。这样做将确保优先权得到尊重,因为antlr4将在加法和减法之前探索(没有大括号)表达式的乘除部分。
我确实使用过访问者。访问者绝对是管理结果计算的最佳位置。访客的建立是为了返回结果。听众没有返回结果。 建议的实现是用python编写的。访问者创建一个字典,其中键是在相等的左侧找到的变量的名称。每次在访问者中触发操作符时,我们都会询问操作数,根据操作类型计算结果,并将结果值存储在变量的字典中。
此致
答案 2 :(得分:-1)
我对ANTLR 4并不熟悉,但总的来说 - 制作计算器意味着你需要考虑的不是单独的令牌/术语,而是你还需要考虑它们在解析树中的关系和位置(例如,进程添加,您还需要访问操作数)。
访问者似乎是一种更合适的方法(因为你决定访问节点的顺序和方式)而不是监听器(何时调用监听器?你确定解析树所需的子代已经是处理过并可以访问?)