Skip to content

Commit 177f973

Browse files
committed
Support leading logical operators
1 parent a08cde8 commit 177f973

File tree

7 files changed

+184
-7
lines changed

7 files changed

+184
-7
lines changed

src/prism.c

+76-7
Original file line numberDiff line numberDiff line change
@@ -10829,14 +10829,37 @@ parser_lex(pm_parser_t *parser) {
1082910829
following = next_newline(following, parser->end - following);
1083010830
}
1083110831

10832-
// If the lex state was ignored, or we hit a '.' or a '&.',
10833-
// we will lex the ignored newline
10832+
// If the lex state was ignored, we will lex the
10833+
// ignored newline.
10834+
if (lex_state_ignored_p(parser)) {
10835+
if (!lexed_comment) parser_lex_ignored_newline(parser);
10836+
lexed_comment = false;
10837+
goto lex_next_token;
10838+
}
10839+
10840+
// If we hit a '.' or a '&.' we will lex the ignored
10841+
// newline.
10842+
if (following && (
10843+
(peek_at(parser, following) == '.') ||
10844+
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10845+
)) {
10846+
if (!lexed_comment) parser_lex_ignored_newline(parser);
10847+
lexed_comment = false;
10848+
goto lex_next_token;
10849+
}
10850+
10851+
10852+
// If we are parsing as CRuby 3.5 or later and we
10853+
// hit a '&&' or a '||' then we will lex the ignored
10854+
// newline.
1083410855
if (
10835-
lex_state_ignored_p(parser) ||
10836-
(following && (
10837-
(peek_at(parser, following) == '.') ||
10838-
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10839-
))
10856+
(parser->version == PM_OPTIONS_VERSION_LATEST) &&
10857+
following && (
10858+
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10859+
(peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10860+
(peek_at(parser, following) == 'a' && peek_at(parser, following + 1) == 'n' && peek_at(parser, following + 2) == 'd') ||
10861+
(peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r')
10862+
)
1084010863
) {
1084110864
if (!lexed_comment) parser_lex_ignored_newline(parser);
1084210865
lexed_comment = false;
@@ -10876,6 +10899,52 @@ parser_lex(pm_parser_t *parser) {
1087610899
parser->next_start = NULL;
1087710900
LEX(PM_TOKEN_AMPERSAND_DOT);
1087810901
}
10902+
10903+
if (parser->version == PM_OPTIONS_VERSION_LATEST) {
10904+
// If we hit an && then we are in a logical chain
10905+
// and we need to return the logical operator.
10906+
if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10907+
if (!lexed_comment) parser_lex_ignored_newline(parser);
10908+
lex_state_set(parser, PM_LEX_STATE_BEG);
10909+
parser->current.start = next_content;
10910+
parser->current.end = next_content + 2;
10911+
parser->next_start = NULL;
10912+
LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10913+
}
10914+
10915+
// If we hit an 'and' then we are in a logical chain
10916+
// and we need to return the logical operator.
10917+
if (peek_at(parser, next_content) == 'a' && peek_at(parser, next_content + 1) == 'n' && peek_at(parser, next_content + 2) == 'd') {
10918+
if (!lexed_comment) parser_lex_ignored_newline(parser);
10919+
lex_state_set(parser, PM_LEX_STATE_BEG);
10920+
parser->current.start = next_content;
10921+
parser->current.end = next_content + 3;
10922+
parser->next_start = NULL;
10923+
LEX(PM_TOKEN_KEYWORD_AND);
10924+
}
10925+
10926+
// If we hit a || then we are in a logical chain and
10927+
// we need to return the logical operator.
10928+
if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10929+
if (!lexed_comment) parser_lex_ignored_newline(parser);
10930+
lex_state_set(parser, PM_LEX_STATE_BEG);
10931+
parser->current.start = next_content;
10932+
parser->current.end = next_content + 2;
10933+
parser->next_start = NULL;
10934+
LEX(PM_TOKEN_PIPE_PIPE);
10935+
}
10936+
10937+
// If we hit a 'or' then we are in a logical chain
10938+
// and we need to return the logical operator.
10939+
if (peek_at(parser, next_content) == 'o' && peek_at(parser, next_content + 1) == 'r') {
10940+
if (!lexed_comment) parser_lex_ignored_newline(parser);
10941+
lex_state_set(parser, PM_LEX_STATE_BEG);
10942+
parser->current.start = next_content;
10943+
parser->current.end = next_content + 2;
10944+
parser->next_start = NULL;
10945+
LEX(PM_TOKEN_KEYWORD_OR);
10946+
}
10947+
}
1087910948
}
1088010949

1088110950
// At this point we know this is a regular newline, and we can set the
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
1
2+
&& 2
3+
&& 3
4+
5+
1
6+
|| 2
7+
|| 3
8+
9+
1
10+
and 2
11+
and 3
12+
13+
1
14+
or 2
15+
or 3

test/prism/fixtures_test.rb

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class FixturesTest < TestCase
1414
# https://bugs.ruby-lang.org/issues/19539
1515
except << "heredocs_leading_whitespace.txt" if RUBY_VERSION < "3.3.0"
1616

17+
except << "leading_logical.txt" if RUBY_VERSION < "3.5.0"
18+
1719
Fixture.each(except: except) do |fixture|
1820
define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }
1921
end

test/prism/lex_test.rb

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class LexTest < TestCase
3030
except << "heredocs_leading_whitespace.txt"
3131
end
3232

33+
if RUBY_VERSION < "3.5.0"
34+
except << "leading_logical.txt"
35+
end
36+
3337
Fixture.each(except: except) do |fixture|
3438
define_method(fixture.test_name) { assert_lex(fixture) }
3539
end

test/prism/ruby/ripper_test.rb

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ module Prism
88
class RipperTest < TestCase
99
# Skip these tests that Ripper is reporting the wrong results for.
1010
incorrect = [
11+
# Not yet supported.
12+
"leading_logical.txt",
13+
1114
# Ripper incorrectly attributes the block to the keyword.
1215
"seattlerb/block_break.txt",
1316
"seattlerb/block_next.txt",

test/prism/ruby/ruby_parser_test.rb

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class RubyParserTest < TestCase
4848
"alias.txt",
4949
"dos_endings.txt",
5050
"heredocs_with_ignored_newlines.txt",
51+
"leading_logical.txt",
5152
"method_calls.txt",
5253
"methods.txt",
5354
"multi_write.txt",

test/prism/snapshots/leading_logical.txt

+83
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)