Skip to content

Commit 95c1e3f

Browse files
committed
[Feature #20925] Support leading logical operators
1 parent a346dec commit 95c1e3f

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

parse.y

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6980,6 +6980,14 @@ is_identchar(struct parser_params *p, const char *ptr, const char *MAYBE_UNUSED(
69806980
return rb_enc_isalnum((unsigned char)*ptr, enc) || *ptr == '_' || !ISASCII(*ptr);
69816981
}
69826982

6983+
static inline bool
6984+
peek_ident_p(struct parser_params *p, size_t n)
6985+
{
6986+
const char *ptr = p->lex.pcur + n;
6987+
if (lex_eol_ptr_p(p, ptr)) return false;
6988+
return is_identchar(p, ptr, p->lex.pend, p->enc);
6989+
}
6990+
69836991
static inline int
69846992
parser_is_identchar(struct parser_params *p)
69856993
{
@@ -10551,15 +10559,36 @@ parser_yylex(struct parser_params *p)
1055110559
token_flush(p);
1055210560
}
1055310561
goto retry;
10562+
case 'a':
10563+
dispatch_delayed_token(p, tIGNORED_NL);
10564+
if (peek_n(p, 'n', 0) && peek_n(p, 'd', 1) && !peek_ident_p(p, 2)) {
10565+
goto fluent;
10566+
}
10567+
goto bol;
10568+
case 'o':
10569+
dispatch_delayed_token(p, tIGNORED_NL);
10570+
if (peek_n(p, 'r', 0) && !peek_ident_p(p, 1)) {
10571+
goto fluent;
10572+
}
10573+
goto bol;
10574+
10575+
case '|':
10576+
dispatch_delayed_token(p, tIGNORED_NL);
10577+
if (peek(p, '|')) {
10578+
goto fluent;
10579+
}
10580+
goto bol;
1055410581
case '&':
1055510582
case '.': {
1055610583
dispatch_delayed_token(p, tIGNORED_NL);
10557-
if (peek(p, '.') == (c == '&')) {
10584+
if (peek(p, '.') == (c == '&') || ((c == '&') && peek(p, '&'))) {
10585+
fluent:
1055810586
pushback(p, c);
1055910587
dispatch_scan_event(p, tSP);
1056010588
goto retry;
1056110589
}
1056210590
}
10591+
bol:
1056310592
default:
1056410593
p->ruby_sourceline--;
1056510594
p->lex.nextline = p->lex.lastline;

test/ruby/test_syntax.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,56 @@ def test_fluent_dot
12591259
assert_valid_syntax("a #\n#\n&.foo\n")
12601260
end
12611261

1262+
def test_fluent_and
1263+
omit if /\+PRISM\b/ =~ RUBY_DESCRIPTION
1264+
1265+
assert_valid_syntax("a\n" "&& foo")
1266+
assert_valid_syntax("a\n" "and foo")
1267+
1268+
assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
1269+
begin;
1270+
a = true
1271+
if a
1272+
&& (a = :ok; true)
1273+
a
1274+
end
1275+
end;
1276+
1277+
assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
1278+
begin;
1279+
a = true
1280+
if a
1281+
and (a = :ok; true)
1282+
a
1283+
end
1284+
end;
1285+
end
1286+
1287+
def test_fluent_or
1288+
omit if /\+PRISM\b/ =~ RUBY_DESCRIPTION
1289+
1290+
assert_valid_syntax("a\n" "|| foo")
1291+
assert_valid_syntax("a\n" "or foo")
1292+
1293+
assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
1294+
begin;
1295+
a = false
1296+
if a
1297+
|| (a = :ok; true)
1298+
a
1299+
end
1300+
end;
1301+
1302+
assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
1303+
begin;
1304+
a = false
1305+
if a
1306+
or (a = :ok; true)
1307+
a
1308+
end
1309+
end;
1310+
end
1311+
12621312
def test_safe_call_in_massign_lhs
12631313
assert_syntax_error("*a&.x=0", /multiple assignment destination/)
12641314
assert_syntax_error("a&.x,=0", /multiple assignment destination/)

0 commit comments

Comments
 (0)