001 /*
002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003 *
004 * This file is part of
005 * CleanSheets - a spreadsheet application for the Java platform.
006 *
007 * CleanSheets is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or
010 * (at your option) any later version.
011 *
012 * CleanSheets is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with CleanSheets; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020 */
021 package csheets.core.formula.compiler;
022
023 import java.io.StringReader;
024 import java.text.ParseException;
025 import java.util.ArrayList;
026 import java.util.List;
027
028 import antlr.ANTLRException;
029 import antlr.collections.AST;
030 import csheets.core.Cell;
031 import csheets.core.Value;
032 import csheets.core.formula.BinaryOperation;
033 import csheets.core.formula.BinaryOperator;
034 import csheets.core.formula.Expression;
035 import csheets.core.formula.Function;
036 import csheets.core.formula.FunctionCall;
037 import csheets.core.formula.Literal;
038 import csheets.core.formula.Reference;
039 import csheets.core.formula.UnaryOperation;
040 import csheets.core.formula.lang.CellReference;
041 import csheets.core.formula.lang.Language;
042 import csheets.core.formula.lang.RangeReference;
043 import csheets.core.formula.lang.ReferenceOperation;
044 import csheets.core.formula.lang.UnknownElementException;
045
046 /**
047 * A compiler that generates Excel-style formulas from strings.
048 * @author Einar Pehrson
049 */
050 public class ExcelExpressionCompiler implements ExpressionCompiler {
051
052 /** The character that signals that a cell's content is a formula ('=') */
053 public static final char FORMULA_STARTER = '=';
054
055 /**
056 * Creates the Excel expression compiler.
057 */
058 public ExcelExpressionCompiler() {}
059
060 public char getStarter() {
061 return FORMULA_STARTER;
062 }
063
064 public Expression compile(Cell cell, String source) throws FormulaCompilationException {
065 // Creates the lexer and parser
066 FormulaParser parser = new FormulaParser(
067 new FormulaLexer(new StringReader(source)));
068
069 try {
070 // Attempts to match an expression
071 parser.expression();
072 } catch (ANTLRException e) {
073 throw new FormulaCompilationException(e);
074 }
075
076 // Converts the expression and returns it
077 return convert(cell, parser.getAST());
078 }
079
080 /**
081 * Converts the given ANTLR AST to an expression.
082 * @param node the abstract syntax tree node to convert
083 * @return the result of the conversion
084 */
085 protected Expression convert(Cell cell, AST node) throws FormulaCompilationException {
086 // System.out.println("Converting node '" + node.getText() + "' of tree '" + node.toStringTree() + "' with " + node.getNumberOfChildren() + " children.");
087 if (node.getNumberOfChildren() == 0) {
088 try {
089 switch (node.getType()) {
090 case FormulaParserTokenTypes.NUMBER:
091 return new Literal(Value.parseNumericValue(node.getText()));
092 case FormulaParserTokenTypes.STRING:
093 return new Literal(Value.parseValue(node.getText(), Value.Type.BOOLEAN, Value.Type.DATE));
094 case FormulaParserTokenTypes.CELL_REF:
095 return new CellReference(cell.getSpreadsheet(), node.getText());
096 case FormulaParserTokenTypes.NAME:
097 /* return cell.getSpreadsheet().getWorkbook().
098 getRange(node.getText()) (Reference)*/
099 }
100 } catch (ParseException e) {
101 throw new FormulaCompilationException(e);
102 }
103 }
104
105 // Convert function call
106 Function function = null;
107 try {
108 function = Language.getInstance().getFunction(node.getText());
109 } catch (UnknownElementException e) {}
110
111 if (function != null) {
112 List<Expression> args = new ArrayList<Expression>();
113 AST child = node.getFirstChild();
114 if (child != null) {
115 args.add(convert(cell, child));
116 while ((child = child.getNextSibling()) != null)
117 args.add(convert(cell, child));
118 }
119 Expression[] argArray = args.toArray(new Expression[args.size()]);
120 return new FunctionCall(function, argArray);
121 }
122
123 if (node.getNumberOfChildren() == 1)
124 // Convert unary operation
125 return new UnaryOperation(
126 Language.getInstance().getUnaryOperator(node.getText()),
127 convert(cell, node.getFirstChild())
128 );
129 else if (node.getNumberOfChildren() == 2) {
130 // Convert binary operation
131 BinaryOperator operator = Language.getInstance().getBinaryOperator(node.getText());
132 if (operator instanceof RangeReference)
133 return new ReferenceOperation(
134 (Reference)convert(cell, node.getFirstChild()),
135 (RangeReference)operator,
136 (Reference)convert(cell, node.getFirstChild().getNextSibling())
137 );
138 else
139 return new BinaryOperation(
140 convert(cell, node.getFirstChild()),
141 operator,
142 convert(cell, node.getFirstChild().getNextSibling())
143 );
144 } else
145 // Shouldn't happen
146 throw new FormulaCompilationException();
147 }
148 }