diff options
Diffstat (limited to 'parse.y')
-rw-r--r-- | parse.y | 2585 |
1 files changed, 2585 insertions, 0 deletions
diff --git a/parse.y b/parse.y new file mode 100644 index 0000000000..0f4f640934 --- /dev/null +++ b/parse.y @@ -0,0 +1,2585 @@ +/************************************************ + + parse.y - + + $Author: matz $ + $Date: 1994/06/27 15:48:34 $ + created at: Fri May 28 18:02:42 JST 1993 + + Copyright (C) 1994 Yukihiro Matsumoto + +************************************************/ + +%{ + +#define YYDEBUG 1 +#include "ruby.h" +#include "env.h" +#include "node.h" +#include "st.h" + +#include "ident.h" +#define is_id_nonop(id) ((id)>LAST_TOKEN) +#define is_local_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL) +#define is_global_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL) +#define is_instance_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE) +#define is_variable_id(id) (is_id_nonop(id)&&((id)&ID_VARMASK)) +#define is_attrset_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET) +#define is_const_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_CONST) + +struct op_tbl { + ID tok; + char *name; +}; + +NODE *eval_tree = Qnil; +static int in_regexp; + +enum { + KEEP_STATE = 0, /* don't change lex_state. */ + EXPR_BEG, /* ignore newline, +/- is a sign. */ + EXPR_MID, /* newline significant, +/- is a sign. */ + EXPR_END, /* +/- is a operator. newline significant */ +}; + +static int lex_state; + +static ID cur_class = Qnil, cur_mid = Qnil; +static int in_module, in_single; + +static void value_expr(); +static NODE *cond(); +static NODE *cond2(); + +static NODE *block_append(); +static NODE *list_append(); +static NODE *list_concat(); +static NODE *list_copy(); +static NODE *call_op(); + +static NODE *asignable(); +static NODE *aryset(); +static NODE *attrset(); + +static void push_local(); +static void pop_local(); +static int local_cnt(); +static int local_id(); +static ID *local_tbl(); + +struct global_entry* rb_global_entry(); + +static void init_top_local(); +static void setup_top_local(); +%} + +%union { + struct node *node; + VALUE val; + ID id; +} + +%token CLASS + MODULE + DEF + FUNC + UNDEF + INCLUDE + IF + THEN + ELSIF + ELSE + CASE + WHEN + UNLESS + UNTIL + WHILE + FOR + IN + DO + USING + PROTECT + RESQUE + ENSURE + END + REDO + BREAK + CONTINUE + RETURN + YIELD + SUPER + RETRY + SELF + NIL + +%token <id> IDENTIFIER GVAR IVAR CONSTANT +%token <val> INTEGER FLOAT STRING REGEXP + +%type <node> singleton inc_list +%type <val> literal numeric +%type <node> compexpr exprs expr expr2 primary var_ref +%type <node> if_tail opt_else cases resque ensure opt_using +%type <node> call_args opt_args args f_arglist f_args f_arg +%type <node> assoc_list assocs assoc +%type <node> mlhs mlhs_head mlhs_tail lhs +%type <id> superclass variable symbol +%type <id> fname fname0 op rest_arg end_mark + +%token UPLUS /* unary+ */ +%token UMINUS /* unary- */ +%token POW /* ** */ +%token CMP /* <=> */ +%token EQ /* == */ +%token NEQ /* != <> */ +%token GEQ /* >= */ +%token LEQ /* <= */ +%token AND OR /* && and || */ +%token MATCH NMATCH /* =~ and !~ */ +%token DOT2 DOT3 /* .. and ... */ +%token AREF ASET /* [] and []= */ +%token LSHFT RSHFT /* << and >> */ +%token COLON2 /* :: */ +%token <id> SELF_ASGN /* +=, -= etc. */ +%token ASSOC /* => */ + +/* + * precedence table + */ + +%left YIELD RETURN +%right '=' SELF_ASGN +%right COLON2 +%nonassoc DOT2 DOT3 +%left OR +%left AND +%left '|' '^' +%left '&' +%nonassoc CMP EQ NEQ MATCH NMATCH +%left '>' GEQ '<' LEQ +%left LSHFT RSHFT +%left '+' '-' +%left '*' '/' '%' +%right POW +%right '!' '~' UPLUS UMINUS + +%token LAST_TOKEN + +%% +program : { + lex_state = EXPR_BEG; + init_top_local(); + } + compexpr + { + eval_tree = block_append(eval_tree, $2); + setup_top_local(); + } + +compexpr : exprs opt_term + +exprs : /* none */ + { + $$ = Qnil; + } + | expr + | exprs term expr + { + $$ = block_append($1, $3); + } + | exprs error expr + { + yyerrok; + $$ = $1; + } + +expr : CLASS IDENTIFIER superclass + { + if (cur_class || cur_mid || in_single) + Error("nested class definition"); + cur_class = $2; + push_local(); + } + compexpr + END end_mark + { + if ($7 && $7 != CLASS) { + Error("unmatched end keyword(expected `class')"); + } + $$ = NEW_CLASS($2, $5, $3); + pop_local(); + cur_class = Qnil; + } + | MODULE IDENTIFIER + { + if (cur_class != Qnil) + Error("nested module definition"); + cur_class = $2; + in_module = 1; + push_local(); + } + compexpr + END end_mark + { + if ($6 && $6 != MODULE) { + Error("unmatched end keyword(expected `module')"); + } + $$ = NEW_MODULE($2, $4); + pop_local(); + cur_class = Qnil; + in_module = 0; + } + | DEF fname + { + if (cur_mid || in_single) + Error("nested method definition"); + cur_mid = $2; + push_local(); + } + f_arglist compexpr + END end_mark + { + if ($7 && $7 != DEF) { + Error("unmatched end keyword(expected `def')"); + } + $$ = NEW_DEFN($2, NEW_RFUNC($4, $5), MTH_METHOD); + pop_local(); + cur_mid = Qnil; + } + | DEF FUNC fname + { + if (cur_mid || in_single) + Error("nested method definition"); + cur_mid = $3; + push_local(); + } + f_arglist compexpr + END end_mark + { + if ($8 && $8 != DEF) { + Error("unmatched end keyword(expected `def')"); + } + $$ = NEW_DEFN($3, NEW_RFUNC($5, $6), MTH_FUNC); + pop_local(); + cur_mid = Qnil; + } + | DEF singleton '.' fname + { + value_expr($2); + in_single++; + push_local(); + } + f_arglist + compexpr + END end_mark + { + if ($9 && $9 != DEF) { + Error("unmatched end keyword(expected `def')"); + } + $$ = NEW_DEFS($2, $4, NEW_RFUNC($6, $7)); + pop_local(); + in_single--; + } + | UNDEF fname + { + $$ = NEW_UNDEF($2); + } + | DEF fname fname + { + $$ = NEW_ALIAS($2, $3); + } + | INCLUDE inc_list + { + if (cur_mid || in_single) + Error("include appeared in method definition"); + $$ = $2; + } + | mlhs '=' args + { + NODE *rhs; + + if ($3->nd_next == Qnil) { + rhs = $3->nd_head; + free($3); + } + else { + rhs = $3; + } + + $$ = NEW_MASGN($1, rhs); + } + | expr2 + + +mlhs : mlhs_head + | mlhs_head mlhs_tail + { + $$ = list_concat($1, $2); + } + +mlhs_head : variable comma + { + $$ = NEW_LIST(asignable($1, Qnil)); + } + | primary '[' args rbracket comma + { + $$ = NEW_LIST(aryset($1, $3, Qnil)); + } + | primary '.' IDENTIFIER comma + { + $$ = NEW_LIST(attrset($1, $3, Qnil)); + } + +mlhs_tail : lhs + { + $$ = NEW_LIST($1); + } + | mlhs_tail comma lhs + { + $$ = list_append($1, $3); + } + +lhs : variable + { + $$ = asignable($1, Qnil); + } + | primary '[' args rbracket + { + $$ = aryset($1, $3, Qnil); + } + | primary '.' IDENTIFIER + { + $$ = attrset($1, $3, Qnil); + } + +superclass : /* none */ + { + $$ = Qnil; + } + | ':' IDENTIFIER + { + $$ = $2; + } + +inc_list : IDENTIFIER + { + $$ = NEW_INC($1); + } + | inc_list comma IDENTIFIER + { + $$ = block_append($1, NEW_INC($3)); + } + | error + { + $$ = Qnil; + } + | inc_list comma error + +fname : fname0 + | IVAR + +fname0 : IDENTIFIER + | IDENTIFIER '=' + { + ID id = $1; + + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + $$ = id; + } + | op + +op : COLON2 { $$ = COLON2; } + | DOT2 { $$ = DOT2; } + | '|' { $$ = '|'; } + | '^' { $$ = '^'; } + | '&' { $$ = '&'; } + | CMP { $$ = CMP; } + | EQ { $$ = EQ; } + | NEQ { $$ = NEQ; } + | MATCH { $$ = MATCH; } + | NMATCH { $$ = NMATCH; } + | '>' { $$ = '>'; } + | GEQ { $$ = GEQ; } + | '<' { $$ = '<'; } + | LEQ { $$ = LEQ; } + | LSHFT { $$ = LSHFT; } + | RSHFT { $$ = RSHFT; } + | '+' { $$ = '+'; } + | '-' { $$ = '-'; } + | '*' { $$ = '*'; } + | '/' { $$ = '/'; } + | '%' { $$ = '%'; } + | POW { $$ = POW; } + | '!' { $$ = '!'; } + | '~' { $$ = '~'; } + | '!' '@' { $$ = '!'; } + | '~' '@' { $$ = '~'; } + | '-' '@' { $$ = UMINUS; } + | '+' '@' { $$ = UPLUS; } + | '[' ']' { $$ = AREF; } + | '[' ']' '=' { $$ = ASET; } + +f_arglist : '(' f_args rparen + { + $$ = $2; + } + | term + { + $$ = Qnil; + } + +f_args : /* no arg */ + { + $$ = Qnil; + } + | f_arg + { + $$ = NEW_ARGS($1, -1); + } + | f_arg comma rest_arg + { + $$ = NEW_ARGS($1, $3); + } + | rest_arg + { + $$ = NEW_ARGS(Qnil, $1); + } + | f_arg error + { + $$ = NEW_ARGS($1, -1); + } + | error + { + $$ = Qnil; + } + +f_arg : IDENTIFIER + { + if (!is_local_id($1)) + Error("formal argument must be local variable"); + $$ = NEW_LIST(local_cnt($1)); + } + | f_arg comma IDENTIFIER + { + if (!is_local_id($3)) + Error("formal argument must be local variable"); + $$ = list_append($1, local_cnt($3)); + } + +rest_arg : '*' IDENTIFIER + { + if (!is_local_id($2)) + Error("rest argument must be local variable"); + $$ = local_cnt($2); + } + +singleton : var_ref + { + if ($1->type == NODE_SELF) { + $$ = NEW_SELF(); + } + else if ($1->type == NODE_NIL) { + Error("Can't define single method for nil."); + freenode($1); + $$ = Qnil; + } + else { + $$ = $1; + } + } + | '(' compexpr rparen + { + switch ($2->type) { + case NODE_STR: + case NODE_LIT: + case NODE_ARRAY: + case NODE_ZARRAY: + Error("Can't define single method for literals."); + default: + break; + } + $$ = $2; + } + +expr2 : IF expr2 then + compexpr + if_tail + END end_mark + { + if ($7 && $7 != IF) { + Error("unmatched end keyword(expected `if')"); + } + $$ = NEW_IF(cond($2), $4, $5); + } + | UNLESS expr2 then + compexpr opt_else END end_mark + { + if ($7 && $7 != UNLESS) { + Error("unmatched end keyword(expected `if')"); + } + $$ = NEW_UNLESS(cond($2), $4, $5); + } + | CASE expr2 opt_term + cases + END end_mark + { + if ($6 && $6 != CASE) { + Error("unmatched end keyword(expected `case')"); + } + value_expr($2); + $$ = NEW_CASE($2, $4); + } + | WHILE expr2 term compexpr END end_mark + { + if ($6 && $6 != WHILE) { + Error("unmatched end keyword(expected `while')"); + } + $$ = NEW_WHILE(cond($2), $4); + } + | UNTIL expr2 term compexpr END end_mark + { + if ($6 && $6 != UNTIL) { + Error("unmatched end keyword(expected `until')"); + } + $$ = NEW_UNTIL(cond($2), $4); + } + | FOR lhs IN expr2 term + compexpr + END end_mark + { + if ($8 && $8 != FOR) { + Error("unmatched end keyword(expected `for')"); + } + value_expr($4); + $$ = NEW_FOR($2, $4, $6); + } + | DO expr2 opt_using + compexpr + END end_mark + { + if ($6 && $6 != DO) { + Error("unmatched end keyword(expected `do')"); + } + value_expr($2); + $$ = NEW_DO($3, $2, $4); + } + | PROTECT + compexpr + resque + ensure + END end_mark + { + if ($6 && $6 != PROTECT) { + Error("unmatched end keyword(expected `protect')"); + } + if ($3 == Qnil && $4 == Qnil) { + Warning("useless protect clause"); + $$ = $2; + } + else { + $$ = NEW_PROT($2, $3, $4); + } + } + | REDO + { + $$ = NEW_REDO(); + } + | BREAK + { + $$ = NEW_BREAK(); + } + | CONTINUE + { + $$ = NEW_CONT(); + } + | RETRY + { + $$ = NEW_RETRY(); + } + | RETURN expr2 + { + value_expr($2); + if (!cur_mid && !in_single) + Error("return appeared outside of method"); + $$ = NEW_RET($2); + } + | RETURN + { + if (!cur_mid && !in_single) + Error("return appeared outside of method"); + $$ = NEW_RET(Qnil); + } + | variable '=' expr2 + { + value_expr($3); + $$ = asignable($1, $3); + } + | primary '[' args rbracket '=' expr2 + { + value_expr($6); + $$ = aryset($1, $3, $6); + } + | primary '.' IDENTIFIER '=' expr2 + { + value_expr($5); + $$ = attrset($1, $3, $5); + } + | variable SELF_ASGN expr2 + { + NODE *val; + + value_expr($3); + if (is_local_id($1)) { + val = NEW_LVAR($1); + } + else if (is_global_id($1)) { + val = NEW_GVAR($1); + } + else if (is_instance_id($1)) { + val = NEW_IVAR($1); + } + else { + val = NEW_CVAR($1); + } + $$ = asignable($1, call_op(val, $2, 1, $3)); + } + | primary '[' args rbracket SELF_ASGN expr2 + { + NODE *rval, *args; + value_expr($1); + value_expr($6); + + args = list_copy($3); + rval = NEW_CALL2($1, AREF, args); + + args = list_append($3, call_op(rval, $5, 1, $6)); + $$ = NEW_CALL($1, ASET, args); + } + | primary '.' IDENTIFIER SELF_ASGN expr2 + { + ID id = $3; + NODE *rval; + + value_expr($1); + value_expr($5); + + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + + rval = call_op(NEW_CALL2($1, $3, Qnil), $4, 1, $5); + $$ = NEW_CALL($1, id, NEW_LIST(rval)); + } + | YIELD expr2 + { + value_expr($2); + $$ = NEW_YIELD($2); + } + | expr2 DOT2 expr2 + { + $$ = call_op($1, DOT2, 1, $3); + } + | expr2 DOT3 expr2 + { + $$ = NEW_DOT3(cond2($1), cond2($3)); + } + | expr2 '+' expr2 + { + $$ = call_op($1, '+', 1, $3); + } + | expr2 '-' expr2 + { + $$ = call_op($1, '-', 1, $3); + } + | expr2 '*' expr2 + { + $$ = call_op($1, '*', 1, $3); + } + | expr2 '/' expr2 + { + $$ = call_op($1, '/', 1, $3); + } + | expr2 '%' expr2 + { + $$ = call_op($1, '%', 1, $3); + } + | expr2 POW expr2 + { + $$ = call_op($1, POW, 1, $3); + } + | '+' expr2 %prec UPLUS + { + $$ = call_op($2, UPLUS, 0); + + } + | '-' expr2 %prec UMINUS + { + $$ = call_op($2, UMINUS, 0); + } + | expr2 '|' expr2 + { + $$ = call_op($1, '|', 1, $3); + } + | expr2 '^' expr2 + { + $$ = call_op($1, '^', 1, $3); + } + | expr2 '&' expr2 + { + $$ = call_op($1, '&', 1, $3); + } + | expr2 CMP expr2 + { + $$ = call_op($1, CMP, 1, $3); + } + | expr2 '>' expr2 + { + $$ = call_op($1, '>', 1, $3); + } + | expr2 GEQ expr2 + { + $$ = call_op($1, GEQ, 1, $3); + } + | expr2 '<' expr2 + { + $$ = call_op($1, '<', 1, $3); + } + | expr2 LEQ expr2 + { + $$ = call_op($1, LEQ, 1, $3); + } + | expr2 EQ expr2 + { + $$ = call_op($1, EQ, 1, $3); + } + | expr2 NEQ expr2 + { + $$ = call_op($1, NEQ, 1, $3); + } + | expr2 MATCH expr2 + { + $$ = call_op($1, MATCH, 1, $3); + } + | expr2 NMATCH expr2 + { + $$ = call_op($1, NMATCH, 1, $3); + } + | '!' expr2 + { + $$ = call_op(cond($2), '!', 0); + } + | '~' expr2 + { + $$ = call_op($2, '~', 0); + } + | expr2 LSHFT expr2 + { + $$ = call_op($1, LSHFT, 1, $3); + } + | expr2 RSHFT expr2 + { + $$ = call_op($1, RSHFT, 1, $3); + } + | expr2 COLON2 expr2 + { + $$ = call_op($1, COLON2, 1, $3); + } + | expr2 AND expr2 + { + $$ = NEW_AND(cond($1), cond($3)); + } + | expr2 OR expr2 + { + $$ = NEW_OR(cond($1), cond($3)); + } + |primary + { + $$ = $1; + } + +then : term + | THEN + | term THEN + +if_tail : opt_else + | ELSIF expr2 then + compexpr + if_tail + { + $$ = NEW_IF(cond($2), $4, $5); + } + +opt_else : /* none */ + { + $$ = Qnil; + } + | ELSE compexpr + { + $$ = $2; + } + +opt_using : term + { + $$ = Qnil; + } + | opt_term USING lhs term + { + $$ = $3; + } + +cases : opt_else + | WHEN args term + compexpr + cases + { + $$ = NEW_WHEN($2, $4, $5); + } + +resque : /* none */ + { + $$ = Qnil; + } + | RESQUE compexpr + { + if ($2 == Qnil) + $$ = (NODE*)1; + else + $$ = $2; + } + +ensure : /* none */ + { + $$ = Qnil; + } + | ENSURE compexpr + { + $$ = $2; + } + +call_args : /* none */ + { + $$ = Qnil; + } + | args + | '*' exprs + { + $$ = $2; + } + | args comma '*' exprs + { + $$ = call_op($1, '+', 1, $4); + } + +opt_args : /* none */ + { + $$ = Qnil; + } + | args + +args : expr2 + { + value_expr($1); + $$ = NEW_LIST($1); + } + | args comma expr2 + { + value_expr($3); + $$ = list_append($1, $3); + } + +primary : var_ref + | '(' compexpr rparen + { + $$ = $2; + } + + | STRING + { + literalize($1); + $$ = NEW_STR($1); + } + | primary '[' args rbracket + { + value_expr($1); + $$ = NEW_CALL($1, AREF, $3); + } + | literal + { + literalize($1); + $$ = NEW_LIT($1); + } + | '[' opt_args rbracket + { + if ($2 == Qnil) + $$ = NEW_ZARRAY(); /* zero length array*/ + else { + $$ = $2; + } + } + | lbrace assoc_list rbrace + { + $$ = NEW_HASH($2); + } + | primary '.' IDENTIFIER '(' call_args rparen + { + value_expr($1); + $$ = NEW_CALL($1, $3, $5); + } + | primary '.' IDENTIFIER + { + value_expr($1); + $$ = NEW_CALL($1, $3, Qnil); + } + | IDENTIFIER '(' call_args rparen + { + $$ = NEW_CALL(Qnil, $1, $3); + } + | IVAR '(' call_args rparen + { + $$ = NEW_CALL(Qnil, $1, $3); + } + | SUPER '(' call_args rparen + { + if (!cur_mid && !in_single) + Error("super called outside of method"); + $$ = NEW_SUPER($3); + } + | SUPER + { + if (!cur_mid && !in_single) + Error("super called outside of method"); + $$ = NEW_ZSUPER(); + } + +literal : numeric + | '\\' symbol + { + $$ = INT2FIX($2); + } + | '/' {in_regexp = 1;} REGEXP + { + $$ = $3; + } + +symbol : fname0 + | IVAR + | GVAR + | CONSTANT + +numeric : INTEGER + | FLOAT + +variable : IDENTIFIER + | IVAR + | GVAR + | CONSTANT + | NIL + { + $$ = NIL; + } + | SELF + { + $$ = SELF; + } + +var_ref : variable + { + if ($1 == SELF) { + $$ = NEW_SELF(); + } + else if ($1 == NIL) { + $$ = NEW_NIL(); + } + else if (is_local_id($1)) { + if (local_id($1)) + $$ = NEW_LVAR($1); + else + $$ = NEW_MVAR($1); + } + else if (is_global_id($1)) { + $$ = NEW_GVAR($1); + } + else if (is_instance_id($1)) { + $$ = NEW_IVAR($1); + } + else if (is_const_id($1)) { + $$ = NEW_CVAR($1); + } + } + +assoc_list : /* none */ + { + $$ = Qnil; + } + | assocs + +assocs : assoc + | assocs comma assoc + { + $$ = list_concat($1, $3); + } + +assoc : expr2 ASSOC expr2 + { + $$ = NEW_LIST($1); + $$ = list_append($$, $3); + } + +end_mark : CLASS { $$ = CLASS; } + | MODULE { $$ = MODULE; } + | DEF { $$ = DEF; } + | FUNC { $$ = FUNC; } + | IF { $$ = IF; } + | UNLESS { $$ = UNLESS; } + | CASE { $$ = CASE; } + | WHILE { $$ = WHILE; } + | UNTIL { $$ = UNTIL; } + | FOR { $$ = FOR; } + | DO { $$ = DO; } + | PROTECT { $$ = PROTECT; } + | { $$ = Qnil;} + +opt_term : /* none */ + | term + +term : sc + | nl + +sc : ';' { yyerrok; } +nl : '\n' { yyerrok; } + +rparen : ')' { yyerrok; } +rbracket : ']' { yyerrok; } +lbrace : '{' { yyerrok; } +rbrace : '}' { yyerrok; } +comma : ',' { yyerrok; } +%% + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include "regex.h" + +#define is_identchar(c) ((c)!=-1&&(isalnum(c) || (c) == '_' || ismbchar(c))) + +static char *tokenbuf = NULL; +static int tokidx, toksiz = 0; + +char *xmalloc(); +char *xrealloc(); +VALUE newregexp(); +VALUE newstring(); +VALUE newfloat(); +VALUE newinteger(); +char *strdup(); + +#define EXPAND_B 1 +#define LEAVE_BS 2 + +static void read_escape(); + +char *sourcefile; /* current source file */ +int sourceline; /* current line no. */ + +static char *lex_p; +static int lex_len; + +lex_setsrc(src, ptr, len) + char *src; + char *ptr; + int len; +{ + sourcefile = (char*)strdup(src); + + sourceline = 1; + lex_p = ptr; + lex_len = len; +} + +#define nextc() ((--lex_len>=0)?(*lex_p++):-1) +#define pushback() (lex_len++, lex_p--) + +#define tokfix() (tokenbuf[tokidx]='\0') +#define tok() tokenbuf +#define toklen() tokidx +#define toknow() &toknbuf[tokidx] + +char * +newtok() +{ + tokidx = 0; + if (!tokenbuf) { + toksiz = 60; + tokenbuf = ALLOC_N(char, 60); + } + if (toksiz > 1024) { + REALLOC_N(tokenbuf, char, 60); + } + return tokenbuf; +} + +void +tokadd(c) + char c; +{ + if (tokidx >= toksiz) { + toksiz *= 2; + REALLOC_N(tokenbuf, char, toksiz); + } + tokenbuf[tokidx++] = c; +} + +#define LAST(v) ((v)-1 + sizeof(v)/sizeof(v[0])) + +static struct kwtable { + char *name; + int id; + int state; +} kwtable [] = { + "__END__", 0, KEEP_STATE, + "break", BREAK, EXPR_END, + "case", CASE, KEEP_STATE, + "class", CLASS, KEEP_STATE, + "continue", CONTINUE, EXPR_END, + "def", DEF, KEEP_STATE, + "do", DO, KEEP_STATE, + "else", ELSE, EXPR_BEG, + "elsif", ELSIF, EXPR_BEG, + "end", END, EXPR_END, + "ensure", ENSURE, EXPR_BEG, + "for", FOR, KEEP_STATE, + "func", FUNC, KEEP_STATE, + "if", IF, KEEP_STATE, + "in", IN, EXPR_BEG, + "include", INCLUDE, EXPR_BEG, + "module", MODULE, KEEP_STATE, + "nil", NIL, EXPR_END, + "protect", PROTECT, KEEP_STATE, + "redo", REDO, EXPR_END, + "resque", RESQUE, EXPR_BEG, + "retry", RETRY, EXPR_END, + "return", RETURN, EXPR_MID, + "self", SELF, EXPR_END, + "super", SUPER, EXPR_END, + "then", THEN, EXPR_BEG, + "undef", UNDEF, EXPR_BEG, + "unless", UNLESS, EXPR_BEG, + "until", UNTIL, EXPR_BEG, + "using", USING, KEEP_STATE, + "when", WHEN, EXPR_BEG, + "while", WHILE, KEEP_STATE, + "yield", YIELD, EXPR_BEG, +}; + +yylex() +{ + register int c; + struct kwtable *low = kwtable, *mid, *high = LAST(kwtable); + int last; + + if (in_regexp) { + int in_brack = 0; + int re_start = sourceline; + + in_regexp = 0; + newtok(); + while (c = nextc()) { + switch (c) { + case '[': + in_brack = 1; + break; + case ']': + in_brack = 0; + break; + case '\\': + if ((c = nextc()) == -1) { + sourceline = re_start; + Error("unterminated regexp meets end of file"); + return 0; + } + else if (c == '\n') { + sourceline++; + } + else if (in_brack && c == 'b') { + tokadd('\b'); + } + else { + pushback(); + read_escape(LEAVE_BS); + } + continue; + + case '/': /* end of the regexp */ + if (in_brack) + break; + + tokfix(); + yylval.val = regexp_new(tok(), toklen()); + lex_state = EXPR_END; + return REGEXP; + + case -1: + Error("unterminated regexp"); + return 0; + + default: + if (ismbchar(c)) { + tokadd(c); + c = nextc(); + } + break; + } + tokadd(c); + } + } + +retry: + switch (c = nextc()) { + case '\0': + case '\004': + case '\032': + case -1: /* end of script. */ + return 0; + + /* white spaces */ + case ' ': case '\t': case '\f': case '\r': + goto retry; + + case '#': /* it's a comment */ + while ((c = nextc()) != '\n') { + if (c == -1) + return 0; + } + /* fall through */ + case '\n': + sourceline++; + if (lex_state == EXPR_BEG) goto retry; + lex_state = EXPR_BEG; + return '\n'; + + case '*': + lex_state = EXPR_BEG; + if ((c = nextc()) == '*') { + if (nextc() == '=') { + yylval.id = POW; + return SELF_ASGN; + } + pushback(); + return POW; + } + else if (c == '=') { + yylval.id = '*'; + return SELF_ASGN; + } + pushback(); + return '*'; + + case '!': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return NEQ; + } + if (c == '~') { + return NMATCH; + } + pushback(); + return '!'; + + case '=': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return EQ; + } + if (c == '~') { + return MATCH; + } + else if (c == '>') { + return ASSOC; + } + pushback(); + return '='; + + case '<': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + if ((c = nextc()) == '>') { + return CMP; + } + pushback(); + return LEQ; + } + if (c == '<') { + if (nextc() == '=') { + yylval.id = LSHFT; + return SELF_ASGN; + } + pushback(); + return LSHFT; + } + pushback(); + return '<'; + + case '>': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return GEQ; + } + if (c == '>') { + if (nextc() == '=') { + yylval.id = RSHFT; + return SELF_ASGN; + } + pushback(); + return RSHFT; + } + pushback(); + return '>'; + + case '"': + { + int strstart = sourceline; + + newtok(); + while ((c = nextc()) != '"') { + if (c == -1) { + sourceline = strstart; + Error("unterminated string meets end of file"); + return 0; + } + if (ismbchar(c)) { + tokadd(c); + c = nextc(); + } + else if (c == '\n') { + sourceline++; + } + else if (c == '\\') { + c = nextc(); + if (c == '\n') { + sourceline++; + } + else if (c == '"') { + tokadd(c); + } + else { + pushback(); + read_escape(LEAVE_BS | EXPAND_B); + } + continue; + } + tokadd(c); + } + tokfix(); + yylval.val = str_new(tok(), toklen()); + lex_state = EXPR_END; + return STRING; + } + + case '\'': + { + int strstart = sourceline; + + newtok(); + while ((c = nextc()) != '\'') { + if (c == -1) { + sourceline = strstart; + Error("unterminated string meets end of file"); + return 0; + } + if (ismbchar(c)) { + tokadd(c); + c = nextc(); + } + else if (c == '\n') { + sourceline++; + } + else if (c == '\\') { + c = nextc(); + switch (c) { + case '\n': + sourceline++; + continue; + + case '\'': + c = '\''; + break; + case '\\': + c = '\\'; + break; + + default: + tokadd('\\'); + } + } + tokadd(c); + } + tokfix(); + yylval.val = str_new(tok(), toklen()); + lex_state = EXPR_END; + return STRING; + } + + case '?': + if ((c = nextc()) == '\\') { + newtok(); + read_escape(EXPAND_B); + c = tok()[0]; + } + c &= 0xff; + yylval.val = INT2FIX(c); + lex_state = EXPR_END; + return INTEGER; + + case '&': + lex_state = EXPR_BEG; + if ((c = nextc()) == '&') { + return AND; + } + else if (c == '=') { + yylval.id = '&'; + return SELF_ASGN; + } + pushback(); + return '&'; + + case '|': + lex_state = EXPR_BEG; + if ((c = nextc()) == '|') { + return OR; + } + else if (c == '=') { + yylval.id = '|'; + return SELF_ASGN; + } + pushback(); + return '|'; + + case '+': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = nextc(); + pushback(); + if (isdigit(c)) { + goto start_num; + } + } + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + yylval.id = '+'; + return SELF_ASGN; + } + pushback(); + return '+'; + + case '-': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = nextc(); + pushback(); + if (isdigit(c)) { + c = '-'; + goto start_num; + } + } + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + yylval.id = '-'; + return SELF_ASGN; + } + pushback(); + return '-'; + + case '.': + if ((c = nextc()) == '.') { + if ((c = nextc()) == '.') { + return DOT3; + } + pushback(); + lex_state = EXPR_BEG; + return DOT2; + } + pushback(); + if (!isdigit(c)) { + lex_state = EXPR_BEG; + return '.'; + } + c = '.'; + /* fall through */ + + start_num: + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int is_float, seen_point, seen_e; + + lex_state = EXPR_END; + newtok(); + if (c == '0') { + c = nextc(); + if (c == 'x' || c == 'X') { + /* hexadecimal */ + while (c = nextc()) { + if (!isxdigit(c)) break; + tokadd(c); + } + pushback(); + tokfix(); + yylval.val = str2inum(tok(), 16); + return INTEGER; + } + else if (c >= '0' && c <= '9') { + /* octal */ + do { + tokadd(c); + c = nextc(); + } while (c >= '0' && c <= '9'); + pushback(); + tokfix(); +#if 0 + yylval.val = INT2FIX(strtoul(tok(), Qnil, 8)); +#else + yylval.val = str2inum(tok(), 8); +#endif + return INTEGER; + } + } + if (c == '-' || c == '+') { + tokadd(c); + c = nextc(); + } + + is_float = seen_point = seen_e = 0; + + for (;;) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + tokadd(c); + break; + + case '.': + if (seen_point) { + goto decode_num; + } + c = nextc(); + if (!isdigit(c)) { + pushback(); + goto decode_num; + } + tokadd('.'); + tokadd(c); + is_float++; + seen_point++; + break; + + case 'e': + case 'E': + if (seen_e) { + goto decode_num; + } + tokadd(c); + seen_e++; + is_float++; + if ((c = nextc()) == '-' || c == '+') + tokadd(c); + else + continue; + break; + + case '_': /* `_' in decimal just ignored */ + break; + + default: + goto decode_num; + } + c = nextc(); + } + + decode_num: + pushback(); + tokfix(); + if (is_float) { + double atof(); + + yylval.val = float_new(atof(tok())); + return FLOAT; + } + yylval.val = str2inum(tok(), 10); + return INTEGER; + } + + case ']': + case '}': + case ')': + lex_state = EXPR_END; + return c; + + case ':': + lex_state = EXPR_BEG; + if (nextc() == ':') { + return COLON2; + } + pushback(); + return ':'; + + case '/': + lex_state = EXPR_BEG; + if (nextc() == '=') { + yylval.id = '/'; + return SELF_ASGN; + } + pushback(); + return c; + + case '^': + lex_state = EXPR_BEG; + if (nextc() == '=') { + yylval.id = '^'; + return SELF_ASGN; + } + pushback(); + return c; + + case ',': + case ';': + case '`': + case '[': + case '(': + case '{': + case '~': + lex_state = EXPR_BEG; + return c; + + case '\\': + c = nextc(); + if (c == '\n') goto retry; /* skip \\n */ + lex_state = EXPR_BEG; + pushback(); + return '\\'; + + case '%': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + /* class constant */ + newtok(); + tokadd('%'); + c = nextc(); + break; + } + else { + lex_state = EXPR_BEG; + if (nextc() == '=') { + yylval.id = '%'; + return SELF_ASGN; + } + pushback(); + return c; + } + + case '$': + newtok(); + tokadd(c); + c = nextc(); + switch (c) { + case '*': /* $*: argv */ + case '$': /* $$: pid */ + case '?': /* $?: last status */ + case '!': /* $!: error string */ + case '@': /* $@: error position */ + case '/': /* $/: input record separator */ + case '\\': /* $\: output record separator */ + case ',': /* $,: output field separator */ + case '.': /* $.: last read line number */ + case '_': /* $_: last read line string */ + case '&': /* $&: last match */ + case '~': /* $~: match-data */ + case '=': /* $=: ignorecase */ + tokadd(c); + tokadd('\0'); + goto id_fetch; + + default: + if (is_identchar(c)) + break; + pushback(); + return tok()[0]; + } + break; + + case '@': + c = nextc(); + if (!is_identchar(c)) { + pushback(); + return '@'; + } + newtok(); + tokadd('@'); + break; + + default: + if (c != '_' && !isalpha(c) && !ismbchar(c)) { + Error("Invalid char '%c' in expression", c); + goto retry; + } + + newtok(); + break; + } + + while (is_identchar(c)) { + tokadd(c); + if (ismbchar(c)) { + c = nextc(); + tokadd(c); + } + c = nextc(); + } + pushback(); + tokfix(); + + /* See if it is a reserved word. */ + while (low <= high) { + mid = low + (high - low)/2; + if (( c = strcmp(mid->name, tok())) == 0) { + if (mid->state != KEEP_STATE) { + lex_state = mid->state; + } + return mid->id; + } + else if (c < 0) { + low = mid + 1; + } + else { + high = mid - 1; + } + } + + id_fetch: + lex_state = EXPR_END; + yylval.id = rb_intern(tok()); + switch (tok()[0]) { + case '%': + return CONSTANT; + case '$': + return GVAR; + case '@': + return IVAR; + default: + return IDENTIFIER; + } +} + +static void +read_escape(flag) + int flag; +{ + char c; + + switch (c = nextc()) { + case '\\': /* Backslash */ + tokadd('\\'); + break; + + case 'n': /* newline */ + tokadd('\n'); + break; + + case 't': /* horizontal tab */ + tokadd('\t'); + break; + + case 'r': /* carriage-return */ + tokadd('\r'); + break; + + case 'f': /* form-feed */ + tokadd('\r'); + break; + + case 'v': /* vertical tab */ + tokadd('\13'); + break; + + case 'a': + tokadd('\a'); + break; + + case 'e': /* escape */ + tokadd(033); + break; + + case 'M': + if ((c = nextc()) != '-') { + Error("Invalid escape character syntax"); + tokadd('\0'); + return; + } + if ((c = nextc()) == '\\') { + read_escape(flag); + tokenbuf[tokidx-1] |= 0200; /* kludge */ + } + else { + tokadd((c & 0xff) | 0200); + } + break; + + case 'C': + if ((c = nextc()) != '-') { + Error("Invalid escape character syntax"); + tokadd('\0'); + return; + } + case '^': + if ((c = nextc())== '\\') { + read_escape (flag); + tokenbuf[tokidx-1] &= 0237; /* kludge */ + } + else if (c == '?') + tokadd(0177); + else + tokadd(c & 0237); + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + { /* octal constant */ + register int i = c - '0'; + register int count = 0; + + while (++count < 3) { + if ((c = nextc()) >= '0' && c <= '7') { + i *= 8; + i += c - '0'; + } + else { + pushback(); + break; + } + } + tokadd(i&0xff); + } + break; + + case 'x': /* hex constant */ + { + register int i = c - '0'; + register int count = 0; + + while (++count < 2) { + c = nextc(); + if ((c = nextc()) >= '0' && c <= '9') { + i *= 16; + i += c - '0'; + } + else if ((int)index("abcdefABCDEF", (c = nextc()))) { + i *= 16; + i += toupper(c) - 'A' + 10; + } + else { + pushback(); + break; + } + } + tokadd(i&0xff); + } + break; + + case 'b': /* backspace */ + if (flag & EXPAND_B) { + tokadd('\b'); + return; + } + /* go turough */ + default: + if (flag & LEAVE_BS) { + tokadd('\\'); + } + tokadd(c); + break; + } +} + +NODE* +newnode(type, a0, a1, a2) + enum node_type type; + NODE *a0, *a1, *a2; +{ + NODE *n = ALLOC(NODE); + + n->type = type; + n->line = sourceline; + n->src = sourcefile; + + n->u1.node = a0; + n->u2.node = a1; + n->u3.node = a2; + + return n; +} + +static NODE* +block_append(head, tail) + NODE *head, *tail; +{ + extern int verbose; + + if (tail == Qnil) return head; + if (head == Qnil) return tail; + + if (head->type != NODE_BLOCK) + head = NEW_BLOCK(head); + + if (head->nd_last == Qnil) head->nd_last = head; + + if (verbose) { + switch (head->nd_last->nd_head->type) { + case NODE_BREAK: + case NODE_CONTINUE: + case NODE_REDO: + case NODE_RETURN: + case NODE_RETRY: + Warning("statement not reached"); + break; + + default: + break; + } + } + + if (tail->type == NODE_BLOCK) { + head->nd_last->nd_next = tail; + head->nd_last = tail->nd_last; + } + else { + head->nd_last->nd_next = NEW_BLOCK(tail); + head->nd_last = head->nd_last->nd_next; + } + return head; +} + +static NODE* +list_append(head, tail) + NODE *head, *tail; +{ + if (head == Qnil) return NEW_ARRAY(tail); + + if (head->nd_last == Qnil) head->nd_last = head; + + head->nd_last->nd_next = NEW_ARRAY(tail); + head->nd_last = head->nd_last->nd_next; + return head; +} + +static NODE* +list_concat(head, tail) + NODE *head, *tail; +{ + NODE *last; + + if (head->type != NODE_ARRAY || tail->type != NODE_ARRAY) + Bug("list_concat() called with non-list"); + + last = (head->nd_last)?head->nd_last:head; + last->nd_next = tail; + head->nd_last = tail->nd_last; + + return head; +} + +static NODE* +list_copy(list) + NODE *list; +{ + NODE *tmp; + + if (list == Qnil) return Qnil; + + tmp = Qnil; + while(list) { + tmp = list_append(tmp, list->nd_head); + list = list->nd_next; + } + return tmp; +} + +void freenode(node) + NODE *node; +{ + if (node == Qnil) return; + + switch (node->type) { + case NODE_BLOCK: + case NODE_ARRAY: + freenode(node->nd_head); + freenode(node->nd_next); + break; + case NODE_IF: + case NODE_WHEN: + case NODE_PROT: + freenode(node->nd_cond); + freenode(node->nd_body); + freenode(node->nd_else); + break; + case NODE_CASE: + case NODE_WHILE: + case NODE_UNTIL: + case NODE_AND: + case NODE_OR: + freenode(node->nd_head); + freenode(node->nd_body); + break; + case NODE_DO: + case NODE_FOR: + freenode(node->nd_ibdy); + freenode(node->nd_iter); + break; + case NODE_LASGN: + case NODE_GASGN: + case NODE_IASGN: + freenode(node->nd_value); + break; + case NODE_CALL: + case NODE_SUPER: + freenode(node->nd_recv); + case NODE_CALL2: + freenode(node->nd_args); + break; + case NODE_DEFS: + freenode(node->nd_recv); + break; + case NODE_RETURN: + case NODE_YIELD: + freenode(node->nd_stts); + break; + case NODE_STR: + case NODE_LIT: + unliteralize(node->nd_lit); + break; + case NODE_CONST: + unliteralize(node->nd_cval); + break; + case NODE_ARGS: + freenode(node->nd_frml); + break; + case NODE_SCOPE: + free(node->nd_tbl); + freenode(node->nd_body); + break; + case NODE_DEFN: + case NODE_ZARRAY: + case NODE_CFUNC: + case NODE_BREAK: + case NODE_CONTINUE: + case NODE_RETRY: + case NODE_LVAR: + case NODE_GVAR: + case NODE_IVAR: + case NODE_MVAR: + case NODE_CLASS: + case NODE_MODULE: + case NODE_INC: + case NODE_NIL: + break; + default: + Bug("freenode: unknown node type %d", node->type); + break; + } + free(node); +} + +struct call_arg { + ID id; + VALUE recv; + int narg; + VALUE arg; +}; + +static VALUE +call_lit(arg) + struct call_arg *arg; +{ + return rb_funcall(arg->recv, arg->id, arg->narg, arg->arg); +} + +static VALUE +except_lit() +{ + extern VALUE errstr; + + Error("%s", RSTRING(errstr)->ptr); + return Qnil; +} + +static NODE * +call_op(recv, id, narg, arg1) + NODE *recv; + ID id; + int narg; + NODE *arg1; +{ + NODE *args; + + value_expr(recv); + if (narg == 1) + value_expr(arg1); + + if (recv->type != NODE_LIT || recv->type != NODE_STR + || (narg == 0 && id == '~' + && (TYPE(recv->nd_lit)==T_REGEXP || TYPE(recv->nd_lit)==T_STRING)) + || arg1->type == NODE_LIT || arg1->type == NODE_STR) { + if (narg > 0) { + args = NEW_ARRAY(arg1); + args->nd_argc = 1; + } + else { + args = Qnil; + } + return NEW_CALL(recv, id, args); + } + else { + struct call_arg arg_data; + NODE *result; + + arg_data.recv = recv->nd_lit; + arg_data.id = id; + arg_data.narg = narg; + if (narg == 1) arg_data.arg = arg1->nd_lit; + result = NEW_LIT(rb_resque(call_lit, &arg_data, except_lit, Qnil)); + freenode(recv); + if (narg == 1) freenode(arg1); + return result; + } +} + +static NODE* +asignable(id, val) + ID id; + NODE *val; +{ + NODE *lhs; + + if (id == SELF) { + lhs = Qnil; + Error("Can't change the value of self"); + } + else if (id == NIL) { + lhs = Qnil; + Error("Can't asign to nil"); + } + else if (is_local_id(id)) { + lhs = NEW_LASGN(id, val); + } + else if (is_global_id(id)) { + lhs = NEW_GASGN(id, val); + } + else if (is_instance_id(id)) { + lhs = NEW_IASGN(id, val); + } + else if (is_const_id(id)) { + if (cur_mid || in_single) + Error("class constant asigned in method body"); + lhs = NEW_CASGN(id, val); + } + else { + Bug("bad id for variable"); + } + return lhs; +} + +static NODE * +aryset(recv, idx, val) + NODE *recv, *idx, *val; +{ + NODE *args; + + value_expr(recv); + return NEW_CALL(recv, ASET, list_append(idx, val)); +} + +static NODE * +attrset(recv, id, val) + NODE *recv, *val; + ID id; +{ + value_expr(recv); + + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + + return NEW_CALL(recv, id, NEW_ARRAY(val)); +} + +static void +value_expr(node) + NODE *node; +{ + switch (node->type) { + case NODE_RETURN: + case NODE_CONTINUE: + case NODE_BREAK: + case NODE_REDO: + case NODE_RETRY: + Error("void value expression"); + break; + + case NODE_BLOCK: + if (node->nd_last) + return value_expr(node->nd_last->nd_head); + break; + + default: + break; + } +} + +static NODE* +cond(node) + NODE *node; +{ + value_expr(node); + if (node->type == NODE_STR) { + return call_op(NEW_GVAR(rb_intern("$_")),MATCH,1,node); + } + else if (node->type == NODE_LIT && TYPE(node->nd_lit) == T_REGEXP) { + return call_op(node,MATCH,1,NEW_GVAR(rb_intern("$_"))); + } + return node; +} + +static NODE* +cond2(node) + NODE *node; +{ + node = cond(node); + if (node->type == NODE_LIT) { + if (FIXNUM_P(node->nd_lit)) { + return call_op(node,EQ,1,NEW_GVAR(rb_intern("$."))); + } + } + return node; +} + +st_table *new_idhash(); + +static struct local_vars { + ID *tbl; + int cnt; + struct local_vars *next; +} *lvtbl; + +static void +push_local() +{ + struct local_vars *local; + + local = ALLOC(struct local_vars); + local->next = lvtbl; + local->cnt = 0; + local->tbl = Qnil; + lvtbl = local; +} + +void +pop_local() +{ + struct local_vars *local = lvtbl; + + lvtbl = local->next; + if (local->tbl) local->tbl[0] = local->cnt; + free(local); +} + +static ID* +local_tbl() +{ + return lvtbl->tbl; +} + +static int +local_cnt(id) + ID id; +{ + int cnt, max; + + if (id == 0) return lvtbl->cnt; + + for (cnt=0, max=lvtbl->cnt; cnt<max ;cnt++) { + if (lvtbl->tbl[cnt+1] == id) return cnt; + } + + if (lvtbl->tbl == Qnil) + lvtbl->tbl = ALLOC_N(ID, 2); + else + REALLOC_N(lvtbl->tbl, ID, lvtbl->cnt+2); + + lvtbl->tbl[lvtbl->cnt+1] = id; + return lvtbl->cnt++; +} + +static int +local_id(id) + ID id; +{ + int i, max; + + if (lvtbl == Qnil) return FALSE; + for (i=1, max=lvtbl->cnt+1; i<max; i++) { + if (lvtbl->tbl[i] == id) return TRUE; + } + return FALSE; +} + +static void +init_top_local() +{ + if (lvtbl == Qnil) { + push_local(); + } + else if (the_env->local_tbl) { + lvtbl->cnt = the_env->local_tbl[0]; + } + else { + lvtbl->cnt = 0; + } + lvtbl->tbl = the_env->local_tbl; +} + +static void +setup_top_local() +{ + if (lvtbl->cnt > 0) { + if (the_env->local_vars == Qnil) { + the_env->local_vars = ALLOC_N(VALUE, lvtbl->cnt); + bzero(the_env->local_vars, lvtbl->cnt * sizeof(VALUE)); + } + else { + int i; + + REALLOC_N(the_env->local_vars, VALUE, lvtbl->cnt); + for (i=lvtbl->tbl[0]; i<lvtbl->cnt; i++) { + the_env->local_vars[i] = Qnil; + } + } + lvtbl->tbl[0] = lvtbl->cnt; + the_env->local_tbl = lvtbl->tbl; + } + else { + the_env->local_vars = Qnil; + } +} + +void +yyappend_print() +{ + eval_tree = + block_append(eval_tree, NEW_CALL(Qnil, rb_intern("print"), + NEW_ARRAY(NEW_GVAR(rb_intern("$_"))))); +} + +void +yywhole_loop(chop, split) + int chop, split; +{ + if (split) { + eval_tree = + block_append(NEW_GASGN(rb_intern("$F"), + NEW_CALL(NEW_GVAR(rb_intern("$_")), + rb_intern("split"), Qnil)), + eval_tree); + } + if (chop) { + eval_tree = + block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")), + rb_intern("chop"), Qnil), eval_tree); + } + eval_tree = NEW_WHILE(NEW_CALL(0,rb_intern("gets"),0),eval_tree); +} + +static struct op_tbl rb_op_tbl[] = { + DOT2, "..", + '+', "+", + '-', "-", + '+', "+(binary)", + '-', "-(binary)", + '*', "*", + '/', "/", + '%', "%", + POW, "**", + UPLUS, "+(unary)", + UMINUS, "-(unary)", + UPLUS, "+@", + UMINUS, "-@", + '|', "|", + '^', "^", + '&', "&", + CMP, "<=>", + '>', ">", + GEQ, ">=", + '<', "<", + LEQ, "<=", + EQ, "==", + NEQ, "!=", + MATCH, "=~", + NMATCH, "!~", + '!', "!", + '~', "~", + '!', "!(unary)", + '~', "~(unary)", + '!', "!@", + '~', "~@", + AREF, "[]", + ASET, "[]=", + LSHFT, "<<", + RSHFT, ">>", + COLON2, "::", + Qnil, Qnil, +}; + +char *rb_id2name(); +char *rb_class2name(); + +st_table *rb_symbol_tbl; + +#define sym_tbl rb_symbol_tbl + +void +Init_sym() +{ + int strcmp(); + + sym_tbl = st_init_table(strcmp, st_strhash); +} + +ID +rb_intern(name) + char *name; +{ + static ID last_id = LAST_TOKEN; + int id; + int last; + + if (st_lookup(sym_tbl, name, &id)) + return id; + + id = ++last_id; + id <<= 3; + switch (name[0]) { + case '$': + id |= ID_GLOBAL; + break; + case '@': + id |= ID_INSTANCE; + break; + case '%': + if (name[1] != '\0') { + id |= ID_CONST; + break; + } + /* fall through */ + default: + if (name[0] != '_' && !isalpha(name[0]) && !ismbchar(name[0])) { + /* operator */ + int i; + + id = Qnil; + for (i=0; rb_op_tbl[i].tok; i++) { + if (strcmp(rb_op_tbl[i].name, name) == 0) { + id = rb_op_tbl[i].tok; + break; + } + } + if (id == Qnil) Bug("Unknown operator `%s'", name); + break; + } + last = strlen(name)-1; + if (name[last] == '=') { + /* attribute asignment */ + char *buf = (char*)alloca(last+1); + + strncpy(buf, name, last); + buf[last] = '\0'; + id = rb_intern(buf); + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + } + else { + id |= ID_LOCAL; + } + break; + } + st_add_direct(sym_tbl, strdup(name), id); + return id; +} + +static char *find_ok; + +static +id_find(name, id1, id2) + char *name; + ID id1, id2; +{ + if (id1 == id2) { + find_ok = name; + return ST_STOP; + } + return ST_CONTINUE; +} + +char * +rb_id2name(id) + ID id; +{ + find_ok = Qnil; + + if (id < LAST_TOKEN) { + int i = 0; + + for (i=0; rb_op_tbl[i].tok; i++) { + if (rb_op_tbl[i].tok == id) + return rb_op_tbl[i].name; + } + } + + st_foreach(sym_tbl, id_find, id); + if (!find_ok && is_attrset_id(id)) { + char *res; + ID id2; + + id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL; + res = rb_id2name(id2); + + if (res) { + char *buf = (char*)alloca(strlen(res)+2); + + strcpy(buf, res); + strcat(buf, "="); + rb_intern(buf); + return rb_id2name(id); + } + } + return find_ok; +} + +char * +rb_class2name(class) + struct RClass *class; +{ + extern st_table *rb_class_tbl; + + find_ok = Qnil; + + switch (TYPE(class)) { + case T_CLASS: + case T_MODULE: + case T_ICLASS: + break; + default: + Fail("0x%x is not a class/module", class); + } + + while (FL_TEST(class, FL_SINGLE)) { + class = (struct RClass*)class->super; + } + + st_foreach(rb_class_tbl, id_find, class); + if (find_ok) { + return rb_id2name((ID)find_ok); + } + Bug("class 0x%x not named", class); +} |