diff options
author | Kevin Newton <kddnewton@gmail.com> | 2023-09-27 12:22:36 -0400 |
---|---|---|
committer | Kevin Newton <kddnewton@gmail.com> | 2023-09-27 13:57:38 -0400 |
commit | 8ab56869a64fdccc094f4a83c6367fb23b72d38b (patch) | |
tree | 46ef2bd5c51d5b7f923eda6a60edefc7a08200db /test/prism/desugar_compiler_test.rb | |
parent | 7e0971eb5d679bb6219abb0ec238139aa6502c5a (diff) | |
download | ruby-8ab56869a64fdccc094f4a83c6367fb23b72d38b.tar.gz |
Rename YARP filepaths to prism filepaths
Diffstat (limited to 'test/prism/desugar_compiler_test.rb')
-rw-r--r-- | test/prism/desugar_compiler_test.rb | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/test/prism/desugar_compiler_test.rb b/test/prism/desugar_compiler_test.rb new file mode 100644 index 0000000000..8d2b207fed --- /dev/null +++ b/test/prism/desugar_compiler_test.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require_relative "test_helper" + +module YARP + class DesugarCompilerTest < TestCase + def test_and_write + assert_desugars("(AndNode (ClassVariableReadNode) (ClassVariableWriteNode (CallNode)))", "@@foo &&= bar") + assert_not_desugared("Foo::Bar &&= baz", "Desugaring would execute Foo twice or need temporary variables") + assert_desugars("(AndNode (ConstantReadNode) (ConstantWriteNode (CallNode)))", "Foo &&= bar") + assert_desugars("(AndNode (GlobalVariableReadNode) (GlobalVariableWriteNode (CallNode)))", "$foo &&= bar") + assert_desugars("(AndNode (InstanceVariableReadNode) (InstanceVariableWriteNode (CallNode)))", "@foo &&= bar") + assert_desugars("(AndNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo &&= bar") + assert_desugars("(AndNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo = 1; foo &&= bar") + end + + def test_or_write + assert_desugars("(IfNode (DefinedNode (ClassVariableReadNode)) (StatementsNode (ClassVariableReadNode)) (ElseNode (StatementsNode (ClassVariableWriteNode (CallNode)))))", "@@foo ||= bar") + assert_not_desugared("Foo::Bar ||= baz", "Desugaring would execute Foo twice or need temporary variables") + assert_desugars("(IfNode (DefinedNode (ConstantReadNode)) (StatementsNode (ConstantReadNode)) (ElseNode (StatementsNode (ConstantWriteNode (CallNode)))))", "Foo ||= bar") + assert_desugars("(IfNode (DefinedNode (GlobalVariableReadNode)) (StatementsNode (GlobalVariableReadNode)) (ElseNode (StatementsNode (GlobalVariableWriteNode (CallNode)))))", "$foo ||= bar") + assert_desugars("(OrNode (InstanceVariableReadNode) (InstanceVariableWriteNode (CallNode)))", "@foo ||= bar") + assert_desugars("(OrNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo ||= bar") + assert_desugars("(OrNode (LocalVariableReadNode) (LocalVariableWriteNode (CallNode)))", "foo = 1; foo ||= bar") + end + + def test_operator_write + assert_desugars("(ClassVariableWriteNode (CallNode (ClassVariableReadNode) (ArgumentsNode (CallNode))))", "@@foo += bar") + assert_not_desugared("Foo::Bar += baz", "Desugaring would execute Foo twice or need temporary variables") + assert_desugars("(ConstantWriteNode (CallNode (ConstantReadNode) (ArgumentsNode (CallNode))))", "Foo += bar") + assert_desugars("(GlobalVariableWriteNode (CallNode (GlobalVariableReadNode) (ArgumentsNode (CallNode))))", "$foo += bar") + assert_desugars("(InstanceVariableWriteNode (CallNode (InstanceVariableReadNode) (ArgumentsNode (CallNode))))", "@foo += bar") + assert_desugars("(LocalVariableWriteNode (CallNode (LocalVariableReadNode) (ArgumentsNode (CallNode))))", "foo += bar") + assert_desugars("(LocalVariableWriteNode (CallNode (LocalVariableReadNode) (ArgumentsNode (CallNode))))", "foo = 1; foo += bar") + end + + private + + def ast_inspect(node) + parts = [node.class.name.split("::").last] + + node.deconstruct_keys(nil).each do |_, value| + case value + when Node + parts << ast_inspect(value) + when Array + parts.concat(value.map { |element| ast_inspect(element) }) + end + end + + "(#{parts.join(" ")})" + end + + # Ensure every node is only present once in the AST. + # If the same node is present twice it would most likely indicate it is executed twice, which is invalid semantically. + # This also acts as a sanity check that Node#child_nodes returns only nodes or nil (which caught a couple bugs). + class EnsureEveryNodeOnceInAST < Visitor + def initialize + @all_nodes = {}.compare_by_identity + end + + def visit(node) + if node + if @all_nodes.include?(node) + raise "#{node.inspect} is present multiple times in the desugared AST and likely executed multiple times" + else + @all_nodes[node] = true + end + end + super(node) + end + end + + def assert_desugars(expected, source) + ast = YARP.parse(source).value.accept(DesugarCompiler.new) + assert_equal expected, ast_inspect(ast.statements.body.last) + + ast.accept(EnsureEveryNodeOnceInAST.new) + end + + def assert_not_desugared(source, reason) + ast = YARP.parse(source).value + assert_equal_nodes(ast, ast.accept(DesugarCompiler.new)) + end + end +end |