Renamed some commands, cleaned-up new code, added comments.
[rpn.git] / src / commands.c
blob6f2588ff327a333933cac158252c8d13a75ef9a5
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 * commands.c - implements the commands table. *
29 ******************************************************************************/
31 #include <math.h>
32 #include "rpn.h"
33 #include <stdlib.h>
35 /*******************************************************************************
36 * The RPN command functions, in all their glory. *
37 ******************************************************************************/
39 // Don't look at these, Doxygen.
40 #ifndef DOXYGEN_SKIP
42 static void commandPrint(RPNCalculator *calculator, char **args)
44 RPN_printStack(RPN_currentStack(calculator));
47 static void commandPrintDetailed(RPNCalculator *calculator, char **args)
49 RPN_printStackDetailed(RPN_currentStack(calculator));
52 static void commandPrintVariables(RPNCalculator *calculator, char **args)
54 RPN_printVariables(calculator->variables);
57 static void commandPrintVariablesDetailed(RPNCalculator *calculator,
58 char **args)
60 RPN_printVariablesDetailed(calculator->variables);
63 static void commandPrintHistory(RPNCalculator *calculator, char **args)
65 RPN_printHistory(calculator->history);
68 static void commandPrintHistoryDetailed(RPNCalculator *calculator, char **args)
70 RPN_printHistoryDetailed(calculator->history);
73 static void commandPrintHelp(RPNCalculator *calculator, char **args)
75 RPN_printHelp();
78 static void commandExit(RPNCalculator *calculator, char **args)
80 calculator->status = RPN_STATUS_EXIT;
83 static void commandPop(RPNCalculator *calculator, char **args)
85 RPN_pop(RPN_currentStack(calculator));
88 static void commandDup(RPNCalculator *calculator, char **args)
90 RPNStack *stack = RPN_currentStack(calculator);
92 if(RPN_canOperate(stack, 1))
93 RPN_push(stack, RPN_peek(stack));
96 static void commandSqrt(RPNCalculator *calculator, char **args)
98 RPNValue a;
99 RPNStack *stack = RPN_currentStack(calculator);
100 if(!RPN_canOperate(stack, 1)) return;
101 a = RPN_pop(stack);
102 #ifdef RPN_LONG_DOUBLE
103 RPN_push(stack, sqrtl(a));
104 #elif RPN_DOUBLE
105 RPN_push(stack, sqrt(a));
106 #endif
109 static void commandSwap(RPNCalculator *calculator, char **args)
111 RPN_swap(RPN_currentStack(calculator));
114 static void commandUnset(RPNCalculator *calculator, char **args)
116 char *varname = args[0];
117 RPNVariable *variable = RPN_findVariable(calculator->variables, varname);
118 if(variable)
119 RPN_removeVariable(calculator->variables, variable);
122 static void commandPushHistory(RPNCalculator *calculator, char **args)
124 RPN_pushHistory(calculator->history);
127 static void commandPopHistory(RPNCalculator *calculator, char **args)
129 RPN_popHistory(calculator->history);
132 #endif // DOXYGEN_SKIP
134 /*******************************************************************************
135 * The RPN command table. Allows for simple but powerful extendability. *
136 ******************************************************************************/
138 //! Creates a new, empty command hash table.
140 * @return A new, empty command hash table.
142 RPNCommands *RPN_newCommands()
144 RPNCommands *commands = new(RPNCommands);
145 if(!commands)
146 RPN_error("could not allocate memory for command table.");
147 // uthash requires that an empty table be set to NULL.
148 commands->table = NULL;
149 RPN_dprintf("allocated commands table %p", commands);
150 return commands;
153 //! Creates a new command, but does not insert it into a table.
155 * @param cmd The string representation of the command.
156 * @param nargs The number of arguments the command takes.
157 * @param func The function that performs the command.
158 * @return A new command.
160 RPNCommand *RPN_newCommand(char *cmd, size_t nargs, RPNCommandFunc func)
162 RPNCommand *command = new(RPNCommand);
163 if(!command)
164 RPN_error("could not allocate memory for command.");
165 command->cmd = cmd;
166 command->nargs = nargs;
167 command->func = func;
168 RPN_dprintf("allocated command %p", command);
169 return command;
172 //! Frees a command and its string representation.
174 * @param command The command to free.
176 void RPN_freeCommand(RPNCommand *command)
178 if(!command || !command->cmd)
179 RPN_error("attempted to free a NULL command.");
180 RPN_free(command->cmd);
181 RPN_free(command);
182 RPN_dprintf("freed command %p", command);
185 //! Removes a command from a command table, then frees it.
187 * @param commands The command table.
188 * @param command The command to remove.
190 void RPN_removeCommand(RPNCommands *commands, RPNCommand *command)
192 if(!commands)
193 RPN_error("attempted to remove a command from a NULL command table.");
194 if(!command)
195 RPN_error("attempted to remove a NULL command.");
196 HASH_DEL( commands->table, command );
197 RPN_freeCommand(command);
198 RPN_dprintf("removed command %p from table %p", command, commands);
201 //! Frees a command table and all if its commands.
203 * @param commands The command table to free.
205 void RPN_freeCommands(RPNCommands *commands)
207 RPNCommand *command, *next;
209 // go through every command and remove/free it.
210 for(command = commands->table; command != NULL; command = next) {
211 next = command->hh.next;
212 RPN_removeCommand(commands, command);
215 RPN_free(commands);
216 RPN_dprintf("freed command table %p", commands);
219 //! Adds a command to a command table.
221 * @param commands The command table.
222 * @param cmd The string representation of a command.
223 * @param nargs The number of arguments the command needs.
224 * @param func The function that performs the command.
225 * @return true if succeeds.
227 void RPN_addCommand(RPNCommands *commands, char *cmd, size_t nargs,
228 RPNCommandFunc func)
230 RPNCommand *command = RPN_newCommand(cmd, nargs, func);
231 if(!commands)
232 RPN_error("tried to add command to NULL command table.");
233 HASH_ADD_KEYPTR( hh, commands->table, cmd, strlen(cmd), command );
234 RPN_dprintf("added command %p to table %p", command, commands);
237 //! Finds a command based on its string representation.
239 * @param commands The commands table to search.
240 * @param cmd The string representation of the command.
241 * @return A pointer to the command.
243 RPNCommand *RPN_findCommand(RPNCommands *commands, char *cmd)
245 RPNCommand *command;
246 HASH_FIND_STR( commands->table, cmd, command );
247 return command;
250 //! Finds and executes a command based on its string representation.
252 * @param calculator The calculator on which to execute the command.
253 * @param cmd The string representation of the command.
254 * @return true if found and executed, false otherwise.
256 bool RPN_executeCommand(RPNCalculator *calculator, char *cmd)
258 RPNCommand *command;
259 RPNTokens *tokens = calculator->tokens;
260 bool executed = false;
262 command = RPN_findCommand(calculator->commands, cmd);
263 if(command)
265 // only execute if there are enough arguments.
266 if(tokens->size - tokens->pos + 1 >= command->nargs)
267 command->func(calculator, &tokens->tokens[tokens->pos + 1]);
268 // skip the arguments
269 tokens->pos += command->nargs;
270 executed = true;
273 return executed;
276 //! Returns a command table with default commands.
278 * @return The default commands.
280 RPNCommands *RPN_defaultCommands()
282 RPNCommands *commands = RPN_newCommands();
284 RPN_addCommand(commands, strdup("dup"), 0, commandDup);
285 RPN_addCommand(commands, strdup("pop"), 0, commandPop);
286 RPN_addCommand(commands, strdup("ps"), 0, commandPrint);
287 RPN_addCommand(commands, strdup("psd"), 0, commandPrintDetailed);
288 RPN_addCommand(commands, strdup("pv"), 0, commandPrintVariables);
289 RPN_addCommand(commands, strdup("pvd"), 0, commandPrintVariablesDetailed);
290 RPN_addCommand(commands, strdup("ph"), 0, commandPrintHistory);
291 RPN_addCommand(commands, strdup("phd"), 0, commandPrintHistoryDetailed);
292 RPN_addCommand(commands, strdup("help"), 0, commandPrintHelp);
293 RPN_addCommand(commands, strdup("x"), 0, commandExit);
294 RPN_addCommand(commands, strdup("sqrt"), 0, commandSqrt);
295 RPN_addCommand(commands, strdup("swap"), 0, commandSwap);
296 RPN_addCommand(commands, strdup("unset"), 1, commandUnset);
297 RPN_addCommand(commands, strdup("pushh"), 0, commandPushHistory);
298 RPN_addCommand(commands, strdup("poph"), 0, commandPopHistory);
299 RPN_dprintf("created default command table");
301 return commands;