// 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 main

// generate operator implementations

import (
	"log";
	"os";
	"template";
)

type Op struct {
	Name		string;
	Expr		string;
	Body		string;	// overrides Expr
	ConstExpr	string;
	AsRightName	string;
	ReturnType	string;
	Types		[]*Type;
}

type Size struct {
	Bits	int;
	Sized	string;
}

type Type struct {
	Repr		string;
	Value		string;
	Native		string;
	As		string;
	IsIdeal		bool;
	HasAssign	bool;
	Sizes		[]Size;
}

var (
	boolType	= &Type{Repr: "*boolType", Value: "BoolValue", Native: "bool", As: "asBool"};
	uintType	= &Type{Repr: "*uintType", Value: "UintValue", Native: "uint64", As: "asUint",
		Sizes: []Size{Size{8, "uint8"}, Size{16, "uint16"}, Size{32, "uint32"}, Size{64, "uint64"}, Size{0, "uint"}},
	};
	intType	= &Type{Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt",
		Sizes: []Size{Size{8, "int8"}, Size{16, "int16"}, Size{32, "int32"}, Size{64, "int64"}, Size{0, "int"}},
	};
	idealIntType	= &Type{Repr: "*idealIntType", Value: "IdealIntValue", Native: "*bignum.Integer", As: "asIdealInt", IsIdeal: true};
	floatType	= &Type{Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat",
		Sizes: []Size{Size{32, "float32"}, Size{64, "float64"}, Size{0, "float"}},
	};
	idealFloatType	= &Type{Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*bignum.Rational", As: "asIdealFloat", IsIdeal: true};
	stringType	= &Type{Repr: "*stringType", Value: "StringValue", Native: "string", As: "asString"};
	arrayType	= &Type{Repr: "*ArrayType", Value: "ArrayValue", Native: "ArrayValue", As: "asArray", HasAssign: true};
	structType	= &Type{Repr: "*StructType", Value: "StructValue", Native: "StructValue", As: "asStruct", HasAssign: true};
	ptrType		= &Type{Repr: "*PtrType", Value: "PtrValue", Native: "Value", As: "asPtr"};
	funcType	= &Type{Repr: "*FuncType", Value: "FuncValue", Native: "Func", As: "asFunc"};
	sliceType	= &Type{Repr: "*SliceType", Value: "SliceValue", Native: "Slice", As: "asSlice"};
	mapType		= &Type{Repr: "*MapType", Value: "MapValue", Native: "Map", As: "asMap"};

	all	= []*Type{
		boolType,
		uintType,
		intType,
		idealIntType,
		floatType,
		idealFloatType,
		stringType,
		arrayType,
		structType,
		ptrType,
		funcType,
		sliceType,
		mapType,
	};
	bools		= all[0:1];
	integers	= all[1:4];
	shiftable	= all[1:3];
	numbers		= all[1:6];
	addable		= all[1:7];
	cmpable		= []*Type{
		boolType,
		uintType,
		intType,
		idealIntType,
		floatType,
		idealFloatType,
		stringType,
		ptrType,
		funcType,
		mapType,
	};
)

var unOps = []Op{
	Op{Name: "Neg", Expr: "-v", ConstExpr: "v.Neg()", Types: numbers},
	Op{Name: "Not", Expr: "!v", Types: bools},
	Op{Name: "Xor", Expr: "^v", ConstExpr: "v.Neg().Sub(bignum.Int(1))", Types: integers},
}

var binOps = []Op{
	Op{Name: "Add", Expr: "l + r", ConstExpr: "l.Add(r)", Types: addable},
	Op{Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(r)", Types: numbers},
	Op{Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(r)", Types: numbers},
	Op{Name: "Quo",
		Body: "if r == 0 { t.Abort(DivByZeroError{}) } ret =  l / r",
		ConstExpr: "l.Quo(r)",
		Types: numbers,
	},
	Op{Name: "Rem",
		Body: "if r == 0 { t.Abort(DivByZeroError{}) } ret = l % r",
		ConstExpr: "l.Rem(r)",
		Types: integers,
	},
	Op{Name: "And", Expr: "l & r", ConstExpr: "l.And(r)", Types: integers},
	Op{Name: "Or", Expr: "l | r", ConstExpr: "l.Or(r)", Types: integers},
	Op{Name: "Xor", Expr: "l ^ r", ConstExpr: "l.Xor(r)", Types: integers},
	Op{Name: "AndNot", Expr: "l &^ r", ConstExpr: "l.AndNot(r)", Types: integers},
	Op{Name: "Shl", Expr: "l << r", ConstExpr: "l.Shl(uint(r.Value()))",
		AsRightName: "asUint", Types: shiftable,
	},
	Op{Name: "Shr", Expr: "l >> r", ConstExpr: "l.Shr(uint(r.Value()))",
		AsRightName: "asUint", Types: shiftable,
	},
	Op{Name: "Lss", Expr: "l < r", ConstExpr: "l.Cmp(r) < 0", ReturnType: "bool", Types: addable},
	Op{Name: "Gtr", Expr: "l > r", ConstExpr: "l.Cmp(r) > 0", ReturnType: "bool", Types: addable},
	Op{Name: "Leq", Expr: "l <= r", ConstExpr: "l.Cmp(r) <= 0", ReturnType: "bool", Types: addable},
	Op{Name: "Geq", Expr: "l >= r", ConstExpr: "l.Cmp(r) >= 0", ReturnType: "bool", Types: addable},
	Op{Name: "Eql", Expr: "l == r", ConstExpr: "l.Cmp(r) == 0", ReturnType: "bool", Types: cmpable},
	Op{Name: "Neq", Expr: "l != r", ConstExpr: "l.Cmp(r) != 0", ReturnType: "bool", Types: cmpable},
}

type Data struct {
	UnaryOps	[]Op;
	BinaryOps	[]Op;
	Types		[]*Type;
}

var data = Data{
	unOps,
	binOps,
	all,
}

const templateStr = `
// This file is machine generated by gen.go.
// 6g gen.go && 6l gen.6 && ./6.out >expr1.go

package eval

import (
	"bignum";
	"log";
)

/*
 * "As" functions.  These retrieve evaluator functions from an
 * expr, panicking if the requested evaluator has the wrong type.
 */
«.repeated section Types»
«.section IsIdeal»
func (a *expr) «As»() (func() «Native») {
	return a.eval.(func()(«Native»))
}
«.or»
func (a *expr) «As»() (func(*Thread) «Native») {
	return a.eval.(func(*Thread)(«Native»))
}
«.end»
«.end»
func (a *expr) asMulti() (func(*Thread) []Value) {
	return a.eval.(func(*Thread)[]Value)
}

func (a *expr) asInterface() (func(*Thread) interface{}) {
	switch sf := a.eval.(type) {
«.repeated section Types»
«.section IsIdeal»
	case func()«Native»:
		return func(*Thread) interface{} { return sf() }
«.or»
	case func(t *Thread)«Native»:
		return func(t *Thread) interface{} { return sf(t) }
«.end»
«.end»
	default:
		log.Crashf("unexpected expression node type %T at %v", a.eval, a.pos);
	}
	panic();
}

/*
 * Operator generators.
 */

func (a *expr) genConstant(v Value) {
	switch a.t.lit().(type) {
«.repeated section Types»
	case «Repr»:
«.section IsIdeal»
		val := v.(«Value»).Get();
		a.eval = func() «Native» { return val }
«.or»
		a.eval = func(t *Thread) «Native» { return v.(«Value»).Get(t) }
«.end»
«.end»
	default:
		log.Crashf("unexpected constant type %v at %v", a.t, a.pos);
	}
}

func (a *expr) genIdentOp(level, index int) {
	a.evalAddr = func(t *Thread) Value { return t.f.Get(level, index) };
	switch a.t.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
	case «Repr»:
		a.eval = func(t *Thread) «Native» { return t.f.Get(level, index).(«Value»).Get(t) }
«.end»
«.end»
	default:
		log.Crashf("unexpected identifier type %v at %v", a.t, a.pos);
	}
}

func (a *expr) genFuncCall(call func(t *Thread) []Value) {
	a.exec = func(t *Thread) { call(t)};
	switch a.t.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
	case «Repr»:
		a.eval = func(t *Thread) «Native» { return call(t)[0].(«Value»).Get(t) }
«.end»
«.end»
	case *MultiType:
		a.eval = func(t *Thread) []Value { return call(t) }
	default:
		log.Crashf("unexpected result type %v at %v", a.t, a.pos);
	}
}

func (a *expr) genValue(vf func(*Thread) Value) {
	a.evalAddr = vf;
	switch a.t.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
	case «Repr»:
		a.eval = func(t *Thread) «Native» { return vf(t).(«Value»).Get(t) }
«.end»
«.end»
	default:
		log.Crashf("unexpected result type %v at %v", a.t, a.pos);
	}
}

«.repeated section UnaryOps»
func (a *expr) genUnaryOp«Name»(v *expr) {
	switch a.t.lit().(type) {
«.repeated section Types»
	case «Repr»:
«.section IsIdeal»
		v := v.«As»()();
		val := «ConstExpr»;
		a.eval = func() «Native» { return val }
«.or»
		vf := v.«As»();
		a.eval = func(t *Thread) «Native» { v := vf(t); return «Expr» }
«.end»
«.end»
	default:
		log.Crashf("unexpected type %v at %v", a.t, a.pos);
	}
}

«.end»
func (a *expr) genBinOpLogAnd(l, r *expr) {
	lf := l.asBool();
	rf := r.asBool();
	a.eval = func(t *Thread) bool { return lf(t) && rf(t) }
}

func (a *expr) genBinOpLogOr(l, r *expr) {
	lf := l.asBool();
	rf := r.asBool();
	a.eval = func(t *Thread) bool { return lf(t) || rf(t) }
}

«.repeated section BinaryOps»
func (a *expr) genBinOp«Name»(l, r *expr) {
	switch t := l.t.lit().(type) {
«.repeated section Types»
	case «Repr»:
	«.section IsIdeal»
		l := l.«As»()();
		r := r.«As»()();
		val := «ConstExpr»;
		«.section ReturnType»
		a.eval = func(t *Thread) «ReturnType» { return val }
		«.or»
		a.eval = func() «Native» { return val }
		«.end»
	«.or»
		lf := l.«As»();
		rf := r.«.section AsRightName»«@»«.or»«As»«.end»();
		«.section ReturnType»
		a.eval = func(t *Thread) «@» {
			l, r := lf(t), rf(t);
			return «Expr»
		}
		«.or»
		«.section Sizes»
		switch t.Bits {
		«.repeated section @»
		case «Bits»:
			a.eval = func(t *Thread) «Native» {
				l, r := lf(t), rf(t);
				var ret «Native»;
				«.section Body»
				«Body»;
				«.or»
				ret = «Expr»;
				«.end»
				return «Native»(«Sized»(ret))
			}
		«.end»
		default:
			log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos);
		}
		«.or»
		a.eval = func(t *Thread) «Native» {
			l, r := lf(t), rf(t);
			return «Expr»
		}
		«.end»
		«.end»
	«.end»
	«.end»
	default:
		log.Crashf("unexpected type %v at %v", l.t, a.pos);
	}
}

«.end»
func genAssign(lt Type, r *expr) (func(lv Value, t *Thread)) {
	switch lt.lit().(type) {
«.repeated section Types»
«.section IsIdeal»
«.or»
	case «Repr»:
		rf := r.«As»();
		return func(lv Value, t *Thread) { «.section HasAssign»lv.Assign(t, rf(t))«.or»lv.(«Value»).Set(t, rf(t))«.end» }
«.end»
«.end»
	default:
		log.Crashf("unexpected left operand type %v at %v", lt, r.pos);
	}
	panic();
}
`

func main() {
	t := template.New(nil);
	t.SetDelims("«", "»");
	err := t.Parse(templateStr);
	if err != nil {
		log.Exit(err)
	}
	err = t.Execute(data, os.Stdout);
	if err != nil {
		log.Exit(err)
	}
}
