Skip to content

Commit

Permalink
Add specs for evaluation order during assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
dushyantss committed Mar 5, 2023
1 parent e7dc804 commit 9cb4a05
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 0 deletions.
74 changes: 74 additions & 0 deletions language/fixtures/variables.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,78 @@ def self.without_parenthesis a
def self.false
false
end

class EvalOrder
attr_reader :order

def initialize
@order = []
end

def reset
@order = []
end

def foo
self << "foo"
FooClass.new(self)
end

def bar
self << "bar"
BarClass.new(self)
end

def a
self << "a"
end

def b
self << "b"
end

def node
self << "node"

node = Node.new
node.left = Node.new
node.left.right = Node.new

node
end

def <<(value)
order << value
end

private

class FooClass
attr_reader :evaluator

def initialize(evaluator)
@evaluator = evaluator
end

def []=(_index, _value)
evaluator << "foo[]="
end
end

class BarClass
attr_reader :evaluator

def initialize(evaluator)
@evaluator = evaluator
end

def baz=(_value)
evaluator << "bar.baz="
end
end

class Node
attr_accessor :left, :right
end
end
end
80 changes: 80 additions & 0 deletions language/variables_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,86 @@
require_relative '../spec_helper'
require_relative 'fixtures/variables'

describe "Evaluation order during assignment" do
context "with single assignment" do
it "evaluates from left to right" do
obj = VariablesSpecs::EvalOrder.new
obj.instance_eval do
foo[0] = a
end

obj.order.should == ["foo", "a", "foo[]="]
end
end

context "with multiple assignment" do
ruby_version_is ""..."3.1" do
it "does not evaluate from left to right" do
obj = VariablesSpecs::EvalOrder.new

obj.instance_eval do
foo[0], bar.baz = a, b
end

obj.order.should == ["a", "b", "foo", "foo[]=", "bar", "bar.baz="]
end

it "cannot be used to swap variables with nested method calls" do
node = VariablesSpecs::EvalOrder.new.node

original_node = node
original_node_left = node.left
original_node_left_right = node.left.right

node.left, node.left.right, node = node.left.right, node, node.left
# Should evaluate in the order of:
# RHS: node.left.right, node, node.left
# LHS:
# * node(original_node), original_node.left = original_node_left_right
# * node(original_node), node.left(changed in the previous assignment to original_node_left_right),
# original_node_left_right.right = original_node
# * node = original_node_left

node.should == original_node_left
node.right.should_not == original_node
node.right.left.should_not == original_node_left_right
end
end

ruby_version_is "3.1" do
it "evaluates from left to right, receivers first then methods" do
obj = VariablesSpecs::EvalOrder.new
obj.instance_eval do
foo[0], bar.baz = a, b
end

obj.order.should == ["foo", "bar", "a", "b", "foo[]=", "bar.baz="]
end

it "can be used to swap variables with nested method calls" do
node = VariablesSpecs::EvalOrder.new.node

original_node = node
original_node_left = node.left
original_node_left_right = node.left.right

node.left, node.left.right, node = node.left.right, node, node.left
# Should evaluate in the order of:
# LHS: node, node.left(original_node_left)
# RHS: original_node_left_right, original_node, original_node_left
# Ops:
# * node(original_node), original_node.left = original_node_left_right
# * original_node_left.right = original_node
# * node = original_node_left

node.should == original_node_left
node.right.should == original_node
node.right.left.should == original_node_left_right
end
end
end
end

describe "Multiple assignment" do
context "with a single RHS value" do
it "assigns a simple MLHS" do
Expand Down

0 comments on commit 9cb4a05

Please sign in to comment.