/* // Full copyright information is available in the file ../doc/CREDITS */ /* // ------------------------------------------------------------ // // C Declarations // */ %{ #define _grammar_y_ #include "defs.h" #include <stdarg.h> #include "cdc_pcode.h" #include "util.h" int yyparse(void); static void yyerror(char *s); static Prog *prog; static cList *errors; extern Pile *compiler_pile; /* We free this pile after compilation. */ %} /* // ------------------------------------------------------------ // // yacc Declarations // */ %union { Long num; Float fnum; 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 handler %type <expr_list> args arglist cvals %type <s> for map find filter hash %type <id_list> vars idlist errors errlist /* The following tokens are terminals for the parser. */ %token FIRST_TOKEN /* data */ %token <num> INTEGER OBJNUM %token <fnum> FLOAT %token <s> STRING SYMBOL OBJNAME T_ERROR %token LIST DICT BUFFER FROB %token DATA_END /* not data */ %token <s> COMMENT IDENT %token DISALLOW_OVERRIDES ARG VAR %token IF FOR OP_IN UPTO WHILE SWITCH CASE DEFAULT %token BREAK CONTINUE RETURN %token CATCH ANY HANDLER %token FORK %token PASS CRITLEFT CRITRIGHT PROPLEFT PROPRIGHT %right OP_ASSIGN %right MINUS_EQ DIV_EQ MULT_EQ PLUS_EQ OPTIONAL_ASSIGN %left TO %right OP_COND_IF ':' OP_COND_OTHER_ELSE %right OR %right AND %left OP_IN %left EQ NE '>' GE '<' LE %left '+' '-' %left '*' '/' '%' %left '!' %left '[' ']' START_DICT START_BUFFER %right P_INCREMENT INCREMENT P_DECREMENT DECREMENT %left '.' /* Declarations to shut up shift/reduce conflicts. */ %nonassoc LOWER_THAN_ELSE %nonassoc ELSE %nonassoc LOWER_THAN_WITH %nonassoc WITH /* Mapping expressions */ %token OP_MAP WHERE OP_FILTER OP_MAPHASH OP_FIND /* 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 DOEQ INDECR %token CASE_VALUE CASE_RANGE LAST_CASE_VALUE LAST_CASE_RANGE END_CASE RANGE %token FUNCTION_CALL CALL_METHOD EXPR_CALL_METHOD LIST DICT BUFFER FROB INDEX UNARY %token BINARY CONDITIONAL SPLICE NEG SPLICE_ADD POP START_ARGS ZERO ONE %token SET_LOCAL SET_OBJ_VAR GET_LOCAL GET_OBJ_VAR CATCH_END HANDLER_END %token CRITICAL CRITICAL_END PROPAGATE PROPAGATE_END JUMP %token OPTIONAL_END SCATTER_START SCATTER_END %token OP_MAP_RANGE OP_MAPHASH_RANGE OP_FILTER_RANGE OP_FIND_RANGE %token FUNCTION_START %token F_DEBUG_CALLERS F_CALL_TRACE %token F_TYPE F_CLASS F_TOINT F_TOFLOAT F_TOSTR F_TOLITERAL F_FROMLITERAL %token F_FROB_CLASS F_TOOBJNUM F_TOSYM F_TOERR F_VALID F_STRFMT F_STRLEN F_STRIDX %token F_SUBSTR F_EXPLODE F_STRSED F_STRSUB F_PAD F_MATCH_BEGIN %token F_MATCH_TEMPLATE F_STRGRAFT F_LISTGRAFT F_BUFGRAFT %token F_MATCH_PATTERN F_MATCH_REGEXP F_REGEXP F_SPLIT F_CRYPT F_UPPERCASE %token F_LOWERCASE F_STRCMP F_LISTLEN F_SUBLIST F_INSERT F_JOIN F_REPLACE %token F_LISTIDX F_DELETE F_SETADD F_SETREMOVE F_UNION F_MATCH_CRYPTED %token F_DICT_KEYS F_DICT_VALUES F_DICT_ADD F_DICT_DEL F_DICT_UNION %token F_DICT_CONTAINS F_BUFLEN F_BUFIDX F_BUF_REPLACE %token F_BUF_TO_STRINGS F_STRINGS_TO_BUF F_STR_TO_BUF F_BUF_TO_STR %token F_SUBBUF F_VERSION F_RANDOM F_TIME F_LOCALTIME F_MTIME %token F_STRFTIME F_CTIME F_MIN F_MAX F_ABS F_LOOKUP F_METHOD_FLAGS %token F_METHOD_ACCESS F_SET_METHOD_FLAGS F_SET_METHOD_ACCESS %token F_METHODOP F_THIS F_DEFINER F_SENDER F_CALLER F_USER F_SET_USER %token F_TASK_ID F_TICKS_LEFT F_ERROR_FUNC F_TRACEBACK F_ERROR_STR %token F_ERROR_ARG F_THROW F_RETHROW F_CWRITE F_CWRITEF F_FSTAT %token F_FREAD F_FCHMOD F_FMKDIR F_FRMDIR F_FILES F_FILE %token F_FREMOVE F_FRENAME F_FOPEN F_FCLOSE F_FSEEK F_FEOF F_FWRITE %token F_FFLUSH F_CLOSE_CONNECTION F_CONNECTION F_ADD_VAR F_VARIABLES %token F_DEL_VAR F_SET_VAR F_GET_VAR F_INHERITED_VAR F_DEFAULT_VAR %token F_CLEAR_VAR F_ADD_METHOD %token F_RENAME_METHOD F_METHODS F_FIND_METHOD F_FIND_NEXT_METHOD %token F_METHOD_BYTECODE F_LIST_METHOD F_DEL_METHOD F_PARENTS %token F_CHILDREN F_ANCESTORS F_HAS_ANCESTOR F_SIZE %token F_CREATE F_CHPARENTS F_DESTROY F_DBLOG F_REASSIGN_CONNECTION %token F_BACKUP F_BINARY_DUMP F_TEXT_DUMP F_EXECUTE F_SHUTDOWN %token F_BIND_PORT F_UNBIND_PORT F_OPEN_CONNECTION F_SET_HEARTBEAT %token F_DATA F_SET_OBJNAME F_DEL_OBJNAME F_OBJNAME F_OBJNUM %token F_NEXT_OBJNUM F_TICK F_HOSTNAME F_IP F_RESUME F_SUSPEND %token F_TASKS F_TASK_INFO F_CANCEL F_PAUSE F_REFRESH F_STACK F_STATUS %token F_BIND_FUNCTION F_UNBIND_FUNCTION F_ATOMIC %token F_METHOD_INFO F_ENCODE F_DECODE F_SIN F_EXP F_LOG F_COS %token F_TAN F_SQRT F_ASIN F_ACOS F_ATAN F_POW F_ATAN2 F_CONFIG F_ROUND %token F_FLUSH OP_HANDLED_FROB F_VALUE F_HANDLER /* Reserved for future use. */ /*%token FORK*/ /* // LAST_TOKEN tells opcodes.c how much space to allocate // for the opcodes table. */ %token LAST_TOKEN /* // ------------------------------------------------------------ // // Grammar rules // */ %% 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); } | ARG '@' IDENT ';' { $$ = arguments(NULL, $3); } | 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 : T_ERROR { $$ = id_list($1, NULL); } | errlist ',' T_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); } | 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); } | CATCH errors stmt WITH stmt { $$ = catch_stmt($2, $3, $5); } | error ';' { yyerrok; $$ = NULL; } ; if : IF '(' expr ')' stmt { $$ = if_stmt($3, $5); } ; for : FOR IDENT OP_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); } ; handler : '>' { $$ = NULL; } | ',' expr '>' { $$ = $2; } ; expr : INTEGER { $$ = integer_expr($1); } | FLOAT { $$ = float_expr($1); } | STRING { $$ = string_expr($1); } | OBJNUM { $$ = objnum_expr($1); } | OBJNAME { $$ = objname_expr($1); } | SYMBOL { $$ = symbol_expr($1); } | T_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); } | START_BUFFER args ']' { $$ = buffer_expr($2); } | '<' expr ',' expr handler { $$ = frob_expr($2, $4, $5); } | expr '[' expr ']' { $$ = index_expr($1, $3); } | INCREMENT IDENT { $$ = indecr_expr(P_INCREMENT, $2); } | DECREMENT IDENT { $$ = indecr_expr(P_DECREMENT, $2); } | IDENT INCREMENT { $$ = indecr_expr(INCREMENT, $1); } | IDENT DECREMENT { $$ = indecr_expr(DECREMENT, $1); } | '!' 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 OP_IN expr { $$ = binary_expr(OP_IN, $1, $3); } | expr AND expr { $$ = and_expr($1, $3); } | expr OR expr { $$ = or_expr($1, $3); } | expr OP_COND_IF expr ':' expr { $$ = cond_expr($1, $3, $5); } | map '(' expr ')' TO '(' expr ')' { $$ = map_expr($3,$1,$7,OP_MAP); } | map '[' expr UPTO expr ']' TO '(' expr ')' { $$ = map_range_expr($3,$5,$1,$9,OP_MAP_RANGE); } | hash '(' expr ')' TO '(' expr ')' { $$ = map_expr($3,$1,$7,OP_MAPHASH); } | hash '[' expr UPTO expr ']' TO '(' expr ')' { $$ = map_range_expr($3,$5,$1,$9,OP_MAPHASH_RANGE); } | find '(' expr ')' WHERE '(' expr ')' { $$ = map_expr($3,$1,$7,OP_FIND); } | find '[' expr UPTO expr ']' WHERE '(' expr ')' { $$ = map_range_expr($3,$5,$1,$9,OP_FIND_RANGE); } | filter '(' expr ')' WHERE '(' expr ')' { $$ = map_expr($3,$1,$7,OP_FILTER); } | filter '[' expr UPTO expr ']' WHERE '(' expr ')' { $$ = map_range_expr($3,$5,$1,$9,OP_FILTER_RANGE); } | expr OP_COND_IF expr OP_COND_OTHER_ELSE expr { $$ = cond_expr($1, $3, $5); } | IDENT MULT_EQ expr { $$ = doeq_expr(MULT_EQ, $1, $3); } | IDENT DIV_EQ expr { $$ = doeq_expr(DIV_EQ, $1, $3); } | IDENT PLUS_EQ expr { $$ = doeq_expr(PLUS_EQ, $1, $3); } | IDENT MINUS_EQ expr { $$ = doeq_expr(MINUS_EQ, $1, $3); } | IDENT OPTIONAL_ASSIGN expr { $$ = opt_expr($1, $3); } | expr OP_ASSIGN expr { $$ = assign_expr($1, $3); } | '(' expr ')' { $$ = $2; } | CRITLEFT expr CRITRIGHT { $$ = critical_expr($2); } | PROPLEFT expr PROPRIGHT { $$ = propagate_expr($2); } ; map : OP_MAP IDENT OP_IN { $$ = $2; } ; find : OP_FIND IDENT OP_IN { $$ = $2; } ; filter : OP_FILTER IDENT OP_IN { $$ = $2; } ; hash : OP_MAPHASH IDENT OP_IN { $$ = $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); } ; %% /* // ------------------------------------------------------------ // // Additional code // */ Method * compile(Obj * object, cList * code, cList ** error_ret) { Method * method = NULL; /* Initialize compiler globals. */ errors = list_new(0); lex_start(code); /* 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; cStr * errstr, * line; cData 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; d.u.str = 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); } #undef _grammar_y_