View Javadoc
1   package calculator.antlr;
2   
3   import java.math.BigDecimal;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   import calculator.Expression;
8   import calculator.IllegalConstruction;
9   import calculator.atoms.*;
10  import calculator.calculatorBaseVisitor;
11  import calculator.calculatorParser.*;
12  import calculator.functions.*;
13  import calculator.operations.*;
14  
15  /**
16   * Builds Expression objects from the ANTLR parse tree.
17   */
18  public class ParserVisitor extends calculatorBaseVisitor<Expression> {
19  
20      /**
21       * Create an Expression object corresponding to the given operator and arguments.
22       */
23      private Expression buildOperation(String opString, List<Expression> args) {
24          try {
25              switch (opString) {
26                  case "+":
27                      return new Plus(args);
28                  case "-":
29                      return new Minus(args);
30                  case "*":
31                      return new Times(args);
32                  case "/":
33                      return new Divides(args);
34                  case "**":
35                      return new Power(args);
36                  default:
37                      throw new IllegalArgumentException("Unknown operator: " + opString);
38              }
39          } catch (IllegalConstruction e) {
40              throw new RuntimeException(e);
41          }
42      }
43  
44      private Expression buildFunction(String funcName, List<Expression> args) {
45          if (args.size() == 1) {
46              Expression arg = args.get(0);
47              try {
48                  switch (funcName) {
49                      case "cos":
50                          return new Cosinus(arg);
51                      case "sin":
52                          return new Sinus(arg);
53                      case "tan":
54                          return new Tangente(arg);
55                      case "acos":
56                          return new Arccosinus(arg);
57                      case "asin":
58                          return new Arcsinus(arg);
59                      case "atan":
60                          return new Arctangente(arg);
61                      case "cosh":
62                          return new Cosh(arg);
63                      case "sinh":
64                          return new Sinh(arg);
65                      case "tanh":
66                          return new Tanh(arg);
67                      case "sqrt":
68                          return new Sqrt(arg);
69                      case "ln":
70                          return new Ln(arg);
71                      default:
72                          throw new UnsupportedOperationException("Function not implemented yet: " + funcName);
73                  }
74              } catch (IllegalConstruction e) {
75                  throw new RuntimeException(e);
76              }
77          }
78  
79          if ("log".equals(funcName) && args.size() == 2) {
80              try {
81                  return new Log(args.get(0), args.get(1));
82              } catch (IllegalConstruction e) {
83                  throw new RuntimeException(e);
84              }
85          }
86  
87          throw new UnsupportedOperationException("Invalid number of arguments for function " + funcName + ": " + args.size());
88      }
89  
90      /**
91       * Rule: complete : expressionIN EOF #CompleteInfix
92       */
93      @Override
94      public Expression visitCompleteInfix(CompleteInfixContext ctx) {
95          return visit(ctx.expressionIN());
96      }
97  
98      /**
99       * Rule: complete : expressionPOST EOF #CompletePostfix
100      */
101     @Override
102     public Expression visitCompletePostfix(CompletePostfixContext ctx) {
103         return visit(ctx.expressionPOST());
104     }
105 
106     /**
107      * Rule: complete : expressionPRE EOF #CompletePrefix
108      */
109     @Override
110     public Expression visitCompletePrefix(CompletePrefixContext ctx) {
111         return visit(ctx.expressionPRE());
112     }
113 
114     /**
115      * Rule: expressionPRE : operator expressionPRE expressionPRE #Pre2Param
116      */
117     @Override
118     public Expression visitPre2Param(Pre2ParamContext ctx) {
119         List<Expression> args = new ArrayList<>();
120         args.add(visit(ctx.expressionPRE(0)));
121         args.add(visit(ctx.expressionPRE(1)));
122         return buildOperation(ctx.operator().getText(), args);
123     }
124 
125     /**
126      * Rule: expressionPRE : operator LPAR expressionPRE expressionPRE+ RPAR
127      *     | operator LPAR expressionPRE (COMMA expressionPRE)+ RPAR
128      *     #PrePlus2Param
129      */
130     @Override
131     public Expression visitPrePlus2Param(PrePlus2ParamContext ctx) {
132         return buildOperation(ctx.operator().getText(), visitPreArgs(ctx.expressionPRE()));
133     }
134 
135     /**
136      * Rule: expressionPRE : LPAR expressionPRE expressionPRE+ RPAR
137      *     | LPAR expressionPRE (COMMA expressionPRE)+ RPAR
138      *     #PreMult
139      */
140     @Override
141     public Expression visitPreMult(PreMultContext ctx) {
142         return buildOperation("*", visitPreArgs(ctx.expressionPRE()));
143     }
144 
145     /**
146      * Rule: expressionPRE : funcname LPAR expressionPRE+ RPAR
147      *     | funcname LPAR expressionPRE (COMMA expressionPRE)* RPAR
148      *     #PreFunc
149      */
150     @Override
151     public Expression visitPreFunc(PreFuncContext ctx) {
152         return buildFunction(ctx.funcname().getText(), visitPreArgs(ctx.expressionPRE()));
153     }
154 
155     /**
156      * Rule: expressionPRE : atom #PreAtom
157      */
158     @Override
159     public Expression visitPreAtom(PreAtomContext ctx) {
160         return visit(ctx.atom());
161     }
162 
163     /**
164      * Rule: expressionPRE : funcname expressionPRE #PreFunc1Param
165      */
166     @Override
167     public Expression visitPreFunc1Param(PreFunc1ParamContext ctx) {
168         return buildFunction(ctx.funcname().getText(), List.of(visit(ctx.expressionPRE())));
169     }
170 
171     /**
172      * Rule: expressionPOST : expressionPOST expressionPOST operator #Post2Param
173      */
174     @Override
175     public Expression visitPost2Param(Post2ParamContext ctx) {
176         List<Expression> args = new ArrayList<>();
177         args.add(visit(ctx.expressionPOST(0)));
178         args.add(visit(ctx.expressionPOST(1)));
179         return buildOperation(ctx.operator().getText(), args);
180     }
181 
182     /**
183      * Rule: expressionPOST : LPAR expressionPOST expressionPOST+ RPAR operator
184      *     | LPAR expressionPOST (COMMA expressionPOST)+ RPAR operator
185      *     #PostPlus2Param
186      */
187     @Override
188     public Expression visitPostPlus2Param(PostPlus2ParamContext ctx) {
189         return buildOperation(ctx.operator().getText(), visitPostArgs(ctx.expressionPOST()));
190     }
191 
192     /**
193      * Rule: expressionPOST : LPAR expressionPOST expressionPOST+ RPAR
194      *     | LPAR expressionPOST (COMMA expressionPOST)+ RPAR
195      *     #PostMult
196      */
197     @Override
198     public Expression visitPostMult(PostMultContext ctx) {
199         return buildOperation("*", visitPostArgs(ctx.expressionPOST()));
200     }
201 
202     /**
203      * Rule: expressionPOST : LPAR expressionPOST+ RPAR funcname
204      *     | LPAR expressionPOST (COMMA expressionPOST)* RPAR funcname
205      *     #PostFunc
206      */
207     @Override
208     public Expression visitPostFunc(PostFuncContext ctx) {
209         return buildFunction(ctx.funcname().getText(), visitPostArgs(ctx.expressionPOST()));
210     }
211 
212     /**
213      * Rule: expressionPOST : atom #PostAtom
214      */
215     @Override
216     public Expression visitPostAtom(PostAtomContext ctx) {
217         return visit(ctx.atom());
218     }
219 
220     /**
221      * Rule: expressionPOST : expressionPOST funcname #PostFunc1Param
222      */
223     @Override
224     public Expression visitPostFunc1Param(PostFunc1ParamContext ctx) {
225         return buildFunction(ctx.funcname().getText(), List.of(visit(ctx.expressionPOST())));
226     }
227 
228     /**
229      * Rule: expressionIN : multExp ((PLUS | MINUS) multExp)* #INAddSub
230      */
231     @Override
232     public Expression visitINAddSub(INAddSubContext ctx) {
233         Expression result = visit(ctx.multExp(0));
234         for (int i = 1; i < ctx.multExp().size(); i++) {
235             String op = ctx.getChild(2 * i - 1).getText();  // operator is the odd child between multExp
236             List<Expression> args = List.of(result, visit(ctx.multExp(i)));
237             result = buildOperation(op, args);
238         }
239         return result;
240     }
241 
242     /**
243      * Rule: multExp : powExp ((TIMES | DIV) powExp)* #INTimesDiv
244      */
245     @Override
246     public Expression visitINTimesDiv(INTimesDivContext ctx) {
247         Expression result = visit(ctx.powExp(0));
248         for (int i = 1; i < ctx.powExp().size(); i++) {
249             String op = ctx.getChild(2 * i - 1).getText();  // operator is the odd child between powExp
250             List<Expression> args = List.of(result, visit(ctx.powExp(i)));
251             result = buildOperation(op, args);
252         }
253         return result;
254     }
255 
256     /**
257      * Rule: multExp : powExp (LPAR powExp RPAR)* #INMult
258      */
259     @Override
260     public Expression visitINMult(INMultContext ctx) {
261         List<Expression> factors = new ArrayList<>();
262         for (int i = 0; i < ctx.powExp().size(); i++) {
263             factors.add(visit(ctx.powExp(i)));
264         }
265         if (factors.size() == 1) {
266             return factors.get(0);
267         }
268         return buildOperation("*", factors);
269     }
270 
271     /**
272      * Rule: powExp : atomIN (POW atomIN)* #INPow
273      */
274     @Override
275     public Expression visitINPow(INPowContext ctx) {
276         int atomCount = ctx.atomIN().size();
277         if (atomCount == 1) {
278             return visit(ctx.atomIN(0));
279         }
280 
281         // Right-associative folding: a**b**c = a**(b**c).
282         Expression result = visit(ctx.atomIN(atomCount - 1));
283         for (int i = atomCount - 2; i >= 0; i--) {
284             result = buildOperation("**", List.of(visit(ctx.atomIN(i)), result));
285         }
286         return result;
287     }
288 
289     /**
290      * Rule: atomIN : (PLUS | MINUS)* atom (constant)* #INSignedAtom
291      */
292     @Override
293     public Expression visitINSignedAtom(INSignedAtomContext ctx) {
294         List<Expression> factors = new ArrayList<>();
295 
296         factors.add(visit(ctx.atom()));
297         for (int i = 0; i < ctx.constant().size(); i++) {
298             factors.add(visit(ctx.constant(i)));
299         }
300 
301         Expression expression;
302         if (factors.size() == 1) {
303             expression = factors.get(0);
304         } else {
305             expression = buildOperation("*", factors);
306         }
307 
308         // Keep each '-' from the input as an explicit unary minus equivalent: 0 - expr.
309         for (int i = 0; i < ctx.MINUS().size(); i++) {
310             expression = buildOperation("-", List.of(new IntegerAtom(0), expression));
311         }
312 
313         return expression;
314     }
315 
316     /**
317      * Rule: atomIN : (LPAR expressionIN RPAR)? atom (constant)* (LPAR expressionIN RPAR)? #INImplicitMult
318      */
319     @Override
320     public Expression visitINImplicitMult(INImplicitMultContext ctx) {
321         List<Expression> factors = new ArrayList<>();
322 
323         int expIdx = 0;
324         if (!ctx.expressionIN().isEmpty() && "(".equals(ctx.getChild(0).getText())) {
325             factors.add(visit(ctx.expressionIN(expIdx++)));
326         }
327 
328         factors.add(visit(ctx.atom()));
329 
330         for (int i = 0; i < ctx.constant().size(); i++) {
331             factors.add(visit(ctx.constant(i)));
332         }
333 
334         while (expIdx < ctx.expressionIN().size()) {
335             factors.add(visit(ctx.expressionIN(expIdx++)));
336         }
337 
338         if (factors.size() == 1) {
339             return factors.get(0);
340         }
341         return buildOperation("*", factors);
342     }
343 
344     /**
345      * Rule: atomIN : (LPAR expressionIN RPAR) #INParenthesis
346      */
347     @Override
348     public Expression visitINParenthesis(INParenthesisContext ctx) {
349         return visit(ctx.expressionIN());
350     }
351 
352     /**
353      * Rule: atomIN : functionIN #INFunction
354      */
355     @Override
356     public Expression visitINFunction(INFunctionContext ctx) {
357         return visit(ctx.functionIN());
358     }
359 
360     /**
361      * Rule: atom : complex #AtomComplex
362      */
363     @Override
364     public Expression visitAtomComplex(AtomComplexContext ctx) {
365         return visit(ctx.complex());
366     }
367 
368     /**
369      * Rule: atom : number #AtomNumber
370      */
371     @Override
372     public Expression visitAtomNumber(AtomNumberContext ctx) {
373         return visit(ctx.number());
374     }
375 
376     /**
377      * Rule: number : constant #NumberConstant
378      */
379     @Override
380     public Expression visitNumberConstant(NumberConstantContext ctx) {
381         return visit(ctx.constant());
382     }
383 
384     /**
385      * Rule: number : real #NumberReal
386      */
387     @Override
388     public Expression visitNumberReal(NumberRealContext ctx) {
389         return visit(ctx.real());
390     }
391 
392     /**
393      * Rule: number : scientific #NumberScientific
394      */
395     @Override
396     public Expression visitNumberScientific(NumberScientificContext ctx) {
397         return visit(ctx.scientific());
398     }
399 
400     /**
401      * Rule: scientific : (real | INT) E (PLUS | MINUS)? INT #ScientificNumber
402      */
403     @Override
404     public Expression visitScientificNumber(ScientificNumberContext ctx) {
405         return new Real(new BigDecimal(ctx.getText()));
406     }
407 
408     /**
409      * Rule: real : INT (DOT INT)? #RealNumber
410      */
411     @Override
412     public Expression visitRealNumber(RealNumberContext ctx) {
413         if (ctx.DOT() == null) {
414             return new IntegerAtom(Integer.parseInt(ctx.INT(0).getText()));
415         }
416         return new Real(new BigDecimal(ctx.getText()));
417     }
418 
419     /**
420      * Rule: complex : number? (constant)* I #ComplexNumber
421      */
422     @Override
423     public Expression visitComplexNumber(ComplexNumberContext ctx) {
424         List<Expression> factors = new ArrayList<>();
425 
426         if (ctx.number() != null) {
427             factors.add(visit(ctx.number()));
428         }
429 
430         for (int i = 0; i < ctx.constant().size(); i++) {
431             factors.add(visit(ctx.constant(i)));
432         }
433 
434         factors.add(new Complex(0.0, 1.0));
435 
436         if (factors.size() == 1) {
437             return factors.get(0);
438         }
439         return buildOperation("*", factors);
440     }
441 
442     /**
443      * Rule: constant : PI #ConstPi
444      */
445     @Override
446     public Expression visitConstPi(ConstPiContext ctx) {
447         return new Real(Math.PI);  // MAY BE CHANGE THIS BY IMPLEMENTING A PI CONSTANT IN THE CALCULATOR AND BE ABLE TO EVALUATE IT THE EVALATOR
448     }
449 
450     /**
451      * Rule: constant : EULER #ConstEuler
452      */
453     @Override
454     public Expression visitConstEuler(ConstEulerContext ctx) {
455         return new Real(Math.E); // SAME THAN ABOVE
456     }
457 
458     /**
459      * Rule: functionIN : funcname LPAR expressionIN (COMMA expressionIN)* RPAR #InfixFunctionCall
460      */
461     @Override
462     public Expression visitInfixFunctionCall(InfixFunctionCallContext ctx) {
463         List<Expression> args = new ArrayList<>();
464         for (calculator.calculatorParser.ExpressionINContext expCtx : ctx.expressionIN()) {
465             args.add(visit(expCtx));
466         }
467         return buildFunction(ctx.funcname().getText(), args);
468     }
469 
470     /**
471      * Visit a list of sub-expressions prefixed by an operator.
472      */
473     private List<Expression> visitPreArgs(List<? extends calculator.calculatorParser.ExpressionPREContext> contexts) {
474         List<Expression> args = new ArrayList<>();
475         for (int i = 0; i < contexts.size(); i++) {
476             args.add(visit(contexts.get(i)));
477         }
478         return args;
479     }
480 
481     /**
482      * Visit a list of sub-expressions postfixeed by an operator.
483      */
484     private List<Expression> visitPostArgs(List<? extends calculator.calculatorParser.ExpressionPOSTContext> contexts) {
485         List<Expression> args = new ArrayList<>();
486         for (int i = 0; i < contexts.size(); i++) {
487             args.add(visit(contexts.get(i)));
488         }
489         return args;
490     }
491 
492 }