CogPar
A versatile parser for mathematical expressions.
 All Classes Functions Variables
Parser.java
1 /*
2  * This software and all files contained in it are distrubted under the MIT license.
3  *
4  * Copyright (c) 2013 Cogito Learning Ltd
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
52 package uk.co.cogitolearning.cogpar;
53 
54 import java.util.LinkedList;
55 
64 public class Parser
65 {
67  LinkedList<Token> tokens;
69  Token lookahead;
70 
84  {
86  tokenizer.tokenize(expression);
87  LinkedList<Token> tokens = tokenizer.getTokens();
88  return this.parse(tokens);
89  }
90 
100  public ExpressionNode parse(LinkedList<Token> tokens)
101  {
102  // implementing a recursive descent parser
103  this.tokens = (LinkedList<Token>) tokens.clone();
104  lookahead = this.tokens.getFirst();
105 
106  // top level non-terminal is expression
107  return expression();
108  }
109 
112  {
113  // only one rule
114  // expression -> signed_term sum_op
115  ExpressionNode expr = signedTerm();
116  expr = sumOp(expr);
117  return expr;
118  }
119 
122  {
123  // sum_op -> PLUSMINUS term sum_op
124  if (lookahead.token == Token.PLUSMINUS)
125  {
127  // This means we are actually dealing with a sum
128  // If expr is not already a sum, we have to create one
129  if (expr.getType() == ExpressionNode.ADDITION_NODE)
130  sum = (AdditionExpressionNode) expr;
131  else
132  sum = new AdditionExpressionNode(expr, true);
133 
134  // reduce the input and recursively call sum_op
135  boolean positive = lookahead.sequence.equals("+");
136  nextToken();
137  ExpressionNode t = term();
138  sum.add(t, positive);
139 
140  return sumOp(sum);
141  }
142 
143  // sum_op -> EPSILON
144  return expr;
145  }
146 
149  {
150  // signed_term -> PLUSMINUS term
151  if (lookahead.token == Token.PLUSMINUS)
152  {
153  boolean positive = lookahead.sequence.equals("+");
154  nextToken();
155  ExpressionNode t = term();
156  if (positive)
157  return t;
158  else
159  return new AdditionExpressionNode(t, false);
160  }
161 
162  // signed_term -> term
163  return term();
164  }
165 
168  {
169  // term -> factor term_op
170  ExpressionNode f = factor();
171  return termOp(f);
172  }
173 
176  {
177  // term_op -> MULTDIV factor term_op
178  if (lookahead.token == Token.MULTDIV)
179  {
181 
182  // This means we are actually dealing with a product
183  // If expr is not already a sum, we have to create one
184  if (expression.getType() == ExpressionNode.MULTIPLICATION_NODE)
186  else
187  prod = new MultiplicationExpressionNode(expression, true);
188 
189  // reduce the input and recursively call sum_op
190  boolean positive = lookahead.sequence.equals("*");
191  nextToken();
192  ExpressionNode f = factor();
193  prod.add(f, positive);
194 
195  return termOp(prod);
196  }
197 
198  // term_op -> EPSILON
199  return expression;
200  }
201 
204  {
205  // factor -> FUNCTION argument
206  if (lookahead.token == Token.FUNCTION)
207  {
208  int function = FunctionExpressionNode.stringToFunction(lookahead.sequence);
209  if (function < 0) throw new ParserException("Unexpected Function %s found", lookahead);
210  nextToken();
211  ExpressionNode expr = factor();
212  return new FunctionExpressionNode(function, expr);
213  }
214 
215  // factor -> argument factor_op
216  ExpressionNode a = argument();
217  return factorOp(a);
218  }
219 
222  {
223  // factor_op -> RAISED expression
224  if (lookahead.token == Token.RAISED)
225  {
226  nextToken();
227  ExpressionNode exponent = factor();
228 
229  return new ExponentiationExpressionNode(expr, exponent);
230  }
231 
232  // factor_op -> EPSILON
233  return expr;
234  }
235 
238  {
239  // argument -> OPEN_BRACKET sum CLOSE_BRACKET
240  if (lookahead.token == Token.OPEN_BRACKET)
241  {
242  nextToken();
243  ExpressionNode expr = expression();
244  if (lookahead.token != Token.CLOSE_BRACKET)
245  throw new ParserException("Closing brackets expected", lookahead);
246  nextToken();
247  return expr;
248  }
249 
250  // argument -> value
251  return value();
252  }
253 
256  {
257  // argument -> NUMBER
258  if (lookahead.token == Token.NUMBER)
259  {
260  ExpressionNode expr = new ConstantExpressionNode(lookahead.sequence);
261  nextToken();
262  return expr;
263  }
264 
265  // argument -> VARIABLE
266  if (lookahead.token == Token.VARIABLE)
267  {
268  ExpressionNode expr = new VariableExpressionNode(lookahead.sequence);
269  nextToken();
270  return expr;
271  }
272 
273  if (lookahead.token == Token.EPSILON)
274  throw new ParserException("Unexpected end of input");
275  else
276  throw new ParserException("Unexpected symbol %s found", lookahead);
277  }
278 
282  private void nextToken()
283  {
284  tokens.pop();
285  // at the end of input we return an epsilon token
286  if (tokens.isEmpty())
287  lookahead = new Token(Token.EPSILON, "", -1);
288  else
289  lookahead = tokens.getFirst();
290  }
291 }