[macruby-changes] [3420] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Feb 3 12:51:35 PST 2010


Revision: 3420
          http://trac.macosforge.org/projects/ruby/changeset/3420
Author:   martinlagardette at apple.com
Date:     2010-02-03 12:51:32 -0800 (Wed, 03 Feb 2010)
Log Message:
-----------
 - Adds the flip-flop operator (fixes #548)
 - Added some test_vm for the flip-flop too :-)

Modified Paths:
--------------
    MacRuby/trunk/compiler.cpp
    MacRuby/trunk/compiler.h
    MacRuby/trunk/spec/frozen/language/if_spec.rb
    MacRuby/trunk/spec/frozen/language/precedence_spec.rb

Added Paths:
-----------
    MacRuby/trunk/test_vm/flip.rb

Modified: MacRuby/trunk/compiler.cpp
===================================================================
--- MacRuby/trunk/compiler.cpp	2010-02-03 02:36:08 UTC (rev 3419)
+++ MacRuby/trunk/compiler.cpp	2010-02-03 20:51:32 UTC (rev 3420)
@@ -146,6 +146,8 @@
     setCurrentClassFunc = NULL;
     getCacheFunc = NULL;
     debugTrapFunc = NULL;
+    getFFStateFunc = NULL;
+    setFFStateFunc = NULL;
 
     VoidTy = Type::getVoidTy(context);
     Int1Ty = Type::getInt1Ty(context);
@@ -4518,6 +4520,21 @@
 	    }
 	    break;
 
+	case NODE_FLIP2:
+	case NODE_FLIP3:
+	    {
+		    assert(node->nd_beg != NULL);
+		    assert(node->nd_end != NULL);
+
+		    if (nd_type(node) == NODE_FLIP2) {
+			return compile_ff2(node);
+		    }
+		    else {
+			return compile_ff3(node);
+		    }
+	    }
+	    break;
+
 	case NODE_BLOCK:
 	    {
 		NODE *n = node;
@@ -7299,3 +7316,149 @@
 
     return f;
 }
+
+Value *
+RoxorCompiler::compile_get_ffstate(GlobalVariable *ffstate)
+{
+    return new LoadInst(ffstate, "", bb);
+}
+
+Value *
+RoxorCompiler::compile_set_ffstate(Value *val, Value *expected,
+				   GlobalVariable *ffstate, BasicBlock *mergeBB, Function *f)
+{
+    BasicBlock *valEqExpectedBB = BasicBlock::Create(context,
+	"value_equal_expected", f);
+    BasicBlock *valNotEqExpectedBB = BasicBlock::Create(context,
+	"value_not_equal_expected", f);
+
+    Value *valueEqExpectedCond = new ICmpInst(*bb, ICmpInst::ICMP_EQ, val,
+	expected);
+    BranchInst::Create(valEqExpectedBB, valNotEqExpectedBB,
+	valueEqExpectedCond, bb);
+
+    new StoreInst(trueVal,  ffstate, valEqExpectedBB);
+    new StoreInst(falseVal, ffstate, valNotEqExpectedBB);
+
+    BranchInst::Create(mergeBB, valEqExpectedBB);
+    BranchInst::Create(mergeBB, valNotEqExpectedBB);
+
+    PHINode *pn = PHINode::Create(RubyObjTy, "", mergeBB);
+    pn->addIncoming(trueVal, valEqExpectedBB);
+    pn->addIncoming(falseVal, valNotEqExpectedBB);
+
+    return pn;
+}
+
+Value *
+RoxorCompiler::compile_ff2(NODE *node)
+{
+    /*
+     * if ($state == true || nd_beg == true)
+     *   $state = (nd_end == false)
+     *   return true
+     * else
+     *   return false
+     * end
+     */
+
+    GlobalVariable *ffstate = new GlobalVariable(*RoxorCompiler::module,
+	RubyObjTy, false, GlobalValue::InternalLinkage, falseVal, "");
+
+    Function *f = bb->getParent();
+
+    BasicBlock *stateNotTrueBB = BasicBlock::Create(context,
+	"state_not_true", f);
+    BasicBlock *stateOrBegIsTrueBB = BasicBlock::Create(context,
+	"state_or_beg_is_true", f);
+    BasicBlock *returnTrueBB = BasicBlock::Create(context, "return_true", f);
+    BasicBlock *returnFalseBB = BasicBlock::Create(context, "return_false", f);
+    BasicBlock *mergeBB = BasicBlock::Create(context, "merge", f);
+
+    // `if $state == true`
+    Value *stateVal = compile_get_ffstate(ffstate);
+    Value *stateIsTrueCond = new ICmpInst(*bb, ICmpInst::ICMP_EQ, stateVal,
+	trueVal);
+    BranchInst::Create(stateOrBegIsTrueBB, stateNotTrueBB, stateIsTrueCond,
+	bb);
+
+    // `or if nd_beg == true`
+    bb = stateNotTrueBB;
+    Value *beginValue = compile_node(node->nd_beg);
+    stateNotTrueBB = bb;
+    Value *begIsTrueCond = new ICmpInst(*bb, ICmpInst::ICMP_EQ,
+	beginValue, trueVal);
+    BranchInst::Create(stateOrBegIsTrueBB, returnFalseBB, begIsTrueCond, bb);
+
+    // `$state = (nd_end == false)`
+    bb = stateOrBegIsTrueBB;
+    Value *endValue = compile_node(node->nd_end);
+    stateOrBegIsTrueBB = bb;
+    compile_set_ffstate(endValue, falseVal, ffstate, returnTrueBB, f);
+
+    BranchInst::Create(mergeBB, returnTrueBB);
+    BranchInst::Create(mergeBB, returnFalseBB);
+
+    bb = mergeBB;
+    PHINode *pn = PHINode::Create(RubyObjTy, "", mergeBB);
+    pn->addIncoming(trueVal, returnTrueBB);
+    pn->addIncoming(falseVal, returnFalseBB);
+
+    return pn;
+}
+
+Value *
+RoxorCompiler::compile_ff3(NODE *node)
+{
+    /*
+     * if ($state == true)
+     *   $state = (nd_end == false)
+     *   return true
+     * else
+     *   $state = (nd_beg == true)
+     *   return $state
+     * end
+     */
+
+    GlobalVariable *ffstate = new GlobalVariable(*RoxorCompiler::module,
+	RubyObjTy, false, GlobalValue::InternalLinkage, falseVal, "");
+
+    Function *f = bb->getParent();
+
+    BasicBlock *stateIsTrueBB = BasicBlock::Create(context, "state_is_true",
+	f);
+    BasicBlock *stateIsFalseBB = BasicBlock::Create(context, "state_is_false",
+	f);
+    BasicBlock *returnTrueBB = BasicBlock::Create(context, "return_true", f);
+    BasicBlock *returnStateBB = BasicBlock::Create(context, "return_state", f);
+    BasicBlock *mergeBB = BasicBlock::Create(context, "merge", f);
+
+    
+    // `if $state == true`
+    Value *stateVal = compile_get_ffstate(ffstate);
+    Value *stateIsTrueCond = new ICmpInst(*bb, ICmpInst::ICMP_EQ, stateVal,
+	trueVal);
+    BranchInst::Create(stateIsTrueBB, stateIsFalseBB, stateIsTrueCond, bb);
+
+    // `$state = (nd_end == false)`
+    bb = stateIsTrueBB;
+    Value *endValue = compile_node(node->nd_end);
+    stateIsTrueBB = bb;
+    compile_set_ffstate(endValue, falseVal, ffstate, returnTrueBB, f);
+
+    // `$state = (nd_beg == true)`
+    bb = stateIsFalseBB;
+    Value *beginValue = compile_node(node->nd_beg);
+    stateIsFalseBB = bb;
+    stateVal = compile_set_ffstate(beginValue, trueVal, ffstate, returnStateBB, f);
+
+    BranchInst::Create(mergeBB, returnTrueBB);
+    BranchInst::Create(mergeBB, returnStateBB);
+
+    bb = mergeBB;
+    PHINode *pn = PHINode::Create(RubyObjTy, "", mergeBB);
+    pn->addIncoming(trueVal, returnTrueBB);
+    pn->addIncoming(stateVal, returnStateBB);
+
+    return pn;
+}

Modified: MacRuby/trunk/compiler.h
===================================================================
--- MacRuby/trunk/compiler.h	2010-02-03 02:36:08 UTC (rev 3419)
+++ MacRuby/trunk/compiler.h	2010-02-03 20:51:32 UTC (rev 3420)
@@ -193,6 +193,9 @@
 	Function *setCurrentClassFunc;
 	Function *getCacheFunc;
 	Function *debugTrapFunc;
+	// flip-flop
+	Function *getFFStateFunc;
+	Function *setFFStateFunc;
 
 	Constant *zeroVal;
 	Constant *oneVal;
@@ -357,6 +360,11 @@
 
 	SEL mid_to_sel(ID mid, int arity);
 
+	Value *compile_get_ffstate(GlobalVariable *ffstate);
+	Value *compile_set_ffstate(Value *val, Value *expected, GlobalVariable *ffstate, BasicBlock *mergeBB, Function *f);
+	Value *compile_ff2(NODE *current_node);
+	Value *compile_ff3(NODE *current_node);
+
 	void attach_current_line_metadata(Instruction *insn);
 };
 

Modified: MacRuby/trunk/spec/frozen/language/if_spec.rb
===================================================================
--- MacRuby/trunk/spec/frozen/language/if_spec.rb	2010-02-03 02:36:08 UTC (rev 3419)
+++ MacRuby/trunk/spec/frozen/language/if_spec.rb	2010-02-03 20:51:32 UTC (rev 3420)
@@ -213,37 +213,35 @@
     if false then 123; else 456; end.should == 456
   end
 
-  # MacRuby TODO: causes a compile error
-  #
-  # describe "with a boolean range ('flip-flop' operator)" do
-  #   before :each do
-  #     ScratchPad.record []
-  #   end
-  # 
-  #   after :each do
-  #     ScratchPad.clear
-  #   end
-  # 
-  #   it "mimics an awk conditional with a single-element inclusive-end range" do
-  #     10.times { |i| ScratchPad << i if (i == 4)..(i == 4) }
-  #     ScratchPad.recorded.should == [4]
-  #   end
-  # 
-  #   it "mimics an awk conditional with a many-element inclusive-end range" do
-  #     10.times { |i| ScratchPad << i if (i == 4)..(i == 7) }
-  #     ScratchPad.recorded.should == [4, 5, 6, 7]
-  #   end
-  # 
-  #   it "mimics a sed conditional with a zero-element exclusive-end range" do
-  #     10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }
-  #     ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
-  #   end
-  # 
-  #   it "mimics a sed conditional with a many-element exclusive-end range" do
-  #     10.times { |i| ScratchPad << i if (i == 4)...(i == 5) }
-  #     ScratchPad.recorded.should == [4, 5]
-  #   end
-  # end
+  describe "with a boolean range ('flip-flop' operator)" do
+    before :each do
+      ScratchPad.record []
+    end
+  
+    after :each do
+      ScratchPad.clear
+    end
+  
+    it "mimics an awk conditional with a single-element inclusive-end range" do
+      10.times { |i| ScratchPad << i if (i == 4)..(i == 4) }
+      ScratchPad.recorded.should == [4]
+    end
+  
+    it "mimics an awk conditional with a many-element inclusive-end range" do
+      10.times { |i| ScratchPad << i if (i == 4)..(i == 7) }
+      ScratchPad.recorded.should == [4, 5, 6, 7]
+    end
+  
+    it "mimics a sed conditional with a zero-element exclusive-end range" do
+      10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }
+      ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
+    end
+  
+    it "mimics a sed conditional with a many-element exclusive-end range" do
+      10.times { |i| ScratchPad << i if (i == 4)...(i == 5) }
+      ScratchPad.recorded.should == [4, 5]
+    end
+  end
 end
 
 describe "The postfix if form" do

Modified: MacRuby/trunk/spec/frozen/language/precedence_spec.rb
===================================================================
--- MacRuby/trunk/spec/frozen/language/precedence_spec.rb	2010-02-03 02:36:08 UTC (rev 3419)
+++ MacRuby/trunk/spec/frozen/language/precedence_spec.rb	2010-02-03 20:51:32 UTC (rev 3420)
@@ -330,15 +330,10 @@
     lambda { eval("1...2...3")  }.should raise_error(SyntaxError)
   end
 
-# XXX: this is commented now due to a bug in compiler, which cannot
-# distinguish between range and flip-flop operator so far. zenspider is
-# currently working on a new lexer, which will be able to do that.
-# As soon as it's done, these piece should be reenabled.
-#
-#  it ".. ... have higher precedence than ? :" do
-#    (1..2 ? 3 : 4).should == 3
-#    (1...2 ? 3 : 4).should == 3
-#  end
+   it ".. ... have higher precedence than ? :" do
+     (1..2 ? 3 : 4).should == 3
+     (1...2 ? 3 : 4).should == 3
+   end
 
   it "? : is right-associative" do
     (true ? 2 : 3 ? 4 : 5).should == 2

Added: MacRuby/trunk/test_vm/flip.rb
===================================================================
--- MacRuby/trunk/test_vm/flip.rb	                        (rev 0)
+++ MacRuby/trunk/test_vm/flip.rb	2010-02-03 20:51:32 UTC (rev 3420)
@@ -0,0 +1,107 @@
+# flip-flop 2 should preserve its state accross different calls to the block it is in
+# Test from Tomáš Matoušek of IronRuby
+assert "0", %{
+  def y *a; yield *a; end
+  $a = 0
+  def test
+    $p = proc do |b, e|
+      if b..e
+        $a += 1
+      else
+        $a -= 1
+      end
+    end
+
+    y false, &$p
+    y true, true, &$p
+    y false, &$p
+    y true, false, &$p
+  end
+  test
+  p $a
+}
+
+# flip-flop 3 should preserve its state accross different calls to the block it is in
+# Test from Tomáš Matoušek of IronRuby
+assert "2", %{
+  def y *a; yield *a; end
+  $a = 0
+  def test
+    $p = proc do |b, e|
+      if b...e
+        $a += 1
+      else
+        $a -= 1
+      end
+    end
+
+    y false, &$p
+    y true, true, &$p
+    y false, &$p
+    y true, false, &$p
+  end
+  test
+  p $a
+}
+
+# General flip-flop test:
+# Not only does it test the general use of a flip-flop operator,
+# it also tests the `begin` and `end` block are only evaluated if needed
+# Test from Tomáš Matoušek of IronRuby
+assert "bfbetetetbetbfbfbf", %{
+  F = false
+  T = true
+  x = X = '!'
+  B = [F,T,x,x,x,T,x,F,F]
+  E = [x,x,F,F,T,x,T,x,x]
+
+  def b; step('b',B); end
+  def e; step('e',E); end
+
+  def step name, value
+    r = value[$j]
+    putc name
+    $j += 1
+    $continue = !r.nil?
+    r == X ? raise : r
+  end
+
+  $j = 0
+  $continue = true
+  while $continue
+    putc (b..e ? 't' : 'f')
+  end
+}
+
+# Recursive flip-flop test
+assert "dadbaddb", %{
+  def y *a; yield *a; end
+  def test
+    $p = proc { |b, e|
+      if b..e
+        if e..b
+          putc 'a'
+        else
+          putc 'b'
+        end
+      else
+        if e..b
+          putc 'c'
+        else
+          putc 'd'
+        end
+      end
+    }
+
+    y false, &$p
+    y true, true, &$p
+    y false, &$p
+    y true, false, &$p
+    y true, true, &$p
+    y false, &$p
+    y false, &$p
+    y true, false, &$p
+    puts
+  end
+  test
+}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100203/3cceaf09/attachment-0001.html>


More information about the macruby-changes mailing list