blob: aed87b7699e7354e601d0057e6e37be83d81c51a [file] [log] [blame]
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exact
import (
"go/token"
"strings"
"testing"
)
// TODO(gri) expand this test framework
var tests = []string{
// unary operations
`+ 0 = 0`,
`- 1 = -1`,
`! true = false`,
`! false = true`,
// etc.
// binary operations
`"" + "" = ""`,
`"foo" + "" = "foo"`,
`"" + "bar" = "bar"`,
`"foo" + "bar" = "foobar"`,
`0 + 0 = 0`,
`0 + 0.1 = 0.1`,
`0 + 0.1i = 0.1i`,
`0.1 + 0.9 = 1`,
`1e100 + 1e100 = 2e100`,
`0 - 0 = 0`,
`0 - 0.1 = -0.1`,
`0 - 0.1i = -0.1i`,
`1e100 - 1e100 = 0`,
`0 * 0 = 0`,
`1 * 0.1 = 0.1`,
`1 * 0.1i = 0.1i`,
`1i * 1i = -1`,
`0 / 0 = "division_by_zero"`,
`10 / 2 = 5`,
`5 / 3 = 5/3`,
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
`10 % 3 = 1`,
// etc.
// shifts
`0 << 0 = 0`,
`1 << 10 = 1024`,
// etc.
// comparisons
`false == false = true`,
`false == true = false`,
`true == false = false`,
`true == true = true`,
`false != false = false`,
`false != true = true`,
`true != false = true`,
`true != true = false`,
`"foo" == "bar" = false`,
`"foo" != "bar" = true`,
`"foo" < "bar" = false`,
`"foo" <= "bar" = false`,
`"foo" > "bar" = true`,
`"foo" >= "bar" = true`,
`0 != 0 = false`,
// etc.
}
func TestOps(t *testing.T) {
for _, test := range tests {
var got, want Value
switch a := strings.Split(test, " "); len(a) {
case 4:
got = doOp(nil, op[a[0]], val(a[1]))
want = val(a[3])
case 5:
got = doOp(val(a[0]), op[a[1]], val(a[2]))
want = val(a[4])
default:
t.Errorf("invalid test case: %s", test)
continue
}
if !Compare(got, token.EQL, want) {
t.Errorf("%s failed: got %s; want %s", test, got, want)
}
}
}
// ----------------------------------------------------------------------------
// Support functions
func val(lit string) Value {
if len(lit) == 0 {
return MakeUnknown()
}
switch lit {
case "?":
return MakeUnknown()
case "nil":
return MakeNil()
case "true":
return MakeBool(true)
case "false":
return MakeBool(false)
}
tok := token.FLOAT
switch first, last := lit[0], lit[len(lit)-1]; {
case first == '"' || first == '`':
tok = token.STRING
lit = strings.Replace(lit, "_", " ", -1)
case first == '\'':
tok = token.CHAR
case last == 'i':
tok = token.IMAG
}
return MakeFromLiteral(lit, tok)
}
var op = map[string]token.Token{
"!": token.NOT,
"+": token.ADD,
"-": token.SUB,
"*": token.MUL,
"/": token.QUO,
"%": token.REM,
"<<": token.SHL,
">>": token.SHR,
"==": token.EQL,
"!=": token.NEQ,
"<": token.LSS,
"<=": token.LEQ,
">": token.GTR,
">=": token.GEQ,
}
func panicHandler(v *Value) {
switch p := recover().(type) {
case nil:
// nothing to do
case string:
*v = MakeString(p)
case error:
*v = MakeString(p.Error())
default:
panic(p)
}
}
func doOp(x Value, op token.Token, y Value) (z Value) {
defer panicHandler(&z)
if x == nil {
return UnaryOp(op, y, -1)
}
switch op {
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
return MakeBool(Compare(x, op, y))
case token.SHL, token.SHR:
s, _ := Int64Val(y)
return Shift(x, op, uint(s))
default:
return BinaryOp(x, op, y)
}
}