From 9aa5c113691997300137aced478cc6fe98b0a8c1 Mon Sep 17 00:00:00 2001 From: Tibor Bizjak Date: Sun, 9 Jul 2023 18:33:07 +0200 Subject: [PATCH] k&r 04-06: added variable support --- k&r/04-funcs-and-prog-struct/04-calc.c | 84 ++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/k&r/04-funcs-and-prog-struct/04-calc.c b/k&r/04-funcs-and-prog-struct/04-calc.c index 83ba3aa..5b2c5e1 100644 --- a/k&r/04-funcs-and-prog-struct/04-calc.c +++ b/k&r/04-funcs-and-prog-struct/04-calc.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include /* --------- ERROR MESSAGES */ #define printerr(format, ...) printf("error: " format "\n", ##__VA_ARGS__) @@ -12,6 +14,7 @@ #define EMPTY_STACK_ERR "stack empty" #define SHORT_STACK_ERR "not enough elements on stack" #define UNKNOWN_TOKEN_ERR "unknown token %s" +#define VAR_NOT_SET_ERR "variable %c has no value" /* --------- PROMPT FORMATS */ #define PROMPT_F ">> " @@ -28,6 +31,7 @@ int sp = 0; double val[MAXVAL]; +/* length: current length of stack */ #define length() sp /* push: push f onto value stack */ #define push(x) ((sp < MAXVAL) ? val[sp++] = (x) : printerr(FULL_STACK_ERR, ((double) (x)))) @@ -37,8 +41,6 @@ double val[MAXVAL]; #define peek() (sp ? val[sp-1] : (printerr(EMPTY_STACK_ERR), 0)) /* clear: clear stack */ #define clear() (sp = 0) -/* print_peek: print top element, leave stack unchanged */ -#define print_peek() printf(PEEK_PRINT_F, peek()) /* dup: duplicate top element */ #define dup() push(peek()) /* swap: swap top two elements */ @@ -49,6 +51,38 @@ double val[MAXVAL]; } \ } while (0) +/* -------- STACK PRINTING FUNCTIONS */ +double lst_print; +/* print_peek: print top element, leave stack unchanged */ +#define print_peek() printf(PEEK_PRINT_F, lst_print = peek()) +/* print_pop: pop top element and print it */ +#define print_pop() printf(POP_PRINT_F, lst_print = pop()) + +/* ------- GLOBAL VARIABLE NAMESPACE */ +#define NS_SIZE ('z' - 'a' + 1) + +char var_flags[NS_SIZE]; +double var_vals[NS_SIZE]; + +/* set_var: pop val from stack, set variable c to val */ +#define set_var(c) \ + do { \ + int i = c - 'a'; \ + var_flags[i] = 1; \ + var_vals[i] = pop(); \ + } while (0) + +/* get_var: push value of c to stack */ +#define get_var(c) \ + do { \ + int i = c - 'a'; \ + if (!var_flags[i]) \ + printerr(VAR_NOT_SET_ERR, i+'a'); \ + else \ + push(var_vals[i]); \ + } while (0) + + /* --------- CALCULATOR FUNCTION TYPES */ /* the following macros allow us to call native c constructs in a declarative style */ @@ -88,11 +122,13 @@ double val[MAXVAL]; void help(void); #define COMMANDS \ + X(_, "last printed value", CALL, lst_print, VALUE) \ + X(ENTER, "pop top of stack and print it", CALL, print_pop, COMMAND) \ X(print, "print top of stack", CALL, print_peek, COMMAND) \ X(dup, "duplicate top of stack", COMMAND) \ X(swap, "swap top two elements of stack", COMMAND) \ X(clear, "clear stack", COMMAND) \ - X(help, "print all available commands", COMMAND) + X(help, "print all available commands", COMMAND) \ #define OPERATORS \ X(+, "add operands", COMM_OP) \ @@ -103,7 +139,8 @@ void help(void); X(>, "greater than", NONC_OP, double) \ X(<, "less than", NONC_OP, double) \ X(<=, "less than or equal", NONC_OP, double) \ - X(>=, "greater than or equal", NONC_OP, double) + X(>=, "greater than or equal", NONC_OP, double) \ + X(==, "test equality", COMM_OP) #define MATH_H_BINDINGS \ X(sin, "sine", UNARY_FUN) \ @@ -132,12 +169,19 @@ void help(void); OPERATORS \ MATH_H_BINDINGS +/* variable manipulation tokens */ +#define VAR_TOKENS \ + X('=', "pop value top from stack and set %s to value", set_var) \ + X('$', "push value of %s to top of stack", get_var) + + /* --------- MAIN PROGRAM */ #define MAXTOKEN 100 int gettoken(char *token, int max_t); char * parsef(char *s, double *x); + /* reverse Polish calculator */ int main() { @@ -149,7 +193,7 @@ int main() while (t_len = gettoken(token, MAXTOKEN)) { if (t_len < 0) { /* newline token */ if (length()) - printf(POP_PRINT_F, pop()); + print_pop(); printf(PROMPT_F); } else #define X(F, DESC, APPLY, ...) \ @@ -157,9 +201,15 @@ int main() APPLY(F, ##__VA_ARGS__); \ else TOKENS + #undef X + #define X(C, DESC, F) \ + if (t_len == 2 && token[0] == C && isalpha(token[1])) \ + F(tolower(token[1])); \ + else + VAR_TOKENS #undef X if (*parsef(token, &x) == '\0') - push(x); + push(x); else printerr(UNKNOWN_TOKEN_ERR, token); } @@ -171,21 +221,31 @@ int main() return 0; } +/* ---------- HELP COMMAND */ +#define MAX_CMD_W 10 /* token padding */ +#define VAR_STR "X" /* string to represent variable */ + +/* we verify that no token is to large to be pretty printed */ +#define X(TOKEN, ...) \ + static_assert(sizeof(#TOKEN) / sizeof(char) <= MAX_CMD_W, \ + "size of " #TOKEN " is to large for pretty printing " \ + "in help command"); + TOKENS +#undef X -#define COMMAND_W "10" -#define NEWLINE_DESC "pop top of stack and print it" /* help: prints all available calculator functions */ void help(void) { - printf("%-" COMMAND_W "s" NEWLINE_DESC "\n", "ENTER"); -#define X(F, DESC, ...) printf("%-" COMMAND_W "s" DESC "\n", #F); +#define X(C, DESC, ...) \ + printf("%c%-*s" DESC "\n", C, MAX_CMD_W-1, VAR_STR, VAR_STR); + VAR_TOKENS +#undef X +#define X(F, DESC, ...) printf("%-*s" DESC "\n", MAX_CMD_W, #F); TOKENS #undef X } /* -------- PARSING FUNCTIONS */ -#include - #define BLANK ' ' /* gettoken: get next space delimited token from input