Skip to content

Commit

Permalink
- builder.rb, processor.rb: Changed format of the procarg0 node. (#587)
Browse files Browse the repository at this point in the history
Previously a single non-mlhs argument was emitted as `s(:procarg0, :a)`,
this commit introduces a compatibility flag `Parser::Builders::Default.emit_arg_inside_procarg0`
that causes a single argument to be emitted as `s(:procarg0, s(:arg, :a))`.
  • Loading branch information
iliabylich committed Jun 17, 2019
1 parent f15bc33 commit 17a66bd
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 16 deletions.
9 changes: 7 additions & 2 deletions doc/AST_FORMAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -933,10 +933,15 @@ the sole argument (e.g. `|foo,|`).
Format:

~~~
(procarg0 :foo)
(procarg0 (arg :foo))
"|foo|"
~~~ expression
~~~ name
(procarg0 (arg :foo) (arg :bar))
"|(foo, bar)|"
~ begin
~ end
~~~~~~~~~~ expression
~~~

### Expression arguments
Expand Down
17 changes: 16 additions & 1 deletion lib/parser/ast/processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,22 @@ def process_argument_node(node)
alias on_kwarg process_argument_node
alias on_kwoptarg process_argument_node
alias on_kwrestarg process_argument_node
alias on_procarg0 process_argument_node

def on_procarg0(node)
if node.children[0].is_a?(Symbol)
# This branch gets executed when the builder
# is not configured to emit and 'arg' inside 'procarg0', i.e. when
# Parser::Builders::Default.emit_arg_inside_procarg0
# is set to false.
#
# If this flag is set to true this branch is unreachable.
# s(:procarg0, :a)
on_argument(node)
else
# s(:procarg0, s(:arg, :a), s(:arg, :b))
process_regular_node(node)
end
end

alias on_arg_expr process_regular_node
alias on_restarg_expr process_regular_node
Expand Down
55 changes: 45 additions & 10 deletions lib/parser/builders/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ class << self
attr_accessor :emit_index
end

class << self
##
# AST compatibility attribute; causes a single non-mlhs
# block argument to be wrapped in s(:procarg0).
#
# If set to false (the default), block arguments `|a|` are emitted as
# `s(:args, s(:procarg0, :a))`
#
# If set to true, block arguments `|a|` are emitted as
# `s(:args, s(:procarg0, s(:arg, :a))`
#
# @return [Boolean]
attr_accessor :emit_arg_inside_procarg0
end

@emit_index = false

class << self
Expand All @@ -90,6 +105,7 @@ def modernize
@emit_procarg0 = true
@emit_encoding = true
@emit_index = true
@emit_arg_inside_procarg0 = true
end
end

Expand Down Expand Up @@ -726,7 +742,12 @@ def blockarg(amper_t, name_t)

def procarg0(arg)
if self.class.emit_procarg0
arg.updated(:procarg0)
if arg.type == :arg && self.class.emit_arg_inside_procarg0
n(:procarg0, [ arg ],
Source::Map::Collection.new(nil, nil, arg.location.expression))
else
arg.updated(:procarg0)
end
else
arg
end
Expand Down Expand Up @@ -1232,18 +1253,18 @@ def check_duplicate_args(args, map={})
case this_arg.type
when :arg, :optarg, :restarg, :blockarg,
:kwarg, :kwoptarg, :kwrestarg,
:shadowarg, :procarg0
:shadowarg

this_name, = *this_arg
check_duplicate_arg(this_arg, map)

that_arg = map[this_name]
that_name, = *that_arg
when :procarg0

if that_arg.nil?
map[this_name] = this_arg
elsif arg_name_collides?(this_name, that_name)
diagnostic :error, :duplicate_argument, nil,
this_arg.loc.name, [ that_arg.loc.name ]
if this_arg.children[0].is_a?(Symbol)
# s(:procarg0, :a)
check_duplicate_arg(this_arg, map)
else
# s(:procarg0, s(:arg, :a), ...)
check_duplicate_args(this_arg.children, map)
end

when :mlhs
Expand All @@ -1252,6 +1273,20 @@ def check_duplicate_args(args, map={})
end
end

def check_duplicate_arg(this_arg, map={})
this_name, = *this_arg

that_arg = map[this_name]
that_name, = *that_arg

if that_arg.nil?
map[this_name] = this_arg
elsif arg_name_collides?(this_name, that_name)
diagnostic :error, :duplicate_argument, nil,
this_arg.loc.name, [ that_arg.loc.name ]
end
end

def arg_name_collides?(this_name, that_name)
case @parser.version
when 18
Expand Down
39 changes: 36 additions & 3 deletions test/test_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2457,7 +2457,7 @@ def test_block_arg_combinations
# f_arg opt_f_block_arg
# f_arg tCOMMA
assert_parses_blockargs(
s(:args, s(:procarg0, :a)),
s(:args, s(:procarg0, s(:arg, :a))),
%q{|a|},
SINCE_1_9)

Expand Down Expand Up @@ -2700,6 +2700,39 @@ def test_procarg0_legacy
Parser::Builders::Default.emit_procarg0 = true
end

def test_emit_arg_inside_procarg0_legacy
Parser::Builders::Default.emit_arg_inside_procarg0 = false
assert_parses_blockargs(
s(:args,
s(:procarg0, :a)),
%q{|a|},
SINCE_1_9)
ensure
Parser::Builders::Default.emit_arg_inside_procarg0 = true
end

def test_procarg0
assert_parses(
s(:block,
s(:send, nil, :m),
s(:args,
s(:procarg0, s(:arg, :foo))), nil),
%q{m { |foo| } },
%q{ ^^^ expression (args.procarg0)},
SINCE_1_9)

assert_parses(
s(:block,
s(:send, nil, :m),
s(:args,
s(:procarg0, s(:arg, :foo), s(:arg, :bar))), nil),
%q{m { |(foo, bar)| } },
%q{ ^ begin (args.procarg0)
| ^ end (args.procarg0)
| ^^^^^^^^^^ expression (args.procarg0)},
SINCE_1_9)
end

def test_block_kwarg_combinations
# f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
assert_parses_blockargs(
Expand Down Expand Up @@ -5675,7 +5708,7 @@ def test_ruby_bug_10653
s(:send,
s(:int, 1), :tap),
s(:args,
s(:procarg0, :n)),
s(:procarg0, s(:arg, :n))),
s(:send, nil, :p,
s(:lvar, :n))),
s(:int, 0)),
Expand Down Expand Up @@ -6286,7 +6319,7 @@ def test_parser_bug_272
s(:send, nil, :a,
s(:ivar, :@b)),
s(:args,
s(:procarg0, :c)), nil),
s(:procarg0, s(:arg, :c))), nil),
%q{a @b do |c|;end},
%q{},
SINCE_1_9)
Expand Down

0 comments on commit 17a66bd

Please sign in to comment.