/* -*- Mode: C; -*- * ---------------------------------------------------------------------------- * Title: Simple expression parser mimicking DR's ASM.COM for CP/M-80 * Created: 2022-04-22 * Author: Gilbert Baumann * ---------------------------------------------------------------------------- * (c) copyright 2022 by Gilbert Baumann */ /* * Simple expression parser implementing the Digital Research ASM * syntax and semantics. Caevat: It treats all values as unsigned * 16-bit integers. So e.g. (-10 / 3) is not what you expect! * * For identifiers we punt and make all of them evaluate to 4711. Look * for "<-- punt". */ /* ------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #define isconst my_isconst _Noreturn void pr_error (const char *p, size_t n, char *fmt, ...); /* -- Tokenizer ------------------------------------------------------------ */ #define TOKENS \ _(MOD) _(SHL) _(SHR) \ _(EQ) _(LT) _(LE) _(GT) _(GE) _(NE) \ _(NOT) \ _(AND) \ _(OR) _(XOR) \ _(HIGH) _(LOW) #define PUNCT_TOKENS \ _("+", '+') _("-", '-') _("*",'*') _("/",'/') \ _("<=", LE) _(">=", GE) _("<>", NE) \ _("=", EQ) _("<", LT) _(">", GT) \ _("(", '(') _(")", ')') _(":", ':') _("!", '!') \ _(",", ',') enum { dummy_token = 255 #define _(x) ,x TOKENS #undef _ }; const char *whole; /* Whole line */ static int tok; /* Current token */ static int tokval; /* Token numeric value */ static const char *rptr; /* Current read pointer */ static const char *toktext; /* Point to token start */ static size_t toklen; /* Length */ static char *tokptext; /* Processed text */ static size_t tokplen; /* Length */ static int isconst (int c); static int digit_char_p (int c); static int isconst (int c) { return isalnum(c) || c=='_' || c=='.' || c=='$'; } static int opeql (const char *op, const char *s, size_t n) { return (n == strlen(op)) && !strncasecmp(s,op,n); } static int hasprefix (const char *prefix, const char *s) { for (;;) { if (*prefix == 0) return 1; if (*s == 0) return 0; if (toupper((unsigned char)*prefix) != toupper((unsigned char)*s)) return 0; prefix++; s++; } } static void nexttok (void) { const char *s = rptr; const char *t, *ee; int res = -1; tokval = -1; tokplen = 0; while (*s && isspace((unsigned char)*s)) s++; toktext = t = s; if (*s == 0) { res = EOF; } else if (isconst((unsigned char)*s)) { for (; *s && isconst((unsigned char)*s); s++); if (isdigit((unsigned char)*t)) { const char *e = s, *u; int base; uint16_t val; ee=e; e--; if (*e == 'H' || *e == 'h') base=16; else if (*e == 'Q' || *e == 'q') base=8; else if (*e == 'O' || *e == 'o') base=8; else if (*e == 'B' || *e == 'b') base=2; else if (*e == 'D' || *e == 'd') base=10; else base=10, e++; val = 0; for(u=t;u= physlen) { physlen = physlen + physlen/2; tokptext = realloc (tokptext, physlen); } tokptext[tokplen++] = *s; } pr_error(s,0,"Missing <%c>", d); fine: res = 's'; } else if (*s == ';') { res = EOF; } else { pr_error (s, 0, "Unrecognized character"); } toklen = s - toktext; rptr = s; tok = res; return; overflow: pr_error (t,ee-t, "Overflow in constant"); } int digit_char_p (int c) { if ((c >= '0') && (c <= '9')) return c - '0'; if ((c >= 'A') && (c <= 'Z')) return c - 'A' + 10; if ((c >= 'a') && (c <= 'z')) return c - 'a' + 10; return 99; } /* -- Expressions ---------------------------------------------------------- */ static uint16_t pr_term (int); static uint16_t pr_prim (void); static uint16_t pr_expr (void) { uint16_t val = pr_term(100); return val; } static uint16_t pr_term (int prec) { uint16_t val; if (prec < 120 && tok == LOW) { nexttok(); val = pr_term(120) & 255; } else if (prec < 120 && tok == HIGH) { nexttok(); val = pr_term(120) >> 8; } else if (prec < 160 && tok == NOT) { nexttok(); val = ~pr_term(160); } else if (prec < 200 && tok == '-') { nexttok(); val = -pr_prim(); } else if (prec < 200 && tok == '+') { nexttok(); val = pr_prim(); } else val = pr_prim(); for(;;) { int prec_; #define X pr_term(prec_) #define _(p,o,r,c) if (prec < (prec_=p) && tok == o) { nexttok(); val = r; c; } _(140, OR, val | X, continue); _(140, XOR, val ^ X, continue); _(150, AND, val & X, continue); _(170, LT, (val < X)*-1,); _(170, GT, (val > X)*-1,); _(170, LE, (val <= X)*-1,); _(170, GE, (val >= X)*-1,); _(170, EQ, (val == X)*-1,); _(170, NE, (val != X)*-1,); _(180, '+', val + X, continue); _(180, '-', val - X, continue); _(190, '*', val * X, continue); _(190, '/', val / X, continue); _(190, MOD, val % X, continue); _(190, SHL, val << X, ); _(190, SHR, val >> X, ); #undef _ #undef X return val; } } static uint16_t pr_prim (void) { uint16_t val = 0; if (tok == 'i') { val = 4711; /* <-- punt */ nexttok(); } else if (tok == 'c') { val = tokval; nexttok(); } else if (tok == '(') { const char *start = toktext; nexttok(); val = pr_expr(); if (tok != ')') pr_error (start, toktext - start, "')' missing"); nexttok(); } else if (tok == 's') { if (tokplen == 1) val = (unsigned char)tokptext[0]; else if (tokplen == 2) val = (((unsigned char)tokptext[0]) << 8) | (unsigned char)tokptext[1]; else { pr_error (toktext, toklen, "String literal as a numeric value must be of" " length one or two"); } nexttok(); } else { pr_error (toktext, 0, "expression expected"); } return val; } /* ------------------------------------------------------------------------- */ void pr_error (const char *p, size_t n, char *fmt, ...) { va_list ap; int i; va_start (ap,fmt); #if 1 fprintf (stderr, "%s\n", whole); for (i = 0; i < (p-whole); i++) fputc(' ',stderr); if (n<=0) n=1; for (i = 0; i < n; i++) fputc('^',stderr); fprintf(stderr,"\n"); #else { const char *s; int on=0; if (n<=0) n=1; for(i=0,s=whole;*s;i++,s++) { if (s == p) fprintf(stderr,"\033[4;1m"),on=1; if (s == p+n) fprintf(stderr,"\033[0m"),on=0; fputc(*s,stderr); } if (on) fprintf(stderr,"\033[0m"); fprintf(stderr,"\n"); for (i = 0; i < (p-whole)+(n/2); i++) fputc(' ',stderr); fprintf(stderr,"↑\n"); for (i = 0; i < (p-whole); i++) fputc(' ',stderr); } #endif vfprintf (stderr, fmt, ap); fprintf (stderr, "\n"); exit(1); } /* ------------------------------------------------------------------------- */ int main (int argc, char **argv) { if (argc != 2) { fprintf (stderr, "Usage: %s \n", argv[0]); return 1; } whole = rptr = argv[1]; nexttok(); printf ("%d\n", pr_expr()); if (tok!=EOF) pr_error(rptr,0,"Trailing garbage"); return 0; }