Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 300 lines (241 sloc) 7.399 kb
0bf8122 Dave Reisner add MIT License
authored
1 /* Copyright (c) 2010 Dave Reisner
2 *
3 * Permission is hereby granted, free of charge, to any person
4 * obtaining a copy of this software and associated documentation
5 * files (the "Software"), to deal in the Software without
6 * restriction, including without limitation the rights to use,
7 * copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following
10 * conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
dc4d94e Dave Reisner initial commit
authored
25 #include <ctype.h>
dd23408 Dave Reisner Improvments to accuracy in floating point ops, using macro definitions
authored
26 #include <float.h>
dc4d94e Dave Reisner initial commit
authored
27 #include <math.h>
28 #include <stdio.h>
29 #include <stdlib.h>
10b665f Dave Reisner reorder headers includes
authored
30 #include <string.h>
b3a83ed Dave Reisner actively detect if a user piped something on stdin. if it has data, pars...
authored
31 #include <unistd.h>
dc4d94e Dave Reisner initial commit
authored
32
10b665f Dave Reisner reorder headers includes
authored
33 #include <readline/history.h>
34 #include <readline/readline.h>
35
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
36 #include "posty.h"
1c8b547 Dave Reisner Add remaining protos for each function and declare all protos and global...
authored
37
38 char *strtrim(char *str) {
dc4d94e Dave Reisner initial commit
authored
39 char *pch = str;
40
41 if (str == NULL || *str == '\0')
42 return str;
43
44 while (isspace(*pch)) pch++;
45
46 if (pch != str)
47 memmove(str, pch, (strlen(pch) + 1));
48
49 if (*str == '\0')
50 return str;
51
52 pch = (str + strlen(str) - 1);
53
54 while (isspace(*pch))
55 pch--;
56
57 *++pch = '\0';
58
59 return str;
1fd2cd6 Dave Reisner strip vim code folding tags
authored
60 }
dc4d94e Dave Reisner initial commit
authored
61
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
62 char *strlower(char *str) {
63 char *pch = str;
64
5725f02 Dave Reisner Increment pch separately in strlower to make gcc happy
authored
65 while (*pch != '\0') {
66 *pch = tolower(*pch);
67 pch++;
68 }
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
69
70 return str;
71 }
72
caa1c7d Dave Reisner Semantics: resetstack() -> stack_reset()
authored
73 void stack_reset() {
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
74 if (stackptr == &opstack[0])
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
75 return;
76
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
77 if (verbose >= 2) { /* Dump individual items on the stack */
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
78 printf(":: Stack Dump :: ");
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
79 while (stackptr != &opstack[0])
80 printf("%.*f ", precision, *--stackptr);
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
81 putchar('\n');
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
82 } else /* Just reset the pointer */
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
83 stackptr = &opstack[0];
1fd2cd6 Dave Reisner strip vim code folding tags
authored
84 }
dc4d94e Dave Reisner initial commit
authored
85
1fd2cd6 Dave Reisner strip vim code folding tags
authored
86 /** parse operations */
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
87 int parse_constant(const char *constant) {
88 if (strcmp(constant, "pi") == 0)
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
89 *stackptr++ = M_PI;
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
90 else if (strcmp(constant, "e") == 0)
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
91 *stackptr++ = M_E;
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
92
93 return 0;
94 }
95
d8bdbe4 Dave Reisner Add const declarations where applicable
authored
96 int parse_operand(const char *token, double *operand) {
94894c9 Dave Reisner Separate out operand parsing into its own function. Use pass by referenc...
authored
97 char *endPtr;
98
99 *operand = strtod(token, &endPtr);
6444b39 Dave Reisner use a more sane method to check the result of strtol. endPtr will be a n...
authored
100 if (*endPtr != '\0') {
94894c9 Dave Reisner Separate out operand parsing into its own function. Use pass by referenc...
authored
101 fprintf(stderr, "!! Bad input: %s\n", token);
102 return 1;
9b3dc62 Dave Reisner Merge 2 if/thens to an if/else in parse_operand(). Validate before check...
authored
103 } else if (DOUBLE_EQ(*operand, HUGE_VAL)) {
104 fprintf(stderr, "!! Input overflow.\n");
105 return 1;
94894c9 Dave Reisner Separate out operand parsing into its own function. Use pass by referenc...
authored
106 }
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
107
94894c9 Dave Reisner Separate out operand parsing into its own function. Use pass by referenc...
authored
108 return 0;
109 }
110
d8bdbe4 Dave Reisner Add const declarations where applicable
authored
111 int parse_operator(const char operator) {
dc4d94e Dave Reisner initial commit
authored
112 double op1, op2;
113
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
114 op2 = *--stackptr;
115 op1 = *--stackptr;
dc4d94e Dave Reisner initial commit
authored
116
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
117 if (verbose >= 2)
dc4d94e Dave Reisner initial commit
authored
118 printf(":: %.*f ", precision, op1);
119
120 switch (operator) {
121 case '+': op1 += op2; break;
122 case '-': op1 -= op2; break;
123 case '*': op1 *= op2; break;
e48432a Dave Reisner Add support for pow() and modulus
authored
124 case '^': op1 = pow(op1, op2); break;
dd23408 Dave Reisner Improvments to accuracy in floating point ops, using macro definitions
authored
125 case '/': if (DOUBLE_EQ(op2, 0)) {
1a00f57 Dave Reisner use double !! for errors to match double :: for info
authored
126 fprintf(stderr, "!! Divide by zero\n");
127 return 1;
128 }
129 op1 /= op2;
130 break;
dd23408 Dave Reisner Improvments to accuracy in floating point ops, using macro definitions
authored
131 case '%': if (DOUBLE_EQ(op2, 0) || (int)op2 == 0) {
1a00f57 Dave Reisner use double !! for errors to match double :: for info
authored
132 fprintf(stderr, "!! Divide by zero\n");
133 return 1;
134 }
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
135 op1 = (int)op1 % (int)op2;
1a00f57 Dave Reisner use double !! for errors to match double :: for info
authored
136 break;
dc4d94e Dave Reisner initial commit
authored
137 }
138
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
139 if (verbose >= 2)
dc4d94e Dave Reisner initial commit
authored
140 printf("%c %.*f = %.*f\n", operator, precision, op2, precision, op1);
141
dd23408 Dave Reisner Improvments to accuracy in floating point ops, using macro definitions
authored
142 if (DOUBLE_EQ(op1, HUGE_VAL)) {
aa39fca Dave Reisner Check for and throw error on result overflow
authored
143 fprintf(stderr, "!! Result overflow\n");
144 return 1;
145 }
146
48877a1 Dave Reisner test for stack overflow and return immediately if found, rather than tes...
authored
147 *stackptr++ = op1;
148
dc4d94e Dave Reisner initial commit
authored
149 return 0;
1fd2cd6 Dave Reisner strip vim code folding tags
authored
150 }
dc4d94e Dave Reisner initial commit
authored
151
d8bdbe4 Dave Reisner Add const declarations where applicable
authored
152 int parse_precision(const char *p) {
dc4d94e Dave Reisner initial commit
authored
153 char *endPtr;
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
154 int pre;
155
156 pre = (int)strtol(p, &endPtr, 10);
6444b39 Dave Reisner use a more sane method to check the result of strtol. endPtr will be a n...
authored
157 if (*endPtr != '\0') {
1a00f57 Dave Reisner use double !! for errors to match double :: for info
authored
158 fprintf(stderr, "!! Bad precision specified\n");
dc4d94e Dave Reisner initial commit
authored
159 return 1;
160 } else {
161 precision = pre;
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
162
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
163 if (precision < 0) /* clamp negative numbers to 0 */
164 precision ^= precision;
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
165
166 if (verbose >= 1)
167 printf(":: Precision set to %d decimal places.\n", precision);
dc4d94e Dave Reisner initial commit
authored
168 }
169
170 return 0;
1fd2cd6 Dave Reisner strip vim code folding tags
authored
171 }
dc4d94e Dave Reisner initial commit
authored
172
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
173 int parse_mfunc(char *mfunc) {
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
174 double op1;
175 double res;
176
177 op1 = *--stackptr;
178
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
179 if (strcmp(mfunc, "sin") == 0)
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
180 res = sin(op1);
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
181 else if (strcmp(mfunc, "cos") == 0)
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
182 res = cos(op1);
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
183 else if (strcmp(mfunc, "tan") == 0)
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
184 res = tan(op1);
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
185 else if (strcmp(mfunc, "asin") == 0)
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
186 res = asin(op1);
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
187 else if (strcmp(mfunc, "acos") == 0)
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
188 res = acos(op1);
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
189 else if (strcmp(mfunc, "atan") == 0)
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
190 res = atan(op1);
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
191 else if (strcmp(mfunc, "sqrt") == 0)
192 res = sqrt(op1);
e8f7057 Dave Reisner specify ln to log() for natural log evaluation
authored
193 else if (strcmp(mfunc, "ln") == 0)
194 res = log(op1);
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
195
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
196 if (verbose >= 2)
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
197 printf("%s(%.*f) = %.*f\n", mfunc, precision, op1, precision, res);
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
198
199 *stackptr++ = res;
200
201 return 0;
202 }
203
1fd2cd6 Dave Reisner strip vim code folding tags
authored
204 int parse_expression(char *expr) {
5b9bdcf Dave Reisner Link against readline for history and inline navigation
authored
205 if (strlen(strtrim(expr)) == 0)
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
206 return BREAK;
dc4d94e Dave Reisner initial commit
authored
207
94894c9 Dave Reisner Separate out operand parsing into its own function. Use pass by referenc...
authored
208 char *token;
e48432a Dave Reisner Add support for pow() and modulus
authored
209 static const char *operators = "+/*-%^";
e8f7057 Dave Reisner specify ln to log() for natural log evaluation
authored
210 static const char *mfuncs = "|ln|sqrt|sin|cos|tan|asin|acos|atan|";
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
211 static const char *constants = "|e|pi|";
dc4d94e Dave Reisner initial commit
authored
212 double operand;
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
213 char *ptr;
dc4d94e Dave Reisner initial commit
authored
214
215 while ((token = strsep(&expr, " \n"))) {
216 if (strlen(token) == 0) continue;
217
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
218 if (*token == ':') /* precision specified */
219 parse_precision(++token);
220 else if (strchr(operators, *token) && strlen(token) == 1) { /* operator */
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
221 if (stackptr - opstack < 2) {
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
222 fprintf(stderr, "!! Malformed expression -- too few operands.\n");
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
223 return CONTINUE;
224 }
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
225
aa39fca Dave Reisner Check for and throw error on result overflow
authored
226 if (parse_operator(*token) > 0) {
1a00f57 Dave Reisner use double !! for errors to match double :: for info
authored
227 return CONTINUE;
dc4d94e Dave Reisner initial commit
authored
228 }
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
229 } else if ((ptr = strstr(mfuncs, strlower(token))) != NULL &&
d814411 Dave Reisner add parsing for trig functions, lightly salted with testing
authored
230 *(ptr - 1) == '|' && *(ptr + strlen(token)) == '|') {
231 /* validated trig function */
232 if (stackptr - opstack < 1) {
233 fprintf(stderr, "!! Malformed expression -- too few operands.\n");
234 return CONTINUE;
235 }
fd7cc8c Dave Reisner math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc, a...
authored
236 parse_mfunc(token);
cec6766 Dave Reisner Use tolower in strlower and use simple strstr instead of strcasestr sinc...
authored
237 } else if ((ptr = strstr(constants, strlower(token))) != NULL &&
5f2ad17 Dave Reisner Add parsing for constants -- e and pi for now. add a str to lowercase fu...
authored
238 *(ptr - 1) == '|' && *(ptr + strlen(token)) == '|') {
239 /* validated constant */
240 parse_constant(token);
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
241 } else { /* it's an operand, or it's bad input */
242 if (parse_operand(token, &operand) > 0) /* parsing failed on bad input */
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
243 return CONTINUE;
94894c9 Dave Reisner Separate out operand parsing into its own function. Use pass by referenc...
authored
244
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
245 if (stackptr == &opstack[STACK_SIZE]) { /* stack overflow */
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
246 fprintf(stderr, "!! Stack overflow. Expression too large.\n");
1a6757c Dave Reisner Properly return on occasions where the stack overflows. Stack doesn't ne...
authored
247 return CONTINUE;
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
248 }
48877a1 Dave Reisner test for stack overflow and return immediately if found, rather than tes...
authored
249
250 *stackptr++ = operand;
dc4d94e Dave Reisner initial commit
authored
251 }
252 }
253
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
254 if (stackptr - opstack > 1)
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
255 fprintf(stderr, "!! Malformed expression -- too many operands.\n");
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
256 else if (stackptr - opstack == 1) {
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
257 if (verbose >= 1)
258 printf(" = %.*f\n", precision, *--stackptr);
259 else
260 printf("%.*f\n", precision, *--stackptr);
dc4d94e Dave Reisner initial commit
authored
261 }
262
d5f63a6 Dave Reisner Various bug fixes and optimizations
authored
263 return CONTINUE;
1fd2cd6 Dave Reisner strip vim code folding tags
authored
264 }
265
266 int main(int argc, char *argv[]) {
5b9bdcf Dave Reisner Link against readline for history and inline navigation
authored
267 char *buf, line[BUFSIZ + 1];
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
268
dc4d94e Dave Reisner initial commit
authored
269 if (argc > 1 && strcmp(argv[1], "-v") == 0) {
270 fprintf(stderr, "::Stack dumps enabled::\n");
18a035c Dave Reisner output: -v now specifies verbose=2. interactive mode is verbose=1. verbo...
authored
271 verbose = 2;
dc4d94e Dave Reisner initial commit
authored
272 }
273
c867d13 Dave Reisner Mostly cosmetic code changes. Stack size reduced to 64, as it really doe...
authored
274 stackptr = &opstack[0]; /* initialize stack */
3e427e2 Dave Reisner Use a bounded stack rather than a linked struct. Code becomes massively ...
authored
275
b3a83ed Dave Reisner actively detect if a user piped something on stdin. if it has data, pars...
authored
276 /* If stdin has data, hit it and quit it */
277 if (!isatty(fileno(stdin))) {
5b9bdcf Dave Reisner Link against readline for history and inline navigation
authored
278 fgets(line, BUFSIZ, stdin);
279 parse_expression(line);
e3f2713 Dave Reisner reopen stdin after squeezing out its contents (if any)
authored
280 freopen(ctermid(NULL), "r", stdin);
b3a83ed Dave Reisner actively detect if a user piped something on stdin. if it has data, pars...
authored
281 return 0;
282 }
283
5b9bdcf Dave Reisner Link against readline for history and inline navigation
authored
284 /* going interactive. fire up readline */
285 using_history();
286
287 while ((buf = readline("> ")) != NULL) {
288 add_history(buf);
289 strcpy(line, buf);
290 parse_expression(line);
caa1c7d Dave Reisner Semantics: resetstack() -> stack_reset()
authored
291 stack_reset();
5b9bdcf Dave Reisner Link against readline for history and inline navigation
authored
292 free(buf);
293 }
3fff7b8 Dave Reisner print a newline when exiting from interactive mode
authored
294 putchar('\n');
5b9bdcf Dave Reisner Link against readline for history and inline navigation
authored
295
296 clear_history();
dc4d94e Dave Reisner initial commit
authored
297
298 return 0;
1fd2cd6 Dave Reisner strip vim code folding tags
authored
299 }
Something went wrong with that request. Please try again.