// Copyright 2009 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 eval

import (
	"bignum";
	"flag";
	"fmt";
	"log";
	"os";
	"reflect";
	"testing";
)

// Print each statement or expression before parsing it
var noisy = false

func init()	{ flag.BoolVar(&noisy, "noisy", false, "chatter during eval tests") }

/*
 * Generic statement/expression test framework
 */

type test []job

type job struct {
	code	string;
	cerr	string;
	rterr	string;
	val	Value;
	noval	bool;
}

func runTests(t *testing.T, baseName string, tests []test) {
	for i, test := range tests {
		name := fmt.Sprintf("%s[%d]", baseName, i);
		test.run(t, name);
	}
}

func (a test) run(t *testing.T, name string) {
	w := newTestWorld();
	for _, j := range a {
		src := j.code;
		if noisy {
			println("code:", src)
		}

		code, err := w.Compile(src);
		if err != nil {
			if j.cerr == "" {
				t.Errorf("%s: Compile %s: %v", name, src, err);
				break;
			}
			if !match(t, err, j.cerr) {
				t.Errorf("%s: Compile %s = error %s; want %v", name, src, err, j.cerr);
				break;
			}
			continue;
		}
		if j.cerr != "" {
			t.Errorf("%s: Compile %s succeeded; want %s", name, src, j.cerr);
			break;
		}

		val, err := code.Run();
		if err != nil {
			if j.rterr == "" {
				t.Errorf("%s: Run %s: %v", name, src, err);
				break;
			}
			if !match(t, err, j.rterr) {
				t.Errorf("%s: Run %s = error %s; want %v", name, src, err, j.rterr);
				break;
			}
			continue;
		}
		if j.rterr != "" {
			t.Errorf("%s: Run %s succeeded; want %s", name, src, j.rterr);
			break;
		}

		if !j.noval && !reflect.DeepEqual(val, j.val) {
			t.Errorf("%s: Run %s = %T(%v) want %T(%v)", name, src, val, val, j.val, j.val)
		}
	}
}

func match(t *testing.T, err os.Error, pat string) bool {
	ok, errstr := testing.MatchString(pat, err.String());
	if errstr != "" {
		t.Fatalf("compile regexp %s: %v", pat, errstr)
	}
	return ok;
}


/*
 * Test constructors
 */

// Expression compile error
func CErr(expr string, cerr string) test	{ return test([]job{job{code: expr, cerr: cerr}}) }

// Expression runtime error
func RErr(expr string, rterr string) test	{ return test([]job{job{code: expr, rterr: rterr}}) }

// Expression value
func Val(expr string, val interface{}) test {
	return test([]job{job{code: expr, val: toValue(val)}})
}

// Statement runs without error
func Run(stmts string) test	{ return test([]job{job{code: stmts, noval: true}}) }

// Two statements without error.
// TODO(rsc): Should be possible with Run but the parser
// won't let us do both top-level and non-top-level statements.
func Run2(stmt1, stmt2 string) test {
	return test([]job{job{code: stmt1, noval: true}, job{code: stmt2, noval: true}})
}

// Statement runs and test one expression's value
func Val1(stmts string, expr1 string, val1 interface{}) test {
	return test([]job{
		job{code: stmts, noval: true},
		job{code: expr1, val: toValue(val1)},
	})
}

// Statement runs and test two expressions' values
func Val2(stmts string, expr1 string, val1 interface{}, expr2 string, val2 interface{}) test {
	return test([]job{
		job{code: stmts, noval: true},
		job{code: expr1, val: toValue(val1)},
		job{code: expr2, val: toValue(val2)},
	})
}

/*
 * Value constructors
 */

type vstruct []interface{}

type varray []interface{}

type vslice struct {
	arr		varray;
	len, cap	int;
}

func toValue(val interface{}) Value {
	switch val := val.(type) {
	case bool:
		r := boolV(val);
		return &r;
	case uint8:
		r := uint8V(val);
		return &r;
	case uint:
		r := uintV(val);
		return &r;
	case int:
		r := intV(val);
		return &r;
	case *bignum.Integer:
		return &idealIntV{val}
	case float:
		r := floatV(val);
		return &r;
	case *bignum.Rational:
		return &idealFloatV{val}
	case string:
		r := stringV(val);
		return &r;
	case vstruct:
		elems := make([]Value, len(val));
		for i, e := range val {
			elems[i] = toValue(e)
		}
		r := structV(elems);
		return &r;
	case varray:
		elems := make([]Value, len(val));
		for i, e := range val {
			elems[i] = toValue(e)
		}
		r := arrayV(elems);
		return &r;
	case vslice:
		return &sliceV{Slice{toValue(val.arr).(ArrayValue), int64(val.len), int64(val.cap)}}
	case Func:
		return &funcV{val}
	}
	log.Crashf("toValue(%T) not implemented", val);
	panic();
}

/*
 * Default test scope
 */

type testFunc struct{}

func (*testFunc) NewFrame() *Frame	{ return &Frame{nil, &[2]Value{}} }

func (*testFunc) Call(t *Thread) {
	n := t.f.Vars[0].(IntValue).Get(t);

	res := n + 1;

	t.f.Vars[1].(IntValue).Set(t, res);
}

type oneTwoFunc struct{}

func (*oneTwoFunc) NewFrame() *Frame	{ return &Frame{nil, &[2]Value{}} }

func (*oneTwoFunc) Call(t *Thread) {
	t.f.Vars[0].(IntValue).Set(t, 1);
	t.f.Vars[1].(IntValue).Set(t, 2);
}

type voidFunc struct{}

func (*voidFunc) NewFrame() *Frame	{ return &Frame{nil, []Value{}} }

func (*voidFunc) Call(t *Thread)	{}

func newTestWorld() *World {
	w := NewWorld();

	def := func(name string, t Type, val interface{}) { w.DefineVar(name, t, toValue(val)) };

	w.DefineConst("c", IdealIntType, toValue(bignum.Int(1)));
	def("i", IntType, 1);
	def("i2", IntType, 2);
	def("u", UintType, uint(1));
	def("f", FloatType, 1.0);
	def("s", StringType, "abc");
	def("t", NewStructType([]StructField{StructField{"a", IntType, false}}), vstruct{1});
	def("ai", NewArrayType(2, IntType), varray{1, 2});
	def("aai", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{1, 2}, varray{3, 4}});
	def("aai2", NewArrayType(2, NewArrayType(2, IntType)), varray{varray{5, 6}, varray{7, 8}});
	def("fn", NewFuncType([]Type{IntType}, false, []Type{IntType}), &testFunc{});
	def("oneTwo", NewFuncType([]Type{}, false, []Type{IntType, IntType}), &oneTwoFunc{});
	def("void", NewFuncType([]Type{}, false, []Type{}), &voidFunc{});
	def("sli", NewSliceType(IntType), vslice{varray{1, 2, 3}, 2, 3});

	return w;
}
