/* * parse.y - parser * $Id: parse.y,v 1.5 2002/11/19 08:03:38 candy Exp candy $ */ %{ #include #include #include #include #include #include #undef YYDEBUG 1 extern int yyparse(void); extern int yylex(void); extern int yyerror(const char *s); union u_t { double a_val; int a_inst; }; struct atom { struct atom *a_next; char *a_name; int a_type; union u_t u; }; enum code_t { c_ne, c_le, c_lt, c_ge, c_gt, }; static struct atom * atom_find(char *name); static struct atom * atom_install(const char *name, int type, double val); static void atom_init(void); static int linecount = 1; static int label[65536]; %} %union { double d; struct atom *sym; int inst; enum code_t op; } %token STRING_CONSTANT %token FLOATING_CONSTANT %token INTEGER_CONSTANT %token SYM_UNDEF SYM_CMD SYM_FUNC SYM_VAR SYM_SFUNC SYM_SVAR %token k_end k_rem k_dim k_def k_data k_read %token k_goto k_gosub k_return k_on %token k_if k_then k_for k_to k_next k_step %token k_print k_input %type line linenum stmts stmt array_decl expr sexpr nxpr asgn_e %left ',' %left '=' %left CMPOP %left '+' '-' OROP %left '*' '/' ANDOP %right UNARY %left '^' %% prog: /* nothing */ | prog line | prog error '\n' { yyerrok; } ; line: FLOATING_CONSTANT stmts '\n' { int x = $1; if (label[x] < 0) label[x] = -label[x]; else label[x] = 65536; linecount++; } | stmts '\n' ; linenum: FLOATING_CONSTANT { int x = $1; if (label[x] == 0) label[x] = -linecount; else if (label[x] == 65536) label[x] = linecount; } ; linenums: linenum | linenums ',' linenum ; stmts: stmt | stmts ':' stmt ; stmt: asgn_e | k_end | k_rem | k_dim array_decl | k_data args | k_read read_args | k_goto linenum | k_gosub linenum | k_return | k_if expr k_then linenum | k_if expr then_opt stmts /* shift/reduce */ | k_for asgn_e k_to expr step_opt | k_next | k_on expr k_goto linenums | k_on expr k_gosub linenums | k_print | k_print print_args | k_print print_args ';' | k_input prompt_opt SYM_VAR; { $3->a_type = SYM_VAR; } | k_input prompt_opt SYM_SVAR; /* shift/reduce */ { $3->a_type = SYM_SVAR; } | k_def SYM_VAR SYM_VAR '(' SYM_VAR ')' '=' expr | SYM_CMD args ; then_opt: /* empty */ | k_then ; step_opt: /* empty */ | k_step expr ; prompt_opt: /* empty */ | sexpr ';' ; array_decl: SYM_VAR '[' array_index ']' { $1->a_type = SYM_VAR; } | SYM_SVAR '[' array_index ']' { $1->a_type = SYM_SVAR; } | array_decl ',' array_decl ; read_args: SYM_VAR | SYM_SVAR | SYM_VAR '[' array_index ']' | SYM_SVAR '[' array_index ']' | read_args ',' read_args ; asgn_e: SYM_VAR '=' expr { $1->a_type = SYM_VAR; } | SYM_VAR '[' array_index ']' '=' expr | SYM_SVAR '=' sexpr { $1->a_type = SYM_SVAR; } | SYM_SVAR '[' array_index ']' '=' sexpr ; array_index: expr | expr ',' expr ; print_args: nxpr | print_args ';' nxpr ; args: nxpr | args ',' nxpr ; nxpr: expr | sexpr ; sexpr: STRING_CONSTANT | SYM_SVAR { $1->u.a_val += 1; } | SYM_SVAR '[' array_index ']' { $1->u.a_val += 1; } | SYM_SFUNC '(' args ')' | sexpr '+' sexpr ; expr: FLOATING_CONSTANT | SYM_VAR { $1->u.a_val += 1; } | SYM_VAR '[' array_index ']' { $1->u.a_val += 1; } | SYM_FUNC '(' args ')' | '(' expr ')' { $$ = $2; } | '-' expr { $$ = $2; } | '+' expr { $$ = $2; } | expr '^' expr | expr '*' expr | expr '/' expr | expr ANDOP expr | expr '+' expr | expr '-' expr | expr OROP expr | expr '=' expr | sexpr '=' sexpr | expr CMPOP expr ; %% static struct { /* Keywords */ const char *name; int kval; } keywords[] = { {"and", ANDOP}, {"or", OROP}, {"data", k_data}, {"def", k_def}, {"dim", k_dim}, {"end", k_end}, {"for", k_for}, {"gosub", k_gosub}, {"goto", k_goto}, {"if", k_if}, {"next", k_next}, {"on", k_on}, {"print", k_print}, {"input", k_input}, {"read", k_read}, {"rem", k_rem}, {"return", k_return}, {"step", k_step}, {"then", k_then}, {"to", k_to}, /* function */ {"abs", SYM_FUNC}, {"asc", SYM_FUNC}, {"atn", SYM_FUNC}, {"chr$", SYM_SFUNC}, {"cos", SYM_FUNC}, {"fnr", SYM_FUNC}, {"int", SYM_FUNC}, {"left$", SYM_SFUNC}, {"len", SYM_FUNC}, {"mid$", SYM_SFUNC}, {"peek", SYM_FUNC}, {"right$", SYM_SFUNC}, {"rnd", SYM_FUNC}, {"sgn", SYM_FUNC}, {"sin", SYM_FUNC}, {"spc", SYM_SFUNC}, {"sqr", SYM_FUNC}, {"str$", SYM_SFUNC}, {"tab", SYM_FUNC}, {"val", SYM_FUNC}, /* command */ {"cursor", SYM_CMD}, {"limit", SYM_CMD}, {"music", SYM_CMD}, {"poke", SYM_CMD}, {"tempo", SYM_CMD}, {"usr", SYM_CMD}, {NULL, 0}, }; static unsigned char *input_string; static unsigned char *input_string0; int yyerror(const char *s) { unsigned char *p = input_string; unsigned char *p0; printf("%d: %s\n", linecount, s); if (p - 1 > input_string0) p--; while (p > input_string0 && *p != '\n') p--; if (*p == '\n') p++; p0 = p; while (*p != '\0' && *p != '\n' && p < input_string) { putchar(*p); p++; } printf("<*>"); while (*p != '\0' && *p != '\n') { putchar(*p); p++; } printf("\n"); return 0; } void yysetstr(const char *s) { input_string = (unsigned char *)s; input_string0 = input_string; } int yylex(void) { int ch; if (input_string == NULL) return 0; while (*input_string == ' ' || *input_string == '\t') input_string++; ch = *input_string++; if (isdigit(ch) || ch == '.') { double d = strtod(input_string - 1, (char **)&input_string); yylval.d = d; return FLOATING_CONSTANT; } else if (ch == '$') { while (isxdigit(*input_string)) input_string++; return FLOATING_CONSTANT; } else if (ch == '"') { while (*input_string != '\0' && *input_string != '\n' && *input_string != '"') { input_string++; } if (*input_string == '"') input_string++; return STRING_CONSTANT; } else if (isalpha(ch)) { char name[256], *d = name; struct atom *s; int isstr = 0; *d++ = ch; while ((ch = *input_string++) != '\0' && isalnum(ch)) { if (d - name < sizeof(name) - 1) *d++ = ch; } if (ch == '$') { if (d - name < sizeof(name) - 1) *d++ = ch; isstr = 1; } else input_string--; *d = '\0'; s = atom_find(name); if (s == NULL) { s = atom_install(name, SYM_UNDEF, 0.0); if (s == NULL) { yyerror("memory exhausted"); } } yylval.sym = s; if (s->a_type == k_rem) { while (*input_string != '\0' && *input_string != '\n' && *input_string != ':') input_string++; } return s->a_type == SYM_UNDEF ? (isstr ? SYM_SVAR : SYM_VAR) : s->a_type; } else if (ch == '<' && *input_string == '>') { input_string++; yylval.op = c_ne; return CMPOP; } else if (ch == '>' || ch == '<') { if (*input_string == '=') { input_string++; yylval.op = (ch == '>' ? c_ge : c_le); } else { yylval.op = (ch == '>' ? c_gt : c_lt); } return CMPOP; } else return ch; } static struct atom root; static struct atom * atom_find(char *name) { struct atom *mv; int cp = -1; { static int init_done; if (!init_done) { init_done = 1; atom_init(); } } mv = root.a_next; while (mv != NULL && (cp = strcmp(mv->a_name, name)) < 0) { mv = mv->a_next; }/* while */ if (cp != 0) mv = NULL; return mv; }/* atom_find */ static struct atom * atom_install(const char *name, int type, double val) { struct atom *newAtom = (struct atom *)malloc(sizeof(*newAtom)); if (newAtom != NULL) { newAtom->a_name = (char *)malloc(strlen(name) + 1); if (newAtom->a_name == NULL) { free(newAtom); newAtom = NULL; } else { strcpy(newAtom->a_name, name); newAtom->a_type = type; newAtom->u.a_val = val; } } if (newAtom != NULL) { struct atom *prev = &root, *mv = root.a_next; int cp = -1; while (mv != NULL && (cp = strcmp(mv->a_name, name)) < 0) { prev = mv; mv = mv->a_next; }/* while */ newAtom->a_next = mv; prev->a_next = newAtom; } return newAtom; }/* atom_install */ static void atom_init(void) { int i; struct atom *s; for (i = 0; keywords[i].name; i++) s = atom_install(keywords[i].name, keywords[i].kval, 0.0); }/* atom_init */ static void atom_dump(int t) { struct atom *mv = root.a_next; while (mv != NULL) { if (mv->a_type == t) printf("%s: %.0f\n", mv->a_name, mv->u.a_val); mv = mv->a_next; }/* while */ }/* atom_dump */ static char * readfile(FILE *fp) { int err = 0; char *buf = NULL; int len = 0; char lbuf[256]; while (err == 0 && fgets(lbuf, sizeof(lbuf), fp) != NULL) { int llen = strlen(lbuf); char *p = realloc(buf, len + llen + 1); if (p == NULL) { perror("realloc"); if (buf != NULL) free(buf); buf = NULL; err = -1; } else { buf = p; memcpy(buf + len, lbuf, llen + 1); len += llen; } }/* while */ return buf; }/* readfile */ static char * sgets(char *buf, size_t size, const char **sptr) { const char *s = *sptr; if (*s == '\0') { buf = NULL; s = NULL; } else { int i = 0; while (i + 1 < size && s[i] != '\0' && s[i] != '\n') { buf[i] = s[i]; i++; } if (i + 1 < size && s[i] == '\n') { buf[i] = s[i]; i++; } if (i < size) buf[i] = '\0'; s += i; } *sptr = s; return buf; }/* sgets */ static void lprint(const char *s) { char lbuf[256]; while (sgets(lbuf, sizeof(lbuf), &s) != NULL) { char *p; int n = strtol(lbuf, &p, 10); if (label[n] != 0 && label[n] != 65536) printf("%d", n); printf("%s", p); }/* while */ }/* lprint */ static int fnain(FILE *fp) { int err = -1; char *source = readfile(fp); if (source != NULL) { atom_init(); yysetstr(source); err = yyparse(); if (err == 0) { int i; printf(">>> UNDEFINED GOTO/GOSUB\n"); for (i = 0; i < 65536; i++) { if (label[i] < 0) printf("%d: undefined label %d\n", -label[i], i); } printf(">>> UNINITIALIZED VARIABLES\n"); atom_dump(SYM_UNDEF); printf(">>> REFERENCE COUNT\n"); atom_dump(SYM_SVAR); atom_dump(SYM_VAR); printf(">>> STRIPPED LIST\n"); lprint(source); } free(source); } return err; }/* fnain */ static int nain(const char *name) { int err = -1; FILE *fp = fopen(name, "r"); if (fp != NULL) { err = fnain(fp); fclose(fp); } else perror(name); return err; }/* nain */ int main(int argc, char *argv[]) { int err = -1; if (argc == 1) err = fnain(stdin); else err = nain(argv[1]); return 0; }