diff --git a/config.yml b/config.yml index d8a10bc113..c82c239de6 100644 --- a/config.yml +++ b/config.yml @@ -494,6 +494,8 @@ tokens: comment: "def" - name: KEYWORD_DEFINED comment: "defined?" + - name: KEYWORD_DO_BLOCK + comment: "do keyword for a block attached to a command" - name: KEYWORD_DO_LOOP comment: "do keyword for a predicate in a while, until, or for loop" - name: KEYWORD_END_UPCASE diff --git a/include/prism/parser.h b/include/prism/parser.h index c76fba58cf..ed4871197c 100644 --- a/include/prism/parser.h +++ b/include/prism/parser.h @@ -885,6 +885,13 @@ struct pm_parser { /** Whether or not we're at the beginning of a command. */ bool command_start; + /** + * Whether or not we're currently parsing the body of an endless method + * definition. In this context, PM_TOKEN_KEYWORD_DO_BLOCK should not be + * consumed by commands (it should bubble up to the outer context). + */ + bool in_endless_def_body; + /** Whether or not we're currently recovering from a syntax error. */ bool recovering; diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index 99d8daacdd..0bc56ec592 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -134,6 +134,7 @@ def deconstruct_keys(keys) # :nodoc: KEYWORD_DEF: :on_kw, KEYWORD_DEFINED: :on_kw, KEYWORD_DO: :on_kw, + KEYWORD_DO_BLOCK: :on_kw, KEYWORD_DO_LOOP: :on_kw, KEYWORD_ELSE: :on_kw, KEYWORD_ELSIF: :on_kw, diff --git a/lib/prism/translation/parser/lexer.rb b/lib/prism/translation/parser/lexer.rb index 8e18a3cd1e..e82042867f 100644 --- a/lib/prism/translation/parser/lexer.rb +++ b/lib/prism/translation/parser/lexer.rb @@ -87,6 +87,7 @@ class Lexer # :nodoc: KEYWORD_DEF: :kDEF, KEYWORD_DEFINED: :kDEFINED, KEYWORD_DO: :kDO, + KEYWORD_DO_BLOCK: :kDO_BLOCK, KEYWORD_DO_LOOP: :kDO_COND, KEYWORD_END: :kEND, KEYWORD_END_UPCASE: :klEND, diff --git a/snapshots/4.0/endless_methods_command_call.txt b/snapshots/4.0/endless_methods_command_call.txt index d2c58ec8b0..cb819358ef 100644 --- a/snapshots/4.0/endless_methods_command_call.txt +++ b/snapshots/4.0/endless_methods_command_call.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(8,31)) +@ ProgramNode (location: (1,0)-(11,37)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(8,31)) + @ StatementsNode (location: (1,0)-(11,37)) ├── flags: ∅ - └── body: (length: 8) + └── body: (length: 10) ├── @ CallNode (location: (1,0)-(1,30)) │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ @@ -421,75 +421,208 @@ │ ├── closing_loc: ∅ │ ├── equal_loc: ∅ │ └── block: ∅ - └── @ CallNode (location: (8,0)-(8,31)) + ├── @ CallNode (location: (8,0)-(8,31)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :private + │ ├── message_loc: (8,0)-(8,7) = "private" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (8,8)-(8,31)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ DefNode (location: (8,8)-(8,31)) + │ │ ├── flags: ∅ + │ │ ├── name: :foo + │ │ ├── name_loc: (8,16)-(8,19) = "foo" + │ │ ├── receiver: + │ │ │ @ CallNode (location: (8,12)-(8,15)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :obj + │ │ │ ├── message_loc: (8,12)-(8,15) = "obj" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── parameters: + │ │ │ @ ParametersNode (location: (8,20)-(8,21)) + │ │ │ ├── flags: ∅ + │ │ │ ├── requireds: (length: 1) + │ │ │ │ └── @ RequiredParameterNode (location: (8,20)-(8,21)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :x + │ │ │ ├── optionals: (length: 0) + │ │ │ ├── rest: ∅ + │ │ │ ├── posts: (length: 0) + │ │ │ ├── keywords: (length: 0) + │ │ │ ├── keyword_rest: ∅ + │ │ │ └── block: ∅ + │ │ ├── body: + │ │ │ @ StatementsNode (location: (8,25)-(8,31)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (8,25)-(8,31)) + │ │ │ ├── flags: ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :puts + │ │ │ ├── message_loc: (8,25)-(8,29) = "puts" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (8,30)-(8,31)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ LocalVariableReadNode (location: (8,30)-(8,31)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── name: :x + │ │ │ │ └── depth: 0 + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: [:x] + │ │ ├── def_keyword_loc: (8,8)-(8,11) = "def" + │ │ ├── operator_loc: (8,15)-(8,16) = "." + │ │ ├── lparen_loc: (8,19)-(8,20) = "(" + │ │ ├── rparen_loc: (8,21)-(8,22) = ")" + │ │ ├── equal_loc: (8,23)-(8,24) = "=" + │ │ └── end_keyword_loc: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (10,0)-(10,25)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :private + │ ├── message_loc: (10,0)-(10,7) = "private" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (10,8)-(10,25)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ DefNode (location: (10,8)-(10,25)) + │ │ ├── flags: ∅ + │ │ ├── name: :foo + │ │ ├── name_loc: (10,12)-(10,15) = "foo" + │ │ ├── receiver: ∅ + │ │ ├── parameters: ∅ + │ │ ├── body: + │ │ │ @ StatementsNode (location: (10,18)-(10,25)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (10,18)-(10,25)) + │ │ │ ├── flags: ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :bar + │ │ │ ├── message_loc: (10,18)-(10,21) = "bar" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (10,22)-(10,25)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ CallNode (location: (10,22)-(10,25)) + │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── name: :baz + │ │ │ │ ├── message_loc: (10,22)-(10,25) = "baz" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── equal_loc: ∅ + │ │ │ │ └── block: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: [] + │ │ ├── def_keyword_loc: (10,8)-(10,11) = "def" + │ │ ├── operator_loc: ∅ + │ │ ├── lparen_loc: ∅ + │ │ ├── rparen_loc: ∅ + │ │ ├── equal_loc: (10,16)-(10,17) = "=" + │ │ └── end_keyword_loc: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + └── @ CallNode (location: (11,0)-(11,37)) ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :private - ├── message_loc: (8,0)-(8,7) = "private" + ├── message_loc: (11,0)-(11,7) = "private" ├── opening_loc: ∅ ├── arguments: - │ @ ArgumentsNode (location: (8,8)-(8,31)) + │ @ ArgumentsNode (location: (11,8)-(11,25)) │ ├── flags: ∅ │ └── arguments: (length: 1) - │ └── @ DefNode (location: (8,8)-(8,31)) + │ └── @ DefNode (location: (11,8)-(11,25)) │ ├── flags: ∅ │ ├── name: :foo - │ ├── name_loc: (8,16)-(8,19) = "foo" - │ ├── receiver: - │ │ @ CallNode (location: (8,12)-(8,15)) - │ │ ├── flags: variable_call, ignore_visibility - │ │ ├── receiver: ∅ - │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :obj - │ │ ├── message_loc: (8,12)-(8,15) = "obj" - │ │ ├── opening_loc: ∅ - │ │ ├── arguments: ∅ - │ │ ├── closing_loc: ∅ - │ │ ├── equal_loc: ∅ - │ │ └── block: ∅ - │ ├── parameters: - │ │ @ ParametersNode (location: (8,20)-(8,21)) - │ │ ├── flags: ∅ - │ │ ├── requireds: (length: 1) - │ │ │ └── @ RequiredParameterNode (location: (8,20)-(8,21)) - │ │ │ ├── flags: ∅ - │ │ │ └── name: :x - │ │ ├── optionals: (length: 0) - │ │ ├── rest: ∅ - │ │ ├── posts: (length: 0) - │ │ ├── keywords: (length: 0) - │ │ ├── keyword_rest: ∅ - │ │ └── block: ∅ + │ ├── name_loc: (11,12)-(11,15) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: ∅ │ ├── body: - │ │ @ StatementsNode (location: (8,25)-(8,31)) + │ │ @ StatementsNode (location: (11,18)-(11,25)) │ │ ├── flags: ∅ │ │ └── body: (length: 1) - │ │ └── @ CallNode (location: (8,25)-(8,31)) + │ │ └── @ CallNode (location: (11,18)-(11,25)) │ │ ├── flags: ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :puts - │ │ ├── message_loc: (8,25)-(8,29) = "puts" + │ │ ├── name: :bar + │ │ ├── message_loc: (11,18)-(11,21) = "bar" │ │ ├── opening_loc: ∅ │ │ ├── arguments: - │ │ │ @ ArgumentsNode (location: (8,30)-(8,31)) + │ │ │ @ ArgumentsNode (location: (11,22)-(11,25)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) - │ │ │ └── @ LocalVariableReadNode (location: (8,30)-(8,31)) - │ │ │ ├── flags: ∅ - │ │ │ ├── name: :x - │ │ │ └── depth: 0 + │ │ │ └── @ CallNode (location: (11,22)-(11,25)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :baz + │ │ │ ├── message_loc: (11,22)-(11,25) = "baz" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ │ │ ├── closing_loc: ∅ │ │ ├── equal_loc: ∅ │ │ └── block: ∅ - │ ├── locals: [:x] - │ ├── def_keyword_loc: (8,8)-(8,11) = "def" - │ ├── operator_loc: (8,15)-(8,16) = "." - │ ├── lparen_loc: (8,19)-(8,20) = "(" - │ ├── rparen_loc: (8,21)-(8,22) = ")" - │ ├── equal_loc: (8,23)-(8,24) = "=" + │ ├── locals: [] + │ ├── def_keyword_loc: (11,8)-(11,11) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: ∅ + │ ├── rparen_loc: ∅ + │ ├── equal_loc: (11,16)-(11,17) = "=" │ └── end_keyword_loc: ∅ ├── closing_loc: ∅ ├── equal_loc: ∅ - └── block: ∅ + └── block: + @ BlockNode (location: (11,26)-(11,37)) + ├── flags: ∅ + ├── locals: [] + ├── parameters: ∅ + ├── body: + │ @ StatementsNode (location: (11,29)-(11,33)) + │ ├── flags: ∅ + │ └── body: (length: 1) + │ └── @ CallNode (location: (11,29)-(11,33)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :expr + │ ├── message_loc: (11,29)-(11,33) = "expr" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── opening_loc: (11,26)-(11,28) = "do" + └── closing_loc: (11,34)-(11,37) = "end" diff --git a/snapshots/blocks.txt b/snapshots/blocks.txt index 62943535e1..03148044f9 100644 --- a/snapshots/blocks.txt +++ b/snapshots/blocks.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(54,17)) +@ ProgramNode (location: (1,0)-(62,25)) ├── flags: ∅ ├── locals: [:fork] └── statements: - @ StatementsNode (location: (1,0)-(54,17)) + @ StatementsNode (location: (1,0)-(62,25)) ├── flags: ∅ - └── body: (length: 20) + └── body: (length: 24) ├── @ CallNode (location: (1,0)-(1,16)) │ ├── flags: newline │ ├── receiver: @@ -829,41 +829,284 @@ │ ├── closing_loc: ∅ │ ├── equal_loc: ∅ │ └── block: ∅ - └── @ CallNode (location: (54,0)-(54,17)) + ├── @ CallNode (location: (54,0)-(54,17)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (54,0)-(54,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (54,4)-(54,17)) + │ ├── flags: ∅ + │ ├── locals: [:bar] + │ ├── parameters: + │ │ @ BlockParametersNode (location: (54,7)-(54,13)) + │ │ ├── flags: ∅ + │ │ ├── parameters: + │ │ │ @ ParametersNode (location: (54,8)-(54,12)) + │ │ │ ├── flags: ∅ + │ │ │ ├── requireds: (length: 1) + │ │ │ │ └── @ RequiredParameterNode (location: (54,8)-(54,11)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :bar + │ │ │ ├── optionals: (length: 0) + │ │ │ ├── rest: + │ │ │ │ @ ImplicitRestNode (location: (54,11)-(54,12)) + │ │ │ │ └── flags: ∅ + │ │ │ ├── posts: (length: 0) + │ │ │ ├── keywords: (length: 0) + │ │ │ ├── keyword_rest: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: (length: 0) + │ │ ├── opening_loc: (54,7)-(54,8) = "|" + │ │ └── closing_loc: (54,12)-(54,13) = "|" + │ ├── body: ∅ + │ ├── opening_loc: (54,4)-(54,6) = "do" + │ └── closing_loc: (54,14)-(54,17) = "end" + ├── @ CallNode (location: (56,0)-(56,23)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (56,0)-(56,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (56,4)-(56,16)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (56,4)-(56,16)) + │ │ ├── flags: ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :bar + │ │ ├── message_loc: (56,4)-(56,7) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (56,8)-(56,16)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 2) + │ │ │ ├── @ CallNode (location: (56,8)-(56,11)) + │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── name: :baz + │ │ │ │ ├── message_loc: (56,8)-(56,11) = "baz" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── equal_loc: ∅ + │ │ │ │ └── block: ∅ + │ │ │ └── @ CallNode (location: (56,13)-(56,16)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :qux + │ │ │ ├── message_loc: (56,13)-(56,16) = "qux" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (56,17)-(56,23)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (56,17)-(56,19) = "do" + │ └── closing_loc: (56,20)-(56,23) = "end" + ├── @ CallNode (location: (58,0)-(58,18)) + │ ├── flags: newline + │ ├── receiver: + │ │ @ CallNode (location: (58,0)-(58,3)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :foo + │ │ ├── message_loc: (58,0)-(58,3) = "foo" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── call_operator_loc: (58,3)-(58,4) = "." + │ ├── name: :bar + │ ├── message_loc: (58,4)-(58,7) = "bar" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (58,8)-(58,11)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (58,8)-(58,11)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :baz + │ │ ├── message_loc: (58,8)-(58,11) = "baz" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (58,12)-(58,18)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (58,12)-(58,14) = "do" + │ └── closing_loc: (58,15)-(58,18) = "end" + ├── @ CallNode (location: (60,0)-(60,34)) + │ ├── flags: newline + │ ├── receiver: + │ │ @ CallNode (location: (60,0)-(60,18)) + │ │ ├── flags: ∅ + │ │ ├── receiver: + │ │ │ @ CallNode (location: (60,0)-(60,3)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :foo + │ │ │ ├── message_loc: (60,0)-(60,3) = "foo" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── call_operator_loc: (60,3)-(60,4) = "." + │ │ ├── name: :bar + │ │ ├── message_loc: (60,4)-(60,7) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (60,8)-(60,11)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 1) + │ │ │ └── @ CallNode (location: (60,8)-(60,11)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :baz + │ │ │ ├── message_loc: (60,8)-(60,11) = "baz" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: + │ │ @ BlockNode (location: (60,12)-(60,18)) + │ │ ├── flags: ∅ + │ │ ├── locals: [] + │ │ ├── parameters: ∅ + │ │ ├── body: ∅ + │ │ ├── opening_loc: (60,12)-(60,14) = "do" + │ │ └── closing_loc: (60,15)-(60,18) = "end" + │ ├── call_operator_loc: (60,18)-(60,19) = "." + │ ├── name: :qux + │ ├── message_loc: (60,19)-(60,22) = "qux" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (60,23)-(60,27)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (60,23)-(60,27)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :quux + │ │ ├── message_loc: (60,23)-(60,27) = "quux" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (60,28)-(60,34)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (60,28)-(60,30) = "do" + │ └── closing_loc: (60,31)-(60,34) = "end" + └── @ CallNode (location: (62,0)-(62,25)) ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo - ├── message_loc: (54,0)-(54,3) = "foo" + ├── message_loc: (62,0)-(62,3) = "foo" ├── opening_loc: ∅ - ├── arguments: ∅ + ├── arguments: + │ @ ArgumentsNode (location: (62,4)-(62,12)) + │ ├── flags: ∅ + │ └── arguments: (length: 2) + │ ├── @ CallNode (location: (62,4)-(62,7)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :bar + │ │ ├── message_loc: (62,4)-(62,7) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ └── @ CallNode (location: (62,9)-(62,12)) + │ ├── flags: variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :baz + │ ├── message_loc: (62,9)-(62,12) = "baz" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ ├── closing_loc: ∅ ├── equal_loc: ∅ └── block: - @ BlockNode (location: (54,4)-(54,17)) + @ BlockNode (location: (62,13)-(62,25)) ├── flags: ∅ - ├── locals: [:bar] + ├── locals: [:x] ├── parameters: - │ @ BlockParametersNode (location: (54,7)-(54,13)) + │ @ BlockParametersNode (location: (62,16)-(62,19)) │ ├── flags: ∅ │ ├── parameters: - │ │ @ ParametersNode (location: (54,8)-(54,12)) + │ │ @ ParametersNode (location: (62,17)-(62,18)) │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) - │ │ │ └── @ RequiredParameterNode (location: (54,8)-(54,11)) + │ │ │ └── @ RequiredParameterNode (location: (62,17)-(62,18)) │ │ │ ├── flags: ∅ - │ │ │ └── name: :bar + │ │ │ └── name: :x │ │ ├── optionals: (length: 0) - │ │ ├── rest: - │ │ │ @ ImplicitRestNode (location: (54,11)-(54,12)) - │ │ │ └── flags: ∅ + │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ │ ├── locals: (length: 0) - │ ├── opening_loc: (54,7)-(54,8) = "|" - │ └── closing_loc: (54,12)-(54,13) = "|" - ├── body: ∅ - ├── opening_loc: (54,4)-(54,6) = "do" - └── closing_loc: (54,14)-(54,17) = "end" + │ ├── opening_loc: (62,16)-(62,17) = "|" + │ └── closing_loc: (62,18)-(62,19) = "|" + ├── body: + │ @ StatementsNode (location: (62,20)-(62,21)) + │ ├── flags: ∅ + │ └── body: (length: 1) + │ └── @ LocalVariableReadNode (location: (62,20)-(62,21)) + │ ├── flags: newline + │ ├── name: :x + │ └── depth: 0 + ├── opening_loc: (62,13)-(62,15) = "do" + └── closing_loc: (62,22)-(62,25) = "end" diff --git a/snapshots/endless_methods.txt b/snapshots/endless_methods.txt index 938ec25a56..9334abec63 100644 --- a/snapshots/endless_methods.txt +++ b/snapshots/endless_methods.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(7,15)) +@ ProgramNode (location: (1,0)-(11,18)) ├── flags: ∅ ├── locals: [:x] └── statements: - @ StatementsNode (location: (1,0)-(7,15)) + @ StatementsNode (location: (1,0)-(11,18)) ├── flags: ∅ - └── body: (length: 4) + └── body: (length: 6) ├── @ DefNode (location: (1,0)-(1,11)) │ ├── flags: newline │ ├── name: :foo @@ -116,44 +116,128 @@ │ ├── rparen_loc: ∅ │ ├── equal_loc: (5,11)-(5,12) = "=" │ └── end_keyword_loc: ∅ - └── @ LocalVariableWriteNode (location: (7,0)-(7,15)) + ├── @ LocalVariableWriteNode (location: (7,0)-(7,15)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (7,0)-(7,1) = "x" + │ ├── value: + │ │ @ DefNode (location: (7,4)-(7,15)) + │ │ ├── flags: ∅ + │ │ ├── name: :f + │ │ ├── name_loc: (7,8)-(7,9) = "f" + │ │ ├── receiver: ∅ + │ │ ├── parameters: ∅ + │ │ ├── body: + │ │ │ @ StatementsNode (location: (7,12)-(7,15)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (7,12)-(7,15)) + │ │ │ ├── flags: ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :p + │ │ │ ├── message_loc: (7,12)-(7,13) = "p" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (7,14)-(7,15)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (7,14)-(7,15)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: [] + │ │ ├── def_keyword_loc: (7,4)-(7,7) = "def" + │ │ ├── operator_loc: ∅ + │ │ ├── lparen_loc: ∅ + │ │ ├── rparen_loc: ∅ + │ │ ├── equal_loc: (7,10)-(7,11) = "=" + │ │ └── end_keyword_loc: ∅ + │ └── operator_loc: (7,2)-(7,3) = "=" + ├── @ DefNode (location: (9,0)-(9,17)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (9,4)-(9,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (9,10)-(9,17)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ CallNode (location: (9,10)-(9,17)) + │ │ ├── flags: ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :bar + │ │ ├── message_loc: (9,10)-(9,13) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (9,14)-(9,17)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 1) + │ │ │ └── @ CallNode (location: (9,14)-(9,17)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :baz + │ │ │ ├── message_loc: (9,14)-(9,17) = "baz" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── locals: [] + │ ├── def_keyword_loc: (9,0)-(9,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: ∅ + │ ├── rparen_loc: ∅ + │ ├── equal_loc: (9,8)-(9,9) = "=" + │ └── end_keyword_loc: ∅ + └── @ DefNode (location: (11,0)-(11,18)) ├── flags: newline - ├── name: :x - ├── depth: 0 - ├── name_loc: (7,0)-(7,1) = "x" - ├── value: - │ @ DefNode (location: (7,4)-(7,15)) + ├── name: :foo + ├── name_loc: (11,4)-(11,7) = "foo" + ├── receiver: ∅ + ├── parameters: ∅ + ├── body: + │ @ StatementsNode (location: (11,10)-(11,18)) │ ├── flags: ∅ - │ ├── name: :f - │ ├── name_loc: (7,8)-(7,9) = "f" - │ ├── receiver: ∅ - │ ├── parameters: ∅ - │ ├── body: - │ │ @ StatementsNode (location: (7,12)-(7,15)) - │ │ ├── flags: ∅ - │ │ └── body: (length: 1) - │ │ └── @ CallNode (location: (7,12)-(7,15)) - │ │ ├── flags: ignore_visibility - │ │ ├── receiver: ∅ - │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :p - │ │ ├── message_loc: (7,12)-(7,13) = "p" - │ │ ├── opening_loc: ∅ - │ │ ├── arguments: - │ │ │ @ ArgumentsNode (location: (7,14)-(7,15)) - │ │ │ ├── flags: ∅ - │ │ │ └── arguments: (length: 1) - │ │ │ └── @ IntegerNode (location: (7,14)-(7,15)) - │ │ │ ├── flags: static_literal, decimal - │ │ │ └── value: 1 - │ │ ├── closing_loc: ∅ - │ │ ├── equal_loc: ∅ - │ │ └── block: ∅ - │ ├── locals: [] - │ ├── def_keyword_loc: (7,4)-(7,7) = "def" - │ ├── operator_loc: ∅ - │ ├── lparen_loc: ∅ - │ ├── rparen_loc: ∅ - │ ├── equal_loc: (7,10)-(7,11) = "=" - │ └── end_keyword_loc: ∅ - └── operator_loc: (7,2)-(7,3) = "=" + │ └── body: (length: 1) + │ └── @ CallNode (location: (11,10)-(11,18)) + │ ├── flags: ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :bar + │ ├── message_loc: (11,10)-(11,13) = "bar" + │ ├── opening_loc: (11,13)-(11,14) = "(" + │ ├── arguments: + │ │ @ ArgumentsNode (location: (11,14)-(11,17)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (11,14)-(11,17)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :baz + │ │ ├── message_loc: (11,14)-(11,17) = "baz" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: (11,17)-(11,18) = ")" + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── locals: [] + ├── def_keyword_loc: (11,0)-(11,3) = "def" + ├── operator_loc: ∅ + ├── lparen_loc: ∅ + ├── rparen_loc: ∅ + ├── equal_loc: (11,8)-(11,9) = "=" + └── end_keyword_loc: ∅ diff --git a/src/prism.c b/src/prism.c index 6f21c97bc3..efe21c5e7e 100644 --- a/src/prism.c +++ b/src/prism.c @@ -8330,9 +8330,15 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) { switch (width) { case 2: if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) { + if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) { + return PM_TOKEN_KEYWORD_DO; + } if (pm_do_loop_stack_p(parser)) { return PM_TOKEN_KEYWORD_DO_LOOP; } + if (!pm_accepts_block_stack_p(parser)) { + return PM_TOKEN_KEYWORD_DO_BLOCK; + } return PM_TOKEN_KEYWORD_DO; } @@ -12497,6 +12503,7 @@ token_begins_expression_p(pm_token_type_t type) { case PM_TOKEN_EOF: case PM_TOKEN_LAMBDA_BEGIN: case PM_TOKEN_KEYWORD_DO: + case PM_TOKEN_KEYWORD_DO_BLOCK: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_ELSE: @@ -14825,6 +14832,27 @@ parse_block(pm_parser_t *parser, uint16_t depth) { return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous); } +/** + * Attach a do-block (PM_TOKEN_KEYWORD_DO_BLOCK) to a command-style call node. + * The current token must be PM_TOKEN_KEYWORD_DO_BLOCK when this is called. + */ +static void +parse_command_do_block(pm_parser_t *parser, pm_call_node_t *call, uint16_t depth) { + parser_lex(parser); + pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1)); + + if (call->block != NULL) { + pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI); + if (call->arguments == NULL) { + call->arguments = pm_arguments_node_create(parser); + } + pm_arguments_node_arguments_append(parser->arena, call->arguments, call->block); + } + + call->block = UP(block); + PM_NODE_LENGTH_SET_NODE(call, block); +} + /** * Parse a list of arguments and their surrounding parentheses if they are * present. It returns true if it found any pieces of arguments (parentheses, @@ -14833,6 +14861,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) { static bool parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_block, bool accepts_command_call, uint16_t depth) { bool found = false; + bool parsed_command_args = false; if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { found |= true; @@ -14855,6 +14884,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } } else if (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) { found |= true; + parsed_command_args = true; pm_accepts_block_stack_push(parser, false); // If we get here, then the subsequent token cannot be used as an infix @@ -14885,6 +14915,9 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) { found |= true; block = parse_block(parser, (uint16_t) (depth + 1)); + } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && !parser->in_endless_def_body && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) { + found |= true; + block = parse_block(parser, (uint16_t) (depth + 1)); } if (block != NULL) { @@ -15300,7 +15333,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl #define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \ case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \ case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \ - case PM_TOKEN_KEYWORD_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_ELSE: \ + case PM_TOKEN_KEYWORD_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_BLOCK: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_ELSE: \ case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \ case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \ case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \ @@ -17486,7 +17519,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b element = UP(pm_keyword_hash_node_create(parser)); pm_static_literals_t hash_keys = { 0 }; - if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) { + if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_DO_BLOCK, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) { parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1)); } @@ -18895,20 +18928,30 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION; } + // Inside a def body, we push true onto the + // accepts_block_stack so that `do` is lexed as + // PM_TOKEN_KEYWORD_DO (which can only start a block for + // primary-level constructs, not commands). During command + // argument parsing, the stack is pushed to false, causing + // `do` to be lexed as PM_TOKEN_KEYWORD_DO_BLOCK, which + // is not consumed inside the endless def body and instead + // left for the outer context. + pm_accepts_block_stack_push(parser, true); + bool previous_in_endless_def_body = parser->in_endless_def_body; + parser->in_endless_def_body = true; pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); + parser->in_endless_def_body = previous_in_endless_def_body; + pm_accepts_block_stack_pop(parser); - // In an endless method definition, the body is not allowed to - // be a command with a do..end block. - if (PM_NODE_TYPE_P(statement, PM_CALL_NODE)) { - pm_call_node_t *call = (pm_call_node_t *) statement; - - if (call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) { - pm_block_node_t *block = (pm_block_node_t *) call->block; - - if (parser->start[block->opening_loc.start] != '{') { - pm_parser_err_node(parser, call->block, PM_ERR_DEF_ENDLESS_DO_BLOCK); - } - } + // If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, + // it is an error (e.g., `def f = 1 do end`). + // PM_TOKEN_KEYWORD_DO_BLOCK is intentionally not caught + // here — it should bubble up to the outer context (e.g., + // `private def f = puts "Hello" do end` where the block + // attaches to `private`). + if (accept1(parser, PM_TOKEN_KEYWORD_DO)) { + pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1)); + pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK); } if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { @@ -20066,9 +20109,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b opening = parser->previous; if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { - pm_accepts_block_stack_push(parser, true); body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1))); - pm_accepts_block_stack_pop(parser); } if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { @@ -21518,6 +21559,14 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc } break; case PM_CALL_NODE: + // A do-block can attach to a command-style call at the + // primary level. Inside an endless def body, DO_BLOCK must + // not be consumed so it can bubble up to the outer context + // (e.g., `private` in `private def f = bar baz do end`). + if (match1(parser, PM_TOKEN_KEYWORD_DO_BLOCK) && !parser->in_endless_def_body && pm_accepts_block_stack_p(parser) && pm_call_node_command_p((pm_call_node_t *) node)) { + parse_command_do_block(parser, (pm_call_node_t *) node, depth); + } + // If we have a call node, then we need to check if it looks like a // method call without parentheses that contains arguments. If it // does, then it has different rules for parsing infix operators, @@ -21573,6 +21622,13 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc } break; case PM_CALL_NODE: + // A do-block can attach to a command-style call + // produced by infix operators (e.g., dot-calls like + // `obj.method args do end`). + if (match1(parser, PM_TOKEN_KEYWORD_DO_BLOCK) && !parser->in_endless_def_body && pm_accepts_block_stack_p(parser) && pm_call_node_command_p((pm_call_node_t *) node)) { + parse_command_do_block(parser, (pm_call_node_t *) node, depth); + } + // These expressions are also statements, by virtue of the // right-hand side of the expression (i.e., the last argument to // the call node) being an implicit array. diff --git a/templates/src/token_type.c.erb b/templates/src/token_type.c.erb index 5c6f271310..94e41ec4ba 100644 --- a/templates/src/token_type.c.erb +++ b/templates/src/token_type.c.erb @@ -167,6 +167,8 @@ pm_token_type_human(pm_token_type_t token_type) { return "'defined?'"; case PM_TOKEN_KEYWORD_DO: return "'do'"; + case PM_TOKEN_KEYWORD_DO_BLOCK: + return "'do'"; case PM_TOKEN_KEYWORD_DO_LOOP: return "'do'"; case PM_TOKEN_KEYWORD_ELSE: diff --git a/test/prism/errors/def_endless_do.txt b/test/prism/errors/def_endless_do.txt index 4d786638a6..d66b7086da 100644 --- a/test/prism/errors/def_endless_do.txt +++ b/test/prism/errors/def_endless_do.txt @@ -1,3 +1,6 @@ def a = a b do 1 end - ^~~~~~~~ unexpected `do` for block in an endless method definition + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', expecting end-of-input + ^~~ unexpected 'end', ignoring it diff --git a/test/prism/fixtures/4.0/endless_methods_command_call.txt b/test/prism/fixtures/4.0/endless_methods_command_call.txt index 91a9d156d5..146a6ee579 100644 --- a/test/prism/fixtures/4.0/endless_methods_command_call.txt +++ b/test/prism/fixtures/4.0/endless_methods_command_call.txt @@ -6,3 +6,6 @@ private def foo(x) = puts x private def obj.foo = puts "Hello" private def obj.foo() = puts "Hello" private def obj.foo(x) = puts x + +private def foo = bar baz +private def foo = bar baz do expr end diff --git a/test/prism/fixtures/blocks.txt b/test/prism/fixtures/blocks.txt index e33d95c150..51ec84950c 100644 --- a/test/prism/fixtures/blocks.txt +++ b/test/prism/fixtures/blocks.txt @@ -52,3 +52,11 @@ foo lambda { | } foo do |bar,| end + +foo bar baz, qux do end + +foo.bar baz do end + +foo.bar baz do end.qux quux do end + +foo bar, baz do |x| x end diff --git a/test/prism/fixtures/endless_methods.txt b/test/prism/fixtures/endless_methods.txt index 7eb3bf4318..6e0488a5ee 100644 --- a/test/prism/fixtures/endless_methods.txt +++ b/test/prism/fixtures/endless_methods.txt @@ -5,3 +5,7 @@ def bar = A "" def method = 1 + 2 + 3 x = def f = p 1 + +def foo = bar baz + +def foo = bar(baz)