/* parser.c - recursive-descent parser for nawm configuration language */ /* Copyright (C) 1999 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. */ static int skip(int token) { int got = next_token(); if (got != token) die("Got %s while expecting %s.", tokenname(got), tokenname(token)); return got; } static void parse_nawmrc(void) { int tok = next_token(); switch (tok) { case INCLUDE: skip(STR); load_module(yytext); skip(';'); break; case OPTION: skip(STR); do_option(yytext); skip(';'); break; case MODE: parse_mode(); break; case DTYPE: parse_vardecl(tok); break; case COMMAND: case FUNCTION: parse_fundecl(); break; default: b = parse_binding(tok); if (b) add_to_anymode(b); else die("Unexpected token '%s' at top level of nawmrc.", tokenname(tok)); break; } } binding *parse_binding(int tok) { switch (tok) { case KEYPRESS: case KEYRELEASE: return parse_key_binding(tok); case BUTTONPRESS: case BUTTONRELEASE: return parse_button_binding(tok); case MOTION: return parse_motion_binding(); case ENTER: case LEAVE: return parse_crossing_binding(tok); case BEGIN: case END: return parse_beginend_binding(tok); default: return NULL; } } static void parse_mode(void) { char *name; bindings *b = NULL, *btmp; tok = skip(STR); name = strdup(yytext); pushlexscope(0); skip('{'); while (1) { tok = next_token(); if (tok == '}') break; switch (tok) { case DTYPE: parse_vardecl(tok); break; default: btmp = parse_binding(tok); if (!btmp) die("Unexpected token '%s' in mode body.", tokenname(tok)); btmp->next = b; b = btmp; break; } } defmode(name, b); } static node *parse_command(void) { int tok = peek_token(); switch (tok) { case IF: return parse_if(); case FOR: return parse_for(); case WHILE: return parse_while(); case DO: return parse_do(); case BREAK: case CONTINUE: return parse_loop_control(tok); case RETURN: return parse_return(); case DEL: return parse_del(); case '{': return parse_commandlist(); default: return parse_expr(); } } static node *parse_if(void) { node *expr, *then; skip(IF); skip('('); expr = typecheck(T_BOOL, parse_expr(), "if condition"); skip(')'); th = parse_command(); if (peek_token() == ELSE) return mknode(IF, 0, 3, expr, then, parse_command()); else return mknode(IF, 0, 2, expr, then); } static node *parse_for(void) { node *e1, *e2, *e3, *ans; skip(FOR); skip('('); e1 = parse_expr(); if (peek_token() == ')' && e1->type == IN && is_lvalue(e1->vals[0])) { skip(')'); loop_nesting++; ans = mknode(FOR, 0, 2, e1, parse_command()); } else { skip(';'); e2 = typecheck(T_BOOL, parse_expr(), "for condition"); skip(';'); e3 = parse_expr(); skip(')'); loop_nesting++; ans = mknode(FOR, 0, 4, e1, e2, e3, parse_command()); } loop_nesting--; return ans; } static node *parse_while(void) { node *expr, *ans; skip(WHILE); skip('('); expr = typecheck(T_BOOL, parse_expr(), "while condition"); skip(')'); loop_nesting++; ans = mknode(WHILE, 0, 2, expr, parse_command()); loop_nesting--; return ans; } static node *parse_do(void) { node *body, *expr; skip(DO); loop_nesting++; body = parse_command(); loop_nesting--; skip(WHILE); skip('('); expr = typecheck(T_BOOL, parse_expr(), "do condition"); skip(')'); skip(';'); return mknode(DO, 0, 2, expr, body); } /* break/continue */ static node *parse_loop_control(int token) { node *ans; if (!loop_nesting) die("Found %s outside of loop.", tokenname(token)); ans = mknode(tok, 0, 0); skip(';'); return ans; } static node *parse_return(void) { node *expr; if (!in_function) die("Found 'return' outside of function body."); if (return_type == T_VOID) { skip(';'); return mknode(RETURN, 0, 0); } else { expr = typecheck(return_type, parse_expr(), "return value"); skip(';'); return mknode(RETURN, 0, 1, expr); } } static node *parse_expr(void) { node *left = parse_bool_expr(), *right; int tok; if (IS_ASSIGNOP(peek_token()) && is_lvalue(left)) { tok = next_token(); right = parse_expr(); return mknode(tok, left->etype, 2, left, typecheck(left->etype, right, "assignment")); } else return left; } #define GENPARSE(parsetype, lefttype, optype) \ static node *parse_##parsetype(void)\ {\ node *left = parse_##lefttype(), *right;\ int tok;\ \ if (IS_##optype(peek_token()))\ {\ tok = next_token();\ right = parse_##parsetype();\ return op_typecheck(tok, left, right);\ }\ else\ return left;\ } GENPARSE(bool_expr, comp_expr, BOOLOP) GENPARSE(comp_expr, memb_expr, COMPOP) GENPARSE(memb_expr, add_expr, INOP) GENPARSE(add_expr, mult_expr, ADDOP) GENPARSE(mult_expr, unary_expr, MULTOP) static node *parse_unary_expr(void) { node *expr; int tok = peek_token(); if (IS_ADDOP(tok) || IS_UNOP(tok)) { next_token(); return op_typecheck(tok, parse_unary_expr(), NULL); } else return parse_subscript_expr(); } static node *parse_subscript_expr(void) { node *expr = parse_primary_expr(), *subexpr; int tok = peek_token(); switch (tok) { case '[': skip('['); subexpr = parse_expr(); skip(']'); return mknode(SUBSCRIPT, XXX, 2, expr, subexpr); case '.': skip('.'); tok = skip(SYM); return mknode(ELEMENT, XXX, 2, expr, XXX_ytext); case '(': skip('('); subexpr = parse_args(); skip(')'); return mknode(APPLY, XXX, 2, expr, subexpr); default: return expr; } } static node *parse_primary_expr(void) { int tok = peek_token(); node *ans; switch (tok) { case NUM: return mknode(NUM, T_INT, 1, XXX_yytext); case STR: return mknode(STR, T_STR, 1, XXX_yytext); case SYM: return lookup(XXX); case '(': skip('('); ans = parse_expr(); skip(')'); return ans; default: error(XXX); } }