%{ #define _XOPEN_SOURCE 700 #include #include #include #include #include #include typedef struct { int type; union { intmax_t i; char *s; } u; } expr_yystype; static char **expr_args; #define YYSTYPE expr_yystype static int yylex(void); static int yyerror(const char *s); static void expr_only_integers(YYSTYPE *y1, YYSTYPE *y2); static int expr_compare(YYSTYPE *y1, YYSTYPE *y2); %} %token INTEGER %token STRING %token '(' ')' %left ':' %left '*' '/' '%' %left '+' '-' %left '=' '<' '>' LE GE NE %left '&' %left '|' %start expr %% expr : STRING { printf("%s\n", $1.u.s); } | INTEGER { printf("%jd\n", $1.u.i); } | '(' expr ')' { $$ = $2; return $$.type; } | expr ':' expr { printf("match\n"); /* TODO: number of initial characters in $1.u.s matching re $2.u.s */ } | expr '*' expr { expr_only_integers(&$1, &$3); $$.u.i = $1.u.i * $3.u.i; printf("%jd\n", $$.u.i); return $$.type; } | expr '/' expr { expr_only_integers(&$1, &$3); $$.u.i = $1.u.i / $3.u.i; printf("%jd\n", $$.u.i); return $$.type; } | expr '%' expr { expr_only_integers(&$1, &$3); $$.u.i = $1.u.i % $3.u.i; printf("%jd\n", $$.u.i); return $$.type; } | expr '+' expr { expr_only_integers(&$1, &$3); $$.u.i = $1.u.i + $3.u.i; printf("%jd\n", $$.u.i); return $$.type; } | expr '-' expr { expr_only_integers(&$1, &$3); $$.u.i = $1.u.i - $3.u.i; printf("%jd\n", $$.u.i); return $$.type; } | expr '=' expr { $$.type = INTEGER; $$.u.i = expr_compare(&$1, &$3) == 0; printf("%jd\n", $$.u.i); return $$.type; } | expr NE expr { $$.type = INTEGER; $$.u.i = expr_compare(&$1, &$3) != 0; printf("%jd\n", $$.u.i); return $$.type; } | expr '<' expr { $$.type = INTEGER; $$.u.i = expr_compare(&$1, &$3) < 0; printf("%jd\n", $$.u.i); return $$.type; } | expr LE expr { $$.type = INTEGER; $$.u.i = expr_compare(&$1, &$3) <= 0; printf("%jd\n", $$.u.i); return $$.type; } | expr '>' expr { $$.type = INTEGER; $$.u.i = expr_compare(&$1, &$3) > 0; printf("%jd\n", $$.u.i); return $$.type; } | expr GE expr { $$.type = INTEGER; $$.u.i = expr_compare(&$1, &$3) >= 0; printf("%jd\n", $$.u.i); return $$.type; } | expr '&' expr { printf("and\n"); } | expr '|' expr { printf("or\n"); } ; %% static void expr_only_integers(YYSTYPE *y1, YYSTYPE *y2) { if (y1->type == STRING) { fprintf(stderr, "expr: %s is not an integer\n", y1->u.s); exit(1); } if (y2->type == STRING) { fprintf(stderr, "expr: %s is not an integer\n", y2->u.s); exit(1); } } static int expr_compare(YYSTYPE *y1, YYSTYPE *y2) { if (y1->type == INTEGER && y2->type == INTEGER) { return y1->u.i - y2->u.i; } char buf[64]; char *s1 = y1->u.s; char *s2 = y2->u.s; if (y1->type == INTEGER) { snprintf(buf, sizeof(buf), "%jd", y1->u.i); s1 = buf; } else if (y2->type == INTEGER) { snprintf(buf, sizeof(buf), "%jd", y2->u.i); s2 = buf; } return strcmp(s1, s2); } static int yylex(void) { if (expr_args[optind] == NULL) { return YYEOF; } char *s = expr_args[optind]; if (strlen(s) == 1) { char c = s[0]; if (strchr("|&=+-*/%():", c)) { optind++; return c; } } if (strlen(s) == 2) { if (!strcmp(s, ">=")) { optind++; return GE; } if (!strcmp(s, "<=")) { optind++; return LE; } if (!strcmp(s, "!=")) { optind++; return NE; } } char *end = NULL; yylval.type = INTEGER; yylval.u.i = strtoimax(s, &end, 10); if (end && *end != '\0') { yylval.type = STRING; yylval.u.s = s; } optind++; return yylval.type; } static int yyerror(const char *s) { fprintf(stderr, "expr: %s\n", s); } int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); int c; while ((c = getopt(argc, argv, "")) != -1) { switch (c) { default: return -1; } } if (optind >= argc) { fprintf(stderr, "expr: missing operands\n"); return 1; } expr_args = argv; return yyparse(); }