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 */
021package csheets.core.formula.compiler;
022
023import java.io.StringReader;
024import java.text.ParseException;
025import java.util.*;
026
027import antlr.ANTLRException;
028import antlr.collections.AST;
029import csheets.core.*;
030import csheets.core.formula.*;
031import csheets.core.formula.lang.*;
032
033/**
034 * A compiler that generates '#'-style formulas from strings. (adapted from
035 * Einar Pehrson class, ExcelExpressionCompiler; changes are indicated below)
036 * 
037 * @author João Carreira
038 * @see cshhets.core.formula.compiler.ExcelExpressionCompiler
039 */
040public class NumberSignExpressionCompiler implements ExpressionCompiler {
041
042        /** The character that signals that a cell's content is a formula ('#') */
043        /* changed from = to # */
044        public static final char FORMULA_STARTER = '#';
045
046        /**
047         * Creates the Excel expression compiler.
048         */
049        public NumberSignExpressionCompiler() {
050        }
051
052        @Override
053        public char getStarter() {
054                return FORMULA_STARTER;
055        }
056
057        @Override
058        public Expression compile(Cell cell, String source)
059                        throws FormulaCompilationException {
060                // Creates the lexer and parser
061                /* adapted to support the new parser and lexer */
062                NumberSignFormulaParser parser = new NumberSignFormulaParser(
063                                new NumberSignFormulaLexer(new StringReader(source)));
064                try {
065                        // Attempts to match an expression
066                        parser.expression();
067                } catch (ANTLRException e) {
068                        throw new FormulaCompilationException(e);
069                }
070                // Converts the expression and returns it
071                return convert(cell, parser.getAST());
072        }
073
074        /**
075         * Converts the given ANTLR AST to an expression.
076         * 
077         * @param node
078         *            the abstract syntax tree node to convert
079         * @return the result of the conversion
080         */
081        protected Expression convert(Cell cell, AST node)
082                        throws FormulaCompilationException {
083                // System.out.println("Converting node '" + node.getText() +
084                // "' of tree '" + node.toStringTree() + "' with " +
085                // node.getNumberOfChildren() + " children.");
086                if (node.getNumberOfChildren() == 0) {
087                        try {
088                                switch (node.getType()) {
089                                case NumberSignFormulaParserTokenTypes.NUMBER:
090                                        return new Literal(Value.parseNumericValue(node.getText()));
091                                case NumberSignFormulaParserTokenTypes.STRING:
092                                        return new Literal(Value.parseValue(node.getText(),
093                                                        Value.Type.BOOLEAN, Value.Type.DATE));
094                                case NumberSignFormulaParserTokenTypes.CELL_REF:
095                                        return new CellReference(cell.getSpreadsheet(),
096                                                        node.getText());
097                                case NumberSignFormulaParserTokenTypes.NAME:
098                                        /*
099                                         * return cell.getSpreadsheet().getWorkbook().
100                                         * getRange(node.getText()) (Reference)
101                                         */
102                                }
103                        } catch (ParseException e) {
104                                throw new FormulaCompilationException(e);
105                        }
106                }
107
108                // Convert function call
109                Function function = null;
110                try {
111                        function = Language.getInstance().getFunction(node.getText());
112                } catch (UnknownElementException e) {
113                }
114
115                if (function != null) {
116                        List<Expression> args = new ArrayList<Expression>();
117                        AST child = node.getFirstChild();
118                        if (function instanceof Eval) {
119
120                                try {
121
122                                        Expression expression = convert(cell, node.getFirstChild());
123                                        cell.setContent(expression.evaluate().toText());
124                                } catch (IllegalValueTypeException e) {
125                                }
126
127                        }
128                        if (child != null) {
129                                args.add(convert(cell, child));
130                                while ((child = child.getNextSibling()) != null)
131                                        args.add(convert(cell, child));
132                        }
133
134                        Expression[] argArray = args.toArray(new Expression[args.size()]);
135
136                        if (function instanceof Whiledo) {
137
138                                try {
139                                        while (argArray[0].evaluate().toBoolean()) {
140                                                convert(cell, node);
141                                        }
142                                } catch (IllegalValueTypeException e) {
143                                }
144                        }
145                        if (function instanceof Dowhile) {
146
147                                try {
148                                        while (argArray[argArray.length - 1].evaluate().toBoolean()) {
149                                                convert(cell, node);
150                                        }
151                                } catch (IllegalValueTypeException e) {
152                                }
153                        }
154                        return new FunctionCall(function, argArray);
155                }
156
157                if (node.getNumberOfChildren() == 1)
158                        // Convert unary operation
159                        return new UnaryOperation(Language.getInstance().getUnaryOperator(
160                                        node.getText()), convert(cell, node.getFirstChild()));
161                else if (node.getNumberOfChildren() >= 2) {
162                        // Convert binary operation
163                        if (node.getType() == NumberSignFormulaParserTokenTypes.SEMI) {
164                                convert(cell, node.getFirstChild());
165                                return convert(cell, node.getFirstChild().getNextSibling());
166                        } else {
167                                BinaryOperator operator = Language.getInstance()
168                                                .getBinaryOperator(node.getText());
169                                if (operator instanceof RangeReference) {
170
171                                        return new ReferenceOperation((Reference) convert(cell,
172                                                        node.getFirstChild()), (RangeReference) operator,
173                                                        (Reference) convert(cell, node.getFirstChild()
174                                                                        .getNextSibling()));
175                                } else if (operator instanceof RelationalOperatorImpl) {
176                                        return new RelationalOperatorImpl((Reference) convert(cell,
177                                                        node.getFirstChild()), (RangeReference) operator,
178                                                        (Reference) convert(cell, node.getFirstChild()
179                                                                        .getNextSibling()));
180                                }
181                                /* verifies is operator is ':=' */
182                                else if (operator instanceof Attribution) {
183                                        try {
184                                                CellReference cellRef = new CellReference(
185                                                                cell.getSpreadsheet(), node.getFirstChild()
186                                                                                .getText());
187                                                Cell destinationCell = cellRef.getCell();
188                                                AST next = node.getFirstChild();
189                                                Expression exp = convert(destinationCell,
190                                                                next.getNextSibling());
191                                                Value val = exp.evaluate();
192                                                UpdateCellContent.getInstance().triggerUpdate(
193                                                                destinationCell, val);
194                                        } catch (ParseException e) {
195                                                throw new FormulaCompilationException(e);
196                                        } catch (IllegalValueTypeException e) {
197                                                throw new FormulaCompilationException(e);
198                                        }
199                                        return new BinaryOperation(convert(cell,
200                                                        node.getFirstChild()), operator, convert(cell, node
201                                                        .getFirstChild().getNextSibling()));
202                                } else {
203                                        return new BinaryOperation(convert(cell,
204                                                        node.getFirstChild()), operator, convert(cell, node
205                                                        .getFirstChild().getNextSibling()));
206                                }
207                        }
208                } else {
209                        // Shouldn't happen
210                        throw new FormulaCompilationException();
211                }
212        }
213}