static int quoted_term; #define quoted_term_char ((unsigned char)quoted_term) #define WHEN_QUOTED_TERM(x) ((quoted_term >= 0) && (x)) #define QUOTED_TERM_P(c) WHEN_QUOTED_TERM((c) == quoted_term_char) #define STR_FUNC_ESCAPE 0x01 #define STR_FUNC_EXPAND 0x02 #define STR_FUNC_REGEXP 0x04 #define STR_FUNC_QWORDS 0x08 #define STR_FUNC_INDENT 0x20 enum string_type { str_squote = (0), str_dquote = (STR_FUNC_EXPAND), str_xquote = (STR_FUNC_ESCAPE|STR_FUNC_EXPAND), str_regexp = (STR_FUNC_REGEXP|STR_FUNC_ESCAPE|STR_FUNC_EXPAND), str_sword = (STR_FUNC_QWORDS), str_dword = (STR_FUNC_QWORDS|STR_FUNC_EXPAND), }; #define NEW_STRTERM(func, term, paren) \ rb_node_newnode(NODE_STRTERM, (func), (term), (paren)) static int parse_string(quote) NODE *quote; { int func = quote->nd_func; int term = quote->nd_term; int paren = quote->nd_paren; int c, space = 0; c = nextc(); if (c == term) { if (!lex_strnest) { eos: return tSTRING_END; } } if (c == '\\' && WHEN_QUOTED_TERM(peek(quoted_term_char))) { if ((c = nextc()) == term) goto eos; } if (space) { pushback(c); return ' '; } newtok(); pushback(c); if (tokadd_string(func, term, paren) == -1) { ruby_sourceline = nd_line(quote); rb_compile_error("unterminated string meets end of file"); return tSTRING_END; } tokfix(); yylval.node = NEW_STR(rb_str_new(tok(), toklen())); return tSTRING_CONTENT; } static int tokadd_string(func, term, paren) int func, term, paren; { int c; while ((c = nextc()) != -1) { if (paren && c == paren) { lex_strnest++; } else if (c == term) { if (!lex_strnest) { pushback(c); break; } --lex_strnest; } else if (c == '\\') { c = nextc(); if (QUOTED_TERM_P(c)) { pushback(c); return c; } switch (c) { case '\n': continue; case '\\': break; default: if (c != term && !(paren && c == paren)) { tokadd('\\'); } } } else if (ismbchar(c)) { int i, len = mbclen(c)-1; for (i = 0; i < len; i++) { tokadd(c); c = nextc(); } } tokadd(c); } return c; }