%{ /* grammar.y: C-- grammar. */ #define _POSIX_SOURCE #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include "grammar.h" #include "token.h" #include "codegen.h" #include "object.h" #include "memory.h" #include "util.h" #include "data.h" int yyparse(void); static void yyerror(char *s); static Prog *prog; static List *errors; extern Pile *compiler_pile; /* We free this pile after compilation. */ %} %union { long num; char *s; struct arguments *args; struct stmt *stmt; struct expr *expr; struct id_list *id_list; struct stmt_list *stmt_list; struct expr_list *expr_list; struct case_list *case_list; struct case_entry *case_entry; }; %type <num> ovdecl %type <args> argdecl %type <stmt> stmt if %type <stmt_list> compound stmts stmtlist %type <case_entry> case_ent %type <case_list> caselist cases %type <expr> expr sexpr rexpr %type <expr_list> args arglist cvals %type <s> for %type <id_list> vars idlist errors errlist /* The following tokens are terminals for the parser. */ %token <num> INTEGER %token <s> COMMENT STRING DBREF SYMBOL IDENT ERROR %token DISALLOW_OVERRIDES ARG VAR %token IF FOR IN UPTO WHILE SWITCH CASE DEFAULT %token BREAK CONTINUE RETURN %token CATCH ANY HANDLER %token FORK %token PASS CRITLEFT CRITRIGHT PROPLEFT PROPRIGHT %left TO %right '?' '|' %right OR %right AND %left IN %left EQ NE '>' GE '<' LE %left '+' '-' %left '*' '/' '%' %left '!' %left '[' ']' START_DICT %left '.' /* Declarations to shut up shift/reduce conflicts. */ %nonassoc LOWER_THAN_ELSE %nonassoc ELSE %nonassoc LOWER_THAN_WITH %nonassoc WITH /* The parser does not use the following tokens. I define them here so * that I can use them, along with the above tokens, as statement and * expression types for the code generator, and as opcodes for the * interpreter. */ %token NOOP EXPR COMPOUND ASSIGN IF_ELSE FOR_RANGE FOR_LIST END RETURN_EXPR %token CASE_VALUE CASE_RANGE LAST_CASE_VALUE LAST_CASE_RANGE END_CASE RANGE %token FUNCTION_CALL MESSAGE EXPR_MESSAGE LIST DICT FROB INDEX UNARY BINARY %token CONDITIONAL SPLICE NEG SPLICE_ADD POP START_ARGS ZERO ONE SET_LOCAL %token SET_OBJ_VAR GET_LOCAL GET_OBJ_VAR CATCH_END HANDLER_END CRITICAL %token CRITICAL_END PROPAGATE PROPAGATE_END JUMP %token TYPE CLASS TOINT TOSTR TOLITERAL TODBREF TOSYM TOERR VALID %token STRLEN SUBSTR EXPLODE STRSUB PAD MATCH_BEGIN MATCH_TEMPLATE %token MATCH_PATTERN MATCH_REGEXP CRYPT UPPERCASE LOWERCASE STRCMP %token LISTLEN SUBLIST INSERT REPLACE DELETE SETADD SETREMOVE UNION %token DICT_KEYS DICT_ADD DICT_DEL DICT_CONTAINS %token VERSION RANDOM TIME CTIME MIN MAX ABS %token THIS DEFINER SENDER CALLER TASK_ID %token ERROR_FUNC TRACEBACK ERROR_ARG THROW RETHROW %token ECHO_FUNC ECHO_FILE DISCONNECT %token ADD_PARAMETER PARAMETERS DEL_PARAMETER SET_VAR GET_VAR RMVAR COMPILE %token METHODS FIND_METHOD FIND_NEXT_METHOD LIST_METHOD DEL_METHOD PARENTS %token CHILDREN ANCESTORS HAS_ANCESTOR SIZE %token CREATE CHPARENTS DESTROY LOG CONN_ASSIGN BINARY_DUMP TEXT_DUMP %token RUN_SCRIPT SHUTDOWN BIND UNBIND CONNECT SET_HEARTBEAT_FREQ DATA /* Reserved for future use. */ %token FORK ATOMIC NON_ATOMIC /* LAST_TOKEN tells opcodes.c how much space to allocate for the opcodes * table. */ %token LAST_TOKEN %% method : ovdecl argdecl vars stmtlist { prog = make_prog($1, $2, $3, $4); } ; ovdecl : /* nothing */ { $$ = 1; } | DISALLOW_OVERRIDES ';' { $$ = 0; } ; argdecl : /* nothing */ { $$ = arguments(NULL, NULL); } | ARG '[' IDENT ']' ';' { $$ = arguments(NULL, $3); } | ARG idlist ';' { $$ = arguments($2, NULL); } | ARG idlist ',' '[' IDENT ']' ';' { $$ = arguments($2, $5); } ; vars : /* nothing */ { $$ = NULL; } | VAR idlist ';' { $$ = $2; } ; idlist : IDENT { $$ = id_list($1, NULL); } | idlist ',' IDENT { $$ = id_list($3, $1); } ; errors : ANY { $$ = NULL; } | errlist { $$ = $1; } ; errlist : ERROR { $$ = id_list($1, NULL); } | errlist ',' ERROR { $$ = id_list($3, $1); } ; compound: '{' stmtlist '}' { $$ = $2; } ; stmtlist: /* nothing */ { $$ = NULL; } | stmts { $$ = $1; } ; stmts : stmt { $$ = stmt_list($1, NULL); } | stmts stmt { $$ = stmt_list($2, $1); } ; stmt : COMMENT { $$ = comment_stmt($1); } | ';' { $$ = noop_stmt(); } | expr ';' { $$ = expr_stmt($1); } | compound { $$ = compound_stmt($1); } | IDENT '=' expr ';' { $$ = assign_stmt($1, $3); } | if %prec LOWER_THAN_ELSE { $$ = $1; } | if ELSE stmt { $$ = if_else_stmt($1, $3); } | for '[' expr UPTO expr ']' stmt { $$ = for_range_stmt($1, $3, $5,$7); } | for '(' expr ')' stmt { $$ = for_list_stmt($1, $3, $5); } | WHILE '(' expr ')' stmt { $$ = while_stmt($3, $5); } | SWITCH '(' expr ')' caselist { $$ = switch_stmt($3, $5); } | BREAK ';' { $$ = break_stmt(); } | CONTINUE ';' { $$ = continue_stmt(); } | RETURN ';' { $$ = return_stmt(); } | RETURN expr ';' { $$ = return_expr_stmt($2); } | CATCH errors stmt %prec LOWER_THAN_WITH { $$ = catch_stmt($2, $3, NULL); } | CATCH errors stmt WITH HANDLER stmt { $$ = catch_stmt($2, $3, $6); } | error ';' { yyerrok; $$ = NULL; } ; if : IF '(' expr ')' stmt { $$ = if_stmt($3, $5); } ; for : FOR IDENT IN { $$ = $2; } ; caselist: '{' '}' { $$ = NULL; } | '{' cases '}' { $$ = $2; } ; cases : case_ent { $$ = case_list($1, NULL); } | cases case_ent { $$ = case_list($2, $1); } ; case_ent: CASE cvals ':' stmtlist { $$ = case_entry($2, $4); } | DEFAULT ':' stmtlist { $$ = case_entry(NULL, $3); } ; expr : INTEGER { $$ = integer_expr($1); } | STRING { $$ = string_expr($1); } | DBREF { $$ = dbref_expr($1); } | SYMBOL { $$ = symbol_expr($1); } | ERROR { $$ = error_expr($1); } | IDENT { $$ = var_expr($1); } | IDENT '(' args ')' { $$ = function_call_expr($1, $3); } | PASS '(' args ')' { $$ = pass_expr($3); } | expr '.' IDENT '(' args ')' { $$ = message_expr($1, $3, $5); } | '.' IDENT '(' args ')' { $$ = message_expr(NULL, $2, $4); } | expr '.' '(' expr ')' '(' args ')' { $$ = expr_message_expr($1, $4, $7); } | '.' '(' expr ')' '(' args ')' { $$ = expr_message_expr(NULL, $3, $6); } | '[' args ']' { $$ = list_expr($2); } | START_DICT args ']' { $$ = dict_expr($2); } | '<' expr ',' expr '>' { $$ = frob_expr($2, $4); } | expr '[' expr ']' { $$ = index_expr($1, $3); } | '!' expr { $$ = unary_expr('!', $2); } | '-' expr %prec '!' { $$ = unary_expr(NEG, $2); } | '+' expr %prec '!' { $$ = $2; } | expr '*' expr { $$ = binary_expr('*', $1, $3); } | expr '/' expr { $$ = binary_expr('/', $1, $3); } | expr '%' expr { $$ = binary_expr('%', $1, $3); } | expr '+' expr { $$ = binary_expr('+', $1, $3); } | expr '-' expr { $$ = binary_expr('-', $1, $3); } | expr EQ expr { $$ = binary_expr(EQ, $1, $3); } | expr NE expr { $$ = binary_expr(NE, $1, $3); } | expr '>' expr { $$ = binary_expr('>', $1, $3); } | expr GE expr { $$ = binary_expr(GE, $1, $3); } | expr '<' expr { $$ = binary_expr('<', $1, $3); } | expr LE expr { $$ = binary_expr(LE, $1, $3); } | expr IN expr { $$ = binary_expr(IN, $1, $3); } | expr AND expr { $$ = and_expr($1, $3); } | expr OR expr { $$ = or_expr($1, $3); } | expr '?' expr '|' expr { $$ = cond_expr($1, $3, $5); } | '(' expr ')' { $$ = $2; } | CRITLEFT expr CRITRIGHT { $$ = critical_expr($2); } | PROPLEFT expr PROPRIGHT { $$ = propagate_expr($2); } ; sexpr : expr { $$ = $1; } | '@' expr { $$ = splice_expr($2); } ; args : /* nothing */ { $$ = NULL; } | arglist { $$ = $1; } ; arglist : sexpr { $$ = expr_list($1, NULL); } | arglist ',' sexpr { $$ = expr_list($3, $1); } ; rexpr : expr { $$ = $1; } | expr UPTO expr { $$ = range_expr($1, $3); } ; cvals : rexpr { $$ = expr_list($1, NULL); } | cvals ',' rexpr { $$ = expr_list($3, $1); } ; %% Method *compile(Object *object, Data *code, int lines, List **error_ret) { Method *method = NULL; /* Initialize compiler globals. */ errors = list_new(0); lex_start(code, lines); /* Parse text. This sets prog if successful. */ yyparse(); if (!errors->len) { /* No errors in parsing. Compile to linear code. method will be * NULL if unsuccessful. */ method = generate_method(prog, object); } /* Free up all temporary storage we allocated during compilation. */ pfree(compiler_pile); /* error_ret gets reference count on errors. */ *error_ret = errors; return method; } void compiler_error(int lineno, char *fmt, ...) { va_list arg; String *errstr, *line; Data d; va_start(arg, fmt); errstr = vformat(fmt, arg); if (lineno == -1) { line = errstr; } else { line = format("Line %d: %s", lineno, errstr->s); string_discard(errstr); } d.type = STRING; substr_set_to_full_string(&d.u.substr, line); errors = list_add(errors, &d); string_discard(line); va_end(arg); } int no_errors(void) { return (errors->len == 0); } static void yyerror(char *s) { compiler_error(cur_lineno(), s); }