Fixed bug that prevented the subtraction operator.
[rpn.git] / src / parser.c
blob7987bdfed07e2bc9ffda4f912ee2fce28afac061
1 /*******************************************************************************
2 * Reverse Polish Notation calculator. *
3 * Copyright (c) 2007-2008, Samuel Fredrickson <kinghajj@gmail.com> *
4 * All rights reserved. *
5 * *
6 * Redistribution and use in source and binary forms, with or without *
7 * modification, are permitted provided that the following conditions are met: *
8 * * Redistributions of source code must retain the above copyright *
9 * notice, this list of conditions and the following disclaimer. *
10 * * Redistributions in binary form must reproduce the above copyright *
11 * notice, this list of conditions and the following disclaimer in the *
12 * documentation and/or other materials provided with the distribution. *
13 * *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS *
15 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY *
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
24 * DAMAGE. *
25 ******************************************************************************/
27 /*******************************************************************************
28 * parser.c - parses RPN expressions. *
29 ******************************************************************************/
31 #include <ctype.h>
32 #include "rpn.h"
33 #include <stdlib.h>
34 #include <string.h>
36 //! Tests if a string can be converted into a number.
37 /**
38 * @param s The string to test.
39 * @return true or false.
41 static bool isNumber(char *s)
43 size_t i = 0, len = strlen(s);
44 bool isnum = true;
46 if(len && s[0] == '-')
48 // if it's just a dash, it's not a number.
49 if(len == 1)
50 isnum = false;
51 else
52 i = 1;
55 for(; isnum && i < len; ++i)
56 if(!isdigit(s[i]) && s[i] != '.') isnum = false;
58 // caveat: if length is one and that's just a dot, then it's NOT a number.
59 if(isnum && len == 1 && *s == '.')
60 isnum = false;
62 return isnum;
65 //! Evaluates a token on the calculator.
66 /**
67 * @param calculator The calculator.
68 * @param tok The token.
70 static void evalToken(RPNCalculator *calculator, char *tok)
72 RPNStack *stack = RPN_currentStack(calculator);
73 RPNVariable *var;
74 bool executed;
76 // try to execute an operator; if that fails, then try to execute a command
77 executed = RPN_executeOperator(calculator, tok) ||
78 RPN_executeCommand(calculator, tok);
80 // no? then treat this as a variable name.
81 if(!executed)
83 // find variable with this name
84 var = RPN_findVariable(calculator->variables, tok);
86 // if found,
87 if(var)
88 // push it's value to the stack
89 RPN_push(stack, var->value);
90 else
91 // add a new variable to the variable table, whose value is that of
92 // the topmost item in the stack.
93 RPN_addVariable(calculator->variables, strdup(tok),
94 RPN_peek(stack));
98 //! Evaluates a string on a calculator.
99 /**
100 * @param s The string to evaluate.
101 * @param calculator The calculator on which to evaluate.
102 * @return The topmost item of the calculator's stack.
104 RPNValue RPN_eval(char *s, RPNCalculator *calculator)
106 /* these are just used as shorthands to make the code more readable. */
107 RPNTokens *tokens;
108 char *token;
110 /* split the string. */
111 tokens = calculator->tokens = RPN_splitString(s);
113 /* go through the tokens. */
114 for(tokens->pos = 0; tokens->pos < tokens->size; ++tokens->pos)
116 token = tokens->tokens[tokens->pos];
117 /* push numeric tokens to the stack. */
118 if(isNumber(token))
119 RPN_push(RPN_currentStack(calculator), RPN_strtod(token, NULL));
120 /* delegate other tokens to evalToken(). */
121 else
122 evalToken(calculator, token);
125 /* cleanup. */
126 RPN_freeTokens(tokens);
127 calculator->tokens = NULL;
129 if(calculator->needs_newline) {
130 RPN_printf("\n");
131 calculator->needs_newline = false;
134 return RPN_peek(RPN_currentStack(calculator));