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 * commands.c - implements the commands table. *
29 ******************************************************************************/
35 /*******************************************************************************
36 * The RPN command functions, in all their glory. *
37 ******************************************************************************/
39 // Don't look at these, Doxygen.
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
,
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
)
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
)
99 RPNStack
*stack
= RPN_currentStack(calculator
);
100 if(!RPN_canOperate(stack
, 1)) return;
102 #ifdef RPN_LONG_DOUBLE
103 RPN_push(stack
, sqrtl(a
));
105 RPN_push(stack
, sqrt(a
));
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
);
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
);
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
);
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
);
164 RPN_error("could not allocate memory for command.");
166 command
->nargs
= nargs
;
167 command
->func
= func
;
168 RPN_dprintf("allocated command %p", 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
);
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
)
193 RPN_error("attempted to remove a command from a NULL command table.");
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
);
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
,
230 RPNCommand
*command
= RPN_newCommand(cmd
, nargs
, func
);
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
)
246 HASH_FIND_STR( commands
->table
, cmd
, 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
)
259 RPNTokens
*tokens
= calculator
->tokens
;
260 bool executed
= false;
262 command
= RPN_findCommand(calculator
->commands
, cmd
);
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
;
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");