2 PC -- programmer's calculator.
4 This program implements a simple recursive descent parser that
5 understands pretty much all standard C language math and logic
6 expressions. It handles the usual add, subtract, multiply, divide,
7 and mod sort of stuff. It can also deal with logical/relational
8 operations and expressions. The logic/relational operations AND, OR,
9 NOT, and EXCLUSIVE OR, &&, ||, ==, !=, <, >, <=, and >= are all
10 supported. It also handles parens and nested expresions as well as
11 left and right shifts. There are variables and assignments (as well
12 as assignment operators like "*=").
14 The other useful feature is that you can use "." in an expression
15 to refer to the value from the previous expression (just like bc).
17 Multiple statements can be separated by semi-colons (;) on a single
18 line (though a single statement can't span multiple lines).
20 This calculator is mainly a programmers calculator because it
21 doesn't work in floating point and only deals with integers.
23 I wrote this because the standard unix calculator (bc) doesn't
24 offer a useful modulo, it doesn't have left and right shifts and
25 sometimes it is a pain in the ass to use (but I still use bc for
26 things that require any kind of floating point). This program is
27 great when you have to do address calculations and bit-wise
28 masking/shifting as you do when working on kernel type code. It's
29 also handy for doing quick conversions between decimal, hex and ascii
30 (and if you want to see octal for some reason, just put it in the
33 The parser is completely separable and could be spliced into other
34 code very easy. The routine parse_expression() just expects a char
35 pointer and returns the value. Implementing command line editing
36 would be easy using a readline() type of library.
38 This isn't the world's best parser or anything, but it works and
39 suits my needs. It faithfully implements C style precedence of
42 ++ -- ~ ! * / % + - << >> < > <= >= == != & ^ | && ||
44 (in that order, from greatest to least precedence).
46 Note: The ! unary operator is a logical negation, not a bitwise
47 negation (if you want bitwise negation, use ~).
49 I've been working on adding variables and assignments, and I've
50 just (10/26/94) got it working right and with code I'm not ashamed of.
51 Now you can have variables (no restrictions on length) and assign to
52 them and use them in expressions. Variable names have the usual C
53 rules (i.e. alpha or underscore followed by alpha-numeric and
54 underscore). Variables are initialized to zero and created as needed.
55 You can have any number of variables. Here are some examples:
60 (y * 2) + (x & 0xffeef)
63 Assignment operators also work. The allowable assignment operators
66 +=, -=, *=, /=, %=, &=, ^=, |=, <<=, and >>=
69 The basic ideas for this code came from the book "Compiler Design
70 in C", by Allen I. Holub, but I've extended them significantly.
72 If you find bugs or parsing bogosites, I'd like to know about them
73 so I can fix them. Other comments and criticism of the code are
76 Thanks go to Joel Tesler (joel@engr.sgi.com) for adding the ability
77 to pass command line arguments and have pc evaluate them instead of reading
81 dbg@be.com (though this was written while I was at sgi)
92 * You should #define USE_LONG_LONG if your compiler supports the long long
93 * data type, has strtoull(), and a %lld conversion specifier for printf.
94 * Otherwise just comment out the #define and pc will use plain longs.
101 #define LONG long long
102 #define ULONG unsigned long long
103 #define STRTOUL strtoull
104 #define STR1 "%20llu 0x%.16llx signed: %20lld"
105 #define STR2 "%20llu 0x%.16llx"
106 #define STR3 " char: "
107 #define STR4 "%20lu 0x%-16.8lx signed: %20ld"
108 #define STR5 "%20lu 0x%-16.8lx"
111 #define ULONG unsigned long
112 #define STRTOUL strtoul
113 #define STR1 "%10lu\t 0x%.8lx\t signed: %10ld"
114 #define STR2 "%10lu\t 0x%.8lx"
115 #define STR3 " char: "
121 ULONG
parse_expression(char *str
); /* top-level interface to parser */
122 ULONG
assignment_expr(char **str
); /* assignments =, +=, *=, etc */
123 ULONG
do_assignment_operator(char **str
, char *var_name
);
124 ULONG
logical_or_expr(char **str
); /* logical OR `||' */
125 ULONG
logical_and_expr(char **str
); /* logical AND `&&' */
126 ULONG
or_expr(char **str
); /* OR `|' */
127 ULONG
xor_expr(char **str
); /* XOR `^' */
128 ULONG
and_expr(char **str
); /* AND `&' */
129 ULONG
equality_expr(char **str
); /* equality ==, != */
130 ULONG
relational_expr(char **str
); /* relational <, >, <=, >= */
131 ULONG
shift_expr(char **str
); /* shifts <<, >> */
132 ULONG
add_expression(char **str
); /* addition/subtraction +, - */
133 ULONG
term(char **str
); /* multiplication/division *,%,/ */
134 ULONG
factor(char **str
); /* negation, logical not ~, ! */
135 ULONG
get_value(char **str
);
136 int get_var(char *name
, ULONG
*val
); /* external interfaces to vars */
137 ULONG
set_var(char *name
, ULONG val
);
139 void do_input(void); /* reads stdin and calls parser */
140 char *skipwhite(char *str
); /* skip over input white space */
145 * Variables are kept in a simple singly-linked list. Not high
146 * performance, but it's also an extremely small implementation.
148 * New variables get added to the head of the list. Variables are
149 * never deleted, though it wouldn't be hard to do that.
152 typedef struct variable
156 struct variable
*next
;
159 variable dummy
= { NULL
, 0L, NULL
};
160 variable
*vars
=&dummy
;
162 variable
*lookup_var(char *name
);
163 variable
*add_var(char *name
, ULONG value
);
164 char *get_var_name(char **input_str
);
165 void parse_args(int argc
, char *argv
[]);
166 int (*set_var_lookup_hook(int (*func
)(char *name
, ULONG
*val
)))
167 (char *name
, ULONG
*val
);
170 * last_result is equal to the result of the last expression and
171 * expressions can refer to it as `.' (just like bc).
173 ULONG last_result
= 0;
177 special_vars(char *name
, ULONG
*val
)
179 if (strcmp(name
, "time") == 0)
180 *val
= (ULONG
)time(NULL
);
181 else if (strcmp(name
, "rand") == 0)
182 *val
= (ULONG
)rand();
183 else if (strcmp(name
, "dbg") == 0)
193 main(int argc
, char *argv
[])
196 set_var_lookup_hook(special_vars
);
199 parse_args(argc
, argv
);
207 This function prints the result of the expression.
208 It tries to be smart about printing numbers so it
209 only uses the necessary number of digits. If you
210 have long long (i.e. 64 bit numbers) it's very
211 annoying to have lots of leading zeros when they
212 aren't necessary. By doing the somewhat bizarre
213 casting and comparisons we can determine if a value
214 will fit in a 32 bit quantity and only print that.
217 print_result(ULONG value
)
222 if ((signed LONG
)value
< 0)
224 if ((signed LONG
)value
< (signed LONG
)(LONG_MIN
))
225 printf(STR1
, value
, value
, value
);
227 printf(STR4
, (long)value
, (long)value
, (long)value
);
229 else if ((ULONG
)value
<= (ULONG
)ULONG_MAX
)
230 printf(STR5
, (long)value
, (long)value
);
232 printf(STR2
, value
, value
);
235 Print any printable character (and print dots for unprintable chars
238 for(i
=sizeof(ULONG
)-1; i
>= 0; i
--)
241 ch
= ((ULONG
)value
& ((ULONG
)0xff << shift
)) >> shift
;
243 if (isprint((int)ch
))
244 printf("%c", (char)(ch
));
253 parse_args(int argc
, char *argv
[])
259 for(i
=1, len
=0; i
< argc
; i
++)
260 len
+= strlen(argv
[i
]);
263 buff
= malloc(len
*sizeof(char));
270 strcat(buff
, *++argv
);
272 value
= parse_expression(buff
);
283 char buff
[256], *ptr
;
285 while(fgets(buff
, 256, stdin
) != NULL
)
287 if (buff
[strlen(buff
)-1] == '\n')
288 buff
[strlen(buff
)-1] = '\0'; /* kill the newline character */
290 for(ptr
=buff
; isspace(*ptr
) && *ptr
; ptr
++)
291 /* skip whitespace */;
293 if (*ptr
== '\0') /* hmmm, an empty line, just skip it */
296 value
= parse_expression(buff
);
304 parse_expression(char *str
)
309 ptr
= skipwhite(ptr
);
313 val
= assignment_expr(&ptr
);
315 ptr
= skipwhite(ptr
);
316 while (*ptr
== SEMI_COLON
&& *ptr
!= '\0')
319 if (*ptr
== '\0') /* reached the end of the string, stop parsing */
322 val
= assignment_expr(&ptr
);
331 assignment_expr(char **str
)
338 *str
= skipwhite(*str
);
341 var_name
= get_var_name(str
);
343 *str
= skipwhite(*str
);
344 if (**str
== EQUAL
&& *(*str
+1) != EQUAL
)
346 *str
= skipwhite(*str
+ 1); /* skip the equal sign */
348 val
= assignment_expr(str
); /* go recursive! */
350 if ((v
= lookup_var(var_name
)) == NULL
)
351 add_var(var_name
, val
);
355 else if (((**str
== PLUS
|| **str
== MINUS
|| **str
== OR
||
356 **str
== TIMES
|| **str
== DIVISION
|| **str
== MODULO
||
357 **str
== AND
|| **str
== XOR
) && *(*str
+1) == EQUAL
) ||
358 strncmp(*str
, "<<=", 3) == 0 || strncmp(*str
, ">>=", 3) == 0)
360 val
= do_assignment_operator(str
, var_name
);
365 val
= logical_or_expr(str
); /* no equal sign, just get var value */
367 *str
= skipwhite(*str
);
370 fprintf(stderr
, "Left hand side of expression is not assignable.\n");
382 do_assignment_operator(char **str
, char *var_name
)
390 if (operator == SHIFT_L
|| operator == SHIFT_R
)
391 *str
= skipwhite(*str
+ 3);
393 *str
= skipwhite(*str
+ 2); /* skip the assignment operator */
395 val
= assignment_expr(str
); /* go recursive! */
397 v
= lookup_var(var_name
);
400 v
= add_var(var_name
, 0);
405 if (operator == PLUS
)
407 else if (operator == MINUS
)
409 else if (operator == AND
)
411 else if (operator == XOR
)
413 else if (operator == OR
)
415 else if (operator == SHIFT_L
)
417 else if (operator == SHIFT_R
)
419 else if (operator == TIMES
)
421 else if (operator == DIVISION
)
423 if (val
== 0) /* check for it, but still get the result */
424 fprintf(stderr
, "Divide by zero!\n");
428 else if (operator == MODULO
)
430 if (val
== 0) /* check for it, but still get the result */
431 fprintf(stderr
, "Modulo by zero!\n");
437 fprintf(stderr
, "Unknown operator: %c\n", operator);
446 logical_or_expr(char **str
)
450 *str
= skipwhite(*str
);
452 sum
= logical_and_expr(str
);
454 *str
= skipwhite(*str
);
455 while(**str
== OR
&& *(*str
+ 1) == OR
)
457 *str
= skipwhite(*str
+ 2); /* advance over the operator */
459 val
= logical_and_expr(str
);
470 logical_and_expr(char **str
)
474 *str
= skipwhite(*str
);
478 *str
= skipwhite(*str
);
479 while(**str
== AND
&& *(*str
+ 1) == AND
)
481 *str
= skipwhite(*str
+ 2); /* advance over the operator */
497 *str
= skipwhite(*str
);
501 *str
= skipwhite(*str
);
502 while(**str
== OR
&& *(*str
+1) != OR
)
504 *str
= skipwhite(*str
+ 1); /* advance over the operator */
521 *str
= skipwhite(*str
);
525 *str
= skipwhite(*str
);
528 *str
= skipwhite(*str
+ 1); /* advance over the operator */
545 *str
= skipwhite(*str
);
547 sum
= equality_expr(str
);
549 *str
= skipwhite(*str
);
550 while(**str
== AND
&& *(*str
+1) != AND
)
552 *str
= skipwhite(*str
+ 1); /* advance over the operator */
554 val
= equality_expr(str
);
564 equality_expr(char **str
)
569 *str
= skipwhite(*str
);
571 sum
= relational_expr(str
);
573 *str
= skipwhite(*str
);
574 while((**str
== EQUAL
&& *(*str
+1) == EQUAL
) ||
575 (**str
== BANG
&& *(*str
+1) == EQUAL
))
579 *str
= skipwhite(*str
+ 2); /* advance over the operator */
581 val
= relational_expr(str
);
594 relational_expr(char **str
)
599 *str
= skipwhite(*str
);
601 sum
= shift_expr(str
);
603 *str
= skipwhite(*str
);
604 while(**str
== LESS_THAN
|| **str
== GREATER_THAN
)
609 if (*(*str
+1) == EQUAL
)
612 *str
= *str
+1; /* skip initial operator */
615 *str
= skipwhite(*str
+ 1); /* advance over the operator */
617 val
= shift_expr(str
);
620 Notice that we do the relational expressions as signed
621 comparisons. This is because of expressions like:
623 which would not return the expected value if we did the
624 comparison as unsigned. This may not always be the
625 desired behavior, but aside from adding casting to epxressions,
626 there isn't much of a way around it.
628 if (op
== LESS_THAN
&& equal_to
== 0)
629 sum
= ((LONG
)sum
< (LONG
)val
);
630 else if (op
== LESS_THAN
&& equal_to
== 1)
631 sum
= ((LONG
)sum
<= (LONG
)val
);
632 else if (op
== GREATER_THAN
&& equal_to
== 0)
633 sum
= ((LONG
)sum
> (LONG
)val
);
634 else if (op
== GREATER_THAN
&& equal_to
== 1)
635 sum
= ((LONG
)sum
>= (LONG
)val
);
643 shift_expr(char **str
)
648 *str
= skipwhite(*str
);
650 sum
= add_expression(str
);
652 *str
= skipwhite(*str
);
653 while((strncmp(*str
, "<<", 2) == 0) || (strncmp(*str
, ">>", 2) == 0))
657 *str
= skipwhite(*str
+ 2); /* advance over the operator */
659 val
= add_expression(str
);
663 else if (op
== SHIFT_R
)
674 add_expression(char **str
)
679 *str
= skipwhite(*str
);
683 *str
= skipwhite(*str
);
684 while(**str
== PLUS
|| **str
== MINUS
)
688 *str
= skipwhite(*str
+ 1); /* advance over the operator */
694 else if (op
== MINUS
)
712 *str
= skipwhite(*str
);
715 * We're at the bottom of the parse. At this point we either have
716 * an operator or we're through with this string. Otherwise it's
717 * an error and we print a message.
719 if (**str
!= TIMES
&& **str
!= DIVISION
&& **str
!= MODULO
&&
720 **str
!= PLUS
&& **str
!= MINUS
&& **str
!= OR
&&
721 **str
!= AND
&& **str
!= XOR
&& **str
!= BANG
&&
722 **str
!= NEGATIVE
&& **str
!= TWIDDLE
&& **str
!= RPAREN
&&
723 **str
!= LESS_THAN
&& **str
!= GREATER_THAN
&& **str
!= SEMI_COLON
&&
724 strncmp(*str
, "<<", 2) != 0 && strncmp(*str
, ">>", 2) &&
725 **str
!= EQUAL
&& **str
!= '\0')
727 fprintf(stderr
, "Parsing stopped: unknown operator %s\n", *str
);
731 while(**str
== TIMES
|| **str
== DIVISION
|| **str
== MODULO
)
734 *str
= skipwhite(*str
+ 1);
739 else if (op
== DIVISION
)
742 fprintf(stderr
, "Divide by zero!\n");
746 else if (op
== MODULO
)
749 fprintf(stderr
, "Modulo by zero!\n");
763 char op
= NOTHING
, have_special
=0;
764 char *var_name
, *var_name_ptr
;
767 if (**str
== NEGATIVE
|| **str
== PLUS
|| **str
== TWIDDLE
|| **str
== BANG
)
769 op
= **str
; /* must be a unary op */
771 if ((op
== NEGATIVE
&& *(*str
+ 1) == NEGATIVE
) || /* look for ++/-- */
772 (op
== PLUS
&& *(*str
+ 1) == PLUS
))
778 *str
= skipwhite(*str
+ 1);
779 var_name_ptr
= *str
; /* save where the varname should be */
782 val
= get_value(str
);
784 *str
= skipwhite(*str
);
787 * Now is the time to actually do the unary operation if one
790 if (have_special
) /* we've got a ++ or -- */
792 var_name
= get_var_name(&var_name_ptr
);
793 if (var_name
== NULL
)
795 fprintf(stderr
, "Can only use ++/-- on variables.\n");
798 if ((v
= lookup_var(var_name
)) == NULL
)
800 v
= add_var(var_name
, 0);
811 else /* normal unary operator */
815 case NEGATIVE
: val
*= -1;
818 case BANG
: val
= !val
;
821 case TWIDDLE
: val
= ~val
;
832 get_value(char **str
)
838 if (**str
== SINGLE_QUOTE
) /* a character constant */
842 *str
= *str
+ 1; /* advance over the leading quote */
844 for(i
=0; **str
&& **str
!= SINGLE_QUOTE
&& i
< sizeof(LONG
); *str
+=1,i
++)
846 if (**str
== '\\') /* escape the next char */
850 val
|= (ULONG
)((unsigned)**str
);
853 if (**str
!= SINGLE_QUOTE
) /* constant must have been too long */
855 fprintf(stderr
, "Warning: character constant not terminated or too "
856 "long (max len == %ld bytes)\n", sizeof(LONG
));
857 while(**str
&& **str
!= SINGLE_QUOTE
)
860 else if (**str
!= '\0')
863 else if (isdigit(**str
)) /* a regular number */
865 val
= STRTOUL(*str
, str
, 0);
867 *str
= skipwhite(*str
);
869 else if (**str
== USE_LAST_RESULT
) /* a `.' meaning use the last result */
872 *str
= skipwhite(*str
+1);
874 else if (**str
== LPAREN
) /* a parenthesized expression */
876 *str
= skipwhite(*str
+ 1);
878 val
= assignment_expr(str
); /* start at top and come back down */
883 fprintf(stderr
, "Mismatched paren's\n");
885 else if (isalpha(**str
) || **str
== '_') /* a variable name */
887 if ((var_name
= get_var_name(str
)) == NULL
)
889 fprintf(stderr
, "Can't get var name!\n");
893 if (get_var(var_name
, &val
) == 0)
895 fprintf(stderr
, "No such variable: %s (assigning value of zero)\n",
899 v
= add_var(var_name
, val
);
904 *str
= skipwhite(*str
);
905 if (strncmp(*str
, "++", 2) == 0 || strncmp(*str
, "--", 2) == 0)
907 if ((v
= lookup_var(var_name
)) != NULL
)
918 fprintf(stderr
, "%s is a read-only variable\n", var_name
);
926 fprintf(stderr
, "Expecting left paren, unary op, constant or variable.");
927 fprintf(stderr
, " Got: `%s'\n", *str
);
937 * Here are the functions that manipulate variables.
941 this is a hook function for external read-only
942 variables. If it is set and we don't find a
943 variable name in our name space, we call it to
944 look for the variable. If it finds the name, it
945 fills in val and returns 1. If it returns 0, it
946 didn't find the variable.
948 static int (*external_var_lookup
)(char *name
, ULONG
*val
) = NULL
;
951 this very ugly function declaration is for the function
952 set_var_lookup_hook which accepts one argument, "func", which
953 is a pointer to a function that returns int (and accepts a
954 char * and ULONG *). set_var_lookup_hook returns a pointer to
955 a function that returns int and accepts char * and ULONG *.
957 It's very ugly looking but fairly basic in what it does. You
958 pass in a function to set as the variable name lookup up hook
959 and it passes back to you the old function (which you should
960 call if it is non-null and your function fails to find the
963 int (*set_var_lookup_hook(int (*func
)(char *name
, ULONG
*val
)))(char *name
, ULONG
*val
)
965 int (*old_func
)(char *name
, ULONG
*val
) = external_var_lookup
;
967 external_var_lookup
= func
;
974 lookup_var(char *name
)
978 for(v
=vars
; v
; v
=v
->next
)
979 if (v
->name
&& strcmp(v
->name
, name
) == 0)
987 add_var(char *name
, ULONG value
)
992 /* first make sure this isn't an external read-only variable */
993 if (external_var_lookup
)
994 if (external_var_lookup(name
, &tmp
) != 0)
996 fprintf(stderr
, "Can't assign/create %s, it is a read-only var\n",name
);
1001 v
= (variable
*)malloc(sizeof(variable
));
1004 fprintf(stderr
, "No memory to add variable: %s\n", name
);
1008 v
->name
= strdup(name
);
1012 vars
= v
; /* set head of list to the new guy */
1018 This routine and the companion get_var() are external
1019 interfaces to the variable manipulation routines.
1022 set_var(char *name
, ULONG val
)
1026 v
= lookup_var(name
);
1035 This function returns 1 on success of finding
1036 a variable and 0 on failure to find a variable.
1037 If a variable is found, val is filled with its
1041 get_var(char *name
, ULONG
*val
)
1045 v
= lookup_var(name
);
1051 else if (external_var_lookup
!= NULL
)
1053 return external_var_lookup(name
, val
);
1059 #define DEFAULT_LEN 32
1062 get_var_name(char **str
)
1064 int i
, len
=DEFAULT_LEN
;
1067 if (isalpha(**str
) == 0 && **str
!= '_')
1070 buff
= (char *)malloc(len
*sizeof(char));
1075 * First get the variable name
1078 while(**str
&& (isalnum(**str
) || **str
== '_'))
1083 buff
= (char *)realloc(buff
, len
);
1092 buff
[i
] = '\0'; /* null terminate */
1094 while (isalnum(**str
) || **str
== '_') /* skip over any remaining junk */
1103 skipwhite(char *str
)
1108 while(*str
&& (*str
== ' ' || *str
== '\t' || *str
== '\n' || *str
== '\f'))