AbaKus – a complex calculator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

387 lines
9.9 KiB

  1. /*
  2. * parser.yy - part of abakus
  3. * Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. %{
  20. /* Add necessary includes here. */
  21. #include <kdebug.h>
  22. #include <klocale.h>
  23. #include <kglobal.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <math.h>
  28. #include "result.h"
  29. #include "node.h"
  30. #include "function.h"
  31. #include "valuemanager.h"
  32. extern char *yytext;
  33. extern int gCheckIdents;
  34. int yylex(void);
  35. int yyerror(const char *);
  36. %}
  37. %union {
  38. Node *node;
  39. NumericValue *value;
  40. UnaryFunction *fn;
  41. Identifier *ident;
  42. }
  43. %token <value> NUM
  44. %type <node> EXP FACTOR TERM S EXPONENT NUMBER VALUE FINAL
  45. %type <fn> FUNC
  46. %token <fn> FN
  47. %token <ident> ID
  48. %type <ident> IDENT ASSIGN
  49. %token POWER "**"
  50. %token SET "set"
  51. %token REMOVE "remove"
  52. %token DERIV "deriv"
  53. %%
  54. /**
  55. * Parser design:
  56. *
  57. * This is pretty standard stuff for the calculator part (read in tokens from
  58. * the lexer, and form a syntax tree using the Node* objects). The unusual
  59. * part is that due to the design of bison, we don't actually return a value
  60. * normally to the calling function.
  61. *
  62. * Instead, we make use of the static Result::setLastResult() call in order
  63. * to notify the calling function of the result of the parse. There are
  64. * different statuses you can set, including Error (with a message), Null
  65. * (which indicates that some action happened that doesn't generate a result),
  66. * and Value (with a Node* that holds the result).
  67. *
  68. * If you are done parsing before reaching the FINAL token, you can call:
  69. * YYACCEPT: Done, parsed successfully.
  70. * YYERROR : Done, there was an error.
  71. *
  72. * Note that if you let the parse bubble back up to FINAL, then the result
  73. * will always be a Value.
  74. */
  75. FINAL: { gCheckIdents = 1; } S {
  76. Result::setLastResult(NodePtr($2));
  77. $$ = 0;
  78. }
  79. S: EXP { $$ = $1; }
  80. // Rudimentary error handling
  81. S: error '=' {
  82. Result::setLastResult(i18n("This is an invalid assignment."));
  83. YYABORT;
  84. }
  85. // Can't assign to a function.
  86. S: FUNC '=' {
  87. TQString s(i18n("You can't assign to function %1").arg($1->name()));
  88. Result::setLastResult(s);
  89. YYABORT;
  90. }
  91. // This is a function prototype. abakus currently only supports one-argument
  92. // functions.
  93. ASSIGN: '(' { --gCheckIdents; } IDENT ')' '=' {
  94. $$ = $3;
  95. }
  96. // Blocking a variable with the name deriv is a slight feature regression
  97. // since normally functions and variables with the same name can coexist, but
  98. // I don't want to duplicate code all over the place.
  99. S: SET DERIV {
  100. TQString s(i18n("Function %1 is built-in and cannot be overridden.").arg("deriv"));
  101. Result::setLastResult(s);
  102. YYABORT;
  103. }
  104. S: DERIV '=' {
  105. TQString s(i18n("Function %1 is built-in and cannot be overridden.").arg("deriv"));
  106. Result::setLastResult(s);
  107. YYABORT;
  108. }
  109. S: SET FUNC ASSIGN EXP {
  110. ++gCheckIdents;
  111. // We're trying to reassign an already defined function, make sure it's
  112. // not a built-in.
  113. TQString funcName = $2->name();
  114. TQString ident = $3->name();
  115. FunctionManager *manager = FunctionManager::instance();
  116. if(manager->isFunction(funcName) && !manager->isFunctionUserDefined(funcName)) {
  117. TQString s(i18n("Function %1 is built-in and cannot be overridden.").arg(funcName));
  118. Result::setLastResult(s);
  119. YYABORT;
  120. }
  121. if(manager->isFunction(funcName))
  122. manager->removeFunction(funcName);
  123. BaseFunction *newFn = new UserDefinedFunction(funcName.ascii(), $4);
  124. if(!manager->addFunction(newFn, ident)) {
  125. TQString s(i18n("Unable to define function %1 because it is recursive.").arg(funcName));
  126. Result::setLastResult(s);
  127. YYABORT;
  128. }
  129. Result::setLastResult(Result::Null);
  130. YYACCEPT;
  131. }
  132. // IDENT is the same as FUNC, except that the lexer has determined that IDENT
  133. // is not already a FUNC.
  134. S: SET IDENT ASSIGN EXP {
  135. ++gCheckIdents;
  136. TQString funcName = $2->name();
  137. TQString ident = $3->name();
  138. // No need to check if the function is already defined, because the
  139. // lexer checked for us before returning the IDENT token.
  140. BaseFunction *newFn = new UserDefinedFunction(funcName.ascii(), $4);
  141. FunctionManager::instance()->addFunction(newFn, ident);
  142. Result::setLastResult(Result::Null);
  143. YYACCEPT;
  144. }
  145. // Remove a defined function.
  146. S: REMOVE FUNC '(' ')' {
  147. FunctionManager::instance()->removeFunction($2->name());
  148. Result::setLastResult(Result::Null);
  149. YYACCEPT;
  150. }
  151. // Can't remove an ident using remove-func syntax.
  152. S: REMOVE IDENT '(' ')' {
  153. // This is an error
  154. Result::setLastResult(Result(i18n("Function %1 is not defined.").arg($2->name())));
  155. YYABORT;
  156. }
  157. // This happens when the user tries to remove a function that's not defined.
  158. S: REMOVE IDENT '(' IDENT ')' {
  159. // This is an error
  160. Result::setLastResult(Result(i18n("Function %1 is not defined.").arg($2->name())));
  161. YYABORT;
  162. }
  163. S: REMOVE IDENT {
  164. ValueManager *manager = ValueManager::instance();
  165. if(manager->isValueSet($2->name()) && !manager->isValueReadOnly($2->name())) {
  166. manager->removeValue($2->name());
  167. Result::setLastResult(Result::Null);
  168. YYACCEPT;
  169. }
  170. else {
  171. TQString s;
  172. if(manager->isValueSet($2->name()))
  173. s = i18n("Can't remove predefined variable %1.").arg($2->name());
  174. else
  175. s = i18n("Can't remove undefined variable %1.").arg($2->name());
  176. Result::setLastResult(s);
  177. YYABORT;
  178. }
  179. }
  180. S: SET IDENT '=' EXP {
  181. ValueManager *vm = ValueManager::instance();
  182. if(vm->isValueReadOnly($2->name())) {
  183. if($2->name() == "pi" && $4->value() == Abakus::number_t("3.0"))
  184. Result::setLastResult(i18n("This isn't Indiana, you can't just change pi"));
  185. else
  186. Result::setLastResult(i18n("%1 is a constant").arg($2->name()));
  187. YYABORT;
  188. }
  189. ValueManager::instance()->setValue($2->name(), $4->value());
  190. Result::setLastResult(Result::Null);
  191. YYACCEPT;
  192. }
  193. // Set a variable.
  194. S: IDENT '=' EXP {
  195. ValueManager *vm = ValueManager::instance();
  196. if(vm->isValueReadOnly($1->name())) {
  197. if($1->name() == "pi" && $3->value() == Abakus::number_t("3.0"))
  198. Result::setLastResult(i18n("This isn't Indiana, you can't just change pi"));
  199. else
  200. Result::setLastResult(i18n("%1 is a constant").arg($1->name()));
  201. YYABORT;
  202. }
  203. ValueManager::instance()->setValue($1->name(), $3->value());
  204. Result::setLastResult(Result::Null);
  205. YYACCEPT;
  206. }
  207. S: NUMBER '=' {
  208. Result::setLastResult(i18n("Can't assign to %1").arg($1->value().toString()));
  209. YYABORT;
  210. }
  211. // Can't call this as a function.
  212. TERM: IDENT '(' {
  213. Result::setLastResult(i18n("%1 isn't a function (or operator expected)").arg($1->name()));
  214. YYABORT;
  215. }
  216. // Can't do this either.
  217. TERM: IDENT IDENT {
  218. Result::setLastResult(i18n("Missing operator"));
  219. YYABORT;
  220. }
  221. TERM: IDENT NUMBER {
  222. Result::setLastResult(i18n("Missing operator"));
  223. YYABORT;
  224. }
  225. TERM: NUMBER NUMBER {
  226. Result::setLastResult(i18n("Missing operator"));
  227. YYABORT;
  228. }
  229. S: error {
  230. Result::setLastResult(i18n("Sorry, I can't figure it out."));
  231. YYABORT;
  232. }
  233. /**
  234. * Here be the standard calculator-parsing part. Nothing here should be too
  235. * fancy.
  236. */
  237. EXP: EXP '+' FACTOR { $$ = new BinaryOperator(BinaryOperator::Addition, $1, $3); }
  238. EXP: EXP '-' FACTOR { $$ = new BinaryOperator(BinaryOperator::Subtraction, $1, $3); }
  239. EXP: FACTOR { $$ = $1; }
  240. FACTOR: FACTOR '*' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3); }
  241. FACTOR: FACTOR '/' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Division, $1, $3); }
  242. FACTOR: EXPONENT { $$ = $1; }
  243. EXPONENT: TERM POWER EXPONENT { $$ = new BinaryOperator(BinaryOperator::Exponentiation, $1, $3); }
  244. EXPONENT: TERM { $$ = $1; }
  245. TERM: '+' VALUE { $$ = $2; }
  246. TERM: '-' VALUE { $$ = new UnaryOperator(UnaryOperator::Negation, $2); }
  247. TERM: '(' EXP ')' { $$ = $2; }
  248. TERM: '-' '(' EXP ')' { $$ = new UnaryOperator(UnaryOperator::Negation, $3); }
  249. TERM: VALUE { $$ = $1; }
  250. VALUE: NUMBER { $$ = $1; }
  251. NUMBER: NUM {
  252. TDELocale *locale = TDEGlobal::locale();
  253. QChar decimal = locale->decimalSymbol()[0];
  254. // Replace current decimal separator with US Decimal separator to be
  255. // evil.
  256. unsigned len = strlen(yytext);
  257. for(unsigned i = 0; i < len; ++i)
  258. if(yytext[i] == decimal)
  259. yytext[i] = '.';
  260. Abakus::number_t value(yytext);
  261. $$ = new NumericValue(value);
  262. }
  263. TERM: DERIV { --gCheckIdents; } '(' EXP ',' { ++gCheckIdents; } EXP ')' {
  264. $$ = new DerivativeFunction($4, $7);
  265. }
  266. TERM: FUNC TERM {
  267. $1->setOperand($2);
  268. $$ = $1;
  269. }
  270. /* Handle implicit multiplication */
  271. TERM: NUMBER FUNC TERM {
  272. $2->setOperand($3);
  273. $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2);
  274. }
  275. TERM: NUMBER '(' EXP ')' {
  276. $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3);
  277. }
  278. TERM: NUMBER IDENT {
  279. if(gCheckIdents > 0 && !ValueManager::instance()->isValueSet($2->name())) {
  280. Result::setLastResult(i18n("Unknown variable %1").arg($2->name()));
  281. YYABORT;
  282. }
  283. $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2);
  284. }
  285. VALUE: IDENT {
  286. if(gCheckIdents <= 0 || ValueManager::instance()->isValueSet($1->name()))
  287. $$ = $1;
  288. else {
  289. Result::setLastResult(i18n("Unknown variable %1").arg($1->name()));
  290. YYABORT;
  291. }
  292. }
  293. IDENT: ID {
  294. $$ = new Identifier(yytext);
  295. }
  296. FUNC: FN {
  297. /* No check necessary, the lexer has already checked for us. */
  298. $$ = new BuiltinFunction(yytext, 0);
  299. }
  300. %%
  301. int gCheckIdents = 0;
  302. int yyerror(const char *)
  303. {
  304. return 0;
  305. }