/* * parser.yy - part of abakus * Copyright (C) 2004, 2005 Michael Pyne * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* Add necessary includes here. */ #include #include #include #include #include #include #include #include "result.h" #include "node.h" #include "function.h" #include "valuemanager.h" extern char *yytext; extern int gCheckIdents; int yylex(void); int yyerror(const char *); %} %union { Node *node; NumericValue *value; UnaryFunction *fn; Identifier *ident; } %token NUM %type EXP FACTOR TERM S EXPONENT NUMBER VALUE FINAL %type FUNC %token FN %token ID %type IDENT ASSIGN %token POWER "**" %token SET "set" %token REMOVE "remove" %token DERIV "deriv" %% /** * Parser design: * * This is pretty standard stuff for the calculator part (read in tokens from * the lexer, and form a syntax tree using the Node* objects). The unusual * part is that due to the design of bison, we don't actually return a value * normally to the calling function. * * Instead, we make use of the static Result::setLastResult() call in order * to notify the calling function of the result of the parse. There are * different statuses you can set, including Error (with a message), Null * (which indicates that some action happened that doesn't generate a result), * and Value (with a Node* that holds the result). * * If you are done parsing before reaching the FINAL token, you can call: * YYACCEPT: Done, parsed successfully. * YYERROR : Done, there was an error. * * Note that if you let the parse bubble back up to FINAL, then the result * will always be a Value. */ FINAL: { gCheckIdents = 1; } S { Result::setLastResult(NodePtr($2)); $$ = 0; } S: EXP { $$ = $1; } // Rudimentary error handling S: error '=' { Result::setLastResult(i18n("This is an invalid assignment.")); YYABORT; } // Can't assign to a function. S: FUNC '=' { TQString s(i18n("You can't assign to function %1").tqarg($1->name())); Result::setLastResult(s); YYABORT; } // This is a function prototype. abakus currently only supports one-argument // functions. ASSIGN: '(' { --gCheckIdents; } IDENT ')' '=' { $$ = $3; } // Blocking a variable with the name deriv is a slight feature regression // since normally functions and variables with the same name can coexist, but // I don't want to duplicate code all over the place. S: SET DERIV { TQString s(i18n("Function %1 is built-in and cannot be overridden.").tqarg("deriv")); Result::setLastResult(s); YYABORT; } S: DERIV '=' { TQString s(i18n("Function %1 is built-in and cannot be overridden.").tqarg("deriv")); Result::setLastResult(s); YYABORT; } S: SET FUNC ASSIGN EXP { ++gCheckIdents; // We're trying to reassign an already defined function, make sure it's // not a built-in. TQString funcName = $2->name(); TQString ident = $3->name(); FunctionManager *manager = FunctionManager::instance(); if(manager->isFunction(funcName) && !manager->isFunctionUserDefined(funcName)) { TQString s(i18n("Function %1 is built-in and cannot be overridden.").tqarg(funcName)); Result::setLastResult(s); YYABORT; } if(manager->isFunction(funcName)) manager->removeFunction(funcName); BaseFunction *newFn = new UserDefinedFunction(funcName, $4); if(!manager->addFunction(newFn, ident)) { TQString s(i18n("Unable to define function %1 because it is recursive.").tqarg(funcName)); Result::setLastResult(s); YYABORT; } Result::setLastResult(Result::Null); YYACCEPT; } // IDENT is the same as FUNC, except that the lexer has determined that IDENT // is not already a FUNC. S: SET IDENT ASSIGN EXP { ++gCheckIdents; TQString funcName = $2->name(); TQString ident = $3->name(); // No need to check if the function is already defined, because the // lexer checked for us before returning the IDENT token. BaseFunction *newFn = new UserDefinedFunction(funcName, $4); FunctionManager::instance()->addFunction(newFn, ident); Result::setLastResult(Result::Null); YYACCEPT; } // Remove a defined function. S: REMOVE FUNC '(' ')' { FunctionManager::instance()->removeFunction($2->name()); Result::setLastResult(Result::Null); YYACCEPT; } // Can't remove an ident using remove-func syntax. S: REMOVE IDENT '(' ')' { // This is an error Result::setLastResult(Result(i18n("Function %1 is not defined.").tqarg($2->name()))); YYABORT; } // This happens when the user tries to remove a function that's not defined. S: REMOVE IDENT '(' IDENT ')' { // This is an error Result::setLastResult(Result(i18n("Function %1 is not defined.").tqarg($2->name()))); YYABORT; } S: REMOVE IDENT { ValueManager *manager = ValueManager::instance(); if(manager->isValueSet($2->name()) && !manager->isValueReadOnly($2->name())) { manager->removeValue($2->name()); Result::setLastResult(Result::Null); YYACCEPT; } else { TQString s; if(manager->isValueSet($2->name())) s = i18n("Can't remove predefined variable %1.").tqarg($2->name()); else s = i18n("Can't remove undefined variable %1.").tqarg($2->name()); Result::setLastResult(s); YYABORT; } } S: SET IDENT '=' EXP { ValueManager *vm = ValueManager::instance(); if(vm->isValueReadOnly($2->name())) { if($2->name() == "pi" && $4->value() == Abakus::number_t("3.0")) Result::setLastResult(i18n("This isn't Indiana, you can't just change pi")); else Result::setLastResult(i18n("%1 is a constant").tqarg($2->name())); YYABORT; } ValueManager::instance()->setValue($2->name(), $4->value()); Result::setLastResult(Result::Null); YYACCEPT; } // Set a variable. S: IDENT '=' EXP { ValueManager *vm = ValueManager::instance(); if(vm->isValueReadOnly($1->name())) { if($1->name() == "pi" && $3->value() == Abakus::number_t("3.0")) Result::setLastResult(i18n("This isn't Indiana, you can't just change pi")); else Result::setLastResult(i18n("%1 is a constant").tqarg($1->name())); YYABORT; } ValueManager::instance()->setValue($1->name(), $3->value()); Result::setLastResult(Result::Null); YYACCEPT; } S: NUMBER '=' { Result::setLastResult(i18n("Can't assign to %1").tqarg($1->value().toString())); YYABORT; } // Can't call this as a function. TERM: IDENT '(' { Result::setLastResult(i18n("%1 isn't a function (or operator expected)").tqarg($1->name())); YYABORT; } // Can't do this either. TERM: IDENT IDENT { Result::setLastResult(i18n("Missing operator")); YYABORT; } TERM: IDENT NUMBER { Result::setLastResult(i18n("Missing operator")); YYABORT; } TERM: NUMBER NUMBER { Result::setLastResult(i18n("Missing operator")); YYABORT; } S: error { Result::setLastResult(i18n("Sorry, I can't figure it out.")); YYABORT; } /** * Here be the standard calculator-parsing part. Nothing here should be too * fancy. */ EXP: EXP '+' FACTOR { $$ = new BinaryOperator(BinaryOperator::Addition, $1, $3); } EXP: EXP '-' FACTOR { $$ = new BinaryOperator(BinaryOperator::Subtraction, $1, $3); } EXP: FACTOR { $$ = $1; } FACTOR: FACTOR '*' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3); } FACTOR: FACTOR '/' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Division, $1, $3); } FACTOR: EXPONENT { $$ = $1; } EXPONENT: TERM POWER EXPONENT { $$ = new BinaryOperator(BinaryOperator::Exponentiation, $1, $3); } EXPONENT: TERM { $$ = $1; } TERM: '+' VALUE { $$ = $2; } TERM: '-' VALUE { $$ = new UnaryOperator(UnaryOperator::Negation, $2); } TERM: '(' EXP ')' { $$ = $2; } TERM: '-' '(' EXP ')' { $$ = new UnaryOperator(UnaryOperator::Negation, $3); } TERM: VALUE { $$ = $1; } VALUE: NUMBER { $$ = $1; } NUMBER: NUM { KLocale *locale = KGlobal::locale(); QChar decimal = locale->decimalSymbol()[0]; // Replace current decimal separator with US Decimal separator to be // evil. unsigned len = strlen(yytext); for(unsigned i = 0; i < len; ++i) if(yytext[i] == decimal) yytext[i] = '.'; Abakus::number_t value(yytext); $$ = new NumericValue(value); } TERM: DERIV { --gCheckIdents; } '(' EXP ',' { ++gCheckIdents; } EXP ')' { $$ = new DerivativeFunction($4, $7); } TERM: FUNC TERM { $1->setOperand($2); $$ = $1; } /* Handle implicit multiplication */ TERM: NUMBER FUNC TERM { $2->setOperand($3); $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2); } TERM: NUMBER '(' EXP ')' { $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3); } TERM: NUMBER IDENT { if(gCheckIdents > 0 && !ValueManager::instance()->isValueSet($2->name())) { Result::setLastResult(i18n("Unknown variable %1").tqarg($2->name())); YYABORT; } $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2); } VALUE: IDENT { if(gCheckIdents <= 0 || ValueManager::instance()->isValueSet($1->name())) $$ = $1; else { Result::setLastResult(i18n("Unknown variable %1").tqarg($1->name())); YYABORT; } } IDENT: ID { $$ = new Identifier(yytext); } FUNC: FN { /* No check necessary, the lexer has already checked for us. */ $$ = new BuiltinFunction(yytext, 0); } %% int gCheckIdents = 0; int yyerror(const char *) { return 0; }