From: Markus Date: 2004-10-08T04:18:14+09:00 Subject: ANN: Free-form operator patch --=-bGfylPCk97kwreyt0ejI Content-Type: text/plain Content-Transfer-Encoding: 7bit !!! -> <- -- +++ -=- */* !! ~~~ <=..< .... * Have you ever wanted to define your own operators in ruby? * Have you ever wanted to play around with experimental hash or range semantics, but wanted a concise syntax for the constructors? * Are you crazy enough to install a compiler patch from someone WHO OPENLY ADMITS TO NOT KNOW C? (for those of you that need everything quantified, that's a metric craziness index of about 0.6 Why) If you answered yes to all of the above, have I got a patch for you. ---------------------------------------------------------------- As mentioned previously, I've been working on a patch that would let you write things like: class Pair attr_accessor :l,:r def initialize(l,r) @l,@r = l,r end end class Object def -->(other) Pair.new(self,other) end end print 1-->5,"\n" This is the pre-alpha release of that patch. Basically, any sequence of "operator characters" that isn't otherwise used is now user definable, though you are warned to use spaces where this would be ambiguous (e.g. x+=-1). In this version, the operators are always binary, non-associative, and mid-precedence. I can see how to let the users set the precedence, but can not figure out what "scope" the precedence declarations should have. I can NOT see how it could depend on the class of the recipient, which would be in some ways idea and in others hideous. Several of you have expressed interest in this; let me know if you try it out & what you think. Bug reports are especially welcome. -- Markus ---------------------------------------------------------------- Normally try to preface code I post with some sort of warning about the quality/soundness of the code I'm posting. In this case, though, my C skills aren't up to rating my code here. To me, it is much easier to read (albeit in theory microscopically slower) than the way C is usually written. Native speakers of C will doubtlessly be scandalized by my accent. So the best I can do is say, 1) it works on my machine, 2) I haven't been able to measure any performance impact, 3) it doesn't seem to break anything that I know of, though it does generate some warnings (by design) on existing scripts that run operators together (e.g. x+=-1). I am NOT using this in a production environment, nor would I recommend any else do so. But it's fun to play with. --=-bGfylPCk97kwreyt0ejI Content-Disposition: attachment; filename=ruby_free_op_patch.0.00001.diff Content-Type: text/plain; name=ruby_free_op_patch.0.00001.diff; charset=UTF-8 Content-Transfer-Encoding: 7bit --- ruby-1.8.2.mqr/parse.y 2004-10-07 11:32:40.000000000 -0700 +++ ruby-1.8.2/parse.y 2004-07-21 20:34:08.000000000 -0700 @@ -293,7 +293,7 @@ %token tAMPER /* & */ %token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG %token tSTRING_DBEG tSTRING_DVAR tSTRING_END -%token tGENERIC_OP /* -->, <++<, etc. */ + /* * precedence table */ @@ -311,7 +311,7 @@ %nonassoc tDOT2 tDOT3 %left tOROP %left tANDOP -%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH tGENERIC_OP +%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH %left '>' tGEQ '<' tLEQ %left '|' '^' %left '&' @@ -917,7 +917,6 @@ | '^' { $$ = '^'; } | '&' { $$ = '&'; } | tCMP { $$ = tCMP; } - | tGENERIC_OP { $$ = tGENERIC_OP; } | tEQ { $$ = tEQ; } | tEQQ { $$ = tEQQ; } | tMATCH { $$ = tMATCH; } @@ -1127,10 +1126,6 @@ { $$ = call_op($1, tCMP, 1, $3); } - | arg tGENERIC_OP arg - { - $$ = call_op($1, tGENERIC_OP, 1, $3); - } | arg '>' arg { $$ = call_op($1, '>', 1, $3); @@ -3304,65 +3299,12 @@ #define IS_ARG() (lex_state == EXPR_ARG || lex_state == EXPR_CMDARG) -/* MQR */ -#define WAS_ARG() (pre_op_state == EXPR_ARG || pre_op_state == EXPR_CMDARG) -#define AMBI_ARG() (WAS_ARG() && space_seen && !ISSPACE(*lex_p)) -#define TRUE 1 -#define FALSE 0 -#define is_operator_character(c) (ISASCII(c) && ((c) == '+' || (c) == '-' || (c) == '*' || (c) == '/' || (c) == '=' || (c) == '<' || (c) == '>' || (c) == '.' || (c) == '%' || (c) == '^' || (c) == '!' || (c) == '&' || (c) == '|' || (c) == '~')) - -static int is_operator(found_len,len,op,state) - char *op; - int found_len,len; - enum lex_state state; - { - if (found_len == len && strncmp(lex_p, op, len) == 0) { - lex_p += len; - lex_state = state; - return TRUE; - } - else - return FALSE; - } -static int space_op_warn(len,result) - int len,result; - { - int i; - for (i=len;i > 0;i--) pushback(i); - rb_warning("Put space between operators for future version"); - return result; - } -static int operand_prefix_warn(ambiguious,pre_op_state,as_op,as_prefix) - int ambiguious,as_op,as_prefix; - enum lex_state pre_op_state; - { - if (ambiguious) { - /* patch the 'as_op' character into message? */ - rb_warning("`*' or '&' interpreted as argument prefix"); - return as_prefix; - } - else if (pre_op_state == EXPR_BEG || pre_op_state == EXPR_MID) { - return as_prefix; - } - else { - return as_op; - } - } -static int assignment_operator(op) - int op; - { - yylval.id = op; - lex_state = EXPR_BEG; - return tOP_ASGN; - } - static int yylex() { - register int c,c2; + register int c; int space_seen = 0; int cmd_state; - enum lex_state pre_op_state,post_op_state; if (lex_strterm) { int token; @@ -3386,7 +3328,6 @@ cmd_state = command_start; command_start = Qfalse; retry: - switch (c = nextc()) { case '\0': /* NUL */ case '\004': /* ^D */ @@ -3419,6 +3360,54 @@ command_start = Qtrue; lex_state = EXPR_BEG; return '\n'; + + case '*': + if ((c = nextc()) == '*') { + if ((c = nextc()) == '=') { + yylval.id = tPOW; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + c = tPOW; + } + else { + if (c == '=') { + yylval.id = '*'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + if (IS_ARG() && space_seen && !ISSPACE(c)){ + rb_warning("`*' interpreted as argument prefix"); + c = tSTAR; + } + else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = tSTAR; + } + else { + c = '*'; + } + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + return c; + + case '!': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return tNEQ; + } + if (c == '~') { + return tNMATCH; + } + pushback(c); + return '!'; + case '=': if (was_bol()) { /* skip embedded rd document */ @@ -3439,8 +3428,30 @@ lex_p = lex_pend; goto retry; } - break; } + + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + if ((c = nextc()) == '=') { + if ((c = nextc()) == '=') { + return tEQQ; + } + pushback(c); + return tEQ; + } + if (c == '~') { + return tMATCH; + } + else if (c == '>') { + return tASSOC; + } + pushback(c); + return '='; + case '<': c = nextc(); if (c == '<' && @@ -3452,8 +3463,53 @@ int token = heredoc_identifier(); if (token) return token; } - pushback(c); - break; + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + if (c == '=') { + if ((c = nextc()) == '>') { + return tCMP; + } + pushback(c); + return tLEQ; + } + if (c == '<') { + if ((c = nextc()) == '=') { + yylval.id = tLSHFT; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + return tLSHFT; + } + pushback(c); + return '<'; + + case '>': + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + if ((c = nextc()) == '=') { + return tGEQ; + } + if (c == '>') { + if ((c = nextc()) == '=') { + yylval.id = tRSHFT; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + return tRSHFT; + } + pushback(c); + return '>'; + case '"': lex_strterm = NEW_STRTERM(str_dquote, '"', 0); return tSTRING_BEG; @@ -3491,12 +3547,24 @@ if (!IS_ARG()){ int c2 = 0; switch (c) { - case ' ': c2 = 's'; break; - case '\n': c2 = 'n'; break; - case '\t': c2 = 't'; break; - case '\v': c2 = 'v'; break; - case '\r': c2 = 'r'; break; - case '\f': c2 = 'f'; break; + case ' ': + c2 = 's'; + break; + case '\n': + c2 = 'n'; + break; + case '\t': + c2 = 't'; + break; + case '\v': + c2 = 'v'; + break; + case '\r': + c2 = 'r'; + break; + case '\f': + c2 = 'f'; + break; } if (c2) { rb_warn("invalid character syntax; use ?\\%c", c2); @@ -3521,193 +3589,142 @@ lex_state = EXPR_END; yylval.node = NEW_LIT(INT2FIX(c)); return tINTEGER; - case '+': case '-': - if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) break; - c2 = nextc(); - if (lex_state == EXPR_BEG || lex_state == EXPR_MID || - (IS_ARG() && space_seen && !ISSPACE(c2))) { - if (IS_ARG()) arg_ambiguous(); + + case '&': + if ((c = nextc()) == '&') { lex_state = EXPR_BEG; - pushback(c2); - if (ISDIGIT(c2)) { - if (c == '+') goto start_num; else return tUMINUS_NUM; - } - return (c == '+') ? tUPLUS : tUMINUS; - } - pushback(c2); - break; - case '/': - if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { - lex_strterm = NEW_STRTERM(str_regexp, '/', 0); - return tREGEXP_BEG; - } - c = nextc(); - pushback(c); - if (c == '=') break; - if (IS_ARG() && space_seen && (!ISSPACE(c))) { /* && (c != '=')) { */ - arg_ambiguous(); - lex_strterm = NEW_STRTERM(str_regexp, '/', 0); - return tREGEXP_BEG; + if ((c = nextc()) == '=') { + yylval.id = tANDOP; + lex_state = EXPR_BEG; + return tOP_ASGN; } - break; - /* needed? - case '.': - c = nextc(); + pushback(c); + return tANDOP; + } + else if (c == '=') { + yylval.id = '&'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } pushback(c); - if (ISDIGIT(c)) { - yyerror("no . floating literal anymore; put 0 before dot"); + if (IS_ARG() && space_seen && !ISSPACE(c)){ + rb_warning("`&' interpreted as argument prefix"); + c = tAMPER; } - */ - case '%': - if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { - int term; - int paren; + else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = tAMPER; + } + else { + c = '&'; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; + } + return c; - c = nextc(); - quotation: - if (!ISALNUM(c)) { - term = c; - c = 'Q'; + case '|': + if ((c = nextc()) == '|') { + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + yylval.id = tOROP; + lex_state = EXPR_BEG; + return tOP_ASGN; } - else { - term = nextc(); - if (ISALNUM(term) || ismbchar(term)) { - yyerror("unknown type of %string"); - return 0; - } + pushback(c); + return tOROP; + } + if (c == '=') { + yylval.id = '|'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + } + else { + lex_state = EXPR_BEG; + } + pushback(c); + return '|'; + + case '+': + c = nextc(); + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + if (c == '@') { + return tUPLUS; } - if (c == -1 || term == -1) { - rb_compile_error("unterminated quoted string meets end of file"); - return 0; + pushback(c); + return '+'; + } + if (c == '=') { + yylval.id = '+'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (lex_state == EXPR_BEG || lex_state == EXPR_MID || + (IS_ARG() && space_seen && !ISSPACE(c))) { + if (IS_ARG()) arg_ambiguous(); + lex_state = EXPR_BEG; + pushback(c); + if (ISDIGIT(c)) { + c = '+'; + goto start_num; } - paren = term; - if (term == '(') term = ')'; - else if (term == '[') term = ']'; - else if (term == '{') term = '}'; - else if (term == '<') term = '>'; - else paren = 0; - - switch (c) { - case 'Q': - lex_strterm = NEW_STRTERM(str_dquote, term, paren); - return tSTRING_BEG; - - case 'q': - lex_strterm = NEW_STRTERM(str_squote, term, paren); - return tSTRING_BEG; - - case 'W': - lex_strterm = NEW_STRTERM(str_dquote | STR_FUNC_QWORDS, term, paren); - do {c = nextc();} while (ISSPACE(c)); - pushback(c); - return tWORDS_BEG; - - case 'w': - lex_strterm = NEW_STRTERM(str_squote | STR_FUNC_QWORDS, term, paren); - do {c = nextc();} while (ISSPACE(c)); - pushback(c); - return tQWORDS_BEG; - - case 'x': - lex_strterm = NEW_STRTERM(str_xquote, term, paren); - return tXSTRING_BEG; - - case 'r': - lex_strterm = NEW_STRTERM(str_regexp, term, paren); - return tREGEXP_BEG; + return tUPLUS; + } + lex_state = EXPR_BEG; + pushback(c); + return '+'; - case 's': - lex_strterm = NEW_STRTERM(str_ssym, term, paren); - lex_state = EXPR_FNAME; - return tSYMBEG; + case '-': + c = nextc(); + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + if (c == '@') { + return tUMINUS; + } + pushback(c); + return '-'; + } + if (c == '=') { + yylval.id = '-'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (lex_state == EXPR_BEG || lex_state == EXPR_MID || + (IS_ARG() && space_seen && !ISSPACE(c))) { + if (IS_ARG()) arg_ambiguous(); + lex_state = EXPR_BEG; + pushback(c); + if (ISDIGIT(c)) { + return tUMINUS_NUM; + } + return tUMINUS; + } + lex_state = EXPR_BEG; + pushback(c); + return '-'; - default: - yyerror("unknown type of %string"); - return 0; + case '.': + lex_state = EXPR_BEG; + if ((c = nextc()) == '.') { + if ((c = nextc()) == '.') { + return tDOT3; } + pushback(c); + return tDOT2; } - c = nextc(); - if (IS_ARG() && space_seen && !ISSPACE(c)) goto quotation; pushback(c); - break; - } - - pushback(c); - int op_len = 0; - while (lex_p+op_len < lex_pend && is_operator_character(c = lex_p[op_len])) { - op_len++; - } - if (op_len > 0) { - c = lex_p[op_len-1]; - if (op_len > 1 && (c == '-' || c == '+') && !(ISSPACE(lex_p[op_len]) || lex_p+op_len >= lex_pend) ) { - rb_warn("operators ending in +/- should be space delimited"); - op_len--; - } - } - pre_op_state = lex_state; - switch (lex_state) { - case EXPR_FNAME: case EXPR_DOT: - post_op_state = EXPR_ARG; break; - default: - post_op_state = EXPR_BEG; break; - } - if (is_operator(op_len,3,"**=", EXPR_BEG )) { return assignment_operator(tPOW); } - if (is_operator(op_len,2,"**", post_op_state)) { return tPOW; } - if (is_operator(op_len,2,"*=", EXPR_BEG )) { return assignment_operator('*'); } - if (is_operator(op_len,1,"*", post_op_state)) { return operand_prefix_warn(AMBI_ARG(),pre_op_state,'*',tSTAR); } - if (is_operator(op_len,2,"!=", EXPR_BEG )) { return tNEQ; } - if (is_operator(op_len,2,"!~", EXPR_BEG )) { return tMATCH; } - if (is_operator(op_len,1,"!", EXPR_BEG )) { return '!'; } - if (is_operator(op_len,2,"=~", post_op_state)) { return tMATCH; } - if (is_operator(op_len,2,"=>", post_op_state)) { return tASSOC; } - if (is_operator(op_len,3,"===", post_op_state)) { return tEQQ; } - if (is_operator(op_len,2,"==", post_op_state)) { return tEQ; } - if (is_operator(op_len,1,"=", post_op_state)) { return '='; } - if (is_operator(op_len,2,"=%", post_op_state)) { return space_op_warn(1,'='); } - if (is_operator(op_len,3,"<=>", post_op_state)) { return tCMP; } - if (is_operator(op_len,2,"<=", post_op_state)) { return tLEQ; } - if (is_operator(op_len,3,"<<=", EXPR_BEG )) { return assignment_operator(tLSHFT); } - if (is_operator(op_len,2,"<<", post_op_state)) { return tLSHFT; } - if (is_operator(op_len,1,"<", post_op_state)) { return '<'; } - if (is_operator(op_len,2,">=", post_op_state)) { return tGEQ; } - if (is_operator(op_len,3,">>=", EXPR_BEG )) { return assignment_operator(tRSHFT); } - if (is_operator(op_len,2,">>", post_op_state)) { return tRSHFT; } - if (is_operator(op_len,1,">", post_op_state)) { return '>'; } - if (is_operator(op_len,3,"&&=", EXPR_BEG )) { return assignment_operator(tANDOP); } - if (is_operator(op_len,2,"&&", post_op_state)) { return tANDOP; } - if (is_operator(op_len,2,"&=", EXPR_BEG )) { return assignment_operator('&'); } - if (is_operator(op_len,1,"&", post_op_state)) { return operand_prefix_warn(AMBI_ARG(),pre_op_state,'&',tAMPER); } - if (is_operator(op_len,3,"||=", EXPR_BEG )) { return assignment_operator(tOROP); } - if (is_operator(op_len,2,"||", post_op_state)) { return tOROP; } - if (is_operator(op_len,2,"|=", EXPR_BEG )) { return assignment_operator('|'); } - if (is_operator(op_len,1,"|", post_op_state)) { return '|'; } - if (is_operator(op_len,2,"+@", EXPR_ARG )) { return tUPLUS; } - if (is_operator(op_len,2,"+=", EXPR_BEG )) { return assignment_operator('+'); } - if (is_operator(op_len,1,"+", post_op_state)) { return '+'; } - if (is_operator(op_len,2,"-@", EXPR_ARG )) { return tUMINUS; } - if (is_operator(op_len,2,"-=", EXPR_BEG )) { return assignment_operator('-'); } - if (is_operator(op_len,1,"-", post_op_state)) { return '-'; } - if (is_operator(op_len,2,"/=", EXPR_BEG )) { return assignment_operator('/'); } - if (is_operator(op_len,1,"/", post_op_state)) { return '/'; } - if (is_operator(op_len,2,"^=", EXPR_BEG )) { return assignment_operator('^'); } - if (is_operator(op_len,1,"^", post_op_state)) { return '^'; } - if (is_operator(op_len,2,"~@", post_op_state)) { return '~'; } - if (is_operator(op_len,1,"~", post_op_state)) { return '~'; } - if (is_operator(op_len,3,"...", EXPR_BEG )) { return tDOT3; } - if (is_operator(op_len,2,"..", EXPR_BEG )) { return tDOT2; } - if (is_operator(op_len,1,".", EXPR_DOT )) { return '.'; } - if (is_operator(op_len,2,"%=", EXPR_BEG )) { return assignment_operator('%'); } - if (is_operator(op_len,1,"%", post_op_state)) { return '%'; } - if (op_len > 0) { - int i=0; - newtok(); - for (i=0;i floating literal anymore; put 0 before dot"); + } + lex_state = EXPR_DOT; + return '.'; + start_num: case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -3964,12 +3981,67 @@ lex_state = EXPR_FNAME; return tSYMBEG; + case '/': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + lex_strterm = NEW_STRTERM(str_regexp, '/', 0); + return tREGEXP_BEG; + } + if ((c = nextc()) == '=') { + yylval.id = '/'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + if (IS_ARG() && space_seen) { + if (!ISSPACE(c)) { + arg_ambiguous(); + lex_strterm = NEW_STRTERM(str_regexp, '/', 0); + return tREGEXP_BEG; + } + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + return '/'; + + case '^': + if ((c = nextc()) == '=') { + yylval.id = '^'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + pushback(c); + return '^'; + case ';': command_start = Qtrue; case ',': lex_state = EXPR_BEG; return c; + case '~': + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + if ((c = nextc()) != '@') { + pushback(c); + } + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + return '~'; + case '(': command_start = Qtrue; if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { @@ -4034,6 +4106,91 @@ pushback(c); return '\\'; + case '%': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + int term; + int paren; + + c = nextc(); + quotation: + if (!ISALNUM(c)) { + term = c; + c = 'Q'; + } + else { + term = nextc(); + if (ISALNUM(term) || ismbchar(term)) { + yyerror("unknown type of %string"); + return 0; + } + } + if (c == -1 || term == -1) { + rb_compile_error("unterminated quoted string meets end of file"); + return 0; + } + paren = term; + if (term == '(') term = ')'; + else if (term == '[') term = ']'; + else if (term == '{') term = '}'; + else if (term == '<') term = '>'; + else paren = 0; + + switch (c) { + case 'Q': + lex_strterm = NEW_STRTERM(str_dquote, term, paren); + return tSTRING_BEG; + + case 'q': + lex_strterm = NEW_STRTERM(str_squote, term, paren); + return tSTRING_BEG; + + case 'W': + lex_strterm = NEW_STRTERM(str_dquote | STR_FUNC_QWORDS, term, paren); + do {c = nextc();} while (ISSPACE(c)); + pushback(c); + return tWORDS_BEG; + + case 'w': + lex_strterm = NEW_STRTERM(str_squote | STR_FUNC_QWORDS, term, paren); + do {c = nextc();} while (ISSPACE(c)); + pushback(c); + return tQWORDS_BEG; + + case 'x': + lex_strterm = NEW_STRTERM(str_xquote, term, paren); + return tXSTRING_BEG; + + case 'r': + lex_strterm = NEW_STRTERM(str_regexp, term, paren); + return tREGEXP_BEG; + + case 's': + lex_strterm = NEW_STRTERM(str_ssym, term, paren); + lex_state = EXPR_FNAME; + return tSYMBEG; + + default: + yyerror("unknown type of %string"); + return 0; + } + } + if ((c = nextc()) == '=') { + yylval.id = '%'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (IS_ARG() && space_seen && !ISSPACE(c)) { + goto quotation; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + pushback(c); + return '%'; + case '$': lex_state = EXPR_END; newtok(); @@ -4886,7 +5043,6 @@ case '^': case '&': case tCMP: - case tGENERIC_OP: case '>': case tGEQ: case '<': --=-bGfylPCk97kwreyt0ejI--