1 /*******************************************************************************
2 * Reverse Polish Notation calculator. *
3 * Copyright (c) 2007-2008, Samuel Fredrickson <kinghajj@gmail.com> *
4 * All rights reserved. *
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. *
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 *
25 ******************************************************************************/
27 /*******************************************************************************
28 * parser.c - parses RPN expressions. *
29 ******************************************************************************/
36 //! Tests if a string can be converted into a number.
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
);
46 if(len
&& s
[0] == '-')
49 for(; isnum
&& i
< len
; ++i
)
50 if(!isdigit(s
[i
]) && s
[i
] != '.') isnum
= false;
52 // caveat: if length is one and that's just a dot, then it's NOT a number.
53 if(isnum
&& len
== 1 && *s
== '.')
59 //! Evaluates a token on the calculator.
61 * @param calculator The calculator.
62 * @param tok The token.
64 static void evalToken(RPNCalculator
*calculator
, char *tok
)
66 RPNStack
*stack
= RPN_currentStack(calculator
);
70 // try to execute an operator; if that fails, then try to execute a command
71 executed
= RPN_executeOperator(calculator
, tok
) ||
72 RPN_executeCommand(calculator
, tok
);
74 // no? then treat this as a variable name.
77 // find variable with this name
78 var
= RPN_findVariable(calculator
->variables
, tok
);
82 // push it's value to the stack
83 RPN_push(stack
, var
->value
);
85 // add a new variable to the variable table, whose value is that of
86 // the topmost item in the stack.
87 RPN_addVariable(calculator
->variables
, strdup(tok
),
92 //! Evaluates a string on a calculator.
94 * @param s The string to evaluate.
95 * @param calculator The calculator on which to evaluate.
96 * @return The topmost item of the calculator's stack.
98 RPNValue
RPN_eval(char *s
, RPNCalculator
*calculator
)
100 /* these are just used as shorthands to make the code more readable. */
104 /* split the string. */
105 tokens
= calculator
->tokens
= RPN_splitString(s
);
107 /* go through the tokens. */
108 for(tokens
->pos
= 0; tokens
->pos
< tokens
->size
; ++tokens
->pos
)
110 token
= tokens
->tokens
[tokens
->pos
];
111 /* push numeric tokens to the stack. */
113 RPN_push(RPN_currentStack(calculator
), RPN_strtod(token
, NULL
));
114 /* delegate other tokens to evalToken(). */
116 evalToken(calculator
, token
);
120 RPN_freeTokens(tokens
);
121 calculator
->tokens
= NULL
;
123 if(calculator
->needs_newline
) {
125 calculator
->needs_newline
= false;
128 return RPN_peek(RPN_currentStack(calculator
));