Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 300 lines (241 sloc) 7.399 kB
0bf8122 @falconindy 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 @falconindy initial commit
authored
25 #include <ctype.h>
dd23408 @falconindy Improvments to accuracy in floating point ops, using macro definitions
authored
26 #include <float.h>
dc4d94e @falconindy initial commit
authored
27 #include <math.h>
28 #include <stdio.h>
29 #include <stdlib.h>
10b665f @falconindy reorder headers includes
authored
30 #include <string.h>
b3a83ed @falconindy actively detect if a user piped something on stdin. if it has data, p…
authored
31 #include <unistd.h>
dc4d94e @falconindy initial commit
authored
32
10b665f @falconindy reorder headers includes
authored
33 #include <readline/history.h>
34 #include <readline/readline.h>
35
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
36 #include "posty.h"
1c8b547 @falconindy Add remaining protos for each function and declare all protos and glo…
authored
37
38 char *strtrim(char *str) {
dc4d94e @falconindy 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 @falconindy strip vim code folding tags
authored
60 }
dc4d94e @falconindy initial commit
authored
61
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
62 char *strlower(char *str) {
63 char *pch = str;
64
5725f02 @falconindy Increment pch separately in strlower to make gcc happy
authored
65 while (*pch != '\0') {
66 *pch = tolower(*pch);
67 pch++;
68 }
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
69
70 return str;
71 }
72
caa1c7d @falconindy Semantics: resetstack() -> stack_reset()
authored
73 void stack_reset() {
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
74 if (stackptr == &opstack[0])
d5f63a6 @falconindy Various bug fixes and optimizations
authored
75 return;
76
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
77 if (verbose >= 2) { /* Dump individual items on the stack */
d5f63a6 @falconindy Various bug fixes and optimizations
authored
78 printf(":: Stack Dump :: ");
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
79 while (stackptr != &opstack[0])
80 printf("%.*f ", precision, *--stackptr);
d5f63a6 @falconindy Various bug fixes and optimizations
authored
81 putchar('\n');
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
82 } else /* Just reset the pointer */
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
83 stackptr = &opstack[0];
1fd2cd6 @falconindy strip vim code folding tags
authored
84 }
dc4d94e @falconindy initial commit
authored
85
1fd2cd6 @falconindy strip vim code folding tags
authored
86 /** parse operations */
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
87 int parse_constant(const char *constant) {
88 if (strcmp(constant, "pi") == 0)
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
89 *stackptr++ = M_PI;
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
90 else if (strcmp(constant, "e") == 0)
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
91 *stackptr++ = M_E;
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
92
93 return 0;
94 }
95
d8bdbe4 @falconindy Add const declarations where applicable
authored
96 int parse_operand(const char *token, double *operand) {
94894c9 @falconindy Separate out operand parsing into its own function. Use pass by refer…
authored
97 char *endPtr;
98
99 *operand = strtod(token, &endPtr);
6444b39 @falconindy use a more sane method to check the result of strtol. endPtr will be …
authored
100 if (*endPtr != '\0') {
94894c9 @falconindy Separate out operand parsing into its own function. Use pass by refer…
authored
101 fprintf(stderr, "!! Bad input: %s\n", token);
102 return 1;
9b3dc62 @falconindy Merge 2 if/thens to an if/else in parse_operand(). Validate before ch…
authored
103 } else if (DOUBLE_EQ(*operand, HUGE_VAL)) {
104 fprintf(stderr, "!! Input overflow.\n");
105 return 1;
94894c9 @falconindy Separate out operand parsing into its own function. Use pass by refer…
authored
106 }
d5f63a6 @falconindy Various bug fixes and optimizations
authored
107
94894c9 @falconindy Separate out operand parsing into its own function. Use pass by refer…
authored
108 return 0;
109 }
110
d8bdbe4 @falconindy Add const declarations where applicable
authored
111 int parse_operator(const char operator) {
dc4d94e @falconindy initial commit
authored
112 double op1, op2;
113
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
114 op2 = *--stackptr;
115 op1 = *--stackptr;
dc4d94e @falconindy initial commit
authored
116
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
117 if (verbose >= 2)
dc4d94e @falconindy 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 @falconindy Add support for pow() and modulus
authored
124 case '^': op1 = pow(op1, op2); break;
dd23408 @falconindy Improvments to accuracy in floating point ops, using macro definitions
authored
125 case '/': if (DOUBLE_EQ(op2, 0)) {
1a00f57 @falconindy 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 @falconindy Improvments to accuracy in floating point ops, using macro definitions
authored
131 case '%': if (DOUBLE_EQ(op2, 0) || (int)op2 == 0) {
1a00f57 @falconindy use double !! for errors to match double :: for info
authored
132 fprintf(stderr, "!! Divide by zero\n");
133 return 1;
134 }
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
135 op1 = (int)op1 % (int)op2;
1a00f57 @falconindy use double !! for errors to match double :: for info
authored
136 break;
dc4d94e @falconindy initial commit
authored
137 }
138
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
139 if (verbose >= 2)
dc4d94e @falconindy initial commit
authored
140 printf("%c %.*f = %.*f\n", operator, precision, op2, precision, op1);
141
dd23408 @falconindy Improvments to accuracy in floating point ops, using macro definitions
authored
142 if (DOUBLE_EQ(op1, HUGE_VAL)) {
aa39fca @falconindy Check for and throw error on result overflow
authored
143 fprintf(stderr, "!! Result overflow\n");
144 return 1;
145 }
146
48877a1 @falconindy test for stack overflow and return immediately if found, rather than …
authored
147 *stackptr++ = op1;
148
dc4d94e @falconindy initial commit
authored
149 return 0;
1fd2cd6 @falconindy strip vim code folding tags
authored
150 }
dc4d94e @falconindy initial commit
authored
151
d8bdbe4 @falconindy Add const declarations where applicable
authored
152 int parse_precision(const char *p) {
dc4d94e @falconindy initial commit
authored
153 char *endPtr;
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
154 int pre;
155
156 pre = (int)strtol(p, &endPtr, 10);
6444b39 @falconindy use a more sane method to check the result of strtol. endPtr will be …
authored
157 if (*endPtr != '\0') {
1a00f57 @falconindy use double !! for errors to match double :: for info
authored
158 fprintf(stderr, "!! Bad precision specified\n");
dc4d94e @falconindy initial commit
authored
159 return 1;
160 } else {
161 precision = pre;
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
162
d5f63a6 @falconindy Various bug fixes and optimizations
authored
163 if (precision < 0) /* clamp negative numbers to 0 */
164 precision ^= precision;
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
165
166 if (verbose >= 1)
167 printf(":: Precision set to %d decimal places.\n", precision);
dc4d94e @falconindy initial commit
authored
168 }
169
170 return 0;
1fd2cd6 @falconindy strip vim code folding tags
authored
171 }
dc4d94e @falconindy initial commit
authored
172
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
173 int parse_mfunc(char *mfunc) {
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
174 double op1;
175 double res;
176
177 op1 = *--stackptr;
178
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
179 if (strcmp(mfunc, "sin") == 0)
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
180 res = sin(op1);
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
181 else if (strcmp(mfunc, "cos") == 0)
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
182 res = cos(op1);
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
183 else if (strcmp(mfunc, "tan") == 0)
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
184 res = tan(op1);
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
185 else if (strcmp(mfunc, "asin") == 0)
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
186 res = asin(op1);
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
187 else if (strcmp(mfunc, "acos") == 0)
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
188 res = acos(op1);
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
189 else if (strcmp(mfunc, "atan") == 0)
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
190 res = atan(op1);
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
191 else if (strcmp(mfunc, "sqrt") == 0)
192 res = sqrt(op1);
e8f7057 @falconindy specify ln to log() for natural log evaluation
authored
193 else if (strcmp(mfunc, "ln") == 0)
194 res = log(op1);
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
195
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
196 if (verbose >= 2)
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
197 printf("%s(%.*f) = %.*f\n", mfunc, precision, op1, precision, res);
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
198
199 *stackptr++ = res;
200
201 return 0;
202 }
203
1fd2cd6 @falconindy strip vim code folding tags
authored
204 int parse_expression(char *expr) {
5b9bdcf @falconindy Link against readline for history and inline navigation
authored
205 if (strlen(strtrim(expr)) == 0)
d5f63a6 @falconindy Various bug fixes and optimizations
authored
206 return BREAK;
dc4d94e @falconindy initial commit
authored
207
94894c9 @falconindy Separate out operand parsing into its own function. Use pass by refer…
authored
208 char *token;
e48432a @falconindy Add support for pow() and modulus
authored
209 static const char *operators = "+/*-%^";
e8f7057 @falconindy specify ln to log() for natural log evaluation
authored
210 static const char *mfuncs = "|ln|sqrt|sin|cos|tan|asin|acos|atan|";
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
211 static const char *constants = "|e|pi|";
dc4d94e @falconindy initial commit
authored
212 double operand;
d814411 @falconindy add parsing for trig functions, lightly salted with testing
authored
213 char *ptr;
dc4d94e @falconindy initial commit
authored
214
215 while ((token = strsep(&expr, " \n"))) {
216 if (strlen(token) == 0) continue;
217
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
218 if (*token == ':') /* precision specified */
219 parse_precision(++token);
220 else if (strchr(operators, *token) && strlen(token) == 1) { /* operator */
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
221 if (stackptr - opstack < 2) {
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
222 fprintf(stderr, "!! Malformed expression -- too few operands.\n");
d5f63a6 @falconindy Various bug fixes and optimizations
authored
223 return CONTINUE;
224 }
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
225
aa39fca @falconindy Check for and throw error on result overflow
authored
226 if (parse_operator(*token) > 0) {
1a00f57 @falconindy use double !! for errors to match double :: for info
authored
227 return CONTINUE;
dc4d94e @falconindy initial commit
authored
228 }
fd7cc8c @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
229 } else if ((ptr = strstr(mfuncs, strlower(token))) != NULL &&
d814411 @falconindy 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 @falconindy math.h defines M_PI and M_E for us. Generalize trig keywords to mfunc…
authored
236 parse_mfunc(token);
cec6766 @falconindy Use tolower in strlower and use simple strstr instead of strcasestr s…
authored
237 } else if ((ptr = strstr(constants, strlower(token))) != NULL &&
5f2ad17 @falconindy Add parsing for constants -- e and pi for now. add a str to lowercase…
authored
238 *(ptr - 1) == '|' && *(ptr + strlen(token)) == '|') {
239 /* validated constant */
240 parse_constant(token);
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
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 @falconindy Various bug fixes and optimizations
authored
243 return CONTINUE;
94894c9 @falconindy Separate out operand parsing into its own function. Use pass by refer…
authored
244
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
245 if (stackptr == &opstack[STACK_SIZE]) { /* stack overflow */
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
246 fprintf(stderr, "!! Stack overflow. Expression too large.\n");
1a6757c @falconindy Properly return on occasions where the stack overflows. Stack doesn't…
authored
247 return CONTINUE;
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
248 }
48877a1 @falconindy test for stack overflow and return immediately if found, rather than …
authored
249
250 *stackptr++ = operand;
dc4d94e @falconindy initial commit
authored
251 }
252 }
253
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
254 if (stackptr - opstack > 1)
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
255 fprintf(stderr, "!! Malformed expression -- too many operands.\n");
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
256 else if (stackptr - opstack == 1) {
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
257 if (verbose >= 1)
258 printf(" = %.*f\n", precision, *--stackptr);
259 else
260 printf("%.*f\n", precision, *--stackptr);
dc4d94e @falconindy initial commit
authored
261 }
262
d5f63a6 @falconindy Various bug fixes and optimizations
authored
263 return CONTINUE;
1fd2cd6 @falconindy strip vim code folding tags
authored
264 }
265
266 int main(int argc, char *argv[]) {
5b9bdcf @falconindy Link against readline for history and inline navigation
authored
267 char *buf, line[BUFSIZ + 1];
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
268
dc4d94e @falconindy initial commit
authored
269 if (argc > 1 && strcmp(argv[1], "-v") == 0) {
270 fprintf(stderr, "::Stack dumps enabled::\n");
18a035c @falconindy output: -v now specifies verbose=2. interactive mode is verbose=1. ve…
authored
271 verbose = 2;
dc4d94e @falconindy initial commit
authored
272 }
273
c867d13 @falconindy Mostly cosmetic code changes. Stack size reduced to 64, as it really …
authored
274 stackptr = &opstack[0]; /* initialize stack */
3e427e2 @falconindy Use a bounded stack rather than a linked struct. Code becomes massive…
authored
275
b3a83ed @falconindy actively detect if a user piped something on stdin. if it has data, p…
authored
276 /* If stdin has data, hit it and quit it */
277 if (!isatty(fileno(stdin))) {
5b9bdcf @falconindy Link against readline for history and inline navigation
authored
278 fgets(line, BUFSIZ, stdin);
279 parse_expression(line);
e3f2713 @falconindy reopen stdin after squeezing out its contents (if any)
authored
280 freopen(ctermid(NULL), "r", stdin);
b3a83ed @falconindy actively detect if a user piped something on stdin. if it has data, p…
authored
281 return 0;
282 }
283
5b9bdcf @falconindy 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 @falconindy Semantics: resetstack() -> stack_reset()
authored
291 stack_reset();
5b9bdcf @falconindy Link against readline for history and inline navigation
authored
292 free(buf);
293 }
3fff7b8 @falconindy print a newline when exiting from interactive mode
authored
294 putchar('\n');
5b9bdcf @falconindy Link against readline for history and inline navigation
authored
295
296 clear_history();
dc4d94e @falconindy initial commit
authored
297
298 return 0;
1fd2cd6 @falconindy strip vim code folding tags
authored
299 }
Something went wrong with that request. Please try again.