Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions snapshots/leading_logical.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@ ProgramNode (location: (1,0)-(21,5))
├── flags: ∅
├── locals: []
└── statements:
@ StatementsNode (location: (1,0)-(21,5))
├── flags: ∅
└── body: (length: 8)
├── @ AndNode (location: (1,0)-(3,4))
│ ├── flags: newline
│ ├── left:
│ │ @ AndNode (location: (1,0)-(2,4))
│ │ ├── flags: ∅
│ │ ├── left:
│ │ │ @ IntegerNode (location: (1,0)-(1,1))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 1
│ │ ├── right:
│ │ │ @ IntegerNode (location: (2,3)-(2,4))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 2
│ │ └── operator_loc: (2,0)-(2,2) = "&&"
│ ├── right:
│ │ @ IntegerNode (location: (3,3)-(3,4))
│ │ ├── flags: static_literal, decimal
│ │ └── value: 3
│ └── operator_loc: (3,0)-(3,2) = "&&"
├── @ OrNode (location: (5,0)-(7,4))
│ ├── flags: newline
│ ├── left:
│ │ @ OrNode (location: (5,0)-(6,4))
│ │ ├── flags: ∅
│ │ ├── left:
│ │ │ @ IntegerNode (location: (5,0)-(5,1))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 1
│ │ ├── right:
│ │ │ @ IntegerNode (location: (6,3)-(6,4))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 2
│ │ └── operator_loc: (6,0)-(6,2) = "||"
│ ├── right:
│ │ @ IntegerNode (location: (7,3)-(7,4))
│ │ ├── flags: static_literal, decimal
│ │ └── value: 3
│ └── operator_loc: (7,0)-(7,2) = "||"
├── @ AndNode (location: (9,0)-(11,5))
│ ├── flags: newline
│ ├── left:
│ │ @ AndNode (location: (9,0)-(10,5))
│ │ ├── flags: ∅
│ │ ├── left:
│ │ │ @ IntegerNode (location: (9,0)-(9,1))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 1
│ │ ├── right:
│ │ │ @ IntegerNode (location: (10,4)-(10,5))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 2
│ │ └── operator_loc: (10,0)-(10,3) = "and"
│ ├── right:
│ │ @ IntegerNode (location: (11,4)-(11,5))
│ │ ├── flags: static_literal, decimal
│ │ └── value: 3
│ └── operator_loc: (11,0)-(11,3) = "and"
├── @ OrNode (location: (13,0)-(15,4))
│ ├── flags: newline
│ ├── left:
│ │ @ OrNode (location: (13,0)-(14,4))
│ │ ├── flags: ∅
│ │ ├── left:
│ │ │ @ IntegerNode (location: (13,0)-(13,1))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 1
│ │ ├── right:
│ │ │ @ IntegerNode (location: (14,3)-(14,4))
│ │ │ ├── flags: static_literal, decimal
│ │ │ └── value: 2
│ │ └── operator_loc: (14,0)-(14,2) = "or"
│ ├── right:
│ │ @ IntegerNode (location: (15,3)-(15,4))
│ │ ├── flags: static_literal, decimal
│ │ └── value: 3
│ └── operator_loc: (15,0)-(15,2) = "or"
├── @ IntegerNode (location: (17,0)-(17,1))
│ ├── flags: newline, static_literal, decimal
│ └── value: 1
├── @ CallNode (location: (18,0)-(18,6))
│ ├── flags: newline, variable_call, ignore_visibility
│ ├── receiver: ∅
│ ├── call_operator_loc: ∅
│ ├── name: :andfoo
│ ├── message_loc: (18,0)-(18,6) = "andfoo"
│ ├── opening_loc: ∅
│ ├── arguments: ∅
│ ├── closing_loc: ∅
│ └── block: ∅
├── @ IntegerNode (location: (20,0)-(20,1))
│ ├── flags: newline, static_literal, decimal
│ └── value: 2
└── @ CallNode (location: (21,0)-(21,5))
├── flags: newline, variable_call, ignore_visibility
├── receiver: ∅
├── call_operator_loc: ∅
├── name: :orfoo
├── message_loc: (21,0)-(21,5) = "orfoo"
├── opening_loc: ∅
├── arguments: ∅
├── closing_loc: ∅
└── block: ∅
94 changes: 87 additions & 7 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -10834,14 +10834,37 @@ parser_lex(pm_parser_t *parser) {
following = next_newline(following, parser->end - following);
}

// If the lex state was ignored, or we hit a '.' or a '&.',
// we will lex the ignored newline
// If the lex state was ignored, we will lex the
// ignored newline.
if (lex_state_ignored_p(parser)) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lexed_comment = false;
goto lex_next_token;
}

// If we hit a '.' or a '&.' we will lex the ignored
// newline.
if (following && (
(peek_at(parser, following) == '.') ||
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
Comment on lines +10848 to +10849
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to use store peek_at(parser, following) into an intermediate variable?

)) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lexed_comment = false;
goto lex_next_token;
}


// If we are parsing as CRuby 3.5 or later and we
// hit a '&&' or a '||' then we will lex the ignored
// newline.
if (
lex_state_ignored_p(parser) ||
(following && (
(peek_at(parser, following) == '.') ||
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
))
(parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) &&
following && (
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
(peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
(peek_at(parser, following) == 'a' && peek_at(parser, following + 1) == 'n' && peek_at(parser, following + 2) == 'd' && !char_is_identifier(parser, following + 3, parser->end - (following + 3))) ||
(peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r' && !char_is_identifier(parser, following + 2, parser->end - (following + 2)))
Comment on lines +10863 to +10866
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here about all the peek_at(parser, following)

)
) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lexed_comment = false;
Expand Down Expand Up @@ -10881,6 +10904,63 @@ parser_lex(pm_parser_t *parser) {
parser->next_start = NULL;
LEX(PM_TOKEN_AMPERSAND_DOT);
}

if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
// If we hit an && then we are in a logical chain
// and we need to return the logical operator.
if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lex_state_set(parser, PM_LEX_STATE_BEG);
parser->current.start = next_content;
parser->current.end = next_content + 2;
parser->next_start = NULL;
LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
}

// If we hit a || then we are in a logical chain and
// we need to return the logical operator.
if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lex_state_set(parser, PM_LEX_STATE_BEG);
parser->current.start = next_content;
parser->current.end = next_content + 2;
parser->next_start = NULL;
LEX(PM_TOKEN_PIPE_PIPE);
}

// If we hit an 'and' then we are in a logical chain
// and we need to return the logical operator.
if (
peek_at(parser, next_content) == 'a' &&
peek_at(parser, next_content + 1) == 'n' &&
peek_at(parser, next_content + 2) == 'd' &&
!char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lex_state_set(parser, PM_LEX_STATE_BEG);
parser->current.start = next_content;
parser->current.end = next_content + 3;
parser->next_start = NULL;
parser->command_start = true;
LEX(PM_TOKEN_KEYWORD_AND);
}

// If we hit a 'or' then we are in a logical chain
// and we need to return the logical operator.
if (
peek_at(parser, next_content) == 'o' &&
peek_at(parser, next_content + 1) == 'r' &&
!char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
lex_state_set(parser, PM_LEX_STATE_BEG);
parser->current.start = next_content;
parser->current.end = next_content + 2;
parser->next_start = NULL;
parser->command_start = true;
LEX(PM_TOKEN_KEYWORD_OR);
}
}
}

// At this point we know this is a regular newline, and we can set the
Expand Down
21 changes: 21 additions & 0 deletions test/prism/fixtures/leading_logical.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
1
&& 2
&& 3

1
|| 2
|| 3

1
and 2
and 3

1
or 2
or 3

1
andfoo

2
orfoo
2 changes: 2 additions & 0 deletions test/prism/fixtures_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class FixturesTest < TestCase
except << "whitequark/ruby_bug_19281.txt"
end

except << "leading_logical.txt" if RUBY_VERSION < "3.5.0"

Fixture.each(except: except) do |fixture|
define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }
end
Expand Down
3 changes: 3 additions & 0 deletions test/prism/lex_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class LexTest < TestCase
except << "whitequark/ruby_bug_19281.txt"
end

# https://bugs.ruby-lang.org/issues/20925
except << "leading_logical.txt"

Fixture.each(except: except) do |fixture|
define_method(fixture.test_name) { assert_lex(fixture) }
end
Expand Down
3 changes: 3 additions & 0 deletions test/prism/ruby/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class ParserTest < TestCase

# 1.. && 2
"ranges.txt",

# Cannot yet handling leading logical operators.
"leading_logical.txt",
]

# These files contain code that is being parsed incorrectly by the parser
Expand Down
3 changes: 3 additions & 0 deletions test/prism/ruby/ripper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ module Prism
class RipperTest < TestCase
# Skip these tests that Ripper is reporting the wrong results for.
incorrect = [
# Not yet supported.
"leading_logical.txt",

# Ripper incorrectly attributes the block to the keyword.
"seattlerb/block_break.txt",
"seattlerb/block_next.txt",
Expand Down
1 change: 1 addition & 0 deletions test/prism/ruby/ruby_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class RubyParserTest < TestCase
"dos_endings.txt",
"heredocs_with_fake_newlines.txt",
"heredocs_with_ignored_newlines.txt",
"leading_logical.txt",
"method_calls.txt",
"methods.txt",
"multi_write.txt",
Expand Down
Loading