ext/ripper/tools/dsl.rb: Serialize dispatch calls

To avoid the unspecified behavior (the evaluation order of arguments).
In `$$ = foo(bar(), baz());`, it is unspecified which `bar` or `baz` is
called earlier.

This commit changes the code to `v1=bar(); v2=baz(); $$ = foo();`.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61991 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
mame 2018-01-20 17:45:24 +00:00
Родитель 7ba7a2f70e
Коммит d24f1fddd7
1 изменённых файлов: 22 добавлений и 4 удалений

Просмотреть файл

@ -1,7 +1,11 @@
# Simple DSL implementation for Ripper code generation # Simple DSL implementation for Ripper code generation
# #
# input: /*% ripper: stmts_add(stmts_new, void_stmt) %*/ # input: /*% ripper: stmts_add(stmts_new, void_stmt) %*/
# output: $$ = dispatch2(stmts_add, dispatch0(stmts_new), dispatch0(void_stmt)) # output:
# VALUE v1, v2;
# v1 = dispatch0(stmts_new);
# v2 = dispatch0(void_stmt);
# $$ = dispatch2(stmts_add, v1, v2);
class DSL class DSL
def initialize(code, options) def initialize(code, options)
@ -9,6 +13,7 @@ class DSL
@error = options.include?("error") @error = options.include?("error")
@brace = options.include?("brace") @brace = options.include?("brace")
@final = options.include?("final") @final = options.include?("final")
@vars = 0
# create $1 == "$1", $2 == "$2", ... # create $1 == "$1", $2 == "$2", ...
re, s = "", "" re, s = "", ""
@ -21,7 +26,8 @@ class DSL
# struct parser_params *p # struct parser_params *p
p = "p" p = "p"
@code = eval(code) @code = ""
@last_value = eval(code)
end end
attr_reader :events attr_reader :events
@ -33,17 +39,29 @@ class DSL
def generate def generate
s = "$$" s = "$$"
s = "p->result" if @final s = "p->result" if @final
s = "#{ s } = #@code;" s = "#@code#{ s }=#@last_value;"
s = "{VALUE #{ (1..@vars).map {|v| "v#{ v }" }.join(",") };#{ s }}" if @vars > 0
s << "ripper_error(p);" if @error s << "ripper_error(p);" if @error
s = "{#{ s }}" if @brace s = "{#{ s }}" if @brace
"\t\t\t#{s}" "\t\t\t#{s}"
end end
def new_var
"v#{ @vars += 1 }"
end
def method_missing(event, *args) def method_missing(event, *args)
if event.to_s =~ /!\z/ if event.to_s =~ /!\z/
event = $` event = $`
@events[event] = args.size @events[event] = args.size
"dispatch#{ args.size }(#{ [event, *args].join(", ") })" vars = []
args.each do |arg|
vars << v = new_var
@code << "#{ v }=#{ arg };"
end
v = new_var
@code << "#{ v }=dispatch#{ args.size }(#{ [event, *vars].join(",") });"
v
elsif args.empty? and /\Aid[A-Z]/ =~ event.to_s elsif args.empty? and /\Aid[A-Z]/ =~ event.to_s
event event
else else