require 'yarvtest/yarvtest'

# test of syntax
class TestSYNTAX < YarvTestBase

  def test_if_unless
    ae %q(if true  then 1 ; end)
    ae %q(if false then 1 ; end)
    ae %q(if true  then 1 ; else; 2; end)
    ae %q(if false then 1 ; else; 2; end)
    ae %q(if true  then   ; elsif true then ; 1 ; end)
    ae %q(if false then   ; elsif true then ; 1 ; end)

    ae %q(unless true  then 1 ; end)
    ae %q(unless false then 1 ; end)
    ae %q(unless true  then 1 ; else; 2; end)
    ae %q(unless false then 1 ; else; 2; end)

    ae %q(1 if true)
    ae %q(1 if false)
    ae %q(1 if nil)
    
    ae %q(1 unless true)
    ae %q(1 unless false)
    ae %q(1 unless nil)
  end

  def test_while_until
    ae %q(
      i = 0
      while i < 10
        i+=1
      end)

    ae %q(
      i = 0
      while i < 10
        i+=1
      end; i)

    ae %q(
      i = 0
      until i > 10
        i+=1
      end)

    ae %q(
      i = 0
      until i > 10
        i+=1
      end; i)
    # 
    ae %q{
      i = 0
      begin
        i+=1
      end while false
      i
    }
    ae %q{
      i = 0
      begin
        i+=1
      end until true
      i
    }
  end

  def test_and
    ae %q(1 && 2 && 3 && 4)
    ae %q(1 && nil && 3 && 4)
    ae %q(1 && 2 && 3 && nil)
    ae %q(1 && 2 && 3 && false)

    ae %q(1 and 2 and 3 and 4)
    ae %q(1 and nil and 3 and 4)
    ae %q(1 and 2 and 3 and nil)
    ae %q(1 and 2 and 3 and false)
    ae %q(nil && true)
    ae %q(false && true)

  end
  
  def test_or
    ae %q(1 || 2 || 3 || 4)
    ae %q(1 || false || 3 || 4)
    ae %q(nil || 2 || 3 || 4)
    ae %q(false || 2 || 3 || 4)
    ae %q(nil || false || nil || false)

    ae %q(1 or 2 or 3 or 4)
    ae %q(1 or false or 3 or 4)
    ae %q(nil or 2 or 3 or 4)
    ae %q(false or 2 or 3 or 4)
    ae %q(nil or false or nil or false)
  end

  def test_case
    ae %q(
      case 1
      when 2
        :ng
      end)
      
    ae %q(
      case 1
      when 10,20,30
        :ng1
      when 1,2,3
        :ok
      when 100,200,300
        :ng2
      else
        :elseng
      end)
    ae %q(
      case 123
      when 10,20,30
        :ng1
      when 1,2,3
        :ng2
      when 100,200,300
        :ng3
      else
        :elseok
      end
    )
    ae %q(
      case 'test'
      when /testx/
        :ng1
      when /test/
        :ok
      when /tetxx/
        :ng2
      else
        :ng_else
      end
    )
    ae %q(
      case Object.new
      when Object
        :ok
      end
    )
    ae %q(
      case Object
      when Object.new
        :ng
      else
        :ok
      end
    )
    ae %q{
      case 'test'
      when 'tes'
        :ng
      when 'te'
        :ng
      else
        :ok
      end
    }
    ae %q{
      case 'test'
      when 'tes'
        :ng
      when 'te'
        :ng
      when 'test'
        :ok
      end
    }
    ae %q{
      case 'test'
      when 'tes'
        :ng
      when /te/
        :ng
      else
        :ok
      end
    }
    ae %q{
      case 'test'
      when 'tes'
        :ng
      when /test/
        :ok
      else
        :ng
      end
    }
    ae %q{
      def test(arg)
        case 1
        when 2
          3
        end
        return arg
      end
      
      test(100)
    }
  end

  def test_case_splat
    ae %q{
      ary = [1, 2]
      case 1
      when *ary
        :ok
      else
        :ng
      end
    }
    ae %q{
      ary = [1, 2]
      case 3
      when *ary
        :ng
      else
        :ok
      end
    }
    ae %q{
      ary = [1, 2]
      case 1
      when :x, *ary
        :ok
      when :z
        :ng1
      else
        :ng2
      end
    }
    ae %q{
      ary = [1, 2]
      case 3
      when :x, *ary
        :ng1
      when :z
        :ng2
      else
        :ok
      end
    }
  end

  def test_when
    ae %q(
      case
      when 1==2, 2==3
        :ng1
      when false, 4==5
        :ok
      when false
        :ng2
      else
        :elseng
      end)

    ae %q(
      case
      when nil, nil
        :ng1
      when 1,2,3
        :ok
      when false, false
        :ng2
      else
        :elseng
      end)
      
    ae %q(
      case
      when nil
        :ng1
      when false
        :ng2
      else
        :elseok
      end)
      
    ae %q{
      case
      when 1
      end
    }
  end

  def test_when_splat
    ae %q{
      ary = []
      case
      when false, *ary
        :ng
      else
        :ok
      end
    }
    ae %q{
      ary = [false, nil]
      case
      when *ary
        :ng
      else
        :ok
      end
    }
    ae %q{
      ary = [false, nil]
      case
      when *ary
        :ng
      when true
        :ok
      else
        :ng2
      end
    }
    ae %q{
      ary = [false, nil]
      case
      when *ary
        :ok
      else
        :ng
      end
    }
    ae %q{
      ary = [false, true]
      case
      when *ary
        :ok
      else
        :ng
      end
    }
    ae %q{
      ary = [false, true]
      case
      when false, false
      when false, *ary
        :ok
      else
        :ng
      end
    }
  end

  def test_flipflop
    ae %q{
      sum = 0
      30.times{|ib|
        if ib % 10 == 0 .. true
          sum += ib
        end
      }
      sum
    }
    ae %q{
      sum = 0
      30.times{|ib|
        if ib % 10 == 0 ... true
          sum += ib
        end
      }
      sum
    }
    ae %q{
      t = nil
      "this must not print
      Type: NUM
      123
      456
      Type: ARP
      aaa
      bbb
      \f
      this must not print
      hoge
      Type: ARP
      aaa
      bbb
      ".each{|l|
        if (t = l[/^Type: (.*)/, 1])..(/^\f/ =~ l)
          p [t, l]
        end
      }
    }
  end

  def test_defined_vars
    ae %q{
      defined?(nil) + defined?(self) +
        defined?(true) + defined?(false)
    }
    #ae %q{
    #  a = 1
    #  defined?(a) # yarv returns "in block" in eval context
    #}
    ae %q{
      defined?(@a)
    }
    ae %q{
      @a = 1
      defined?(@a)
    }
    ae %q{
      defined?(@@a)
    }
    ae %q{
      @@a = 1
      defined?(@@a)
    }
    ae %q{
      defined?($a)
    }
    ae %q{
      $a = 1
      defined?($a)
    }
    ae %q{
      defined?(C_definedtest)
    }
    ae %q{
      C_definedtest = 1
      defined?(C_definedtest)
    } do
      remove_const :C_definedtest
    end
    
    ae %q{
      defined?(::C_definedtest)
    }
    ae %q{
      C_definedtest = 1
      defined?(::C_definedtest)
    } do
      remove_const :C_definedtest
    end

    ae %q{
      defined?(C_definedtestA::C_definedtestB::C_definedtestC)
    }
    ae %q{
      class C_definedtestA
        class C_definedtestB
          C_definedtestC = 1
        end
      end
      defined?(C_definedtestA::C_definedtestB::C_definedtestC)
    } do
      remove_const :C_definedtestA
    end
  end

  def test_defined_method
    ae %q{
      defined?(m)
    }
    ae %q{
      def m
      end
      defined?(m)
    }
    
    ae %q{
      defined?(a.class)
    }
    ae %q{
      a = 1
      defined?(a.class)
    }
    ae %q{
      class C
        def test
          [defined?(m1()), defined?(self.m1), defined?(C.new.m1),
           defined?(m2()), defined?(self.m2), defined?(C.new.m2),
           defined?(m3()), defined?(self.m3), defined?(C.new.m3)]
        end
        def m1
        end
        private
        def m2
        end
        protected
        def m3
        end
      end
      C.new.test + [defined?(C.new.m3)]
    }
    ae %q{
      $ans = [defined?($1), defined?($2), defined?($3), defined?($4)]
      /(a)(b)/ =~ 'ab'
      $ans + [defined?($1), defined?($2), defined?($3), defined?($4)]
    }
  end
  
  def test_condition
    ae %q{

      def make_perm ary, num
        if num == 1
          ary.map{|e| [e]}
        else
          base = make_perm(ary, num-1)
          res  = []
          base.each{|b|
            ary.each{|e|
              res << [e] + b
            }
          }
          res
        end
      end
      
      def each_test
        conds = make_perm(['fv', 'tv'], 3)
        bangs = make_perm(['', '!'], 3)
        exprs = make_perm(['and', 'or'], 3)
        ['if', 'unless'].each{|syn|
          conds.each{|cs|
            bangs.each{|bs|
              exprs.each{|es|
                yield(syn, cs, bs, es)
              }
            }
          }
        }
      end
      
      fv = false
      tv = true
      
      $ans = []
      each_test{|syn, conds, bangs, exprs|
        c1, c2, c3 = conds
        bang1, bang2, bang3 = bangs
        e1, e2 = exprs
        eval %Q{
          #{syn} #{bang1}#{c1} #{e1} #{bang2}#{c2} #{e2} #{bang3}#{c3}
            $ans << :then
          else
            $ans << :false
          end
        }
      }
    
      each_test{|syn, conds, bangs, exprs|
        c1, c2, c3 = conds
        bang1, bang2, bang3 = bangs
        e1, e2 = exprs
        eval %Q{
          #{syn} #{bang1}#{c1} #{e1} #{bang2}#{c2} #{e2} #{bang3}#{c3}
            $ans << :then
          end
          $ans << :sep
        }
      }
      $ans
    }
  end
end

