With antlr4 to achieve the "compiler theory along the lines of the design of a calculator" in the Calculator

The last time in the company stresses " - lexical analysis using regular grammar attempt" was a failure - in the morning a dozen people there, the afternoon came only four listeners.

Originally I was conceived in terms of knowledge of how to "parse" it, but now seems unlikely.

 

This program is not expected in the popular, the reason may be:

1. The course content is relatively complex, the audience background and knowledge base are great differences.

2. teaching skills is not enough, not the simplistic presentation of complex knowledge base to a slightly worse people.

 

For two possible reasons, I want to try to make the following adjustments:

1. Use antlr to achieve some lexical and syntax.

2. temporarily "compiling" process to "explain" to achieve.

 

The reason for using antlr are:

1. Using grammar generator can skip part of the morphology and syntax directly into the semantic analysis, so conducive to crash, while avoiding the complexity of the students are scared lexical analysis and parsing, but lost the courage to continue learning.

2.antlr grammar is LL (k) type, very easy to write - although the performance k-type approach certainly not as type 1 grammar, but beginners talk about performance issues is not a good idea, it is better to avoid direct performance aside, you can run.

3.antlr java code is generated by default, which most existing knowledge within the company's employees is consistent.

 

 Enter the following text.

 

First, what is antlr? how to install?

This is not a scrape together words of the article, so please refer to the official website directly (http://www.antlr.org/).

I am using the latest version (V4.2.2).

I uploaded reference materials (including jar package, e-books and official example) to the Baidu cloud, can be downloaded from this address (http://pan.baidu.com/s/1hq65XWC).

 

Second, the grammar and syntax of the present example explains calculator.

The syntax lexical entire calculator will be implemented by antlr4 the following lines of code, first posted the following:

Calc Grammar;                             // grammar name for Calc

// hereinafter referred to as the beginning of the grammar lowercase letters syntax elements
 // represented by a capital letter for the beginning of the grammar lexical elements
 // expressed similar lexical elements in the regular expression
 // syntax elements similar to BNF 

exprs: setExpr                             // set expression 
    | calcExpr                             // or calc expression 
    ;

setExpr: 'set' agmts;                     // to set command at the beginning, followed by a plurality assignments 
agmts: agmt ( ';' agmts) ';';??         // multiple assignment is an assignment statement after the multi-root an assignment statement, the intermediate are separated by semicolons, semicolon at the end of an optional 
agmt: = ID ID '=' NUM = nUMBER;         // an assignment by an ID, followed by an equal sign post, then followed by transmitting a numbers 
calcExpr: 'calc' expr;                     // to begin calc command, followed by a calculation expression

// expr may be provided by a plurality of production
 // In the foregoing production in preference to later production
 // this problem to resolve priority 

expr: expr OP = (the MUL | the DIV) expr             // multiplication or division 
    | expr = OP (the ADD | the SUB) expr             // addition or subtraction 
    | factor                             // a calculated factor - can be used as + - * / stuff operation data 
    ;

factor: (Sign = (the ADD | the SUB)) NUMBER NUM =?     // calculated factor may be a positive or negative 
    | '(' expr ')'                         // calculated factor may be represented by the formula in brackets 
    | id = ID                                 // calculate the factor may be a variable 
    | funcall                             // calculate the factor may be a function call 
    ;

funcall: name = ID '(' params ')';         // function name plus parameter list 
params: expr ( ',' params);?             // parameter list is made off the back of an expression with an optional parameter list composed of 

the WS: [\ T \ n-\ R & lt] + -> skip;                 // blank, the latter -> skip expressed antlr4 when the text of the language, in line with the lexical rules will be ignored 
ID: [AZ] +;                             / / identifiers, from 0 to more lowercase letters 
nUMBER: [0-9] + (([0-9] +) '.'?);?         // digital 
the ADD: '+' ;
SUB : '-' ;
MUL: '*' ;
DIV : '/' ;

 

 

We save this to a file Calc.g4 in grammar, and run the command "antlr4 -visitor Calc.g4" java file that is generated six and two tokens files.

These files include this calculator "lexical analyzer", "parser" and a visitor (CalcBaseVisitor.java), but this time inside the visitor achieve this are empty, we need to implement it.

Before implementing this visitor, let's implement a context, the context has to do with two:

1. Save variable - means for calculating a reference variable in the expressions.

2. Save the stack - the parameters for the transfer function.

This context of little content, code is also very short, directly attached to the following:

 1 public class Context {
 2     private static Context ourInstance = new Context();
 3 
 4     public static Context getInstance() {
 5         return ourInstance;
 6     }
 7 
 8     private Context() {
 9     }
10 
11     private Map<String, Double> map = new HashMap<>();
12     private Deque<Double> stack = new ArrayDeque<>();
13 
14     public Double getValue(String key) {
15         Double d = map.get(key);
16         return d == null ? Double.NaN : d;
17     }
18 
19     public void setContext(String key, Double value) {
20         map.put(key, value);
21     }
22 
23     public void setContext(String key, String value) {
24         setContext(key, Double.valueOf(value));
25     }
26 
27     public void pushStack(Double d) {
28         stack.push(d);
29     }
30 
31     public Double popStack() {
32         return stack.pop();
33     }
34 }

 

Here we begin to realize visitor of this calculator,

 1 public class MyCalcVisitor extends CalcBaseVisitor<Double> {
 2 
 3     @Override
 4     public Double visitExprs(CalcParser.ExprsContext ctx) {
 5         return visit(ctx.getChild(0));
 6     }
 7 
 8     @Override
 9     public Double visitAgmt(CalcParser.AgmtContext ctx) {
10         Context.getInstance().setContext(ctx.id.getText(), ctx.num.getText());
11         return null;
12     }
13 
14     @Override
15     public Double visitAgmts(CalcParser.AgmtsContext ctx) {
16         visit(ctx.agmt());
17         if (ctx.agmts() != null)
18             visit(ctx.agmts());
19         return null;
20     }
21 
22     @Override
23     public Double visitCalcExpr(CalcParser.CalcExprContext ctx) {
24         return visit(ctx.expr());
25     }
26 
27     @Override
28     public Double visitExpr(CalcParser.ExprContext ctx) {
29         int cc = ctx.getChildCount();
30         if (cc == 3) {
31             switch (ctx.op.getType()) {
32             case CalcParser.ADD:
33                 return visit(ctx.expr(0)) + visit(ctx.expr(1));
34             case CalcParser.SUB:
35                 return visit(ctx.expr(0)) - visit(ctx.expr(1));
36             case CalcParser.MUL:
37                 return visit(ctx.expr(0)) * visit(ctx.expr(1));
38             case CalcParser.DIV:
39                 return visit(ctx.expr(0)) / visit(ctx.expr(1));
40             }
41         } else if (cc == 1) {
42             return visit(ctx.getChild(0));
43         }
44         throw new RuntimeException();
45     }
46 
47     @Override
48     public Double visitFactor(CalcParser.FactorContext ctx) {
49         int cc = ctx.getChildCount();
50         if (cc == 3) {
51             return visit(ctx.getChild(1));
52         } else if (cc == 2) {
53             if (ctx.sign.getType() == CalcParser.ADD)
54                 return Double.valueOf(ctx.getChild(1).getText());
55             if (ctx.sign.getType() == CalcParser.SUB)
56                 return -1 * Double.valueOf(ctx.getChild(1).getText());
57         } else if (cc == 1) {
58             if (ctx.num != null)
59                 return Double.valueOf(ctx.getChild(0).getText());
60             if (ctx.id != null)
61                 return Context.getInstance().getValue(ctx.id.getText());
62             return visit(ctx.funCall());
63         }
64         throw new RuntimeException();
65     }
66 
67     @Override
68     public Double visitParams(CalcParser.ParamsContext ctx) {
69         if (ctx.params() != null)
70             visit(ctx.params());
71         Context.getInstance().pushStack(visit(ctx.expr()));
72         return null;
73     }
74 
75     @Override
76     public Double visitFunCall(CalcParser.FunCallContext ctx) {
77         visit(ctx.params());
78         String funName = ctx.name.getText();
79         switch (funName) {
80         case "pow":
81             return Math.pow(Context.getInstance().popStack(), Context.getInstance().popStack());
82         case "sqrt":
83             return Math.sqrt(Context.getInstance().popStack());
84         }
85         throw new RuntimeException();
86     }
87 
88     @Override
89     public Double visitSetExpr(CalcParser.SetExprContext ctx) {
90         return visit(ctx.agmts());
91     }
92 
93 }

 Finally, to achieve an entry, call the Visitor to complete our calculator.

Entry code as follows:

 1 import java.util.Scanner;
 2 
 3 import org.antlr.v4.runtime.ANTLRInputStream;
 4 import org.antlr.v4.runtime.CommonTokenStream;
 5 import org.antlr.v4.runtime.tree.ParseTree;
 6 
 7 public class Portal {
 8 
 9     private static final String lineStart = "CALC> ";
10 
11     public static void main(String[] args) {
12         try (Scanner scanner = new Scanner(System.in)) {
13             System.out.print(lineStart);
14             while (scanner.hasNext()) {
15                 String line = scanner.nextLine();
16                 if (line != null) {
17                     line = line.trim();
18                     if (line.length() != 0) {
19                         if ("exit".equals(line) || "bye".equals(line))
20                             break;
21                         ANTLRInputStream input = new ANTLRInputStream(line);
22                         CalcLexer lexer = new CalcLexer(input);
23                         CommonTokenStream tokens = new CommonTokenStream(lexer);
24                         CalcParser parser = new CalcParser(tokens);
25                         ParseTree tree = parser.exprs();
26                         MyCalcVisitor mv = new MyCalcVisitor();
27                         Double res = mv.visit(tree);
28                         if (res != null)
29                             System.out.println(res);
30                     }
31                 }
32 
33                 System.out.print(lineStart);
34             }
35         }
36     }
37 
38 }

 

Just write a whole calculator and three grammar classes, all code attached to the top, as opposed to completely own handwriting calculator is indeed a lot easier.

Reproduced in: https: //www.cnblogs.com/naturemickey/p/3764108.html

Guess you like

Origin blog.csdn.net/weixin_33800463/article/details/93434738