diff --git a/parse.y b/parse.y index b72a71f502..8300c38b84 100644 --- a/parse.y +++ b/parse.y @@ -609,6 +609,12 @@ rb_strterm_mark(VALUE obj) } #endif +#define yytnamerr(yyres, yystr) (YYSIZE_T)rb_yytnamerr(yyres, yystr) +size_t rb_yytnamerr(char *yyres, const char *yystr); +#ifdef RIPPER +#define yystpcpy Not used +#endif + #define TOKEN2ID(tok) ( \ tTOKEN_LOCAL_BEGIN<(tok)&&(tok) - keyword_class "class" - keyword_module "module" - keyword_def "def" - keyword_undef "undef" - keyword_begin "begin" - keyword_rescue "rescue" - keyword_ensure "ensure" - keyword_end "end" - keyword_if "if" - keyword_unless "unless" - keyword_then "then" - keyword_elsif "elsif" - keyword_else "else" - keyword_case "case" - keyword_when "when" - keyword_while "while" - keyword_until "until" - keyword_for "for" - keyword_break "break" - keyword_next "next" - keyword_redo "redo" - keyword_retry "retry" - keyword_in "in" - keyword_do "do" - keyword_do_cond "do (for condition)" - keyword_do_block "do (for block)" - keyword_do_LAMBDA "do (for lambda)" - keyword_return "return" - keyword_yield "yield" - keyword_super "super" - keyword_self "self" - keyword_nil "nil" - keyword_true "true" - keyword_false "false" - keyword_and "and" - keyword_or "or" - keyword_not "not" - modifier_if "if (modifier)" - modifier_unless "unless (modifier)" - modifier_while "while (modifier)" - modifier_until "until (modifier)" - modifier_rescue "rescue (modifier)" - keyword_alias "alias" - keyword_defined "defined?" - keyword_BEGIN "BEGIN" - keyword_END "END" - keyword__LINE__ "__LINE__" - keyword__FILE__ "__FILE__" - keyword__ENCODING__ "__ENCODING__" + keyword_class "`class'" + keyword_module "`module'" + keyword_def "`def'" + keyword_undef "`undef'" + keyword_begin "`begin'" + keyword_rescue "`rescue'" + keyword_ensure "`ensure'" + keyword_end "`end'" + keyword_if "`if'" + keyword_unless "`unless'" + keyword_then "`then'" + keyword_elsif "`elsif'" + keyword_else "`else'" + keyword_case "`case'" + keyword_when "`when'" + keyword_while "`while'" + keyword_until "`until'" + keyword_for "`for'" + keyword_break "`break'" + keyword_next "`next'" + keyword_redo "`redo'" + keyword_retry "`retry'" + keyword_in "`in'" + keyword_do "`do'" + keyword_do_cond "`do' for condition" + keyword_do_block "`do' for block" + keyword_do_LAMBDA "`do' for lambda" + keyword_return "`return'" + keyword_yield "`yield'" + keyword_super "`super'" + keyword_self "`self'" + keyword_nil "`nil'" + keyword_true "`true'" + keyword_false "`false'" + keyword_and "`and'" + keyword_or "`or'" + keyword_not "`not'" + modifier_if "`if' modifier" + modifier_unless "`unless' modifier" + modifier_while "`while' modifier" + modifier_until "`until' modifier" + modifier_rescue "`rescue' modifier" + keyword_alias "`alias'" + keyword_defined "`defined?'" + keyword_BEGIN "`BEGIN'" + keyword_END "`END'" + keyword__LINE__ "`__LINE__'" + keyword__FILE__ "`__FILE__'" + keyword__ENCODING__ "`__ENCODING__'" %token tIDENTIFIER "local variable or method" %token tFID "method" @@ -11177,6 +11183,78 @@ parser_compile_error(struct parser_params *p, const char *fmt, ...) p->enc, fmt, ap); va_end(ap); } + +static size_t +count_char(const char *str, int c) +{ + int n = 0; + while (str[n] == c) ++n; + return n; +} + +/* + * strip enclosing double-quotes, same as the default yytnamerr except + * for that single-quotes matching back-quotes do not stop stripping. + * + * "\"`class' keyword\"" => "`class' keyword" + */ +RUBY_FUNC_EXPORTED size_t +rb_yytnamerr(char *yyres, const char *yystr) +{ + if (*yystr == '"') { + size_t yyn = 0, bquote = 0; + const char *yyp = yystr; + + while (*++yyp) { + switch (*yyp) { + case '`': + if (!bquote) { + bquote = count_char(yyp+1, '`') + 1; + if (yyres) memcpy(&yyres[yyn], yyp, bquote); + yyn += bquote; + yyp += bquote - 1; + break; + } + goto default_char; + + case '\'': + if (bquote && count_char(yyp+1, '\'') + 1 == bquote) { + if (yyres) memcpy(yyres + yyn, yyp, bquote); + yyn += bquote; + yyp += bquote - 1; + bquote = 0; + break; + } + goto do_not_strip_quotes; + + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default_char: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + case '\0': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + } + do_not_strip_quotes: ; + } + + if (!yyres) return strlen(yystr); + + return (YYSIZE_T)(yystpcpy(yyres, yystr) - yyres); +} #endif #ifdef RIPPER diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 8cbb05a449..3d8f6fb72a 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -995,7 +995,7 @@ eom end def test_parenthesised_statement_argument - assert_syntax_error("foo(bar rescue nil)", /unexpected rescue \(modifier\)/) + assert_syntax_error("foo(bar rescue nil)", /unexpected `rescue' modifier/) assert_valid_syntax("foo (bar rescue nil)") end