├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── accesschange_test.go ├── ast.go ├── bigcomplex.go ├── bigcomplex_test.go ├── builtins.go ├── bytetype.go ├── checkassignstmt_gen_test.go ├── checkbasiclit.go ├── checkbinaryexpr.go ├── checkbinaryexpr_gen_test.go ├── checkbinaryexpr_nonconst_gen_test.go ├── checkbinaryexpr_test.go ├── checkbinaryexpr_typed_gen_test.go ├── checkbuiltin.go ├── checkbuiltin_gen_test.go ├── checkbuiltin_make_test.go ├── checkcallexpr.go ├── checkcallexpr_const_conv_gen_test.go ├── checkcallexpr_func_gen_test.go ├── checkcompositelit.go ├── checkcompositelit_gen_test.go ├── checkexpr.go ├── checkfunclit.go ├── checkident.go ├── checkindexexpr.go ├── checkparenexpr.go ├── checkselectorexpr.go ├── checksliceexpr.go ├── checkstarexpr.go ├── checkstarexpr_gen_test.go ├── checkstmt.go ├── checkstmt_test.go ├── checktypeassertexpr.go ├── checkunaryexpr.go ├── checkunaryexpr_addr_gen_test.go ├── checkunaryexpr_gen_test.go ├── checkunaryexpr_test.go ├── checkunaryexpr_typed_gen_test.go ├── constnumber.go ├── consttype.go ├── demo ├── .gitignore ├── identchange.go └── repl.go ├── didyoutypecheck.go ├── doc.go ├── env.go ├── errors.go ├── eval.go ├── evalbasiclit.go ├── evalbinaryexpr.go ├── evalbinaryexpr_test.go ├── evalbuiltin.go ├── evalbuiltin_test.go ├── evalcallexpr.go ├── evalcallexpr_test.go ├── evalcompositelit.go ├── evalcompositelit_test.go ├── evalexpr.go ├── evalfunclit.go ├── evalident.go ├── evalident_test.go ├── evalindexexpr.go ├── evalindexexpr_test.go ├── evalselectorexpr.go ├── evalselectorexpr_test.go ├── evalsliceexpr.go ├── evalsliceexpr_test.go ├── evalstarexpr.go ├── evaltypeassertexpr.go ├── evaltypeassertexpr_test.go ├── evalunaryexpr.go ├── evalunaryexpr_test.go ├── example_eval_test.go ├── fakecheck.go ├── helper_for_test.go ├── inspect.go ├── interpstmt.go ├── interpstmt_test.go ├── panics.go ├── runetype.go ├── testgen ├── .gitignore ├── Makefile ├── checkassignstmt_gen.go ├── checkbinaryexpr_gen.go ├── checkbinaryexpr_nonconst_gen.go ├── checkbinaryexpr_typed_gen.go ├── checkbuiltin_gen.go ├── checkcallexpr_const_conv_gen.go ├── checkcallexpr_func_gen.go ├── checkcompositelit_gen.go ├── checkstarexpr_gen.go ├── checkunaryexpr_addr_gen.go ├── checkunaryexpr_gen.go ├── checkunaryexpr_typed_gen.go ├── common.go └── main.go ├── untypednil.go ├── util.go ├── util_test.go └── walk.go /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/ 2 | src/ 3 | go-repl 4 | demo/identchange 5 | *.swp 6 | *.test 7 | *~ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - tip 5 | 6 | script: 7 | - go get 8 | - go test 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Carl Chatfield 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Comments starting with #: below are remake GNU Makefile comments. See 2 | # https://github.com/rocky/remake/wiki/Rake-tasks-for-gnu-make 3 | 4 | .PHONY: all eval test check gentests install 5 | 6 | #: Same as repl 7 | all: repl 8 | 9 | #: The REPL front-end to the evaluator 10 | repl: lib 11 | go build -o go-repl demo/repl.go 12 | 13 | #: The evaluator library 14 | lib: 15 | go build 16 | 17 | #: Same as "check" 18 | test: check 19 | 20 | #: Install code 21 | install: 22 | go install 23 | 24 | #: Automated generate of the massive number of type checking tests used 25 | gentests: 26 | make -C testgen 27 | 28 | 29 | #: Run all tests (quick and interpreter) 30 | check: 31 | go test -i && go test 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | eval - A library for providing an eval function in Go 2 | ============================================================================ 3 | [![Build Status](https://travis-ci.org/0xfaded/eval.png)](https://travis-ci.org/0xfaded/eval) 4 | 5 | This project adds an *Eval()* function to go. 6 | 7 | Right now only, Go expressions are handled. 8 | 9 | Using 10 | ----- 11 | 12 | The simplest invocation is 13 | 14 | ``` 15 | results, panik, compileErrs := Eval("1 + 2") 16 | // results = []reflect.Value{reflect.ValueOf(3)} 17 | // panik = error(nil) 18 | // compileErrs = []error(nil) 19 | ``` 20 | Results are reflect.Values, and in reality Eval is nothing more than a fancy wrapper around the reflect package. Runtime panics should be detected during evaluation and returned as panik, if an actual runtime panic 21 | occurs please file a bug. Any parse or type errors are returned as compileErrs; it is nil otherwise. 22 | 23 | EvalEnv evaluates an expression inside an environment containing variables, constants, functions, types and packages. 24 | ``` 25 | type T int 26 | x := 1 27 | sum := func(xs ...int) (result int) { 28 | for _, x := range xs { 29 | result += x 30 | } 31 | return result 32 | } 33 | env := eval.MakeSimpleEnv() 34 | // Note the &x 35 | env.Vars["x"] = reflect.ValueOf(&x) 36 | env.Funcs["sum"] = reflect.ValueOf(f) 37 | env.Types["T"] = reflect.TypeOf(T(0)) 38 | pkg := MakeSimpleEnv() 39 | pkg.Consts["C"] = reflect.ValueOf(2) 40 | env.Pkgs["pkg"] = pkg 41 | results, panik, compileErrs := EvalEnv("T(sum(1, x, pkg.C))", env) 42 | ``` 43 | 44 | Extended API 45 | ------------ 46 | 47 | The extented API allows step by step execution of the evaluator. The dance is three part 48 | - Parse 49 | - Check 50 | - Eval 51 | 52 | ``` 53 | env := eval.MakeSimpleEnv() 54 | if expr, err := parser.ParseExpr(expr); err != nil { 55 | fmt.Printf("parse error: %s\n", err) 56 | } else if cexpr, errs := eval.CheckExpr(expr, env); len(errs) != 0 { 57 | for _, cerr := range errs { 58 | fmt.Printf("%v\n", cerr) 59 | } 60 | } else if vals, _, err := eval.EvalExpr(cexpr, env); err != nil { 61 | fmt.Printf("eval error: %s\n", err) 62 | } else { 63 | // do something with pointer to reflect.Value array vals, e.g.: 64 | fmt.Println(vals[0].Interface()) 65 | } 66 | ``` 67 | 68 | 69 | The program [repl.go](https://github.com/0xfaded/eval/tree/master/demo/repl.go) is a full Go program showing this. 70 | 71 | Limitations 72 | ----------- 73 | 74 | Eval is currently limited to the functionality of the reflect package. 75 | 76 | Most noteably, the following are not implemented as they cannot be created: 77 | - struct literals 78 | - array literals 79 | - function literals 80 | 81 | Struct and Array composite named types can still be constructed. E.g. 82 | ``` 83 | env.Types["A"] = reflect.TypeOf([2]int{}) 84 | EvalEnv("A{1, 2}", env) 85 | ``` 86 | In theory this could also work for named Function literals, but this has not been implemented. 87 | 88 | 89 | See Also 90 | -------- 91 | 92 | * [What's left to do?](https://github.com/0xfaded/eval/wiki/What's-left-to-do%3F) 93 | * [go-fish](https://github.com/rocky/go-fish): an interactive read, eval, print loop which uses this to handle the *eval()* step. In that project, see the program *make_eval* for how to create a complete environment given an initial import. 94 | * [gack](https://github.com/0xfaded/gack): another experimental REPL which implements import by continuously recompiling the executable. 95 | * [gub debugger](https://github.com/rocky/ssa-interp): a debugger that uses this to handle the *eval* debugger command 96 | -------------------------------------------------------------------------------- /accesschange_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | /* TODO[crc] Determine what to do with the env lookup 4 | 5 | // Tests replacing the default identifier selection lookup value mechanism with 6 | // our own custom versions. 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | // Here's our custom ident lookup. 14 | func MyEvalIdentExpr(ident *Ident, env Env) ( 15 | *reflect.Value, bool, error) { 16 | name := ident.Name 17 | if name == "nil" { 18 | return nil, false, nil 19 | } else if name[0] == 'v' { 20 | val := reflect.ValueOf(5) 21 | return &val, true, nil 22 | } else if name[0] == 'c' { 23 | val := reflect.ValueOf("constant") 24 | return &val, true, nil 25 | } else if name[0] == 'c' { 26 | val := reflect.ValueOf(true) 27 | return &val, true, nil 28 | } else { 29 | val := reflect.ValueOf('x') 30 | return &val, true, nil 31 | } 32 | } 33 | 34 | 35 | // Here's our custom selector lookup. 36 | func MyEvalSelectorExpr(selector *SelectorExpr, env Env) ( 37 | *reflect.Value, bool, error) { 38 | var err error 39 | var x *[]reflect.Value 40 | if x, _, err = EvalExpr(selector.X, env); err != nil { 41 | return nil, true, err 42 | } 43 | sel := selector.Sel.Name 44 | x0 := (*x)[0] 45 | 46 | if x0.Kind() == reflect.Ptr { 47 | // Special case for handling packages 48 | if x0.Type() == reflect.TypeOf(Pkg(nil)) { 49 | val := reflect.ValueOf("bogus") 50 | return &val, true, nil 51 | } else if !x0.IsNil() && x0.Elem().Kind() == reflect.Struct { 52 | x0 = x0.Elem() 53 | } 54 | } 55 | 56 | switch x0.Type().Kind() { 57 | case reflect.Struct: 58 | if v := x0.FieldByName(sel); v.IsValid() { 59 | return &v, true, nil 60 | } else if x0.CanAddr() { 61 | if v := x0.Addr().MethodByName(sel); v.IsValid() { 62 | return &v, true, nil 63 | } 64 | } 65 | return nil, true, nil 66 | case reflect.Interface: 67 | if v := x0.MethodByName(sel); !v.IsValid() { 68 | return &v, true, nil 69 | } else { 70 | return &v, true, nil 71 | } 72 | default: 73 | return nil, true, nil 74 | } 75 | } 76 | 77 | func TestReplaceIdentLookup(t *testing.T) { 78 | defer SetEvalIdentExprCallback(EvalIdentExpr) 79 | env := MakeSimpleEnv() 80 | SetEvalIdentExprCallback(MyEvalIdentExpr) 81 | expectResult(t, "fdafdsa", env, 'x') 82 | expectResult(t, "c + \" value\"", env, "constant value") 83 | 84 | } 85 | 86 | 87 | func TestReplaceSelectorLookup(t *testing.T) { 88 | defer SetEvalSelectorExprCallback(EvalSelectorExpr) 89 | env := MakeSimpleEnv() 90 | pkg := MakeSimpleEnv() 91 | env.Pkgs["bogusPackage"] = pkg 92 | SetEvalSelectorExprCallback(MyEvalSelectorExpr) 93 | expectResult(t, "bogusPackage.something", env, "bogus") 94 | 95 | } 96 | */ 97 | -------------------------------------------------------------------------------- /bigcomplex.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | ) 7 | 8 | // BigComplex behaves like a *big.Re, but has an imaginary component 9 | // and separate implementation for + - * / 10 | type BigComplex struct { 11 | Re big.Rat 12 | Im big.Rat 13 | } 14 | 15 | func (z *BigComplex) Add(x, y *BigComplex) *BigComplex { 16 | z.Re.Add(&x.Re, &y.Re) 17 | z.Im.Add(&x.Im, &y.Im) 18 | return z 19 | } 20 | 21 | func (z *BigComplex) Sub(x, y *BigComplex) *BigComplex { 22 | z.Re.Sub(&x.Re, &y.Re) 23 | z.Im.Sub(&x.Im, &y.Im) 24 | return z 25 | } 26 | 27 | func (z *BigComplex) Mul(x, y *BigComplex) *BigComplex { 28 | re := new(big.Rat).Mul(&x.Re, &y.Re) 29 | re.Sub(re, new(big.Rat).Mul(&x.Im, &y.Im)) 30 | 31 | im := new(big.Rat).Mul(&x.Re, &y.Im) 32 | im.Add(im, new(big.Rat).Mul(&x.Im, &y.Re)) 33 | 34 | z.Re = *re 35 | z.Im = *im 36 | return z 37 | } 38 | 39 | func (z *BigComplex) Quo(x, y *BigComplex) *BigComplex { 40 | // a+bi ac+bd bc-ad 41 | // ---- = ----- + ----- i 42 | // c+di cc+dd cc+dd 43 | 44 | cc := new(big.Rat).Mul(&y.Re, &y.Re) 45 | dd := new(big.Rat).Mul(&y.Im, &y.Im) 46 | ccdd := new(big.Rat).Add(cc, dd) 47 | 48 | ac := new(big.Rat).Mul(&x.Re, &y.Re) 49 | ad := new(big.Rat).Mul(&x.Re, &y.Im) 50 | bc := new(big.Rat).Mul(&x.Im, &y.Re) 51 | bd := new(big.Rat).Mul(&x.Im, &y.Im) 52 | 53 | z.Re.Add(ac, bd) 54 | z.Re.Quo(&z.Re, ccdd) 55 | 56 | z.Im.Sub(bc, ad) 57 | z.Im.Quo(&z.Im, ccdd) 58 | 59 | return z 60 | } 61 | 62 | func (z *BigComplex) Lsh(x *BigComplex, count uint) *BigComplex { 63 | z.Re.Num().Lsh(x.Re.Num(), count) 64 | return z 65 | } 66 | 67 | func (z *BigComplex) Rsh(x *BigComplex, count uint) *BigComplex { 68 | z.Re.Num().Rsh(x.Re.Num(), count) 69 | return z 70 | } 71 | 72 | func (z *BigComplex) IsZero() bool { 73 | return z.Re.Num().BitLen() == 0 && z.Im.Num().BitLen() == 0 74 | } 75 | 76 | // z.Int() returns a representation of z, truncated to be an int of 77 | // length bits. Valid values for bits are 8, 16, 32, 64. Result is 78 | // otherwise undefined If a truncation occurs, the decimal part is 79 | // dropped and the conversion continues as usual. truncation will be 80 | // true If an overflow occurs, the result is equivelant to a cast of 81 | // the form int32(x). overflow will be true. 82 | func (z *BigComplex) Int(bits int) (_ int64, truncation, overflow bool) { 83 | var integer *BigComplex 84 | integer, truncation = z.Integer() 85 | res := new(big.Int).Set(integer.Re.Num()) 86 | 87 | // Numerator must fit in bits - 1, with 1 bit left for sign. 88 | // An exceptional case when only the signed bit is set. 89 | if overflow = res.BitLen() > bits - 1; overflow { 90 | var mask uint64 = ^uint64(0) >> uint(64 - bits) 91 | if res.BitLen() == bits && res.Sign() < 0 { 92 | // To detect the edge of minus 0b1000..., add one 93 | // to get 0b0ff... and recount the bits 94 | plus1 := new(big.Int).Add(res, big.NewInt(1)) 95 | if plus1.BitLen() < bits { 96 | return res.Int64(), truncation, false 97 | } 98 | } 99 | res.And(res, new(big.Int).SetUint64(mask)) 100 | } 101 | return res.Int64(), truncation, overflow 102 | } 103 | 104 | // z.Uint() returns a representation of z truncated to be a uint of 105 | // length bits. Valid values for bits are 0, 8, 16, 32, 64. The 106 | // returned result is otherwise undefined. If a truncation occurs, the 107 | // decimal part is dropped and the conversion continues as 108 | // usual. Return values truncation and overflow will be true if an 109 | // overflow occurs. The result is equivelant to a cast of the form 110 | // uint32(x). 111 | func (z *BigComplex) Uint(bits int) (_ uint64, truncation, overflow bool) { 112 | var integer *BigComplex 113 | integer, truncation = z.Integer() 114 | res := new(big.Int).Set(integer.Re.Num()) 115 | 116 | var mask uint64 = ^uint64(0) >> uint(64 - bits) 117 | if overflow = res.BitLen() > bits; overflow { 118 | res.And(res, new(big.Int).SetUint64(mask)) 119 | res = new(big.Int).And(res, new(big.Int).SetUint64(mask)) 120 | } 121 | 122 | r := res.Uint64() 123 | if res.Sign() < 0 { 124 | overflow = true 125 | r = (^r + 1) & mask 126 | } 127 | return r, truncation, overflow 128 | } 129 | 130 | // z.Float64() returns a representation of z truncated to a float64 If 131 | // a truncation from a complex occurs. The imaginary part is dropped 132 | // and the conversion continues as usual. return value truncation will 133 | // be true exact will be true if the conversion was completed without 134 | // loss of precision. 135 | func (z *BigComplex) Float64() (f float64, truncation, exact bool) { 136 | f, exact = z.Re.Float64() 137 | return f, !z.IsReal(), exact 138 | } 139 | 140 | // z.Complex128() returns a complex128 representation of z. Return value 141 | // exact will be true if the conversion was completed without loss of 142 | // precision. 143 | func (z *BigComplex) Complex128() (_ complex128, exact bool) { 144 | r, re := z.Re.Float64() 145 | i, ie := z.Im.Float64() 146 | return complex(r, i), re && ie 147 | } 148 | 149 | // z.Integer() returns a representation of z, a *BigComplex, truncated 150 | // to be a integer value. The second return value is true if a 151 | // truncation occured in the real component. 152 | func (z *BigComplex) Integer() (_ *BigComplex, truncation bool) { 153 | if z.IsInteger() { 154 | return z, false 155 | } else if z.Re.IsInt() { 156 | re := new(BigComplex) 157 | re.Re.Set(&z.Re) 158 | return re, false 159 | } else { 160 | trunc := new(BigComplex) 161 | trunc.Re.SetInt(z.Re.Num()) 162 | trunc.Re.Num().Div(trunc.Re.Num(), z.Re.Denom()) 163 | return trunc, true 164 | } 165 | } 166 | 167 | // z.Real() returns a representation of z, truncated to a real 168 | // value. The second return valuie is true if a truncation occured. 169 | func (z *BigComplex) Real() (_ *BigComplex, truncation bool) { 170 | if z.IsReal() { 171 | return z, false 172 | } else { 173 | return &BigComplex{Re: z.Re}, true 174 | } 175 | } 176 | 177 | func (z *BigComplex) IsInteger() bool { 178 | return z.Re.IsInt() && z.Im.Num().BitLen() == 0 179 | } 180 | 181 | func (z *BigComplex) IsReal() bool { 182 | return z.Im.Num().BitLen() == 0 183 | } 184 | 185 | func (z *BigComplex) Equals(other *BigComplex) bool { 186 | return new(BigComplex).Sub(z, other).IsZero() 187 | } 188 | 189 | func (z *BigComplex) String() string { 190 | return z.StringShow0i(true) 191 | } 192 | 193 | func (z *BigComplex) StringShow0i(show0i bool) string { 194 | var s string 195 | if z.Re.Num().BitLen() != 0 || show0i { 196 | if z.Re.IsInt() { 197 | s += z.Re.Num().String() 198 | } else { 199 | f, _ := z.Re.Float64() 200 | s += fmt.Sprintf("%.5g", f) 201 | } 202 | } 203 | if !z.IsReal() || show0i { 204 | if s != "" { 205 | s += "+" 206 | } 207 | if z.Im.IsInt() { 208 | s += z.Im.Num().String() 209 | } else { 210 | f, _ := z.Im.Float64() 211 | s += fmt.Sprintf("%.5g", f) 212 | } 213 | s += "i" 214 | } 215 | if s == "" { 216 | s = "0" 217 | } 218 | return s 219 | } 220 | -------------------------------------------------------------------------------- /bigcomplex_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIntOverflows(t *testing.T) { 8 | expectIntOverflow(t, 8, newBigInt("0x000000fe"), 0xfe) 9 | expectIntOverflow(t, 8, newBigInt("-0x000000fe"), 0x02) 10 | expectIntOverflow(t, 8, newBigInt("0xfffffffe"), 0xfe) 11 | expectIntOverflow(t, 8, newBigInt("-0xfffffffe"), 0x02) 12 | 13 | expectIntOverflow(t, 16, newBigInt("0x0000fffe"), 0xfffe) 14 | expectIntOverflow(t, 16, newBigInt("-0x0000fffe"), 0x0002) 15 | expectIntOverflow(t, 16, newBigInt("0xfffffffe"), 0xfffe) 16 | expectIntOverflow(t, 16, newBigInt("-0xfffffffe"), 0x0002) 17 | 18 | expectIntOverflow(t, 32, newBigInt("0x00fffffffe"), 0xfffffffe) 19 | expectIntOverflow(t, 32, newBigInt("-0x00fffffffe"), 0x00000002) 20 | expectIntOverflow(t, 32, newBigInt("0xfffffffffe"), 0xfffffffe) 21 | expectIntOverflow(t, 32, newBigInt("-0xfffffffffe"), 0x00000002) 22 | 23 | expectIntOverflow(t, 64, newBigInt("0x00fffffffffffffffe"), -0x0000000000000002) 24 | expectIntOverflow(t, 64, newBigInt("-0x00fffffffffffffffe"), 0x0000000000000002) 25 | expectIntOverflow(t, 64, newBigInt("0xfffffffffffffffffe"), -0x0000000000000002) 26 | expectIntOverflow(t, 64, newBigInt("-0xfffffffffffffffffe"), 0x0000000000000002) 27 | } 28 | 29 | func TestUintOverflows(t *testing.T) { 30 | expectUintOverflow(t, 8, newBigInt("0x000001fe"), 0xfe) 31 | expectUintOverflow(t, 8, newBigInt("0xfffffffe"), 0xfe) 32 | expectUintOverflow(t, 8, newBigInt("-0x000000fe"), 0x02) 33 | expectUintOverflow(t, 8, newBigInt("-0x000001fe"), 0x02) 34 | expectUintOverflow(t, 8, newBigInt("-0xfffffffe"), 0x02) 35 | 36 | expectUintOverflow(t, 16, newBigInt("0x0001fffe"), 0xfffe) 37 | expectUintOverflow(t, 16, newBigInt("0xfffffffe"), 0xfffe) 38 | expectUintOverflow(t, 16, newBigInt("-0x0000fffe"), 0x0002) 39 | expectUintOverflow(t, 16, newBigInt("-0x0001fffe"), 0x0002) 40 | expectUintOverflow(t, 16, newBigInt("-0xfffffffe"), 0x0002) 41 | 42 | expectUintOverflow(t, 32, newBigInt("0x01fffffffe"), 0xfffffffe) 43 | expectUintOverflow(t, 32, newBigInt("0xfffffffffe"), 0xfffffffe) 44 | expectUintOverflow(t, 32, newBigInt("-0x00fffffffe"), 0x00000002) 45 | expectUintOverflow(t, 32, newBigInt("-0x01fffffffe"), 0x00000002) 46 | expectUintOverflow(t, 32, newBigInt("-0xfffffffffe"), 0x00000002) 47 | 48 | expectUintOverflow(t, 64, newBigInt("0x01fffffffffffffffe"), 0xfffffffffffffffe) 49 | expectUintOverflow(t, 64, newBigInt("0xfffffffffffffffffe"), 0xfffffffffffffffe) 50 | expectUintOverflow(t, 64, newBigInt("-0x00fffffffffffffffe"), 0x0000000000000002) 51 | expectUintOverflow(t, 64, newBigInt("-0x01fffffffffffffffe"), 0x0000000000000002) 52 | expectUintOverflow(t, 64, newBigInt("-0xfffffffffffffffffe"), 0x0000000000000002) 53 | } 54 | 55 | func expectIntOverflow(t *testing.T, bits int, c *BigComplex, expected int64) { 56 | if result, truncation, overflow := c.Int(bits); truncation { 57 | t.Fatalf("Unexpected truncation") 58 | } else if !overflow { 59 | t.Fatalf("Expected overflow") 60 | } else if result != expected { 61 | t.Fatalf("Expected %v, got %v\n", expected, result) 62 | } 63 | } 64 | 65 | func expectUintOverflow(t *testing.T, bits int, c *BigComplex, expected uint64) { 66 | if result, truncation, overflow := c.Uint(bits); truncation { 67 | t.Fatalf("Unexpected truncation") 68 | } else if !overflow { 69 | t.Fatalf("Expected overflow") 70 | } else if result != expected { 71 | t.Fatalf("Expected %v, got %v\n", expected, result) 72 | } 73 | } 74 | 75 | func newBigInt(i string) *BigComplex { 76 | integer := new(BigComplex) 77 | integer.Re.Denom().SetInt64(1) 78 | if _, ok := integer.Re.Num().SetString(i, 0); !ok { 79 | panic("Invalid BigInt string '" + i + "'") 80 | } else { 81 | return integer 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /builtins.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | ) 7 | 8 | var ( 9 | intType reflect.Type = reflect.TypeOf(int(0)) 10 | i8 reflect.Type = reflect.TypeOf(int8(0)) 11 | i16 reflect.Type = reflect.TypeOf(int16(0)) 12 | i32 reflect.Type = reflect.TypeOf(int32(0)) 13 | i64 reflect.Type = reflect.TypeOf(int64(0)) 14 | 15 | uintType reflect.Type = reflect.TypeOf(uint(0)) 16 | u8 reflect.Type = reflect.TypeOf(uint8(0)) 17 | u16 reflect.Type = reflect.TypeOf(uint16(0)) 18 | u32 reflect.Type = reflect.TypeOf(uint32(0)) 19 | u64 reflect.Type = reflect.TypeOf(uint64(0)) 20 | 21 | f32 reflect.Type = reflect.TypeOf(float32(0)) 22 | f64 reflect.Type = reflect.TypeOf(float64(0)) 23 | c64 reflect.Type = reflect.TypeOf(complex64(0)) 24 | c128 reflect.Type = reflect.TypeOf(complex128(0)) 25 | 26 | boolType reflect.Type = reflect.TypeOf(bool(false)) 27 | stringType reflect.Type = reflect.TypeOf(string("")) 28 | 29 | emptyInterface reflect.Type = reflect.TypeOf(new(interface{})).Elem() 30 | 31 | byteSlice reflect.Type = reflect.SliceOf(u8) 32 | runeSlice reflect.Type = reflect.SliceOf(i32) 33 | untypedNilType reflect.Type = reflect.TypeOf(UntypedNil{}) 34 | ) 35 | 36 | var builtinTypes = map[string] reflect.Type{ 37 | "int": intType, 38 | "int8": i8, 39 | "int16": i16, 40 | "int32": i32, 41 | "int64": i64, 42 | 43 | "uint": uintType, 44 | "uint8": u8, 45 | "uint16": u16, 46 | "uint32": u32, 47 | "uint64": u64, 48 | 49 | "float32": f32, 50 | "float64": f64, 51 | 52 | "complex64": c64, 53 | "complex128": c128, 54 | 55 | "bool": boolType, 56 | "byte": ByteType, 57 | "rune": RuneType, 58 | "string": stringType, 59 | 60 | "error": reflect.TypeOf(errors.New("")), 61 | } 62 | 63 | var builtinFuncs = map[string] reflect.Value{ 64 | "complex": reflect.ValueOf(builtinComplex), 65 | "real": reflect.ValueOf(builtinReal), 66 | "imag": reflect.ValueOf(builtinImag), 67 | "append": reflect.ValueOf(builtinAppend), 68 | "cap": reflect.ValueOf(builtinCap), 69 | "len": reflect.ValueOf(builtinLen), 70 | "new": reflect.ValueOf(builtinNew), 71 | "copy": reflect.ValueOf(builtinCopy), 72 | "delete": reflect.ValueOf(builtinDelete), 73 | "panic": reflect.ValueOf(builtinPanic), 74 | } 75 | 76 | func builtinComplex(re, im reflect.Value) reflect.Value { 77 | if re.Type() == f64 { 78 | return reflect.ValueOf(complex128(complex(re.Float(), im.Float()))) 79 | } else { 80 | return reflect.ValueOf(complex64(complex(re.Float(), im.Float()))) 81 | } 82 | } 83 | 84 | func builtinReal(cplx reflect.Value) reflect.Value { 85 | if cplx.Type() == c128 { 86 | return reflect.ValueOf(float64(real(cplx.Complex()))) 87 | } else { 88 | return reflect.ValueOf(float32(real(cplx.Complex()))) 89 | } 90 | } 91 | 92 | func builtinImag(cplx reflect.Value) reflect.Value { 93 | if cplx.Type() == c128 { 94 | return reflect.ValueOf(float64(imag(cplx.Complex()))) 95 | } else { 96 | return reflect.ValueOf(float32(imag(cplx.Complex()))) 97 | } 98 | } 99 | 100 | func builtinAppend(s, t reflect.Value) reflect.Value { 101 | if s.Type() == byteSlice && t.Type().Kind() == reflect.String { 102 | t = reflect.ValueOf([]byte(t.String())) 103 | } 104 | return reflect.AppendSlice(s, t) 105 | } 106 | 107 | func builtinLen(v reflect.Value) reflect.Value { 108 | return reflect.ValueOf(v.Len()) 109 | } 110 | 111 | func builtinCap(v reflect.Value) reflect.Value { 112 | return reflect.ValueOf(v.Cap()) 113 | } 114 | 115 | func builtinNew(t reflect.Type) reflect.Value { 116 | return reflect.New(t) 117 | } 118 | 119 | func builtinCopy(s, t reflect.Value) reflect.Value { 120 | n := reflect.Copy(s, t) 121 | return reflect.ValueOf(n) 122 | } 123 | 124 | func builtinDelete(m, k reflect.Value) reflect.Value { 125 | m.SetMapIndex(k, reflect.Value{}) 126 | return reflect.Value{} 127 | } 128 | 129 | func builtinPanic(i reflect.Value) error { 130 | return PanicUser(i) 131 | } 132 | -------------------------------------------------------------------------------- /bytetype.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | type Byte struct { 8 | reflect.Type 9 | } 10 | 11 | func (Byte) Name() string { 12 | return "byte" 13 | } 14 | 15 | func (Byte) String() string { 16 | return "byte" 17 | } 18 | 19 | var ByteType = Byte{reflect.TypeOf(byte(0))} 20 | -------------------------------------------------------------------------------- /checkassignstmt_gen_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | // Test TooMany 9 | func TestCheckAssignStmtTooMany(t *testing.T) { 10 | env := MakeSimpleEnv() 11 | f := func() (int, int) { return 1, 1 } 12 | env.Vars["f"] = reflect.ValueOf(&f) 13 | expectCheckError(t, `_, _ = 1, 2, 3`, env, 14 | `assignment count mismatch: 2 = 3`, 15 | ) 16 | } 17 | 18 | // Test TooFew 19 | func TestCheckAssignStmtTooFew(t *testing.T) { 20 | env := MakeSimpleEnv() 21 | f := func() (int, int) { return 1, 1 } 22 | env.Vars["f"] = reflect.ValueOf(&f) 23 | expectCheckError(t, `_, _ = 1`, env, 24 | `assignment count mismatch: 2 = 1`, 25 | ) 26 | } 27 | 28 | // Test NoNewIdents1 29 | func TestCheckAssignStmtNoNewIdents1(t *testing.T) { 30 | env := MakeSimpleEnv() 31 | f := func() (int, int) { return 1, 1 } 32 | env.Vars["f"] = reflect.ValueOf(&f) 33 | expectCheckError(t, `f := nil`, env, 34 | `no new variables on left side of :=`, 35 | ) 36 | } 37 | 38 | // Test NoNewIdents2 39 | func TestCheckAssignStmtNoNewIdents2(t *testing.T) { 40 | env := MakeSimpleEnv() 41 | f := func() (int, int) { return 1, 1 } 42 | env.Vars["f"] = reflect.ValueOf(&f) 43 | expectCheckError(t, `f, _ := nil, 1`, env, 44 | `no new variables on left side of :=`, 45 | ) 46 | } 47 | 48 | // Test UnderscoreNil 49 | func TestCheckAssignStmtUnderscoreNil(t *testing.T) { 50 | env := MakeSimpleEnv() 51 | f := func() (int, int) { return 1, 1 } 52 | env.Vars["f"] = reflect.ValueOf(&f) 53 | expectCheckError(t, `_ = nil`, env, 54 | `use of untyped nil`, 55 | ) 56 | } 57 | 58 | // Test Unaddressable 59 | func TestCheckAssignStmtUnaddressable(t *testing.T) { 60 | env := MakeSimpleEnv() 61 | f := func() (int, int) { return 1, 1 } 62 | env.Vars["f"] = reflect.ValueOf(&f) 63 | expectCheckError(t, `1 = 1`, env, 64 | `cannot assign to 1`, 65 | `cannot use 1 (type int) as type untyped number in assignment`, 66 | ) 67 | } 68 | 69 | // Test Unaddressable2 70 | func TestCheckAssignStmtUnaddressable2(t *testing.T) { 71 | env := MakeSimpleEnv() 72 | f := func() (int, int) { return 1, 1 } 73 | env.Vars["f"] = reflect.ValueOf(&f) 74 | expectCheckError(t, `1, 2 = 1, 2`, env, 75 | `cannot assign to 1`, 76 | `cannot assign to 2`, 77 | `cannot use 1 (type int) as type untyped number in assignment`, 78 | `cannot use 2 (type int) as type untyped number in assignment`, 79 | ) 80 | } 81 | 82 | // Test ToNil 83 | func TestCheckAssignStmtToNil(t *testing.T) { 84 | env := MakeSimpleEnv() 85 | f := func() (int, int) { return 1, 1 } 86 | env.Vars["f"] = reflect.ValueOf(&f) 87 | expectCheckError(t, `nil = 1`, env, 88 | `cannot assign to nil`, 89 | `cannot use 1 (type int) as type nil in assignment`, 90 | ) 91 | } 92 | 93 | // Test Mistyped1 94 | func TestCheckAssignStmtMistyped1(t *testing.T) { 95 | env := MakeSimpleEnv() 96 | f := func() (int, int) { return 1, 1 } 97 | env.Vars["f"] = reflect.ValueOf(&f) 98 | expectCheckError(t, `f = true`, env, 99 | `cannot use true (type bool) as type func() (int, int) in assignment`, 100 | ) 101 | } 102 | 103 | // Test Mistyped2 104 | func TestCheckAssignStmtMistyped2(t *testing.T) { 105 | env := MakeSimpleEnv() 106 | f := func() (int, int) { return 1, 1 } 107 | env.Vars["f"] = reflect.ValueOf(&f) 108 | expectCheckError(t, `f, f = true, false`, env, 109 | `cannot use true (type bool) as type func() (int, int) in assignment`, 110 | `cannot use false (type bool) as type func() (int, int) in assignment`, 111 | ) 112 | } 113 | -------------------------------------------------------------------------------- /checkbasiclit.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "strconv" 5 | 6 | "go/ast" 7 | "go/token" 8 | ) 9 | 10 | func checkBasicLit(lit *ast.BasicLit, env Env) (*BasicLit, []error) { 11 | aexpr := &BasicLit{BasicLit: lit} 12 | 13 | switch lit.Kind { 14 | case token.CHAR: 15 | if r, _, tail, err := strconv.UnquoteChar(lit.Value[1:len(lit.Value)-1], '\''); err != nil { 16 | return aexpr, []error{ErrBadBasicLit{aexpr}} 17 | } else if tail != "" { 18 | // parser.ParseExpr() should raise a syntax error before we get here. 19 | panic("go-interactive: bad char lit " + lit.Value) 20 | } else { 21 | aexpr.constValue = constValueOf(NewConstRune(r)) 22 | aexpr.knownType = knownType{ConstRune} 23 | return aexpr, nil 24 | } 25 | case token.STRING: 26 | if str, err := strconv.Unquote(string(lit.Value)); err != nil { 27 | return aexpr, []error{ErrBadBasicLit{aexpr}} 28 | } else { 29 | aexpr.constValue = constValueOf(str) 30 | aexpr.knownType = knownType{ConstString} 31 | return aexpr, nil 32 | } 33 | case token.INT: 34 | if i, ok := NewConstInteger(lit.Value); !ok { 35 | return aexpr, []error{ErrBadBasicLit{aexpr}} 36 | } else { 37 | aexpr.constValue = constValueOf(i) 38 | aexpr.knownType = knownType{ConstInt} 39 | return aexpr, nil 40 | } 41 | case token.FLOAT: 42 | if f, ok := NewConstFloat(lit.Value); !ok { 43 | return aexpr, []error{ErrBadBasicLit{aexpr}} 44 | } else { 45 | aexpr.constValue = constValueOf(f) 46 | aexpr.knownType = knownType{ConstFloat} 47 | return aexpr, nil 48 | } 49 | case token.IMAG: 50 | if i, ok := NewConstImag(lit.Value); !ok { 51 | return aexpr, []error{ErrBadBasicLit{aexpr}} 52 | } else { 53 | aexpr.constValue = constValueOf(i) 54 | aexpr.knownType = knownType{ConstComplex} 55 | return aexpr, nil 56 | } 57 | default: 58 | return aexpr, []error{ErrBadBasicLit{aexpr}} 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /checkbuiltin_make_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TODO the make() type check in gc 1.2 is quite broken. Delete these tests 8 | // and enable the generated make() tests in 1.3 9 | 10 | func TestCheckBuiltinMakeMissingArgs(t *testing.T) { 11 | env := MakeSimpleEnv() 12 | expectCheckError(t, "make()", env, "missing argument to make") 13 | } 14 | 15 | func TestCheckBuiltinMakeUntypedIntFirstArg(t *testing.T) { 16 | env := MakeSimpleEnv() 17 | expectCheckError(t, "make(1)", env, "1 is not a type") 18 | } 19 | 20 | func TestCheckBuiltinMakeTypedIntFirstArg(t *testing.T) { 21 | env := MakeSimpleEnv() 22 | expectCheckError(t, "make(int(1))", env, "int(1) is not a type") 23 | } 24 | 25 | func TestCheckBuiltinMakeTypedNilFirstArg(t *testing.T) { 26 | env := MakeSimpleEnv() 27 | expectCheckError(t, "make(nil)", env, "nil is not a type") 28 | } 29 | 30 | func TestCheckBuiltinMakeSecondArgNotInt(t *testing.T) { 31 | env := MakeSimpleEnv() 32 | expectCheckError(t, "make([]int, true)", env, "make: non-integer len argument true") 33 | } 34 | 35 | func TestCheckBuiltinMakeThirdArgNotInt(t *testing.T) { 36 | env := MakeSimpleEnv() 37 | expectCheckError(t, "make([]int, 1, bool(true))", env, "make: non-integer cap argument bool(true)") 38 | } 39 | 40 | func TestCheckBuiltinMakeChanTooManyArgs(t *testing.T) { 41 | env := MakeSimpleEnv() 42 | expectCheckError(t, "make(chan<- int, 1, 1)", env, "too many arguments to make: make(chan<- int, 1, 1)") 43 | } 44 | 45 | func TestCheckBuiltinMakeSliceTooFew(t *testing.T) { 46 | env := MakeSimpleEnv() 47 | expectCheckError(t, "make([]int)", env, "too few arguments to make: make([]int)") 48 | } 49 | 50 | func TestCheckBuiltinMakeSliceTooManyArgs(t *testing.T) { 51 | env := MakeSimpleEnv() 52 | expectCheckError(t, "make([]int, 1, 1, 1)", env, "too many arguments to make: make([]int, 1, 1, 1)") 53 | } 54 | 55 | func TestCheckBuiltinMakeSliceLenGtrThanCap(t *testing.T) { 56 | env := MakeSimpleEnv() 57 | expectCheckError(t, "make([]int, 3, 2)", env, "len larger than cap in make([]int, 3, 2)") 58 | } 59 | 60 | func TestCheckBuiltinMakeBadType(t *testing.T) { 61 | env := MakeSimpleEnv() 62 | expectCheckError(t, "make(int)", env, "cannot make type int") 63 | } 64 | 65 | -------------------------------------------------------------------------------- /checkexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "go/ast" 7 | ) 8 | 9 | // Type check an ast.Expr to produce an Expr. Errors are accumulated and 10 | // returned as a single slice. When evaluating constant expressions, 11 | // non fatal truncation/overflow errors may be raised but type checking 12 | // will continue. A common pattern to detect errors is 13 | // 14 | // if expr, errs := CheckExpr(...); errs != nil && !expr.IsConst() { 15 | // fatal 16 | // } 17 | // 18 | // if expr.IsConst() is true, then the resulting Expr has been successfully 19 | // checked, regardless of if errors are present. 20 | func CheckExpr(expr ast.Expr, env Env) (Expr, []error) { 21 | if t, _, isType, _ := checkType(expr, env); isType { 22 | return t, []error{ErrTypeUsedAsExpression{t}} 23 | } 24 | 25 | switch expr := expr.(type) { 26 | case *ast.BadExpr: 27 | return &BadExpr{BadExpr: expr}, nil 28 | case *ast.Ident: 29 | return checkIdent(expr, env) 30 | case *ast.Ellipsis: 31 | return &Ellipsis{Ellipsis: expr}, nil 32 | case *ast.BasicLit: 33 | return checkBasicLit(expr, env) 34 | case *ast.FuncLit: 35 | return checkFuncLit(expr, env) 36 | case *ast.CompositeLit: 37 | return checkCompositeLit(expr, env) 38 | case *ast.ParenExpr: 39 | return checkParenExpr(expr, env) 40 | case *ast.SelectorExpr: 41 | return checkSelectorExpr(expr, env) 42 | case *ast.IndexExpr: 43 | return checkIndexExpr(expr, env) 44 | case *ast.SliceExpr: 45 | return checkSliceExpr(expr, env) 46 | case *ast.TypeAssertExpr: 47 | return checkTypeAssertExpr(expr, env) 48 | case *ast.CallExpr: 49 | return checkCallExpr(expr, env) 50 | case *ast.StarExpr: 51 | return checkStarExpr(expr, env) 52 | case *ast.UnaryExpr: 53 | return checkUnaryExpr(expr, env) 54 | case *ast.BinaryExpr: 55 | return checkBinaryExpr(expr, env) 56 | case *ast.KeyValueExpr: 57 | panic("eval: KeyValueExpr checked") 58 | default: 59 | panic("eval: Bad expr") 60 | } 61 | } 62 | 63 | func checkType(expr ast.Expr, env Env) (Expr, reflect.Type, bool, []error) { 64 | for parens, ok := expr.(*ast.ParenExpr); ok; parens, ok = expr.(*ast.ParenExpr) { 65 | expr = parens.X 66 | } 67 | switch node := expr.(type) { 68 | case *ast.Ident: 69 | ident := &Ident{Ident: node} 70 | if t := env.Type(node.Name); t != nil { 71 | ident.knownType = knownType{t} 72 | return ident, t, true, nil 73 | } else if t, ok := builtinTypes[node.Name]; ok { 74 | ident.knownType = knownType{t} 75 | return ident, t, true, nil 76 | } else { 77 | return ident, nil, false, []error{ErrUndefined{ident}} 78 | } 79 | case *ast.StarExpr: 80 | star := &StarExpr{StarExpr: node} 81 | elem, elemT, isType, errs := checkType(node.X, env) 82 | if isType { 83 | // Only set X if X is a type, as * can be part of an expression or type 84 | t := reflect.PtrTo(elemT) 85 | star.X = elem 86 | star.knownType = knownType{t} 87 | return star, t, isType, nil 88 | } else { 89 | return star, nil, isType, errs 90 | } 91 | case *ast.SelectorExpr: 92 | // TODO[crc] remove this comment after ast cleanup 93 | // Note that Sel SelectorExpr has its own Sel field, shadowing ast.SelectorExpr.Sel 94 | // This is on the cleanup list. 95 | sel := &SelectorExpr{SelectorExpr: node, Sel: &Ident{Ident: node.Sel}} 96 | if ident, ok := node.X.(*ast.Ident); !ok { 97 | return sel, nil, false, nil 98 | } else if pkg := env.Pkg(ident.Name); pkg == nil { 99 | return sel, nil, false, nil 100 | } else if t := pkg.Type(sel.Sel.Name); t == nil { 101 | return sel, nil, false, nil 102 | } else { 103 | // Only set X if the selector is a type, . can be part of an expression or type 104 | sel.X = &Ident{Ident: ident} 105 | sel.knownType = knownType{t} 106 | return sel, t, true, nil 107 | } 108 | case *ast.ArrayType: 109 | arrayT := &ArrayType{ArrayType: node} 110 | if node.Len != nil { 111 | return arrayT, nil, true, []error{errors.New("array types not implemented")} 112 | } else { 113 | elt, eltT, _, errs := checkType(node.Elt, env); 114 | arrayT.Elt = elt 115 | if errs != nil { 116 | return arrayT, nil, true, errs 117 | } else { 118 | t := reflect.SliceOf(unhackType(eltT)) 119 | arrayT.knownType = knownType{t} 120 | return arrayT, t, true, nil 121 | } 122 | } 123 | case *ast.StructType: 124 | structT := &StructType{StructType: node} 125 | return structT, nil, true, []error{errors.New("struct types not implemented")} 126 | case *ast.FuncType: 127 | funcT := &FuncType{FuncType: node} 128 | return funcT, nil, true, []error{errors.New("func types not implemented")} 129 | case *ast.InterfaceType: 130 | interfaceT := &InterfaceType{InterfaceType: node} 131 | // Allow interface{}'s 132 | if node.Methods.List == nil { 133 | interfaceT.knownType = knownType{emptyInterface} 134 | return interfaceT, emptyInterface, true, nil 135 | } 136 | return interfaceT, nil, true, []error{errors.New("interface types not implemented")} 137 | case *ast.MapType: 138 | mapT := &MapType{MapType: node} 139 | keyT, k, _, errs := checkType(node.Key, env) 140 | mapT.Key = keyT 141 | if k != nil && !isStaticTypeComparable(k) { 142 | errs = append(errs, ErrUncomparableMapKey{mapT, k}) 143 | } 144 | valueT, v, _, moreErrs := checkType(node.Value, env) 145 | mapT.Value = valueT 146 | if moreErrs != nil { 147 | errs = append(errs, moreErrs...) 148 | } 149 | if errs == nil { 150 | t := reflect.MapOf(unhackType(k), unhackType(v)) 151 | mapT.knownType = knownType{t} 152 | return mapT, t, true, nil 153 | } 154 | return mapT, nil, true, errs 155 | case *ast.ChanType: 156 | chanT := &ChanType{ChanType: node} 157 | value, valueT, _, errs := checkType(node.Value, env); 158 | chanT.Value = value 159 | if errs != nil { 160 | return chanT, nil, true, errs 161 | } else { 162 | if node.Dir == ast.SEND { 163 | chanT.dir = reflect.SendDir 164 | } else if node.Dir == ast.RECV { 165 | chanT.dir = reflect.RecvDir 166 | } else { 167 | chanT.dir = reflect.BothDir 168 | } 169 | t := reflect.ChanOf(chanT.dir, unhackType(valueT)) 170 | chanT.knownType = knownType{t} 171 | return chanT, t, true, nil 172 | } 173 | } 174 | return nil, nil, false, nil 175 | } 176 | 177 | func checkFieldList(list *ast.FieldList, env Env) (*FieldList, []error) { 178 | if list == nil { 179 | return nil, nil 180 | } 181 | var errs, moreErrs []error 182 | 183 | alist := &FieldList{FieldList: list} 184 | if list.List == nil { 185 | return alist, nil 186 | } 187 | alist.List = make([]*Field, len(list.List)) 188 | for i, field := range list.List { 189 | alist.List [i], moreErrs = checkField(field, env) 190 | errs = append(errs, moreErrs...) 191 | } 192 | return alist, nil 193 | } 194 | 195 | func checkField(field *ast.Field, env Env) (*Field, []error) { 196 | afield := &Field{Field: field} 197 | if field.Names != nil { 198 | afield.Names = make([]*Ident, len(field.Names)) 199 | for i, ident := range field.Names { 200 | afield.Names[i] = &Ident{Ident: ident} 201 | } 202 | } 203 | // the ellipsis is only relevant for func args 204 | var typ Expr 205 | var t reflect.Type 206 | var errs []error 207 | if ellipsis, ok := field.Type.(*ast.Ellipsis); ok { 208 | typ, t, _, errs = checkType(ellipsis.Elt, env) 209 | if t != nil { 210 | t = reflect.SliceOf(t) 211 | } 212 | ellipsis.Elt = typ 213 | typ = &Ellipsis{Ellipsis: ellipsis} 214 | } else { 215 | typ, t, _, errs = checkType(field.Type, env) 216 | } 217 | afield.Type = typ 218 | if t != nil { 219 | afield.knownType = knownType{t} 220 | } 221 | return afield, errs 222 | } 223 | -------------------------------------------------------------------------------- /checkfunclit.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "go/ast" 5 | 6 | "reflect" 7 | ) 8 | 9 | func checkFuncLit(lit *ast.FuncLit, env Env) (*FuncLit, []error) { 10 | alit := &FuncLit{FuncLit: lit} 11 | atype, t, _, errs := checkType(lit.Type, env) 12 | alit.Type = atype.(*FuncType) 13 | if t != nil { 14 | alit.knownType = knownType{t} 15 | } 16 | 17 | // Create a new environment containing all valid params and results. 18 | seen := map[string]bool{} 19 | invalid := map[string]bool{} 20 | env = env.PushScope() 21 | fields := alit.Type.Params 22 | // Type.Results may be nil 23 | for i := 0; i < 2 && fields != nil; i += 1 { 24 | for _, field := range fields.List { 25 | var z reflect.Value 26 | if len(field.KnownType()) != 0 { 27 | z = reflect.New(field.KnownType()[0]) 28 | } 29 | for _, name := range field.Names { 30 | if name.Name == "_" { 31 | continue 32 | } 33 | if !z.IsValid() { 34 | invalid[name.Name] = true 35 | } else { 36 | env.AddVar(name.Name, z) 37 | } 38 | if seen[name.Name] { 39 | errs = append(errs, ErrDuplicateArg{name}) 40 | } 41 | seen[name.Name] = true 42 | } 43 | } 44 | fields = alit.Type.Results 45 | } 46 | 47 | // t may be nil, in this case return statements won't be checked 48 | block, moreErrs := checkBlock(lit.Body, env, checkCtx{outerFunc: t}) 49 | alit.Body = block 50 | 51 | // Filter out undefined errors caused by invalid params 52 | var filtered []error 53 | for _, err := range moreErrs { 54 | if undef, ok := err.(ErrUndefined); ok { 55 | if ident, ok := undef.Expr.(*Ident); ok && invalid[ident.Name] { 56 | continue 57 | } 58 | } 59 | filtered = append(filtered, err) 60 | } 61 | return alit, append(errs, filtered...) 62 | } 63 | -------------------------------------------------------------------------------- /checkident.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "go/ast" 6 | ) 7 | 8 | func checkIdent(ident *ast.Ident, env Env) (_ *Ident, errs []error) { 9 | aexpr := &Ident{Ident: ident} 10 | switch aexpr.Name { 11 | case "nil": 12 | aexpr.constValue = constValueOf(UntypedNil{}) 13 | aexpr.knownType = []reflect.Type{ConstNil} 14 | 15 | case "true": 16 | aexpr.constValue = constValueOf(true) 17 | aexpr.knownType = []reflect.Type{ConstBool} 18 | 19 | case "false": 20 | aexpr.constValue = constValueOf(false) 21 | aexpr.knownType = []reflect.Type{ConstBool} 22 | default: 23 | for searchEnv := env; searchEnv != nil; searchEnv = searchEnv.PopScope() { 24 | if v := searchEnv.Var(aexpr.Name); v.IsValid() { 25 | aexpr.knownType = knownType{v.Elem().Type()} 26 | aexpr.source = envVar 27 | return aexpr, errs 28 | } else if v := searchEnv.Func(aexpr.Name); v.IsValid() { 29 | aexpr.knownType = knownType{v.Type()} 30 | aexpr.source = envFunc 31 | return aexpr, errs 32 | } else if v := searchEnv.Const(aexpr.Name); v.IsValid() { 33 | if n, ok := v.Interface().(*ConstNumber); ok { 34 | aexpr.knownType = knownType{n.Type} 35 | } else { 36 | aexpr.knownType = knownType{v.Type()} 37 | } 38 | aexpr.constValue = constValue(v) 39 | aexpr.source = envConst 40 | return aexpr, errs 41 | } 42 | } 43 | return aexpr, append(errs, ErrUndefined{aexpr}) 44 | } 45 | return aexpr, errs 46 | } 47 | -------------------------------------------------------------------------------- /checkindexexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "go/ast" 6 | ) 7 | 8 | func checkIndexExpr(index *ast.IndexExpr, env Env) (*IndexExpr, []error) { 9 | aexpr := &IndexExpr{IndexExpr: index} 10 | x, errs := CheckExpr(index.X, env) 11 | aexpr.X = x 12 | if errs != nil && !x.IsConst() { 13 | return aexpr, errs 14 | } 15 | 16 | t, err := expectSingleType(x) 17 | if err != nil { 18 | return aexpr, append(errs, err) 19 | } 20 | 21 | // index of array pointer is short hand for dereference and then index 22 | if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Array { 23 | t = t.Elem() 24 | } 25 | 26 | switch t.Kind() { 27 | case reflect.Map: 28 | aexpr.knownType = knownType{t.Elem()} 29 | i, ok, moreErrs := checkExprAssignableTo(index.Index, t.Key(), env) 30 | aexpr.Index = i 31 | if moreErrs != nil { 32 | errs = append(errs, moreErrs...) 33 | } 34 | if !ok { 35 | errs = append(errs, ErrBadMapIndex{i, t.Key()}) 36 | } 37 | return aexpr, errs 38 | case reflect.String: 39 | aexpr.knownType = knownType{u8} 40 | i, moreErrs := checkIndexVectorExpr(x, index.Index, env) 41 | if moreErrs != nil { 42 | errs = append(errs, moreErrs...) 43 | } 44 | aexpr.Index = i 45 | return aexpr, errs 46 | case reflect.Array, reflect.Slice: 47 | aexpr.knownType = knownType{t.Elem()} 48 | i, moreErrs := checkIndexVectorExpr(x, index.Index, env) 49 | if moreErrs != nil { 50 | errs = append(errs, moreErrs...) 51 | } 52 | aexpr.Index = i 53 | return aexpr, errs 54 | default: 55 | aexpr.Index = fakeCheckExpr(index.Index, env) 56 | return aexpr, append(errs, ErrInvalidIndexOperation{aexpr}) 57 | } 58 | } 59 | 60 | func checkIndexVectorExpr(x Expr, index ast.Expr, env Env) (Expr, []error) { 61 | t := x.KnownType()[0] 62 | i, iint, ok, errs := checkInteger(index, env) 63 | if errs != nil && !i.IsConst() { 64 | // Type check of index failed 65 | } else if !ok { 66 | // Type check of index passed but this node is not an integer 67 | printableIndex := fakeCheckExpr(index, env) 68 | printableIndex.setKnownType(i.KnownType()) 69 | errs = append(errs, ErrNonIntegerIndex{printableIndex}) 70 | } else if i.IsConst() { 71 | // If we know the index at compile time, we must assert it is in bounds. 72 | if iint < 0 { 73 | errs = append(errs, ErrIndexOutOfBounds{i, x, iint}) 74 | } else if t.Kind() == reflect.Array { 75 | if iint >= t.Len() { 76 | errs = append(errs, ErrIndexOutOfBounds{i, x, iint}) 77 | } 78 | } else if t.Kind() == reflect.String && x.IsConst() { 79 | str := x.Const() 80 | if iint >= str.Len() { 81 | errs = append(errs, ErrIndexOutOfBounds{i, x, iint}) 82 | } 83 | } 84 | } 85 | return i, errs 86 | } 87 | -------------------------------------------------------------------------------- /checkparenexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "go/ast" 5 | ) 6 | 7 | func checkParenExpr(paren *ast.ParenExpr, env Env) (*ParenExpr, []error) { 8 | aexpr := &ParenExpr{ParenExpr: paren} 9 | x, errs := CheckExpr(paren.X, env) 10 | 11 | aexpr.X = x 12 | aexpr.knownType = knownType(x.KnownType()) 13 | aexpr.constValue = constValue(x.Const()) 14 | return aexpr, errs 15 | } 16 | -------------------------------------------------------------------------------- /checkselectorexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "go/ast" 6 | ) 7 | 8 | func checkSelectorExpr(selector *ast.SelectorExpr, env Env) (*SelectorExpr, []error) { 9 | aexpr := &SelectorExpr{SelectorExpr: selector} 10 | 11 | // First check if this is a package identifier 12 | if ident, ok := selector.X.(*ast.Ident); ok { 13 | if pkg := env.Pkg(ident.Name); pkg != nil { 14 | // Lookup this ident in the context of the package. 15 | sel, errs := checkIdent(aexpr.SelectorExpr.Sel, pkg) 16 | if len(errs) == 1 { 17 | if undefined, ok := (errs[0]).(ErrUndefined); ok { 18 | undefined.Expr = aexpr 19 | errs[0] = undefined 20 | } 21 | } 22 | // This selector node is really a single identifier. 23 | // Convey the type information to the parent. 24 | aexpr.constValue = sel.constValue 25 | aexpr.knownType = sel.knownType 26 | aexpr.pkgName = ident.Name 27 | aexpr.X = &Ident{Ident: ident} 28 | aexpr.Sel = sel 29 | return aexpr, errs 30 | } 31 | } 32 | 33 | x, errs := CheckExpr(selector.X, env) 34 | aexpr.X = x 35 | aexpr.Sel = &Ident{Ident: selector.Sel} 36 | if errs != nil && !x.IsConst() { 37 | return aexpr, errs 38 | } 39 | 40 | t, err := expectSingleType(x) 41 | if err != nil { 42 | return aexpr, append(errs, err) 43 | } else if t == ConstNil { 44 | return aexpr, append(errs, ErrUntypedNil{x}) 45 | } 46 | 47 | name := aexpr.Sel.Name 48 | // Check structs for field selectors 49 | switch t.Kind() { 50 | case reflect.Struct: 51 | if field, ok := t.FieldByName(name); ok { 52 | aexpr.field = field.Index 53 | aexpr.knownType = knownType{field.Type} 54 | return aexpr, errs 55 | } 56 | case reflect.Ptr: 57 | // auto-indirect of *struct types 58 | // filter out const types and non structs 59 | if _, ok := t.(ConstType); ok { 60 | break 61 | } else if t.Elem().Kind() != reflect.Struct { 62 | break 63 | } else if field, ok := t.Elem().FieldByName(name); ok { 64 | aexpr.field = field.Index 65 | aexpr.knownType = knownType{field.Type} 66 | return aexpr, errs 67 | } 68 | } 69 | 70 | // Type.Method() on a non interface type returns a receiver type 71 | // accepting X.KnownType as it's first argument. Value.Method() Binds the 72 | // method to the value, and hence does not have the first argument. 73 | if t.Kind() == reflect.Interface { 74 | if method, ok := t.MethodByName(name); ok { 75 | aexpr.knownType = knownType{method.Type} 76 | aexpr.method = method.Index 77 | return aexpr, errs 78 | } 79 | } else { 80 | for i := 0; i < 2; i += 1 { 81 | if method, ok := t.MethodByName(name); ok { 82 | zero := reflect.Zero(t) 83 | bound := zero.MethodByName(name) 84 | aexpr.knownType = knownType{bound.Type()} 85 | aexpr.method = method.Index 86 | aexpr.isPtrReceiver = i != 0 87 | return aexpr, errs 88 | } 89 | // Check for ptr receivers 90 | t = reflect.PtrTo(t) 91 | } 92 | } 93 | 94 | return aexpr, append(errs, ErrUndefinedFieldOrMethod{aexpr}) 95 | } 96 | -------------------------------------------------------------------------------- /checksliceexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "go/ast" 6 | ) 7 | 8 | // TODO[crc] support [::] syntax after go1.2 upgrade 9 | func checkSliceExpr(slice *ast.SliceExpr, env Env) (*SliceExpr, []error) { 10 | aexpr := &SliceExpr{SliceExpr: slice} 11 | x, errs := CheckExpr(slice.X, env) 12 | aexpr.X = x 13 | if errs != nil && !x.IsConst() { 14 | return aexpr, errs 15 | } 16 | 17 | t, err := expectSingleType(x) 18 | if err != nil { 19 | return aexpr, append(errs, err) 20 | } 21 | 22 | // arrays must be addressable 23 | if t.Kind() == reflect.Array && !isAddressable(x) { 24 | return aexpr, append(errs, ErrUnaddressableSliceOperand{aexpr}) 25 | } 26 | // slice of array pointer is short hand for dereference and then slice 27 | if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Array { 28 | t = t.Elem() 29 | } 30 | 31 | switch t.Kind() { 32 | case reflect.Array, reflect.Slice, reflect.String: 33 | var l, h int 34 | var low, high Expr 35 | var moreErrs []error 36 | if t == ConstString { 37 | // spec: ConstString[:] fields string 38 | aexpr.knownType = knownType{stringType} 39 | } else { 40 | aexpr.knownType = knownType(x.KnownType()) 41 | } 42 | if slice.Low != nil { 43 | low, l, moreErrs = checkSliceVectorExpr(x, slice.Low, env) 44 | aexpr.Low = low 45 | if moreErrs != nil { 46 | errs = append(errs, moreErrs...) 47 | if !low.IsConst() { 48 | return aexpr, errs 49 | } 50 | } 51 | } 52 | if slice.High != nil { 53 | high, h, moreErrs = checkSliceVectorExpr(x, slice.High, env) 54 | aexpr.High = high 55 | if moreErrs != nil { 56 | errs = append(errs, moreErrs...) 57 | if !high.IsConst() { 58 | return aexpr, errs 59 | } 60 | } 61 | if low != nil && low.IsConst() && high.IsConst() && !(l <= h) { 62 | errs = append(errs, ErrInvalidSliceIndex{aexpr}) 63 | } 64 | } 65 | return aexpr, errs 66 | default: 67 | return aexpr, append(errs, ErrInvalidSliceOperation{aexpr}) 68 | } 69 | } 70 | 71 | func checkSliceVectorExpr(x Expr, index ast.Expr, env Env) (Expr, int, []error) { 72 | t := x.KnownType()[0] 73 | i, iint, ok, errs := checkInteger(index, env) 74 | if errs != nil && !i.IsConst() { 75 | // Type check of index failed 76 | } else if !ok { 77 | // Type check of index passed but this node is not an integer 78 | printableIndex := fakeCheckExpr(index, env) 79 | printableIndex.setKnownType(i.KnownType()) 80 | errs = append(errs, ErrNonIntegerIndex{printableIndex}) 81 | } else if i.IsConst() { 82 | // If we know the index at compile time, we must assert it is in bounds. 83 | // NOTE[crc] There is no upper bounds check on a const string. This is 84 | // to match gc. See issue http://code.google.com/p/go/issues/detail?id=7200 85 | if iint < 0 { 86 | errs = append(errs, ErrIndexOutOfBounds{i, x, iint}) 87 | } else if t.Kind() == reflect.Array { 88 | if iint >= t.Len() { 89 | errs = append(errs, ErrIndexOutOfBounds{i, x, iint}) 90 | } 91 | } 92 | } 93 | return i, iint, errs 94 | } 95 | -------------------------------------------------------------------------------- /checkstarexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | 6 | "go/ast" 7 | ) 8 | 9 | func checkStarExpr(star *ast.StarExpr, env Env) (*StarExpr, []error) { 10 | aexpr := &StarExpr{StarExpr: star} 11 | x, errs := CheckExpr(star.X, env) 12 | 13 | if errs != nil && !x.IsConst() { 14 | return aexpr, errs 15 | } else if t, err := expectSingleType(x); err != nil { 16 | errs = append(errs, err) 17 | } else if t == ConstNil { 18 | errs = append(errs, ErrInvalidIndirect{x}) 19 | } else if t.Kind() != reflect.Ptr { 20 | printableX := fakeCheckExpr(star.X, env) 21 | printableX.setKnownType(x.KnownType()) 22 | errs = append(errs, ErrInvalidIndirect{printableX}) 23 | } else { 24 | aexpr.knownType = knownType{t.Elem()} 25 | } 26 | aexpr.X = x 27 | return aexpr, errs 28 | } 29 | -------------------------------------------------------------------------------- /checkstarexpr_gen_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | // Test a 9 | func TestCheckStarExprA(t *testing.T) { 10 | 11 | a := 1 12 | b := &a 13 | _ = b 14 | env := MakeSimpleEnv() 15 | env.Vars["a"] = reflect.ValueOf(&a) 16 | env.Vars["b"] = reflect.ValueOf(&b) 17 | 18 | expectCheckError(t, `*a`, env, 19 | `invalid indirect of a (type int)`, 20 | ) 21 | 22 | } 23 | 24 | // Test b 25 | func TestCheckStarExprB(t *testing.T) { 26 | 27 | a := 1 28 | b := &a 29 | _ = b 30 | env := MakeSimpleEnv() 31 | env.Vars["a"] = reflect.ValueOf(&a) 32 | env.Vars["b"] = reflect.ValueOf(&b) 33 | 34 | expectType(t, `*b`, env, reflect.TypeOf(*b)) 35 | } 36 | 37 | // Test &a 38 | func TestCheckStarExprAtA(t *testing.T) { 39 | 40 | a := 1 41 | b := &a 42 | _ = b 43 | env := MakeSimpleEnv() 44 | env.Vars["a"] = reflect.ValueOf(&a) 45 | env.Vars["b"] = reflect.ValueOf(&b) 46 | 47 | expectType(t, `*&a`, env, reflect.TypeOf(*&a)) 48 | } 49 | 50 | // Test &b 51 | func TestCheckStarExprAtB(t *testing.T) { 52 | 53 | a := 1 54 | b := &a 55 | _ = b 56 | env := MakeSimpleEnv() 57 | env.Vars["a"] = reflect.ValueOf(&a) 58 | env.Vars["b"] = reflect.ValueOf(&b) 59 | 60 | expectType(t, `*&b`, env, reflect.TypeOf(*&b)) 61 | } 62 | 63 | // Test int(1) 64 | func TestCheckStarExprInt(t *testing.T) { 65 | 66 | a := 1 67 | b := &a 68 | _ = b 69 | env := MakeSimpleEnv() 70 | env.Vars["a"] = reflect.ValueOf(&a) 71 | env.Vars["b"] = reflect.ValueOf(&b) 72 | 73 | expectCheckError(t, `*int(1)`, env, 74 | `invalid indirect of int(1) (type int)`, 75 | ) 76 | 77 | } 78 | 79 | // Test 1.4 80 | func TestCheckStarExprNumber(t *testing.T) { 81 | 82 | a := 1 83 | b := &a 84 | _ = b 85 | env := MakeSimpleEnv() 86 | env.Vars["a"] = reflect.ValueOf(&a) 87 | env.Vars["b"] = reflect.ValueOf(&b) 88 | 89 | expectCheckError(t, `*1.4`, env, 90 | `invalid indirect of 1.4 (type untyped number)`, 91 | ) 92 | 93 | } 94 | 95 | // Test 'a' 96 | func TestCheckStarExprRune(t *testing.T) { 97 | 98 | a := 1 99 | b := &a 100 | _ = b 101 | env := MakeSimpleEnv() 102 | env.Vars["a"] = reflect.ValueOf(&a) 103 | env.Vars["b"] = reflect.ValueOf(&b) 104 | 105 | expectCheckError(t, `*'a'`, env, 106 | `invalid indirect of 'a' (type untyped number)`, 107 | ) 108 | 109 | } 110 | 111 | // Test true 112 | func TestCheckStarExprBool(t *testing.T) { 113 | 114 | a := 1 115 | b := &a 116 | _ = b 117 | env := MakeSimpleEnv() 118 | env.Vars["a"] = reflect.ValueOf(&a) 119 | env.Vars["b"] = reflect.ValueOf(&b) 120 | 121 | expectCheckError(t, `*true`, env, 122 | `invalid indirect of true (type untyped bool)`, 123 | ) 124 | 125 | } 126 | 127 | // Test "a" 128 | func TestCheckStarExprString(t *testing.T) { 129 | 130 | a := 1 131 | b := &a 132 | _ = b 133 | env := MakeSimpleEnv() 134 | env.Vars["a"] = reflect.ValueOf(&a) 135 | env.Vars["b"] = reflect.ValueOf(&b) 136 | 137 | expectCheckError(t, `*"a"`, env, 138 | `invalid indirect of "a" (type untyped string)`, 139 | ) 140 | 141 | } 142 | 143 | // Test nil 144 | func TestCheckStarExprNil(t *testing.T) { 145 | 146 | a := 1 147 | b := &a 148 | _ = b 149 | env := MakeSimpleEnv() 150 | env.Vars["a"] = reflect.ValueOf(&a) 151 | env.Vars["b"] = reflect.ValueOf(&b) 152 | 153 | expectCheckError(t, `*nil`, env, 154 | `invalid indirect of nil`, 155 | ) 156 | 157 | } 158 | 159 | // Test *b 160 | func TestCheckStarExprStarB(t *testing.T) { 161 | 162 | a := 1 163 | b := &a 164 | _ = b 165 | env := MakeSimpleEnv() 166 | env.Vars["a"] = reflect.ValueOf(&a) 167 | env.Vars["b"] = reflect.ValueOf(&b) 168 | 169 | expectCheckError(t, `**b`, env, 170 | `invalid indirect of *b (type int)`, 171 | ) 172 | 173 | } 174 | -------------------------------------------------------------------------------- /checkstmt_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "testing" 7 | ) 8 | 9 | func TestGotoBackward(t *testing.T) { 10 | _, ctx:= checkString(`{ 11 | target: 12 | _ = 1+1 13 | goto target 14 | }`) 15 | jump := findLabel(gothere("target"), ctx) 16 | if len(jump) != 1 { 17 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 18 | } 19 | } 20 | 21 | func TestGotoForward(t *testing.T) { 22 | _, ctx:= checkString(`{ 23 | goto target 24 | _ = 1+1 25 | target: 26 | }`) 27 | jump := findLabel(gothere("target"), ctx) 28 | if len(jump) != 2 { 29 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 30 | } 31 | } 32 | 33 | func TestGotoEscapeBlockBackward(t *testing.T) { 34 | _, ctx:= checkString(`{ 35 | target: 36 | _ = 1+1 37 | { 38 | goto target 39 | } 40 | }`) 41 | jump := findLabel(gothere("target"), ctx) 42 | if len(jump) != 1 { 43 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 44 | } 45 | } 46 | 47 | func TestGotoEscapeBlockForwards(t *testing.T) { 48 | _, ctx:= checkString(`{ 49 | { 50 | goto target 51 | } 52 | _ = 1+1 53 | target: 54 | }`) 55 | jump := findLabel(gothere("target"), ctx) 56 | if len(jump) != 2 { 57 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 58 | } 59 | } 60 | 61 | func TestGotoIntoBlockBackwards(t *testing.T) { 62 | _, ctx:= checkString(`{ 63 | { 64 | target: 65 | } 66 | _ = 1+1 67 | goto target 68 | }`) 69 | jump := findLabel(gothere("target"), ctx) 70 | if len(jump) != 2 { 71 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 72 | } 73 | } 74 | 75 | func TestGotoIntoBlockForward(t *testing.T) { 76 | _, ctx:= checkString(`{ 77 | goto target 78 | _ = 1+1 79 | { 80 | target: 81 | } 82 | }`) 83 | jump := findLabel(gothere("target"), ctx) 84 | if len(jump) != 3 { 85 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 86 | } 87 | } 88 | 89 | func TestGotoBetweenBlocksBackwards(t *testing.T) { 90 | _, ctx:= checkString(`{ 91 | { 92 | target: 93 | } 94 | _ = 1+1 95 | { 96 | goto target 97 | } 98 | }`) 99 | jump := findLabel(gothere("target"), ctx) 100 | if len(jump) != 2 { 101 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 102 | } 103 | } 104 | 105 | func TestGotoBetweenBlocksForward(t *testing.T) { 106 | _, ctx:= checkString(`{ 107 | { 108 | goto target 109 | } 110 | _ = 1+1 111 | { 112 | target: 113 | } 114 | }`) 115 | jump := findLabel(gothere("target"), ctx) 116 | if len(jump) != 3 { 117 | t.Errorf("Wrong number of jump stmts\n%#v", jump) 118 | } 119 | } 120 | 121 | func checkString(stmt string) (Stmt, checkCtx) { 122 | env := MakeSimpleEnv() 123 | s, _ := ParseStmt(stmt) 124 | c, _ := CheckStmt(s, env) 125 | ctx := checkCtx{stack: findGoto(c, []Stmt{})} 126 | return c, ctx 127 | } 128 | 129 | func findGoto(stmt Stmt, stack []Stmt) []Stmt { 130 | stack = append(stack, stmt) 131 | switch s := stmt.(type) { 132 | case *BlockStmt: 133 | for _, x := range s.List { 134 | if found := findGoto(x, stack); found != nil { 135 | return found 136 | } 137 | } 138 | case *CaseClause: 139 | for _, x := range s.Body { 140 | if found := findGoto(x, stack); found != nil { 141 | return found 142 | } 143 | } 144 | case *IfStmt: 145 | if found := findGoto(s.Body, stack); found != nil { 146 | return found 147 | } 148 | if s.Else != nil { 149 | return findGoto(s.Else, stack) 150 | } 151 | case *BranchStmt: 152 | if s.Tok == token.GOTO { 153 | return stack 154 | } 155 | case *LabeledStmt: 156 | return findGoto(s.Stmt, stack) 157 | case *ForStmt: 158 | return findGoto(s.Body, stack) 159 | case *SwitchStmt: 160 | return findGoto(s.Body, stack) 161 | case *TypeSwitchStmt: 162 | return findGoto(s.Body, stack) 163 | } 164 | return nil 165 | } 166 | 167 | func gothere(label string) *BranchStmt { 168 | return &BranchStmt{ 169 | BranchStmt: &ast.BranchStmt{ 170 | Tok: token.GOTO, 171 | }, 172 | Label: &Ident{ 173 | Ident: &ast.Ident{ 174 | Name: label, 175 | }, 176 | }, 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /checktypeassertexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | 6 | "go/ast" 7 | ) 8 | 9 | func checkTypeAssertExpr(assert *ast.TypeAssertExpr, env Env) (*TypeAssertExpr, []error) { 10 | aexpr := &TypeAssertExpr{TypeAssertExpr: assert} 11 | x, errs := CheckExpr(assert.X, env) 12 | aexpr.X = x 13 | 14 | if errs != nil && !x.IsConst() { 15 | return aexpr, errs 16 | } else if xT, err := expectSingleType(x); err != nil { 17 | errs = append(errs, err) 18 | } else if xT == ConstNil { 19 | errs = append(errs, ErrUntypedNil{x}) 20 | } else if xT.Kind() != reflect.Interface { 21 | errs = append(errs, ErrInvalidTypeAssert{aexpr}) 22 | } else { 23 | typ, t, _, moreErrs := checkType(assert.Type, env) 24 | aexpr.Type = typ 25 | errs = append(errs, moreErrs...) 26 | if t != nil { 27 | aexpr.knownType = knownType{t} 28 | if t.Kind() != reflect.Interface && !unhackType(t).Implements(xT) { 29 | errs = append(errs, ErrImpossibleTypeAssert{aexpr}) 30 | } 31 | } 32 | } 33 | return aexpr, errs 34 | } 35 | -------------------------------------------------------------------------------- /checkunaryexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | 6 | "go/ast" 7 | "go/token" 8 | ) 9 | 10 | func checkUnaryExpr(unary *ast.UnaryExpr, env Env) (*UnaryExpr, []error) { 11 | aexpr := &UnaryExpr{UnaryExpr: unary} 12 | 13 | x, errs := CheckExpr(unary.X, env) 14 | if errs == nil || x.IsConst() { 15 | if t, err := expectSingleType(x); err != nil { 16 | errs = append(errs, err) 17 | } else if unary.Op == token.AND { // address of 18 | if !isAddressableOrCompositeLit(x) { 19 | printableX := fakeCheckExpr(unary.X, env) 20 | printableX.setKnownType(knownType{t}) 21 | errs = append(errs, ErrInvalidAddressOf{printableX}) 22 | } 23 | t := x.KnownType()[0] 24 | if ct, ok := t.(ConstType); ok { 25 | if ct == ConstNil { 26 | errs = append(errs, ErrUntypedNil{x}) 27 | } else { 28 | ptrT := reflect.PtrTo(unhackType(ct.DefaultPromotion())) 29 | aexpr.knownType = knownType{ptrT} 30 | } 31 | } else { 32 | ptrT := reflect.PtrTo(unhackType(t)) 33 | aexpr.knownType = knownType{ptrT} 34 | } 35 | aexpr.X = x 36 | } else if unary.Op == token.ARROW { // <- 37 | if (t.Kind() != reflect.Chan) || (t.ChanDir() | reflect.RecvDir == 0) { 38 | errs = append(errs, ErrInvalidRecvFrom{x}) 39 | } 40 | } else { 41 | aexpr.X = x 42 | // All numeric and bool unary expressions do not change type 43 | aexpr.knownType = knownType(x.KnownType()) 44 | if x.IsConst() { 45 | if ct, ok := t.(ConstType); ok { 46 | c, moreErrs := evalConstUnaryExpr(aexpr, x, ct) 47 | if moreErrs != nil { 48 | errs = append(errs, moreErrs...) 49 | } 50 | aexpr.constValue = c 51 | 52 | } else { 53 | c, moreErrs := evalConstTypedUnaryExpr(aexpr, x) 54 | if moreErrs != nil { 55 | errs = append(errs, moreErrs...) 56 | } 57 | aexpr.constValue = c 58 | } 59 | } else { 60 | if !isUnaryOpDefinedOn(unary.Op, t) { 61 | errs = append(errs, ErrInvalidUnaryOperation{aexpr}) 62 | } 63 | } 64 | } 65 | } 66 | aexpr.X = x 67 | return aexpr, errs 68 | } 69 | 70 | func evalConstUnaryExpr(unary *UnaryExpr, x Expr, xT ConstType) (constValue, []error) { 71 | switch xT.(type) { 72 | case ConstIntType, ConstRuneType, ConstFloatType, ConstComplexType: 73 | xx := x.Const().Interface().(*ConstNumber) 74 | return evalConstUnaryNumericExpr(unary, xx) 75 | case ConstBoolType: 76 | xx := x.Const().Bool() 77 | return evalConstUnaryBoolExpr(unary, xx) 78 | default: 79 | return constValue{}, []error{ErrInvalidUnaryOperation{unary}} 80 | } 81 | } 82 | 83 | func evalConstTypedUnaryExpr(unary *UnaryExpr, x Expr) (constValue, []error) { 84 | t := x.KnownType()[0] 85 | if xx, ok := convertTypedToConstNumber(x.Const()); ok { 86 | zz, errs := evalConstUnaryNumericExpr(unary, xx) 87 | if !reflect.Value(zz).IsValid() { 88 | return constValue{}, errs 89 | } 90 | rr, moreErrs := promoteConstToTyped(xx.Type, zz, t, x) 91 | return rr, append(errs, moreErrs...) 92 | } else if t.Kind() == reflect.Bool { 93 | return evalConstUnaryBoolExpr(unary, x.Const().Bool()) 94 | } 95 | return constValue{}, []error{ErrInvalidUnaryOperation{unary}} 96 | } 97 | 98 | func evalConstUnaryNumericExpr(unary *UnaryExpr, x *ConstNumber) (constValue, []error) { 99 | switch unary.Op { 100 | case token.ADD: 101 | return constValueOf(x), nil 102 | case token.SUB: 103 | zero := &ConstNumber{Type: x.Type} 104 | return constValueOf(zero.Sub(zero, x)), nil 105 | case token.XOR: 106 | if x.Type.IsIntegral() { 107 | minusOne := NewConstInt64(-1) 108 | return constValueOf(minusOne.Xor(minusOne, x)), nil 109 | } 110 | } 111 | return constValue{}, []error{ErrInvalidUnaryOperation{unary}} 112 | } 113 | 114 | func evalConstUnaryBoolExpr(unary *UnaryExpr, x bool) (constValue, []error) { 115 | switch unary.Op { 116 | case token.NOT: 117 | return constValueOf(!x), nil 118 | default: 119 | return constValue{}, []error{ErrInvalidUnaryOperation{unary}} 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /checkunaryexpr_addr_gen_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | // Test a 9 | func TestCheckAddrExprA(t *testing.T) { 10 | 11 | a := 1 12 | b := &a 13 | _ = b 14 | env := MakeSimpleEnv() 15 | env.Vars["a"] = reflect.ValueOf(&a) 16 | 17 | expectType(t, `&a`, env, reflect.TypeOf(&a)) 18 | } 19 | 20 | // Test int(1) 21 | func TestCheckAddrExprInt(t *testing.T) { 22 | 23 | a := 1 24 | b := &a 25 | _ = b 26 | env := MakeSimpleEnv() 27 | env.Vars["a"] = reflect.ValueOf(&a) 28 | 29 | expectCheckError(t, `&int(1)`, env, 30 | `cannot take the address of int(1)`, 31 | ) 32 | 33 | } 34 | 35 | // Test 1.4 36 | func TestCheckAddrExprNumber(t *testing.T) { 37 | 38 | a := 1 39 | b := &a 40 | _ = b 41 | env := MakeSimpleEnv() 42 | env.Vars["a"] = reflect.ValueOf(&a) 43 | 44 | expectCheckError(t, `&1.4`, env, 45 | `cannot take the address of 1.4`, 46 | ) 47 | 48 | } 49 | 50 | // Test 'a' 51 | func TestCheckAddrExprRune(t *testing.T) { 52 | 53 | a := 1 54 | b := &a 55 | _ = b 56 | env := MakeSimpleEnv() 57 | env.Vars["a"] = reflect.ValueOf(&a) 58 | 59 | expectCheckError(t, `&'a'`, env, 60 | `cannot take the address of 'a'`, 61 | ) 62 | 63 | } 64 | 65 | // Test true 66 | func TestCheckAddrExprBool(t *testing.T) { 67 | 68 | a := 1 69 | b := &a 70 | _ = b 71 | env := MakeSimpleEnv() 72 | env.Vars["a"] = reflect.ValueOf(&a) 73 | 74 | expectCheckError(t, `&true`, env, 75 | `cannot take the address of true`, 76 | ) 77 | 78 | } 79 | 80 | // Test "a" 81 | func TestCheckAddrExprString(t *testing.T) { 82 | 83 | a := 1 84 | b := &a 85 | _ = b 86 | env := MakeSimpleEnv() 87 | env.Vars["a"] = reflect.ValueOf(&a) 88 | 89 | expectCheckError(t, `&"a"`, env, 90 | `cannot take the address of "a"`, 91 | ) 92 | 93 | } 94 | 95 | // Test nil 96 | func TestCheckAddrExprNil(t *testing.T) { 97 | 98 | a := 1 99 | b := &a 100 | _ = b 101 | env := MakeSimpleEnv() 102 | env.Vars["a"] = reflect.ValueOf(&a) 103 | 104 | expectCheckError(t, `&nil`, env, 105 | `cannot take the address of nil`, 106 | `use of untyped nil`, 107 | ) 108 | 109 | } 110 | 111 | // Test &a 112 | func TestCheckAddrExprAtA(t *testing.T) { 113 | 114 | a := 1 115 | b := &a 116 | _ = b 117 | env := MakeSimpleEnv() 118 | env.Vars["a"] = reflect.ValueOf(&a) 119 | 120 | expectCheckError(t, `& &a`, env, 121 | `cannot take the address of &a`, 122 | ) 123 | 124 | } 125 | 126 | // Test *a 127 | func TestCheckAddrExprStarB(t *testing.T) { 128 | 129 | a := 1 130 | b := &a 131 | _ = b 132 | env := MakeSimpleEnv() 133 | env.Vars["a"] = reflect.ValueOf(&a) 134 | 135 | expectCheckError(t, `& *a`, env, 136 | `invalid indirect of a (type int)`, 137 | ) 138 | 139 | } 140 | 141 | // Test []int{1} 142 | func TestCheckAddrExprSlice(t *testing.T) { 143 | 144 | a := 1 145 | b := &a 146 | _ = b 147 | env := MakeSimpleEnv() 148 | env.Vars["a"] = reflect.ValueOf(&a) 149 | 150 | expectType(t, `&[]int{1}`, env, reflect.TypeOf(&[]int{1})) 151 | } 152 | 153 | // Test []int{1}[0] 154 | func TestCheckAddrExprSliceElt(t *testing.T) { 155 | 156 | a := 1 157 | b := &a 158 | _ = b 159 | env := MakeSimpleEnv() 160 | env.Vars["a"] = reflect.ValueOf(&a) 161 | 162 | expectType(t, `&[]int{1}[0]`, env, reflect.TypeOf(&[]int{1}[0])) 163 | } 164 | -------------------------------------------------------------------------------- /checkunaryexpr_gen_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // Test + Int 8 | func TestCheckUnaryExprAddInt(t *testing.T) { 9 | env := MakeSimpleEnv() 10 | 11 | expectConst(t, `+ 4`, env, NewConstInt64(+ 4), ConstInt) 12 | } 13 | 14 | // Test + Rune 15 | func TestCheckUnaryExprAddRune(t *testing.T) { 16 | env := MakeSimpleEnv() 17 | 18 | expectConst(t, `+ '@'`, env, NewConstRune(+ '@'), ConstRune) 19 | } 20 | 21 | // Test + Float 22 | func TestCheckUnaryExprAddFloat(t *testing.T) { 23 | env := MakeSimpleEnv() 24 | 25 | expectConst(t, `+ 2.0`, env, NewConstFloat64(+ 2.0), ConstFloat) 26 | } 27 | 28 | // Test + Complex 29 | func TestCheckUnaryExprAddComplex(t *testing.T) { 30 | env := MakeSimpleEnv() 31 | 32 | expectConst(t, `+ 8.0i`, env, NewConstComplex128(+ 8.0i), ConstComplex) 33 | } 34 | 35 | // Test + Bool 36 | func TestCheckUnaryExprAddBool(t *testing.T) { 37 | env := MakeSimpleEnv() 38 | 39 | expectCheckError(t, `+ true`, env, 40 | `invalid operation: + untyped bool`, 41 | ) 42 | 43 | } 44 | 45 | // Test + String 46 | func TestCheckUnaryExprAddString(t *testing.T) { 47 | env := MakeSimpleEnv() 48 | 49 | expectCheckError(t, `+ "abc"`, env, 50 | `invalid operation: + untyped string`, 51 | ) 52 | 53 | } 54 | 55 | // Test + Nil 56 | func TestCheckUnaryExprAddNil(t *testing.T) { 57 | env := MakeSimpleEnv() 58 | 59 | expectCheckError(t, `+ nil`, env, 60 | `invalid operation: + nil`, 61 | ) 62 | 63 | } 64 | 65 | // Test - Int 66 | func TestCheckUnaryExprSubInt(t *testing.T) { 67 | env := MakeSimpleEnv() 68 | 69 | expectConst(t, `- 4`, env, NewConstInt64(- 4), ConstInt) 70 | } 71 | 72 | // Test - Rune 73 | func TestCheckUnaryExprSubRune(t *testing.T) { 74 | env := MakeSimpleEnv() 75 | 76 | expectConst(t, `- '@'`, env, NewConstRune(- '@'), ConstRune) 77 | } 78 | 79 | // Test - Float 80 | func TestCheckUnaryExprSubFloat(t *testing.T) { 81 | env := MakeSimpleEnv() 82 | 83 | expectConst(t, `- 2.0`, env, NewConstFloat64(- 2.0), ConstFloat) 84 | } 85 | 86 | // Test - Complex 87 | func TestCheckUnaryExprSubComplex(t *testing.T) { 88 | env := MakeSimpleEnv() 89 | 90 | expectConst(t, `- 8.0i`, env, NewConstComplex128(- 8.0i), ConstComplex) 91 | } 92 | 93 | // Test - Bool 94 | func TestCheckUnaryExprSubBool(t *testing.T) { 95 | env := MakeSimpleEnv() 96 | 97 | expectCheckError(t, `- true`, env, 98 | `invalid operation: - untyped bool`, 99 | ) 100 | 101 | } 102 | 103 | // Test - String 104 | func TestCheckUnaryExprSubString(t *testing.T) { 105 | env := MakeSimpleEnv() 106 | 107 | expectCheckError(t, `- "abc"`, env, 108 | `invalid operation: - untyped string`, 109 | ) 110 | 111 | } 112 | 113 | // Test - Nil 114 | func TestCheckUnaryExprSubNil(t *testing.T) { 115 | env := MakeSimpleEnv() 116 | 117 | expectCheckError(t, `- nil`, env, 118 | `invalid operation: - nil`, 119 | ) 120 | 121 | } 122 | 123 | // Test ^ Int 124 | func TestCheckUnaryExprXorInt(t *testing.T) { 125 | env := MakeSimpleEnv() 126 | 127 | expectConst(t, `^ 4`, env, NewConstInt64(^ 4), ConstInt) 128 | } 129 | 130 | // Test ^ Rune 131 | func TestCheckUnaryExprXorRune(t *testing.T) { 132 | env := MakeSimpleEnv() 133 | 134 | expectConst(t, `^ '@'`, env, NewConstRune(^ '@'), ConstRune) 135 | } 136 | 137 | // Test ^ Float 138 | func TestCheckUnaryExprXorFloat(t *testing.T) { 139 | env := MakeSimpleEnv() 140 | 141 | expectCheckError(t, `^ 2.0`, env, 142 | `illegal constant expression ^ untyped number`, 143 | ) 144 | 145 | } 146 | 147 | // Test ^ Complex 148 | func TestCheckUnaryExprXorComplex(t *testing.T) { 149 | env := MakeSimpleEnv() 150 | 151 | expectCheckError(t, `^ 8.0i`, env, 152 | `illegal constant expression ^ untyped number`, 153 | ) 154 | 155 | } 156 | 157 | // Test ^ Bool 158 | func TestCheckUnaryExprXorBool(t *testing.T) { 159 | env := MakeSimpleEnv() 160 | 161 | expectCheckError(t, `^ true`, env, 162 | `invalid operation: ^ untyped bool`, 163 | ) 164 | 165 | } 166 | 167 | // Test ^ String 168 | func TestCheckUnaryExprXorString(t *testing.T) { 169 | env := MakeSimpleEnv() 170 | 171 | expectCheckError(t, `^ "abc"`, env, 172 | `invalid operation: ^ untyped string`, 173 | ) 174 | 175 | } 176 | 177 | // Test ^ Nil 178 | func TestCheckUnaryExprXorNil(t *testing.T) { 179 | env := MakeSimpleEnv() 180 | 181 | expectCheckError(t, `^ nil`, env, 182 | `invalid operation: ^ nil`, 183 | ) 184 | 185 | } 186 | 187 | // Test ! Int 188 | func TestCheckUnaryExprNotInt(t *testing.T) { 189 | env := MakeSimpleEnv() 190 | 191 | expectCheckError(t, `! 4`, env, 192 | `invalid operation: ! untyped number`, 193 | ) 194 | 195 | } 196 | 197 | // Test ! Rune 198 | func TestCheckUnaryExprNotRune(t *testing.T) { 199 | env := MakeSimpleEnv() 200 | 201 | expectCheckError(t, `! '@'`, env, 202 | `invalid operation: ! untyped number`, 203 | ) 204 | 205 | } 206 | 207 | // Test ! Float 208 | func TestCheckUnaryExprNotFloat(t *testing.T) { 209 | env := MakeSimpleEnv() 210 | 211 | expectCheckError(t, `! 2.0`, env, 212 | `invalid operation: ! untyped number`, 213 | ) 214 | 215 | } 216 | 217 | // Test ! Complex 218 | func TestCheckUnaryExprNotComplex(t *testing.T) { 219 | env := MakeSimpleEnv() 220 | 221 | expectCheckError(t, `! 8.0i`, env, 222 | `invalid operation: ! untyped number`, 223 | ) 224 | 225 | } 226 | 227 | // Test ! Bool 228 | func TestCheckUnaryExprNotBool(t *testing.T) { 229 | env := MakeSimpleEnv() 230 | 231 | expectConst(t, `! true`, env, (! true), ConstBool) 232 | } 233 | 234 | // Test ! String 235 | func TestCheckUnaryExprNotString(t *testing.T) { 236 | env := MakeSimpleEnv() 237 | 238 | expectCheckError(t, `! "abc"`, env, 239 | `invalid operation: ! untyped string`, 240 | ) 241 | 242 | } 243 | 244 | // Test ! Nil 245 | func TestCheckUnaryExprNotNil(t *testing.T) { 246 | env := MakeSimpleEnv() 247 | 248 | expectCheckError(t, `! nil`, env, 249 | `invalid operation: ! nil`, 250 | ) 251 | 252 | } 253 | -------------------------------------------------------------------------------- /checkunaryexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | // Test Int32 - Int32 9 | func TestCheckUnaryTypedExpr(t *testing.T) { 10 | env := MakeSimpleEnv() 11 | x := int32(1) 12 | env.Vars["x"] = reflect.ValueOf(&x) 13 | 14 | expectType(t, `-x`, env, reflect.TypeOf(-x)) 15 | } 16 | -------------------------------------------------------------------------------- /checkunaryexpr_typed_gen_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | // Test + Int32 9 | func TestCheckUnaryTypedExprAddInt32(t *testing.T) { 10 | env := MakeSimpleEnv() 11 | 12 | expectConst(t, `+ int32(4)`, env, + int32(4), reflect.TypeOf(+ int32(4))) 13 | } 14 | 15 | // Test + Float64 16 | func TestCheckUnaryTypedExprAddFloat64(t *testing.T) { 17 | env := MakeSimpleEnv() 18 | 19 | expectConst(t, `+ float64(2)`, env, + float64(2), reflect.TypeOf(+ float64(2))) 20 | } 21 | 22 | // Test + Complex128 23 | func TestCheckUnaryTypedExprAddComplex128(t *testing.T) { 24 | env := MakeSimpleEnv() 25 | 26 | expectConst(t, `+ complex128(8i)`, env, + complex128(8i), reflect.TypeOf(+ complex128(8i))) 27 | } 28 | 29 | // Test + Bool 30 | func TestCheckUnaryTypedExprAddBool(t *testing.T) { 31 | env := MakeSimpleEnv() 32 | 33 | expectCheckError(t, `+ bool(true)`, env, 34 | `invalid operation: + bool`, 35 | ) 36 | 37 | } 38 | 39 | // Test + String 40 | func TestCheckUnaryTypedExprAddString(t *testing.T) { 41 | env := MakeSimpleEnv() 42 | 43 | expectCheckError(t, `+ string("abc")`, env, 44 | `invalid operation: + string`, 45 | ) 46 | 47 | } 48 | 49 | // Test - Int32 50 | func TestCheckUnaryTypedExprSubInt32(t *testing.T) { 51 | env := MakeSimpleEnv() 52 | 53 | expectConst(t, `- int32(4)`, env, - int32(4), reflect.TypeOf(- int32(4))) 54 | } 55 | 56 | // Test - Float64 57 | func TestCheckUnaryTypedExprSubFloat64(t *testing.T) { 58 | env := MakeSimpleEnv() 59 | 60 | expectConst(t, `- float64(2)`, env, - float64(2), reflect.TypeOf(- float64(2))) 61 | } 62 | 63 | // Test - Complex128 64 | func TestCheckUnaryTypedExprSubComplex128(t *testing.T) { 65 | env := MakeSimpleEnv() 66 | 67 | expectConst(t, `- complex128(8i)`, env, - complex128(8i), reflect.TypeOf(- complex128(8i))) 68 | } 69 | 70 | // Test - Bool 71 | func TestCheckUnaryTypedExprSubBool(t *testing.T) { 72 | env := MakeSimpleEnv() 73 | 74 | expectCheckError(t, `- bool(true)`, env, 75 | `invalid operation: - bool`, 76 | ) 77 | 78 | } 79 | 80 | // Test - String 81 | func TestCheckUnaryTypedExprSubString(t *testing.T) { 82 | env := MakeSimpleEnv() 83 | 84 | expectCheckError(t, `- string("abc")`, env, 85 | `invalid operation: - string`, 86 | ) 87 | 88 | } 89 | 90 | // Test ^ Int32 91 | func TestCheckUnaryTypedExprXorInt32(t *testing.T) { 92 | env := MakeSimpleEnv() 93 | 94 | expectConst(t, `^ int32(4)`, env, ^ int32(4), reflect.TypeOf(^ int32(4))) 95 | } 96 | 97 | // Test ^ Float64 98 | func TestCheckUnaryTypedExprXorFloat64(t *testing.T) { 99 | env := MakeSimpleEnv() 100 | 101 | expectCheckError(t, `^ float64(2)`, env, 102 | `invalid operation: ^ float64`, 103 | ) 104 | 105 | } 106 | 107 | // Test ^ Complex128 108 | func TestCheckUnaryTypedExprXorComplex128(t *testing.T) { 109 | env := MakeSimpleEnv() 110 | 111 | expectCheckError(t, `^ complex128(8i)`, env, 112 | `invalid operation: ^ complex128`, 113 | ) 114 | 115 | } 116 | 117 | // Test ^ Bool 118 | func TestCheckUnaryTypedExprXorBool(t *testing.T) { 119 | env := MakeSimpleEnv() 120 | 121 | expectCheckError(t, `^ bool(true)`, env, 122 | `invalid operation: ^ bool`, 123 | ) 124 | 125 | } 126 | 127 | // Test ^ String 128 | func TestCheckUnaryTypedExprXorString(t *testing.T) { 129 | env := MakeSimpleEnv() 130 | 131 | expectCheckError(t, `^ string("abc")`, env, 132 | `invalid operation: ^ string`, 133 | ) 134 | 135 | } 136 | 137 | // Test ! Int32 138 | func TestCheckUnaryTypedExprNotInt32(t *testing.T) { 139 | env := MakeSimpleEnv() 140 | 141 | expectCheckError(t, `! int32(4)`, env, 142 | `invalid operation: ! int32`, 143 | ) 144 | 145 | } 146 | 147 | // Test ! Float64 148 | func TestCheckUnaryTypedExprNotFloat64(t *testing.T) { 149 | env := MakeSimpleEnv() 150 | 151 | expectCheckError(t, `! float64(2)`, env, 152 | `invalid operation: ! float64`, 153 | ) 154 | 155 | } 156 | 157 | // Test ! Complex128 158 | func TestCheckUnaryTypedExprNotComplex128(t *testing.T) { 159 | env := MakeSimpleEnv() 160 | 161 | expectCheckError(t, `! complex128(8i)`, env, 162 | `invalid operation: ! complex128`, 163 | ) 164 | 165 | } 166 | 167 | // Test ! Bool 168 | func TestCheckUnaryTypedExprNotBool(t *testing.T) { 169 | env := MakeSimpleEnv() 170 | 171 | expectConst(t, `! bool(true)`, env, ! bool(true), reflect.TypeOf(! bool(true))) 172 | } 173 | 174 | // Test ! String 175 | func TestCheckUnaryTypedExprNotString(t *testing.T) { 176 | env := MakeSimpleEnv() 177 | 178 | expectCheckError(t, `! string("abc")`, env, 179 | `invalid operation: ! string`, 180 | ) 181 | 182 | } 183 | -------------------------------------------------------------------------------- /constnumber.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import "strconv" 4 | 5 | type ConstNumber struct { 6 | Value BigComplex 7 | Type ConstType 8 | } 9 | 10 | // Use with token.INT ast.BasicLit 11 | func NewConstInteger(i string) (*ConstNumber, bool) { 12 | z := new(ConstNumber) 13 | z.Type = ConstInt 14 | z.Value.Re.Denom().SetInt64(1) 15 | _, ok := z.Value.Re.Num().SetString(i, 0) 16 | return z, ok 17 | } 18 | 19 | // Use with token.FLOAT ast.BasicLit 20 | func NewConstFloat(r string) (*ConstNumber, bool) { 21 | z := new(ConstNumber) 22 | z.Type = ConstFloat 23 | _, ok := z.Value.Re.SetString(r) 24 | return z, ok 25 | } 26 | 27 | // Use with token.IMAG ast.BasicLit 28 | func NewConstImag(i string) (*ConstNumber, bool) { 29 | z := new(ConstNumber) 30 | z.Type = ConstComplex 31 | ok := i[len(i)-1] == 'i' 32 | if ok { 33 | _, ok = z.Value.Im.SetString(i[:len(i)-1]) 34 | } 35 | return z, ok 36 | } 37 | 38 | // Use with token.CHAR ast.BasicLit 39 | func NewConstRune(n rune) *ConstNumber { 40 | z := new(ConstNumber) 41 | z.Type = ConstRune 42 | z.Value.Re.Denom().SetInt64(1) 43 | z.Value.Re.Num().SetInt64(int64(n)) 44 | return z 45 | } 46 | 47 | func NewConstInt64(i int64) *ConstNumber { 48 | z := new(ConstNumber) 49 | z.Type = ConstInt 50 | z.Value.Re.Denom().SetInt64(1) 51 | z.Value.Re.Num().SetInt64(i) 52 | return z 53 | } 54 | 55 | func NewConstUint64(u uint64) *ConstNumber { 56 | z := new(ConstNumber) 57 | z.Type = ConstInt 58 | z.Value.Re.Denom().SetInt64(1) 59 | z.Value.Re.Num().SetUint64(u) 60 | return z 61 | } 62 | 63 | func NewConstFloat64(f float64) *ConstNumber { 64 | z := new(ConstNumber) 65 | z.Type = ConstFloat 66 | z.Value.Re.SetFloat64(f) 67 | return z 68 | } 69 | 70 | func NewConstComplex128(c complex128) *ConstNumber { 71 | z := new(ConstNumber) 72 | z.Type = ConstComplex 73 | z.Value.Re.SetFloat64(real(c)) 74 | z.Value.Im.SetFloat64(imag(c)) 75 | return z 76 | } 77 | 78 | func (z *ConstNumber) String() string { 79 | return z.StringShow0i(true) 80 | } 81 | 82 | func (z *ConstNumber) StringShow0i(show0i bool) string { 83 | if z.Type == ConstRune && z.Value.Re.Num().BitLen() <= 32 { 84 | r, _, _ := z.Value.Int(32) 85 | return strconv.QuoteRuneToASCII(rune(r)) 86 | } else if z.Type == ConstComplex { 87 | // Hack to show 0i instead of 0 88 | if z.Value.IsZero() { 89 | return "0i" 90 | } else { 91 | return z.Value.StringShow0i(show0i) 92 | } 93 | } else { 94 | return z.Value.StringShow0i(false) 95 | } 96 | } 97 | 98 | // Add two ConstNumbers, promoting the type automatically. 99 | func (z *ConstNumber) Add(x, y *ConstNumber) *ConstNumber { 100 | z.Type = promoteConstNumbers(x.Type, y.Type) 101 | z.Value.Add(&x.Value, &y.Value) 102 | return z 103 | } 104 | 105 | // z.Sub() subtracts two ConstNumbers, promoting the type 106 | // automatically. 107 | func (z *ConstNumber) Sub(x, y *ConstNumber) *ConstNumber { 108 | z.Type = promoteConstNumbers(x.Type, y.Type) 109 | z.Value.Sub(&x.Value, &y.Value) 110 | return z 111 | } 112 | 113 | // z.Mul multiplies two ConstNumbers, promoting the type 114 | // automatically. 115 | func (z *ConstNumber) Mul(x, y *ConstNumber) *ConstNumber { 116 | z.Type = promoteConstNumbers(x.Type, y.Type) 117 | z.Value.Mul(&x.Value, &y.Value) 118 | return z 119 | } 120 | 121 | // z.Quo divides two ConstNumbers, promoting the type 122 | // automatically. If both operands are of ConstInt, then integer 123 | // division is performed. 124 | func (z *ConstNumber) Quo(x, y *ConstNumber) *ConstNumber { 125 | z.Type = promoteConstNumbers(x.Type, y.Type) 126 | if z.Type.IsIntegral() { 127 | z.Value.Re.Num().Div(x.Value.Re.Num(), y.Value.Re.Num()) 128 | } else { 129 | z.Value.Quo(&x.Value, &y.Value) 130 | } 131 | return z 132 | } 133 | 134 | // z.Rem computes remainder of two ConstNumbers, promoting the type 135 | // automatically. The result is undefined if both x and y are not 136 | // integral types. 137 | func (z *ConstNumber) Rem(x, y *ConstNumber) *ConstNumber { 138 | z.Type = promoteConstNumbers(x.Type, y.Type) 139 | z.Value.Re.Num().Rem(x.Value.Re.Num(), y.Value.Re.Num()) 140 | return z 141 | } 142 | 143 | // z.And compute logical "and" of two ConstNumbers, promoting the type 144 | // automatically. The result is undefined if both x and y are not 145 | // integral types. 146 | func (z *ConstNumber) And(x, y *ConstNumber) *ConstNumber { 147 | z.Type = promoteConstNumbers(x.Type, y.Type) 148 | z.Value.Re.Num().And(x.Value.Re.Num(), y.Value.Re.Num()) 149 | return z 150 | } 151 | 152 | // z.Or computes the logical "o"r of two ConstNumbers, promoting the 153 | // type automatically. The result is undefined if both x and y are not 154 | // integral types. 155 | func (z *ConstNumber) Or(x, y *ConstNumber) *ConstNumber { 156 | z.Type = promoteConstNumbers(x.Type, y.Type) 157 | z.Value.Re.Num().Or(x.Value.Re.Num(), y.Value.Re.Num()) 158 | return z 159 | } 160 | 161 | // z.Xor computes the exclusive "or" of two ConstNumbers, promoting 162 | // the type automatically. The result is undefined if both x and y are 163 | // not integral types. 164 | func (z *ConstNumber) Xor(x, y *ConstNumber) *ConstNumber { 165 | z.Type = promoteConstNumbers(x.Type, y.Type) 166 | z.Value.Re.Num().Xor(x.Value.Re.Num(), y.Value.Re.Num()) 167 | return z 168 | } 169 | 170 | // z.AndNot computes "and not" of two ConstNumbers, promoting the type 171 | // automatically. The result is undefined if both x and y are not 172 | // integral types. 173 | func (z *ConstNumber) AndNot(x, y *ConstNumber) *ConstNumber { 174 | z.Type = promoteConstNumbers(x.Type, y.Type) 175 | z.Value.Re.Num().AndNot(x.Value.Re.Num(), y.Value.Re.Num()) 176 | return z 177 | } 178 | 179 | // z.Shl() shifts x left by count. Type is set to ConstShiftedInt 180 | func (z *ConstNumber) Lsh(x *ConstNumber, count uint) *ConstNumber { 181 | z.Type = ConstShiftedInt 182 | z.Value.Lsh(&x.Value, count) 183 | return z 184 | } 185 | 186 | // z.Shl() shifts x right by count. Type is set to ConstShiftedInt 187 | func (z *ConstNumber) Rsh(x *ConstNumber, count uint) *ConstNumber { 188 | z.Type = ConstShiftedInt 189 | z.Value.Rsh(&x.Value, count) 190 | return z 191 | } 192 | 193 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /binaryexpr 2 | /identchange 3 | /repl 4 | -------------------------------------------------------------------------------- /demo/identchange.go: -------------------------------------------------------------------------------- 1 | // Demos replacing the default identifier lookup value mechanism with 2 | // our own custom version. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | "strings" 10 | "go/parser" 11 | "github.com/0xfaded/eval" 12 | ) 13 | 14 | func expectResult(expr string, env eval.Env, expected interface{}) { 15 | if e, err := parser.ParseExpr(expr); err != nil { 16 | fmt.Printf("Failed to parse expression '%s' (%v)\n", expr, err) 17 | return 18 | } else if cexpr, errs := eval.CheckExpr(e, env); len(errs) != 0 { 19 | fmt.Printf("Error checking expression '%s' (%v)\n", expr, errs) 20 | } else if results, err := eval.EvalExpr(cexpr, env); err != nil { 21 | fmt.Printf("Error evaluating expression '%s' (%v)\n", expr, err) 22 | return 23 | } else { 24 | fmt.Printf("Expression '%s' yielded '%+v', expected '%+v'\n", 25 | expr, results[0].Interface(), expected) 26 | } 27 | } 28 | 29 | type CustomEnv struct { 30 | // Encapsulate the default functionality 31 | eval.Env 32 | } 33 | 34 | // At a minimum, You will probably want to define PushScope and PopScope 35 | func (env *CustomEnv) PushScope() eval.Env { 36 | // The newly created underlying 37 | top := env.Env.PushScope() 38 | 39 | // Wrap the env and return it 40 | return &CustomEnv{top} 41 | } 42 | 43 | func (env *CustomEnv) PopScope() eval.Env { 44 | if top := env.Env.PopScope(); top == nil { 45 | return nil 46 | } else { 47 | return &CustomEnv{top} 48 | } 49 | } 50 | 51 | // Our custom Var will add one to any and all ints 52 | func (env *CustomEnv) Var(ident string) reflect.Value { 53 | // Note that variables are always pointer values. 54 | if v := env.Env.Var(ident); v.IsValid() && v.Type().Elem().Kind() == reflect.Int { 55 | i := v.Elem().Int() 56 | plusOne := reflect.New(v.Type().Elem()).Elem() 57 | plusOne.Set(reflect.ValueOf(int(i + 1))) 58 | return plusOne.Addr() 59 | } else { 60 | return v 61 | } 62 | } 63 | 64 | // Our custom Const will lowercase all strings 65 | func (env *CustomEnv) Const(ident string) reflect.Value { 66 | if v := env.Env.Const(ident); v.IsValid() && v.Type().Kind() == reflect.String { 67 | s := v.String() 68 | return(reflect.ValueOf(strings.ToLower(s))) 69 | } else { 70 | return v 71 | } 72 | } 73 | 74 | func main() { 75 | simpleEnv := eval.MakeSimpleEnv() 76 | v := 4 77 | c := "CONSTANT" 78 | simpleEnv.Vars["v"] = reflect.ValueOf(&v) 79 | simpleEnv.Consts["c"] = reflect.ValueOf(c) 80 | env := &CustomEnv{simpleEnv} 81 | expectResult("v + 1", env, "6") 82 | expectResult("c + \" value\"", env, "constant value") 83 | 84 | } 85 | -------------------------------------------------------------------------------- /demo/repl.go: -------------------------------------------------------------------------------- 1 | // Example repl is a simple REPL (read-eval-print loop) for GO using 2 | // http://github.com/0xfaded/eval to the heavy lifting to implement 3 | // the eval() part. 4 | // 5 | // The intent here is to show how more to use the library, rather than 6 | // be a full-featured REPL. 7 | // 8 | // A more complete REPL including command history, tab completion and 9 | // readline editing is available as a separate package: 10 | // http://github.com/rocky/go-fish 11 | // 12 | // (rocky) My intent here is also to have something that I can debug in 13 | // the ssa-debugger tortoise/gub.sh. Right now that can't handle the 14 | // unsafe package, pointers, and calls to C code. So that let's out 15 | // go-gnureadline and lineedit. 16 | package main 17 | 18 | import ( 19 | "bufio" 20 | "fmt" 21 | "go/parser" 22 | "io" 23 | "os" 24 | "reflect" 25 | "strings" 26 | 27 | "github.com/0xfaded/eval" 28 | ) 29 | 30 | // Simple replacement for GNU readline 31 | func readline(prompt string, in *bufio.Reader) (string, error) { 32 | fmt.Printf(prompt) 33 | line, err := in.ReadString('\n') 34 | if err == nil { 35 | line = strings.TrimRight(line, "\r\n") 36 | } 37 | return line, err 38 | } 39 | 40 | func intro_text() { 41 | fmt.Printf(`=== A simple Go eval REPL === 42 | 43 | Results of expression are stored in variable slice "results". 44 | The environment is stored in global variable "env". 45 | 46 | Enter expressions to be evaluated at the "go>" prompt. 47 | 48 | To see all results, type: "results". 49 | 50 | To quit, enter: "quit" or Ctrl-D (EOF). 51 | `) 52 | 53 | } 54 | 55 | // REPL is the a read, eval, and print loop. 56 | func REPL(env *eval.SimpleEnv) { 57 | 58 | var err error 59 | 60 | // A place to store result values of expressions entered 61 | // interactively 62 | results := make([]interface{}, 0, 10) 63 | env.Vars["results"] = reflect.ValueOf(&results) 64 | 65 | exprs := 0 66 | in := bufio.NewReader(os.Stdin) 67 | line, err := readline("go> ", in) 68 | for line != "quit" { 69 | if err != nil { 70 | if err == io.EOF { break } 71 | panic(err) 72 | } 73 | if expr, err := parser.ParseExpr(line); err != nil { 74 | if pair := eval.FormatErrorPos(line, err.Error()); len(pair) == 2 { 75 | fmt.Println(pair[0]) 76 | fmt.Println(pair[1]) 77 | } 78 | fmt.Printf("parse error: %s\n", err) 79 | } else if cexpr, errs := eval.CheckExpr(expr, env); len(errs) != 0 { 80 | for _, cerr := range errs { 81 | fmt.Printf("check error: %v\n", cerr) 82 | } 83 | } else if vals, err := eval.EvalExpr(cexpr, env); err != nil { 84 | fmt.Printf("panic: %s\n", err) 85 | } else if len(vals) == 0 { 86 | fmt.Printf("Kind=Slice\nvoid\n") 87 | } else if len(vals) == 1 { 88 | value := (vals)[0] 89 | if value.IsValid() { 90 | kind := value.Kind().String() 91 | typ := value.Type().String() 92 | if typ != kind { 93 | fmt.Printf("Kind = %v\n", kind) 94 | fmt.Printf("Type = %v\n", typ) 95 | } else { 96 | fmt.Printf("Kind = Type = %v\n", kind) 97 | } 98 | fmt.Printf("results[%d] = %s\n", exprs, eval.Inspect(value)) 99 | exprs += 1 100 | results = append(results, (vals)[0].Interface()) 101 | } else { 102 | fmt.Printf("%s\n", value) 103 | } 104 | } else { 105 | fmt.Printf("Kind = Multi-Value\n") 106 | size := len(vals) 107 | for i, v := range vals { 108 | fmt.Printf("%s", eval.Inspect(v)) 109 | if i < size-1 { fmt.Printf(", ") } 110 | } 111 | fmt.Printf("\n") 112 | exprs += 1 113 | results = append(results, vals) 114 | } 115 | 116 | line, err = readline("go> ", in) 117 | } 118 | } 119 | 120 | type XI interface { x() } 121 | type YI interface { y() } 122 | type ZI interface { x() } 123 | 124 | type X int 125 | type Y int 126 | type Z int 127 | 128 | func (X) x() {} 129 | func (Y) y() {} 130 | func (Z) x() {} 131 | 132 | // Create an eval.Env environment to use in evaluation. 133 | // This is a bit ugly here, because we are rolling everything by hand, but 134 | // we want some sort of environment to show off in demo'ing. 135 | // The artifical environment we create here consists of 136 | // fmt: 137 | // fns: fmt.Println, fmt.Printf 138 | // os: 139 | // types: MyInt 140 | // vars: Stdout, Args 141 | // main: 142 | // type Alice 143 | // var alice, aliceptr 144 | // 145 | // (REPL also adds var results to main) 146 | // 147 | // See make_env in github.com/rocky/go-fish for an automated way to 148 | // create more complete environment from a starting import. 149 | func makeBogusEnv() *eval.SimpleEnv { 150 | 151 | // A copule of things from the fmt package. 152 | var fmt_funcs map[string] reflect.Value = make(map[string] reflect.Value) 153 | fmt_funcs["Println"] = reflect.ValueOf(fmt.Println) 154 | fmt_funcs["Printf"] = reflect.ValueOf(fmt.Printf) 155 | 156 | // A simple type for demo 157 | type MyInt int 158 | 159 | // A stripped down package environment. See 160 | // http://github.com/rocky/go-fish and repl_imports.go for a more 161 | // complete environment. 162 | pkgs := map[string] eval.Env { 163 | "fmt": &eval.SimpleEnv { 164 | Vars: make(map[string] reflect.Value), 165 | Consts: make(map[string] reflect.Value), 166 | Funcs: fmt_funcs, 167 | Types : make(map[string] reflect.Type), 168 | Pkgs: nil, 169 | }, "os": &eval.SimpleEnv { 170 | Vars: map[string] reflect.Value { 171 | "Stdout": reflect.ValueOf(&os.Stdout), 172 | "Args" : reflect.ValueOf(&os.Args)}, 173 | Consts: make(map[string] reflect.Value), 174 | Funcs: make(map[string] reflect.Value), 175 | Types: map[string] reflect.Type{ 176 | "MyInt": reflect.TypeOf(*new(MyInt))}, 177 | Pkgs: nil, 178 | }, 179 | } 180 | 181 | mainEnv := eval.MakeSimpleEnv() 182 | mainEnv.Pkgs = pkgs 183 | 184 | // Some "alice" things for testing 185 | type Alice struct { 186 | Bob int 187 | Secret string 188 | } 189 | 190 | type R rune 191 | 192 | alice := Alice{1, "shhh"} 193 | alicePtr := &alice 194 | foo := 10 195 | ints := []int{1, 2, 3, 4} 196 | add := func(a, b int) int { 197 | return a + b 198 | } 199 | sum := func(as ...int) int { 200 | r := 0 201 | for _, a := range as { 202 | r += a 203 | } 204 | return r 205 | } 206 | 207 | mainEnv.Vars["alice"] = reflect.ValueOf(&alice) 208 | mainEnv.Vars["alicePtr"] = reflect.ValueOf(&alicePtr) 209 | mainEnv.Vars["foo"] = reflect.ValueOf(&foo) 210 | mainEnv.Vars["ints"] = reflect.ValueOf(&ints) 211 | mainEnv.Consts["bar"] = reflect.ValueOf(eval.NewConstInt64(5)) 212 | mainEnv.Funcs["add"] = reflect.ValueOf(add) 213 | mainEnv.Funcs["sum"] = reflect.ValueOf(sum) 214 | mainEnv.Types["Alice"] = reflect.TypeOf(Alice{}) 215 | mainEnv.Types["R"] = reflect.TypeOf(R(0)) 216 | 217 | var xi *XI = new(XI) 218 | var yi *YI = new(YI) 219 | var zi *ZI = new(ZI) 220 | *xi = XI(X(0)) 221 | *yi = YI(Y(0)) 222 | *zi = ZI(Z(0)) 223 | mainEnv.Types["XI"] = reflect.TypeOf(xi).Elem() 224 | mainEnv.Types["YI"] = reflect.TypeOf(yi).Elem() 225 | mainEnv.Types["ZI"] = reflect.TypeOf(zi).Elem() 226 | mainEnv.Types["X"] = reflect.TypeOf(X(0)) 227 | mainEnv.Types["Y"] = reflect.TypeOf(Y(0)) 228 | mainEnv.Types["Z"] = reflect.TypeOf(Z(0)) 229 | 230 | return mainEnv 231 | } 232 | 233 | func main() { 234 | env := makeBogusEnv() 235 | intro_text() 236 | REPL(env) 237 | } 238 | -------------------------------------------------------------------------------- /didyoutypecheck.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | // Wraps panic message in "eval: %s. Did you type check?" 4 | func dytc(probablyNot string) string { 5 | return "eval: " + probablyNot + ". Did you type check?" 6 | } 7 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // The start of an eval() function for go. 2 | 3 | // The main entry point is: 4 | // func EvalExpr(expr ast.Expr, env Env) 5 | // (*[]reflect.Value, bool, error) 6 | 7 | package eval 8 | -------------------------------------------------------------------------------- /env.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | type envSource int 8 | const ( 9 | envUnknown envSource = iota 10 | envVar 11 | envFunc 12 | envConst 13 | ) 14 | 15 | // A Environment used for evaluation 16 | type Env interface { 17 | // Return a pointer value to the variable ident if defined in the top scope, or reflect.Value{} otherwise 18 | Var(ident string) reflect.Value 19 | 20 | // Return the const value ident if defined in the top scope, or reflect.Value{} otherwise 21 | Const(ident string) reflect.Value 22 | 23 | // Return the func value ident if defined in the top scope, or reflect.Value{} otherwise 24 | Func(ident string) reflect.Value 25 | 26 | // Return the type ident if defined in the top scope, or reflect.Value{} otherwise 27 | Type(ident string) reflect.Type 28 | 29 | // Return the environment containing vars, consts, funcs and types of pkg, or nil if not defined. 30 | // Unlike other lookup methods, packages exist only in the root scope. 31 | Pkg(pkg string) Env 32 | 33 | // Create a new block scope. Only the behaviour of the returned Env should change 34 | PushScope() Env 35 | 36 | // Pop the top block scope. Only the behaviour of the returned Env should change 37 | PopScope() Env 38 | 39 | // Add var ident to the top scope. The value is always a pointer value, and this same value should be 40 | // returned by Var(ident). It is up to the implementation how to handle duplicate identifiers. 41 | AddVar(ident string, v reflect.Value) 42 | 43 | // Add const ident to the top scope. It is up to the implementation how to handle duplicate identifiers. 44 | AddConst(ident string, c reflect.Value) 45 | 46 | // Add func ident to the top scope. It is up to the implementation how to handle duplicate identifiers. 47 | AddFunc(ident string, f reflect.Value) 48 | 49 | // Add type ident to the top scope. It is up to the implementation how to handle duplicate identifiers. 50 | AddType(ident string, t reflect.Type) 51 | 52 | // Add pkg to the root scope. It is up to the implementation how to handle duplicate identifiers. 53 | AddPkg(pkg string, p Env) 54 | } 55 | 56 | func MakeSimpleEnv() *SimpleEnv { 57 | return &SimpleEnv { 58 | Vars: map[string]reflect.Value{}, 59 | Funcs: map[string]reflect.Value{}, 60 | Consts: map[string]reflect.Value{}, 61 | Types: map[string]reflect.Type{}, 62 | Pkgs: map[string]Env{}, 63 | } 64 | } 65 | 66 | type SimpleEnv struct { 67 | // path relative to GOROOT or GOPATH. e.g. github.com/0xfaded/eval 68 | Path string 69 | Parent *SimpleEnv 70 | Vars map[string]reflect.Value 71 | Funcs map[string]reflect.Value 72 | Consts map[string]reflect.Value 73 | Types map[string]reflect.Type 74 | Pkgs map[string]Env 75 | } 76 | 77 | func (env *SimpleEnv) Var(ident string) reflect.Value { 78 | return env.Vars[ident] 79 | } 80 | 81 | func (env *SimpleEnv) Func(ident string) reflect.Value { 82 | return env.Funcs[ident] 83 | } 84 | 85 | func (env *SimpleEnv) Const(ident string) reflect.Value { 86 | return env.Consts[ident] 87 | } 88 | 89 | func (env *SimpleEnv) Type(ident string) reflect.Type { 90 | return env.Types[ident] 91 | } 92 | 93 | func (env *SimpleEnv) Pkg(pkg string) Env { 94 | for env.Parent != nil { 95 | env = env.Parent 96 | } 97 | return env.Pkgs[pkg] 98 | } 99 | 100 | func (env *SimpleEnv) PushScope() Env { 101 | top := MakeSimpleEnv() 102 | top.Parent = env 103 | return top 104 | } 105 | 106 | func (env *SimpleEnv) PopScope() Env { 107 | if env.Parent == nil { 108 | return nil 109 | } else { 110 | return env.Parent 111 | } 112 | } 113 | 114 | func (env *SimpleEnv) AddVar(ident string, v reflect.Value) { 115 | env.Vars[ident] = v 116 | } 117 | 118 | func (env *SimpleEnv) AddFunc(ident string, f reflect.Value) { 119 | env.Funcs[ident] = f 120 | } 121 | 122 | func (env *SimpleEnv) AddConst(ident string, c reflect.Value) { 123 | env.Consts[ident] = c 124 | } 125 | 126 | func (env *SimpleEnv) AddType(ident string, t reflect.Type) { 127 | env.Types[ident] = t 128 | } 129 | 130 | func (env *SimpleEnv) AddPkg(pkg string, p Env) { 131 | for env.Parent != nil { 132 | env = env.Parent 133 | } 134 | env.Pkgs[pkg] = p 135 | } 136 | -------------------------------------------------------------------------------- /eval.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "go/ast" 8 | "go/parser" 9 | "go/scanner" 10 | ) 11 | 12 | var emptyEnv Env = MakeSimpleEnv() 13 | 14 | func Eval(expr string) (result []reflect.Value, panik error, compileErrors []error) { 15 | return EvalEnv(expr, emptyEnv) 16 | } 17 | 18 | func EvalEnv(expr string, env Env) (result []reflect.Value, panik error, compileErrors []error) { 19 | if e, err := parser.ParseExpr(expr); err != nil { 20 | errs := err.(scanner.ErrorList) 21 | for i := range errs { 22 | compileErrors = append(compileErrors, errs[i]) 23 | } 24 | } else if cexpr, errs := CheckExpr(e, env); errs != nil { 25 | compileErrors = errs 26 | } else { 27 | result, panik = EvalExpr(cexpr, env) 28 | } 29 | return 30 | } 31 | 32 | func Interpret(stmt string, env Env) (result []reflect.Value, panik error, compileErrors []error) { 33 | if s, err := ParseStmt(stmt); err != nil { 34 | if errs, ok := err.(scanner.ErrorList); ok { 35 | for i := range errs { 36 | compileErrors = append(compileErrors, errs[i]) 37 | } 38 | } else { 39 | compileErrors = append(compileErrors, err) 40 | } 41 | } else if e, ok := s.(*ast.ExprStmt); ok { 42 | if cexpr, errs := CheckExpr(e.X, env); errs != nil { 43 | compileErrors = errs 44 | } else { 45 | result, panik = EvalExpr(cexpr, env) 46 | } 47 | } else { 48 | if cstmt, errs := CheckStmt(s, env); errs != nil { 49 | compileErrors = errs 50 | } else { 51 | _, panik = InterpStmt(cstmt, env) 52 | } 53 | } 54 | return 55 | } 56 | 57 | // A convenience function for parsing a Stmt by wrapping it in a func literal 58 | func ParseStmt(stmt string) (ast.Stmt, error) { 59 | // parser.ParseExpr has some quirks, for example it won't parse unused map literals as 60 | // ExprStmts. Unused map literals are technically illegal, but it would be 61 | // nice to check them at a later stage. Therefore, we want to parse expressions 62 | // as expressions, and stmts and stmts. We try both. 63 | // However, there is a bug in parser.ParseExpr that it does not detect excess input. 64 | // Therefore, the _ of _ = 1 will be parsed as an expression. To avoid this, attempt 65 | // to parse the input as a statement first, and fall back to an expression 66 | expr := "func(){" + stmt + ";}" 67 | if e, err := parser.ParseExpr(expr); err != nil { 68 | if e, err := parser.ParseExpr(stmt); err == nil { 69 | return &ast.ExprStmt{X: e}, nil 70 | } 71 | errs := err.(scanner.ErrorList) 72 | for i := range errs { 73 | errs[i].Pos.Offset -= 7 74 | errs[i].Pos.Column -= 7 75 | } 76 | return nil, errs 77 | } else { 78 | node := e.(*ast.FuncLit).Body.List[0] 79 | if stmt, ok := node.(Stmt); !ok { 80 | return nil, fmt.Errorf("%T not supported", node) 81 | } else { 82 | return stmt, nil 83 | } 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /evalbasiclit.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalBasicLit(lit *BasicLit) (reflect.Value, error) { 8 | if lit.IsConst() { 9 | return lit.Const(), nil 10 | } 11 | panic(dytc("non-const basic lit")) 12 | } 13 | -------------------------------------------------------------------------------- /evalbuiltin.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalCallBuiltinExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 8 | ident := call.Fun.(*Ident) 9 | switch ident.Name { 10 | case "complex": 11 | return evalBuiltinComplexExpr(call, env) 12 | case "real": 13 | return evalBuiltinRealExpr(call, env) 14 | case "imag": 15 | return evalBuiltinImagExpr(call, env) 16 | case "new": 17 | return evalBuiltinNewExpr(call, env) 18 | case "make": 19 | return evalBuiltinMakeExpr(call, env) 20 | case "len": 21 | return evalBuiltinLenExpr(call, env) 22 | case "cap": 23 | return evalBuiltinCapExpr(call, env) 24 | case "append": 25 | return evalBuiltinAppendExpr(call, env) 26 | case "copy": 27 | return evalBuiltinCopyExpr(call, env) 28 | case "delete": 29 | return evalBuiltinDeleteExpr(call, env) 30 | case "panic": 31 | return evalBuiltinPanicExpr(call, env) 32 | default: 33 | panic("eval: unimplemented builtin " + ident.Name) 34 | } 35 | } 36 | 37 | func evalBuiltinComplexExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 38 | var err error 39 | 40 | resT := call.KnownType()[0] 41 | argT := knownType{comprisingFloatType(resT)} 42 | 43 | var re, im []reflect.Value 44 | if re, err = evalTypedExpr(call.Args[0], argT, env); err != nil { 45 | return nil, err 46 | } else if im, err = evalTypedExpr(call.Args[1], argT, env); err != nil { 47 | return nil, err 48 | } 49 | cplx := builtinComplex(re[0], im[0]) 50 | return []reflect.Value{cplx}, nil 51 | } 52 | 53 | func evalBuiltinRealExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 54 | var err error 55 | 56 | resT := call.KnownType()[0] 57 | argT := knownType{comprisingFloatType(resT)} 58 | 59 | var cplx []reflect.Value 60 | if cplx, err = evalTypedExpr(call.Args[0], argT, env); err != nil { 61 | return nil, err 62 | } 63 | re := builtinReal(cplx[0]) 64 | return []reflect.Value{re}, nil 65 | } 66 | 67 | func evalBuiltinImagExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 68 | var err error 69 | 70 | resT := call.KnownType()[0] 71 | argT := knownType{comprisingFloatType(resT)} 72 | 73 | var cplx []reflect.Value 74 | if cplx, err = evalTypedExpr(call.Args[0], argT, env); err != nil { 75 | return nil, err 76 | } 77 | im := builtinImag(cplx[0]) 78 | return []reflect.Value{im}, nil 79 | } 80 | 81 | func evalBuiltinNewExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 82 | resT := call.KnownType()[0] 83 | ptr := builtinNew(resT.Elem()) 84 | return []reflect.Value{ptr}, nil 85 | } 86 | 87 | func evalBuiltinMakeExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 88 | resT := call.KnownType()[0] 89 | length, capacity := 0, 0 90 | var err error 91 | if len(call.Args) > 1 { 92 | if length, err = evalInteger(call.Args[1], env); err != nil { 93 | return nil, err 94 | } 95 | } 96 | if len(call.Args) > 2 { 97 | if capacity, err = evalInteger(call.Args[2], env); err != nil { 98 | return nil, err 99 | } 100 | } 101 | var res reflect.Value 102 | switch resT.Kind() { 103 | case reflect.Slice: 104 | res = reflect.MakeSlice(resT, length, capacity) 105 | case reflect.Map: 106 | res = reflect.MakeMap(resT) 107 | case reflect.Chan: 108 | res = reflect.MakeChan(resT, length) 109 | default: 110 | panic(dytc("make(bad type)")) 111 | } 112 | return []reflect.Value{res}, nil 113 | } 114 | 115 | func evalBuiltinLenExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 116 | if arg0, err := EvalExpr(call.Args[0], env); err != nil { 117 | return nil, err 118 | } else { 119 | length := builtinLen(arg0[0]) 120 | return []reflect.Value{length}, nil 121 | } 122 | } 123 | 124 | func evalBuiltinCapExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 125 | if arg0, err := EvalExpr(call.Args[0], env); err != nil { 126 | return nil, err 127 | } else { 128 | capacity := builtinCap(arg0[0]) 129 | return []reflect.Value{capacity}, nil 130 | } 131 | } 132 | 133 | func evalBuiltinAppendExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 134 | sliceT := call.KnownType() 135 | head, err := evalTypedExpr(call.Args[0], sliceT, env) 136 | if err != nil { 137 | return nil, err 138 | } 139 | var tail reflect.Value 140 | if call.argNEllipsis { 141 | xs, err := EvalExpr(call.Args[1], env) 142 | if err != nil { 143 | return nil, err 144 | } 145 | tail = xs[0] 146 | } else { 147 | numXs := len(call.Args) - 1 148 | tail = reflect.MakeSlice(sliceT[0], numXs, numXs) 149 | xT := knownType{sliceT[0].Elem()} 150 | for i := 1; i < len(call.Args); i += 1 { 151 | if x, err := evalTypedExpr(call.Args[i], xT, env); err != nil { 152 | return nil, err 153 | } else { 154 | tail.Index(i-1).Set(x[0]) 155 | } 156 | } 157 | } 158 | 159 | res := builtinAppend(head[0], tail) 160 | return []reflect.Value{res}, nil 161 | } 162 | 163 | func evalBuiltinCopyExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 164 | if x, err := EvalExpr(call.Args[0], env); err != nil { 165 | return nil, err 166 | } else if y, err := EvalExpr(call.Args[1], env); err != nil { 167 | return nil, err 168 | } else { 169 | n := builtinCopy(x[0], y[0]) 170 | return []reflect.Value{n}, nil 171 | } 172 | } 173 | 174 | func evalBuiltinDeleteExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 175 | m := call.Args[0] 176 | mT := m.KnownType()[0] 177 | if x, err := EvalExpr(m, env); err != nil { 178 | return nil, err 179 | } else if y, err := evalTypedExpr(call.Args[1], knownType{mT.Key()}, env); err != nil { 180 | return nil, err 181 | } else { 182 | builtinDelete(x[0], y[0]) 183 | return []reflect.Value{}, nil 184 | } 185 | } 186 | 187 | func evalBuiltinPanicExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 188 | if arg0, err := evalTypedExpr(call.Args[0], knownType{emptyInterface}, env); err != nil { 189 | return nil, err 190 | } else { 191 | err := builtinPanic(arg0[0]) 192 | return []reflect.Value{}, err 193 | } 194 | } 195 | 196 | -------------------------------------------------------------------------------- /evalbuiltin_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | 4 | import ( 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestBuiltinComplex(t *testing.T) { 10 | env := MakeSimpleEnv() 11 | 12 | expectResult(t, "complex(1, 2)", env, complex(1, 2)) 13 | expectResult(t, "complex(float64(1), 2)", env, complex(float64(1), 2)) 14 | expectResult(t, "complex(1, float64(2))", env, complex(1, float64(2))) 15 | expectResult(t, "complex(float64(1), float64(2))", env, complex(float64(1), float64(2))) 16 | } 17 | 18 | func TestBuiltinReal(t *testing.T) { 19 | env := MakeSimpleEnv() 20 | 21 | expectResult(t, "real(complex(1, 2))", env, real(complex(1, 2))) 22 | expectResult(t, "real(complex(float64(1), 2))", env, real(complex(float64(1), 2))) 23 | expectResult(t, "real(complex(1, float64(2)))", env, real(complex(1, float64(2)))) 24 | expectResult(t, "real(complex(float64(1), float64(2)))", env, real(complex(float64(1), float64(2)))) 25 | } 26 | 27 | func TestBuiltinImag(t *testing.T) { 28 | env := MakeSimpleEnv() 29 | 30 | expectResult(t, "imag(complex(1, 2))", env, imag(complex(1, 2))) 31 | expectResult(t, "imag(complex(float64(1), 2))", env, imag(complex(float64(1), 2))) 32 | expectResult(t, "imag(complex(1, float64(2)))", env, imag(complex(1, float64(2)))) 33 | expectResult(t, "imag(complex(float64(1), float64(2)))", env, imag(complex(float64(1), float64(2)))) 34 | } 35 | 36 | func TestBuiltinAppend(t *testing.T) { 37 | env := MakeSimpleEnv() 38 | strings := []string {"one", "two"} 39 | ints := []int{1, 2} 40 | env.Vars["strings"] = reflect.ValueOf(&strings) 41 | env.Vars["ints"] = reflect.ValueOf(&ints) 42 | 43 | expectResult(t, "append(strings, \"three\")", env, append(strings, "three")) 44 | expectResult(t, "append(ints, 3, 4)", env, append(ints, 3, 4)) 45 | } 46 | 47 | func TestBuiltinAppendSlice(t *testing.T) { 48 | env := MakeSimpleEnv() 49 | a := []string {"one", "two"} 50 | b := []string {"three", "four"} 51 | env.Vars["a"] = reflect.ValueOf(&a) 52 | env.Vars["b"] = reflect.ValueOf(&b) 53 | 54 | expectResult(t, "append(a, b...)", env, append(a, b...)) 55 | } 56 | 57 | func TestBuiltinCap(t *testing.T) { 58 | env := MakeSimpleEnv() 59 | slice := []int {1, 2} 60 | env.Vars["slice"] = reflect.ValueOf(&slice) 61 | 62 | expectResult(t, "cap(slice)", env, cap(slice)) 63 | } 64 | 65 | func TestBuiltinLen(t *testing.T) { 66 | env := MakeSimpleEnv() 67 | slice := []int {1, 2} 68 | env.Vars["slice"] = reflect.ValueOf(&slice) 69 | 70 | expectResult(t, "len(\"abc\")", env, len("abc")) 71 | expectResult(t, "len(slice)", env, len(slice)) 72 | } 73 | 74 | func TestBuiltinNew(t *testing.T) { 75 | env := MakeSimpleEnv() 76 | expr := "new(int)" 77 | results := getResults(t, expr, env) 78 | returnKind := results[0].Kind().String() 79 | if returnKind != "ptr" { 80 | t.Fatalf("Error Expecting `%s' return Kind to be `ptr' is `%s`", expr, returnKind) 81 | } 82 | } 83 | 84 | func TestBuiltinCopy(t *testing.T) { 85 | env := MakeSimpleEnv() 86 | a := []int{1,2,3} 87 | b := []int{4,5} 88 | env.Vars["a"] = reflect.ValueOf(&a) 89 | env.Vars["b"] = reflect.ValueOf(&b) 90 | expectResult(t, "copy(a, b)", env, copy(a, b)) 91 | expectResult(t, "copy(b, a)", env, copy(b, a)) 92 | } 93 | 94 | func TestBuiltinDelete(t *testing.T) { 95 | env := MakeSimpleEnv() 96 | a := map[int]int{1: 2} 97 | env.Vars["a"] = reflect.ValueOf(&a) 98 | getResults(t, "delete(a, 1)", env) 99 | getResults(t, "delete(a, 1)", env) 100 | if _, ok := a[1]; ok { 101 | t.Fatalf("Failed to delete(a, 1)`") 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /evalcallexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalCallExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 8 | if call.IsConst() { 9 | return []reflect.Value{call.Const()}, nil 10 | } else if call.isBuiltin { 11 | return evalCallBuiltinExpr(call, env) 12 | } else if call.isTypeConversion { 13 | return evalCallTypeExpr(call, env) 14 | } else { 15 | return evalCallFunExpr(call, env) 16 | } 17 | } 18 | 19 | func evalCallTypeExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 20 | // Arg0 can only be const if it is ConstNil, otherwise the entire expression 21 | // would be const and evalCallExpr will have already returned. 22 | arg := call.Args[0] 23 | t := arg.KnownType()[0] 24 | if t == ConstNil { 25 | // This has already been typechecked to be a nil-able type 26 | return []reflect.Value{hackedNew(call.KnownType()[0]).Elem()}, nil 27 | } else if v, err := EvalExpr(arg, env); err != nil { 28 | return nil, nil 29 | } else { 30 | cast := v[0].Convert(unhackType(call.KnownType()[0])) 31 | return []reflect.Value{cast}, nil 32 | } 33 | } 34 | 35 | func evalCallFunExpr(call *CallExpr, env Env) ([]reflect.Value, error) { 36 | v, err := EvalExpr(call.Fun, env) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | fun := v[0] 42 | ft := fun.Type() 43 | numIn := ft.NumIn() 44 | 45 | // Evaluate arguments 46 | args := make([]reflect.Value, len(call.Args)) 47 | if call.arg0MultiValued { 48 | if argp, err := EvalExpr(call.Args[0], env); err != nil { 49 | return nil, err 50 | } else { 51 | args = argp 52 | } 53 | } else if len(args) != 0 { 54 | var i int 55 | for i = 0; i < numIn - 1; i += 1 { 56 | arg := call.Args[i] 57 | argType := knownType{ft.In(i)} 58 | if argV, err := evalTypedExpr(arg, argType, env); err != nil { 59 | return nil, err 60 | } else { 61 | args[i] = argV[0] 62 | } 63 | } 64 | argNT := ft.In(i) 65 | if ft.IsVariadic() && !call.argNEllipsis { 66 | argNT = argNT.Elem() 67 | } 68 | argNKnownType := knownType{argNT} 69 | for ; i < len(call.Args); i += 1 { 70 | arg := call.Args[i] 71 | if argV, err := evalTypedExpr(arg, argNKnownType, env); err != nil { 72 | return nil, err 73 | } else { 74 | args[i] = argV[0] 75 | } 76 | } 77 | } 78 | 79 | var out []reflect.Value 80 | if call.argNEllipsis { 81 | out = fun.CallSlice(args) 82 | } else { 83 | out = fun.Call(args) 84 | } 85 | return out, nil 86 | } 87 | -------------------------------------------------------------------------------- /evalcallexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | func TestFuncCallWithConst(t *testing.T) { 9 | env := MakeSimpleEnv() 10 | env.Consts["X"] = reflect.ValueOf(int(10)) 11 | env.Funcs["Foo"] = reflect.ValueOf(func (int) int { return 1; }) 12 | 13 | expectResult(t, "Foo(X)", env, 1) 14 | } 15 | 16 | func TestFuncCallWithSplatOne(t *testing.T) { 17 | env := MakeSimpleEnv() 18 | 19 | f := func() int { return 1 } 20 | g := func(a int) int { return a } 21 | 22 | env.Funcs["f"] = reflect.ValueOf(f) 23 | env.Funcs["g"] = reflect.ValueOf(g) 24 | 25 | expr := "g(f())" 26 | expected := g(f()) 27 | 28 | expectResult(t, expr, env, expected) 29 | } 30 | 31 | func TestFuncCallWithSplatTwo(t *testing.T) { 32 | env := MakeSimpleEnv() 33 | 34 | f := func() (int, int) { return 1, 2 } 35 | g := func(a int, b int) int { return a + b } 36 | 37 | env.Funcs["f"] = reflect.ValueOf(f) 38 | env.Funcs["g"] = reflect.ValueOf(g) 39 | 40 | expr := "g(f())" 41 | expected := g(f()) 42 | 43 | expectResult(t, expr, env, expected) 44 | } 45 | 46 | func TestTypeConversionWithEmptyInterface(t *testing.T) { 47 | env := MakeSimpleEnv() 48 | expectResult(t, "interface{}(1)", env, interface{}(1)) 49 | expectResult(t, "interface{}(1.5)", env, interface{}(1.5)) 50 | expectResult(t, "interface{}(1i)", env, interface{}(1i)) 51 | expectResult(t, "interface{}('a')", env, interface{}('a')) 52 | expectResult(t, "interface{}(\"a\")", env, interface{}("a")) 53 | expectResult(t, "interface{}(nil)", env, interface{}(nil)) 54 | expectResult(t, "interface{}([]int{})", env, interface{}([]int{})) 55 | expectResult(t, "interface{}('a')", env, interface{}('a')) 56 | } 57 | 58 | -------------------------------------------------------------------------------- /evalcompositelit.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | func evalCompositeLit(lit *CompositeLit, env Env) (reflect.Value, error) { 10 | t := lit.KnownType()[0] 11 | 12 | switch t.Kind() { 13 | case reflect.Map: 14 | return evalCompositeLitMap(t, lit, env) 15 | case reflect.Array, reflect.Slice: 16 | return evalCompositeLitArrayOrSlice(t, lit, env) 17 | case reflect.Struct: 18 | return evalCompositeLitStruct(t, lit, env) 19 | default: 20 | return reflect.Value{}, errors.New(fmt.Sprintf("eval: unimplemented type for composite literal %s", t.Name())) 21 | } 22 | } 23 | 24 | func evalCompositeLitMap(t reflect.Type, lit *CompositeLit, env Env) (reflect.Value, error) { 25 | 26 | m := reflect.New(t).Elem() 27 | m.Set(reflect.MakeMap(t)) 28 | 29 | kT := knownType{t.Key()} 30 | vT := knownType{t.Elem()} 31 | for _, elt := range lit.Elts { 32 | kv := elt.(*KeyValueExpr) 33 | k, err := evalTypedExpr(kv.Key, kT, env) 34 | if err != nil { 35 | return reflect.Value{}, err 36 | } 37 | if kT[0].Kind() == reflect.Interface { 38 | dynamicT := k[0].Elem().Type() 39 | if !isStaticTypeComparable(dynamicT) { 40 | return reflect.Value{}, PanicUnhashableType{dynamicT} 41 | } 42 | } 43 | v, err := evalTypedExpr(kv.Value, vT, env) 44 | if err != nil { 45 | return reflect.Value{}, err 46 | } 47 | m.SetMapIndex(k[0], v[0]) 48 | } 49 | return m, nil 50 | } 51 | 52 | func evalCompositeLitArrayOrSlice(t reflect.Type, lit *CompositeLit, env Env) (reflect.Value, error) { 53 | 54 | v := reflect.New(t).Elem() 55 | if t.Kind() == reflect.Slice { 56 | v.Set(reflect.MakeSlice(t, lit.length, lit.length)) 57 | } 58 | 59 | eT := knownType{t.Elem()} 60 | for src, dst, i := 0, 0, 0; src < len(lit.Elts); src, dst = src + 1, dst + 1 { 61 | var elt Expr 62 | if lit.indices[i].pos == src { 63 | elt = lit.Elts[src].(*KeyValueExpr).Value 64 | dst = lit.indices[i].index 65 | i += 1 66 | } else { 67 | elt = lit.Elts[src] 68 | } 69 | if elem, err := evalTypedExpr(elt, eT, env); err != nil { 70 | return reflect.Value{}, err 71 | } else { 72 | v.Index(dst).Set(elem[0]) 73 | } 74 | } 75 | return v, nil 76 | } 77 | 78 | func evalCompositeLitStruct(t reflect.Type, lit *CompositeLit, env Env) (reflect.Value, error) { 79 | v := reflect.New(t).Elem() 80 | for i, f := range lit.fields { 81 | var elt Expr 82 | if kv, ok := lit.Elts[i].(*KeyValueExpr); ok { 83 | elt = kv.Value 84 | } else { 85 | elt = lit.Elts[i] 86 | } 87 | field := v.Field(f) 88 | if elem, err := evalTypedExpr(elt, knownType{field.Type()}, env); err != nil { 89 | return reflect.Value{}, err 90 | } else { 91 | field.Set(elem[0]) 92 | } 93 | } 94 | return v, nil 95 | } 96 | -------------------------------------------------------------------------------- /evalcompositelit_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | func TestCompositeArrayEmpty(t *testing.T) { 9 | type Alice [0]int 10 | 11 | env := MakeSimpleEnv() 12 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 13 | 14 | expected := Alice { } 15 | expr := "Alice {}" 16 | 17 | expectResult(t, expr, env, expected) 18 | } 19 | 20 | func TestCompositeArrayValues(t *testing.T) { 21 | type Alice [3]int 22 | 23 | env := MakeSimpleEnv() 24 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 25 | 26 | expected := Alice { 1, 2, 3 } 27 | expr := "Alice { 1, 2, 3 }" 28 | 29 | expectResult(t, expr, env, expected) 30 | } 31 | 32 | func TestCompositeArrayKeyValues(t *testing.T) { 33 | type Alice [3]int 34 | 35 | env := MakeSimpleEnv() 36 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 37 | 38 | expected := Alice { 1: 1, 2 } 39 | expr := "Alice { 1: 1, 2 }" 40 | 41 | expectResult(t, expr, env, expected) 42 | } 43 | 44 | func TestCompositeArrayIncompleteValues(t *testing.T) { 45 | type Alice [3]int 46 | 47 | env := MakeSimpleEnv() 48 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 49 | 50 | expected := Alice { 1, 2 } 51 | expr := "Alice { 1, 2 }" 52 | 53 | expectResult(t, expr, env, expected) 54 | } 55 | 56 | func TestCompositeSliceEmpty(t *testing.T) { 57 | type Alice []int 58 | 59 | env := MakeSimpleEnv() 60 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 61 | 62 | expected := Alice { } 63 | expr := "Alice { }" 64 | 65 | expectResult(t, expr, env, expected) 66 | } 67 | 68 | func TestCompositeSliceValues(t *testing.T) { 69 | type Alice []int 70 | 71 | env := MakeSimpleEnv() 72 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 73 | 74 | expected := Alice { 1, 2, 3 } 75 | expr := "Alice { 1, 2, 3 }" 76 | 77 | expectResult(t, expr, env, expected) 78 | } 79 | 80 | func TestCompositeSliceKeyValues(t *testing.T) { 81 | type Alice []int 82 | 83 | env := MakeSimpleEnv() 84 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 85 | 86 | expected := Alice { 1, 10: 1 } 87 | expr := "Alice { 1, 10: 1 }" 88 | 89 | expectResult(t, expr, env, expected) 90 | } 91 | 92 | func TestCompositeStructValues(t *testing.T) { 93 | type Alice struct { 94 | Bob int 95 | } 96 | 97 | env := MakeSimpleEnv() 98 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 99 | 100 | expected := Alice { 10 } 101 | expr := "Alice{ 10 }" 102 | 103 | expectResult(t, expr, env, expected) 104 | } 105 | 106 | func TestCompositeStructKeyValues(t *testing.T) { 107 | type Alice struct { 108 | Bob int 109 | } 110 | 111 | env := MakeSimpleEnv() 112 | env.Types["Alice"] = reflect.TypeOf(Alice{}) 113 | 114 | expected := Alice { Bob: 10 } 115 | expr := "Alice{ Bob: 10 }" 116 | 117 | expectResult(t, expr, env, expected) 118 | } 119 | -------------------------------------------------------------------------------- /evalexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // EvalExpr is the main function to call to evaluate an ast-parsed 8 | // expression, expr. 9 | // Parameter env, contains an evaluation environment from 10 | // which to get reflect.Values from. Note however that env can be 11 | // subverted somewhat by supplying callback hooks routines which 12 | // access variables and by supplying user-defined conversion routines. 13 | func EvalExpr(expr Expr, env Env) ([]reflect.Value, error) { 14 | switch node := expr.(type) { 15 | case *Ident: 16 | v, err := evalIdent(node, env) 17 | return []reflect.Value{v}, err 18 | case *BasicLit: 19 | v, err := evalBasicLit(node) 20 | return []reflect.Value{v}, err 21 | case *FuncLit: 22 | v, err := evalFuncLit(node, env) 23 | return []reflect.Value{v}, err 24 | case *CompositeLit: 25 | v, err := evalCompositeLit(node, env) 26 | return []reflect.Value{v}, err 27 | case *ParenExpr: 28 | return EvalExpr(node.X, env) 29 | case *SelectorExpr: 30 | v, err := evalSelectorExpr(node, env) 31 | return []reflect.Value{v}, err 32 | case *IndexExpr: 33 | return evalIndexExpr(node, env) 34 | case *SliceExpr: 35 | v, err := evalSliceExpr(node, env) 36 | return []reflect.Value{v}, err 37 | case *TypeAssertExpr: 38 | return evalTypeAssertExpr(node, env) 39 | case *CallExpr: 40 | return evalCallExpr(node, env) 41 | case *StarExpr: 42 | v, err := evalStarExpr(node, env) 43 | return []reflect.Value{v}, err 44 | case *UnaryExpr: 45 | return evalUnaryExpr(node, env) 46 | case *BinaryExpr: 47 | v, err := evalBinaryExpr(node, env) 48 | return []reflect.Value{v}, err 49 | default: 50 | panic(dytc("unevalutable node")) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /evalfunclit.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalFuncLit(lit *FuncLit, env Env) (reflect.Value, error) { 8 | ft := lit.Type.KnownType()[0] 9 | env = env.PushScope() 10 | v := reflect.MakeFunc(ft, func(in []reflect.Value) (out []reflect.Value) { 11 | i := 0 12 | for _, field := range lit.Type.Params.List { 13 | if field.Names != nil { 14 | for _, name := range field.Names { 15 | if name.Name != "_" { 16 | v := reflect.New(ft.In(i)) 17 | v.Elem().Set(in[i]) 18 | env.AddVar(name.Name, v) 19 | } 20 | i += 1 21 | } 22 | } 23 | } 24 | i = 0 25 | if lit.Type.Results != nil { 26 | for _, field := range lit.Type.Results.List { 27 | for _, name := range field.Names { 28 | if name.Name != "_" { 29 | env.AddVar(name.Name, reflect.New(ft.Out(i))) 30 | } 31 | i += 1 32 | } 33 | } 34 | } 35 | if last, err := InterpStmt(lit.Body, env); err != nil { 36 | panic(err) 37 | } else if ft.NumOut() == 0 { 38 | // void func which may terminate on a non-ReturnStmt 39 | } else if ret := last.Last.(*ReturnStmt); len(ret.Results) == ft.NumOut() { 40 | for i, result := range ret.Results { 41 | if r, err := evalTypedExpr(result, knownType{ft.Out(i)}, last.Env); err != nil { 42 | panic(err) 43 | } else { 44 | out = append(out, r[0]) 45 | } 46 | } 47 | } else if len(ret.Results) == 1 { 48 | // return multi() 49 | if rs, err := EvalExpr(ret.Results[0], env); err != nil { 50 | panic(err) 51 | } else { 52 | out = rs 53 | } 54 | } else { 55 | for _, field := range lit.Type.Results.List { 56 | for _, name := range field.Names { 57 | if name.Name != "_" { 58 | out = append(out, env.Var(name.Name).Elem()) 59 | } else { 60 | out = append(out, reflect.Zero(field.KnownType()[0])) 61 | } 62 | } 63 | } 64 | } 65 | return out 66 | }) 67 | return v, nil 68 | } 69 | -------------------------------------------------------------------------------- /evalident.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalIdent(ident *Ident, env Env) (reflect.Value, error) { 8 | if ident.IsConst() { 9 | return ident.Const(), nil 10 | } 11 | 12 | name := ident.Name 13 | switch ident.source { 14 | case envVar: 15 | for searchEnv := env; searchEnv != nil; searchEnv = searchEnv.PopScope() { 16 | if v := searchEnv.Var(name); v.IsValid() { 17 | return v.Elem(), nil 18 | } 19 | } 20 | case envFunc: 21 | for searchEnv := env; searchEnv != nil; searchEnv = searchEnv.PopScope() { 22 | if v := searchEnv.Func(name); v.IsValid() { 23 | return v, nil 24 | } 25 | } 26 | } 27 | panic(dytc("missing identifier '"+name+"'")) 28 | } 29 | 30 | -------------------------------------------------------------------------------- /evalident_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestStringVar(t *testing.T) { 9 | env := MakeSimpleEnv() 10 | s := "abc" 11 | env.Vars["arg0"] = reflect.ValueOf(&s) 12 | expectResult(t, "arg0", env, "abc") 13 | } 14 | -------------------------------------------------------------------------------- /evalindexexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalIndexExpr(index *IndexExpr, env Env) ([]reflect.Value, error) { 8 | xs, err := EvalExpr(index.X, env) 9 | if err != nil { 10 | return []reflect.Value{}, err 11 | } 12 | x := xs[0] 13 | 14 | t := index.X.KnownType()[0] 15 | switch t.Kind() { 16 | case reflect.Map: 17 | k, err := evalTypedExpr(index.Index, knownType{t.Key()}, env) 18 | if err != nil { 19 | return []reflect.Value{}, err 20 | } 21 | v := x.MapIndex(k[0]) 22 | ok := v.IsValid() 23 | if !ok { 24 | v = reflect.New(t.Key()).Elem() 25 | } 26 | return []reflect.Value{v, reflect.ValueOf(ok)}, nil 27 | case reflect.Ptr: 28 | // Short hand for array pointers 29 | x = x.Elem() 30 | fallthrough 31 | default: 32 | i, err := evalInteger(index.Index, env) 33 | if err != nil { 34 | return []reflect.Value{}, err 35 | } 36 | if !(0 <= i && i < x.Len()) { 37 | return []reflect.Value{}, PanicIndexOutOfBounds{} 38 | } 39 | return []reflect.Value{x.Index(i)}, nil 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /evalindexexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | func TestIndexArray(t *testing.T) { 9 | a := [2]int{1, 2} 10 | 11 | env := MakeSimpleEnv() 12 | env.Vars["a"] = reflect.ValueOf(&a) 13 | 14 | expected := a[1] 15 | expr := "a[1]" 16 | 17 | expectResult(t, expr, env, expected) 18 | } 19 | 20 | func TestIndexArrayPtr(t *testing.T) { 21 | a := &[2]int{1, 2} 22 | 23 | env := MakeSimpleEnv() 24 | env.Vars["a"] = reflect.ValueOf(&a) 25 | 26 | expected := a[1] 27 | expr := "a[1]" 28 | 29 | expectResult(t, expr, env, expected) 30 | } 31 | 32 | func TestIndexSlice(t *testing.T) { 33 | a := []int{1, 2} 34 | 35 | env := MakeSimpleEnv() 36 | env.Vars["a"] = reflect.ValueOf(&a) 37 | 38 | expected := a[1] 39 | expr := "a[1]" 40 | 41 | expectResult(t, expr, env, expected) 42 | } 43 | 44 | func TestIndexString(t *testing.T) { 45 | a := "ab" 46 | 47 | env := MakeSimpleEnv() 48 | env.Vars["a"] = reflect.ValueOf(&a) 49 | 50 | expected := a[1] 51 | expr := "a[1]" 52 | 53 | expectResult(t, expr, env, expected) 54 | } 55 | 56 | -------------------------------------------------------------------------------- /evalselectorexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalSelectorExpr(selector *SelectorExpr, env Env) (reflect.Value, error) { 8 | 9 | if selector.pkgName != "" { 10 | vs, err := evalIdent(selector.Sel, env.Pkg(selector.pkgName)) 11 | return vs, err 12 | } 13 | 14 | vs, err := EvalExpr(selector.X, env) 15 | if err != nil { 16 | return reflect.Value{}, err 17 | } 18 | v := vs[0] 19 | t := v.Type() 20 | if selector.field != nil { 21 | if t.Kind() == reflect.Ptr { 22 | v = v.Elem() 23 | } 24 | return v.FieldByIndex(selector.field), nil 25 | } 26 | 27 | if selector.isPtrReceiver { 28 | v = v.Addr() 29 | } 30 | return v.Method(selector.method), nil 31 | } 32 | 33 | -------------------------------------------------------------------------------- /evalselectorexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | type SelInterface interface { 10 | E() int 11 | F() int 12 | } 13 | 14 | type SelInt int 15 | type SelNested struct { 16 | D int 17 | } 18 | type SelStruct struct { 19 | A int 20 | B struct { C int } 21 | SelNested 22 | } 23 | 24 | func (SelStruct) E() int { 25 | return 1 26 | } 27 | 28 | func (*SelStruct) F() int { 29 | return 2 30 | } 31 | 32 | func (SelInt) E() int { 33 | return 1 34 | } 35 | 36 | func (*SelInt) F() int { 37 | return 2 38 | } 39 | 40 | func TestSelectStructField(t *testing.T) { 41 | env := MakeSimpleEnv() 42 | s := SelStruct{} 43 | env.Vars["s"] = reflect.ValueOf(&s) 44 | expectResult(t, "s.A", env, s.A) 45 | } 46 | 47 | func TestSelectStructNestedField(t *testing.T) { 48 | env := MakeSimpleEnv() 49 | s := SelStruct{} 50 | env.Vars["s"] = reflect.ValueOf(&s) 51 | expectResult(t, "s.B", env, s.B) 52 | expectResult(t, "s.B.C", env, s.B.C) 53 | } 54 | 55 | func TestSelectStructAnonField(t *testing.T) { 56 | env := MakeSimpleEnv() 57 | s := SelStruct{} 58 | env.Vars["s"] = reflect.ValueOf(&s) 59 | expectResult(t, "s.D", env, s.D) 60 | expectResult(t, "s.SelNested.D", env, s.SelNested.D) 61 | } 62 | 63 | func TestSelectStructMethod(t *testing.T) { 64 | env := MakeSimpleEnv() 65 | s := SelStruct{} 66 | env.Vars["s"] = reflect.ValueOf(&s) 67 | expectResult(t, "s.E()", env, s.E()) 68 | } 69 | 70 | func TestSelectStructMethodP(t *testing.T) { 71 | env := MakeSimpleEnv() 72 | s := SelStruct{} 73 | env.Vars["s"] = reflect.ValueOf(&s) 74 | expectResult(t, "s.F()", env, s.F()) 75 | } 76 | 77 | func TestSelectIntMethod(t *testing.T) { 78 | env := MakeSimpleEnv() 79 | i := SelInt(0) 80 | env.Vars["i"] = reflect.ValueOf(&i) 81 | expectResult(t, "i.E()", env, i.E()) 82 | } 83 | 84 | func TestSelectIntMethodP(t *testing.T) { 85 | env := MakeSimpleEnv() 86 | i := SelInt(0) 87 | env.Vars["i"] = reflect.ValueOf(&i) 88 | expectResult(t, "i.F()", env, i.F()) 89 | } 90 | 91 | func TestSelectStructPField(t *testing.T) { 92 | env := MakeSimpleEnv() 93 | s := &SelStruct{} 94 | env.Vars["s"] = reflect.ValueOf(&s) 95 | expectResult(t, "s.A", env, s.A) 96 | } 97 | 98 | func TestSelectStructPNestedField(t *testing.T) { 99 | env := MakeSimpleEnv() 100 | s := &SelStruct{} 101 | env.Vars["s"] = reflect.ValueOf(&s) 102 | expectResult(t, "s.B", env, s.B) 103 | expectResult(t, "s.B.C", env, s.B.C) 104 | } 105 | 106 | func TestSelectStructPAnonField(t *testing.T) { 107 | env := MakeSimpleEnv() 108 | s := &SelStruct{} 109 | env.Vars["s"] = reflect.ValueOf(&s) 110 | expectResult(t, "s.D", env, s.D) 111 | expectResult(t, "s.SelNested.D", env, s.SelNested.D) 112 | } 113 | 114 | func TestSelectStructPMethod(t *testing.T) { 115 | env := MakeSimpleEnv() 116 | s := &SelStruct{} 117 | env.Vars["s"] = reflect.ValueOf(&s) 118 | expectResult(t, "s.E()", env, s.E()) 119 | } 120 | 121 | func TestSelectStructPMethodP(t *testing.T) { 122 | env := MakeSimpleEnv() 123 | s := &SelStruct{} 124 | env.Vars["s"] = reflect.ValueOf(&s) 125 | expectResult(t, "s.F()", env, s.F()) 126 | } 127 | 128 | func TestSelectIntPMethod(t *testing.T) { 129 | env := MakeSimpleEnv() 130 | i := new(SelInt) 131 | env.Vars["i"] = reflect.ValueOf(&i) 132 | expectResult(t, "i.E()", env, i.E()) 133 | } 134 | 135 | func TestSelectIntPMethodP(t *testing.T) { 136 | env := MakeSimpleEnv() 137 | i := new(SelInt) 138 | env.Vars["i"] = reflect.ValueOf(&i) 139 | expectResult(t, "i.F()", env, i.F()) 140 | } 141 | 142 | func TestSelectInterfaceMethod(t *testing.T) { 143 | env := MakeSimpleEnv() 144 | var i SelInterface = new(SelInt) 145 | env.Vars["i"] = reflect.ValueOf(&i) 146 | expectResult(t, "i.E()", env, i.E()) 147 | } 148 | 149 | func TestSelectInterfaceMethodP(t *testing.T) { 150 | env := MakeSimpleEnv() 151 | var i SelInterface = new(SelInt) 152 | env.Vars["i"] = reflect.ValueOf(&i) 153 | expectResult(t, "i.F()", env, i.F()) 154 | } 155 | 156 | func TestSelectPackageFunc(t *testing.T) { 157 | env := MakeSimpleEnv() 158 | pkg := MakeSimpleEnv() 159 | env.Pkgs["fmt"] = pkg 160 | pkg.Funcs["Sprintf"] = reflect.ValueOf(fmt.Sprintf) 161 | 162 | expectResult(t, `fmt.Sprintf("abc")`, env, fmt.Sprintf("abc")) 163 | } 164 | 165 | -------------------------------------------------------------------------------- /evalsliceexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // TODO[crc] support slice[::] syntax after go1.2 upgrade 8 | func evalSliceExpr(slice *SliceExpr, env Env) (reflect.Value, error) { 9 | xs, err := EvalExpr(slice.X, env) 10 | if err != nil { 11 | return reflect.Value{}, err 12 | } 13 | x := xs[0] 14 | 15 | var l, h int 16 | if slice.Low != nil { 17 | if l, err = evalInteger(slice.Low, env); err != nil { 18 | return reflect.Value{}, err 19 | } 20 | } 21 | if slice.High != nil { 22 | if h, err = evalInteger(slice.High, env); err != nil { 23 | return reflect.Value{}, err 24 | } 25 | } else { 26 | h = x.Len() 27 | } 28 | 29 | t := slice.KnownType()[0] 30 | switch t.Kind() { 31 | case reflect.Ptr: 32 | // Short hand for array pointers 33 | x = x.Elem() 34 | fallthrough 35 | case reflect.Array, reflect.String: 36 | if l < 0 || h > x.Len() || h < l { 37 | return reflect.Value{}, PanicSliceOutOfBounds{} 38 | } 39 | case reflect.Slice: 40 | if l < 0 || h > x.Cap() || h < l { 41 | return reflect.Value{}, PanicSliceOutOfBounds{} 42 | } 43 | } 44 | return x.Slice(l, h), nil 45 | } 46 | -------------------------------------------------------------------------------- /evalsliceexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | func TestSliceArray(t *testing.T) { 9 | a := [3]int{1, 2, 3} 10 | 11 | env := MakeSimpleEnv() 12 | env.Vars["a"] = reflect.ValueOf(&a) 13 | 14 | expected := a[1:1] 15 | expr := "a[1:1]" 16 | 17 | expectResult(t, expr, env, expected) 18 | 19 | expected = a[0:2] 20 | expr = "a[0:2]" 21 | 22 | expectResult(t, expr, env, expected) 23 | } 24 | 25 | func TestSliceSlice(t *testing.T) { 26 | a := []int{1, 2, 3} 27 | 28 | env := MakeSimpleEnv() 29 | env.Vars["a"] = reflect.ValueOf(&a) 30 | 31 | expected := a[0:2] 32 | expr := "a[0:2]" 33 | 34 | expectResult(t, expr, env, expected) 35 | 36 | expected = a[1:1] 37 | expr = "a[1:1]" 38 | 39 | expectResult(t, expr, env, expected) 40 | 41 | } 42 | 43 | func TestSliceString(t *testing.T) { 44 | a := "abc" 45 | 46 | env := MakeSimpleEnv() 47 | env.Vars["a"] = reflect.ValueOf(&a) 48 | 49 | expected := a[1:1] 50 | expr := "a[1:1]" 51 | 52 | expectResult(t, expr, env, expected) 53 | 54 | expected = a[0:2] 55 | expr = "a[0:2]" 56 | 57 | expectResult(t, expr, env, expected) 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /evalstarexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalStarExpr(starExpr *StarExpr, env Env) (reflect.Value, error) { 8 | if vs, err := EvalExpr(starExpr.X, env); err != nil { 9 | return reflect.Value{}, err 10 | } else { 11 | v := vs[0] 12 | if v.IsNil() { 13 | return reflect.Value{}, PanicInvalidDereference{} 14 | } 15 | return v.Elem(), nil 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /evaltypeassertexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func evalTypeAssertExpr(assert *TypeAssertExpr, env Env) ([]reflect.Value, error) { 8 | x := assert.X 9 | if vs, err := EvalExpr(x, env); err != nil { 10 | return []reflect.Value{}, err 11 | } else { 12 | v := vs[0] 13 | xT := x.KnownType()[0] 14 | aT := assert.KnownType()[0] 15 | if v.IsNil() { 16 | vs := []reflect.Value{hackedNew(aT).Elem(), reflect.ValueOf(false)} 17 | return vs, PanicInterfaceConversion{aT: aT} 18 | } 19 | dynamic := v.Elem() 20 | dT := dynamic.Type() 21 | if aT.Kind() == reflect.Interface { 22 | if !dT.Implements(aT) { 23 | vs := []reflect.Value{hackedNew(aT).Elem(), reflect.ValueOf(false)} 24 | return vs, PanicInterfaceConversion{xT, aT, nil} 25 | } 26 | } else { 27 | if dT != aT { 28 | vs := []reflect.Value{hackedNew(aT).Elem(), reflect.ValueOf(false)} 29 | return vs, PanicInterfaceConversion{xT, aT, dT} 30 | } 31 | } 32 | r := reflect.New(aT).Elem() 33 | r.Set(dynamic) 34 | return []reflect.Value{r, reflect.ValueOf(true)}, nil 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /evaltypeassertexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | type XI interface { 9 | x() 10 | } 11 | 12 | type YI interface { 13 | y() 14 | } 15 | 16 | type ZI interface { 17 | x() 18 | } 19 | 20 | type X int 21 | type Y int 22 | type Z int 23 | 24 | func (X) x() {} 25 | func (Y) y() {} 26 | func (Z) x() {} 27 | 28 | func TestTypeAssertInterfaceToInterface(t *testing.T) { 29 | env := makeTypeAssertEnv() 30 | a := XI(X(0)) 31 | env.Vars["a"] = reflect.ValueOf(&a) 32 | expectResults(t, "a.(ZI)", env, a.(ZI), true) 33 | } 34 | 35 | func TestTypeAssertInterfaceToDynamic(t *testing.T) { 36 | env := makeTypeAssertEnv() 37 | a := XI(X(0)) 38 | env.Vars["a"] = reflect.ValueOf(&a) 39 | expectResults(t, "a.(X)", env, a.(ZI), true) 40 | } 41 | 42 | func TestTypeAssertInterfaceToInterfacePanic(t *testing.T) { 43 | env := makeTypeAssertEnv() 44 | a := XI(X(0)) 45 | env.Vars["a"] = reflect.ValueOf(&a) 46 | expectPanic(t, "a.(YI)", env, "interface conversion: eval.XI is not eval.YI: missing method y") 47 | } 48 | 49 | func TestTypeAssertInterfaceToDynamicPanic(t *testing.T) { 50 | env := makeTypeAssertEnv() 51 | a := XI(X(0)) 52 | env.Vars["a"] = reflect.ValueOf(&a) 53 | expectPanic(t, "a.(Z)", env, "interface conversion: eval.XI is eval.X, not eval.Z") 54 | } 55 | 56 | func TestTypeAssertNilToInterfacePanic(t *testing.T) { 57 | env := makeTypeAssertEnv() 58 | a := XI(nil) 59 | env.Vars["a"] = reflect.ValueOf(&a) 60 | expectPanic(t, "a.(XI)", env, "interface conversion: nil is not eval.XI") 61 | } 62 | 63 | func TestTypeAssertNilToDynamicPanic(t *testing.T) { 64 | env := makeTypeAssertEnv() 65 | a := XI(nil) 66 | env.Vars["a"] = reflect.ValueOf(&a) 67 | expectPanic(t, "a.(X)", env, "interface conversion: nil is not eval.X") 68 | } 69 | 70 | func makeTypeAssertEnv() *SimpleEnv { 71 | // This little pointer dance is required to prevent 72 | // reflect.TypeOf(typ interface{}) from interpreting 73 | // typ as value which has been promoted to interface{} 74 | // 75 | // Basically, there is no way for reflect.TypeOf to 76 | // distinguish between 77 | // reflect.TypeOf(X(0)) - and - 78 | // reflect.TypeOf(XI(X(0))) 79 | // as XI is demoted to interface{} without any change to 80 | // the interal interface data. 81 | 82 | env := MakeSimpleEnv() 83 | var xi *XI = new(XI) 84 | var yi *YI = new(YI) 85 | var zi *ZI = new(ZI) 86 | *xi = XI(X(0)) 87 | *yi = YI(Y(0)) 88 | *zi = ZI(Z(0)) 89 | env.Types["XI"] = reflect.TypeOf(xi).Elem() 90 | env.Types["YI"] = reflect.TypeOf(yi).Elem() 91 | env.Types["ZI"] = reflect.TypeOf(zi).Elem() 92 | env.Types["X"] = reflect.TypeOf(X(0)) 93 | env.Types["Y"] = reflect.TypeOf(Y(0)) 94 | env.Types["Z"] = reflect.TypeOf(Z(0)) 95 | 96 | return env 97 | } 98 | -------------------------------------------------------------------------------- /evalunaryexpr.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | 6 | "go/token" 7 | ) 8 | 9 | func evalUnaryExpr(unary *UnaryExpr, env Env) ([]reflect.Value, error) { 10 | if unary.IsConst() { 11 | return []reflect.Value{unary.Const()}, nil 12 | } 13 | 14 | xx, err := EvalExpr(unary.X, env) 15 | if err != nil { 16 | return []reflect.Value{}, err 17 | } 18 | x := xx[0] 19 | 20 | // handle & and <- first 21 | if unary.Op == token.AND { 22 | return []reflect.Value{x.Addr()}, nil 23 | } else if unary.Op == token.ARROW { 24 | // TODO[crc] use x.Recv in contexts where blocking is acceptable 25 | v, ok := x.TryRecv() 26 | if !v.IsValid() { 27 | v = reflect.New(x.Type().Elem()).Elem() 28 | } 29 | // TODO[crc] also return ok once assignability context is implemented 30 | return []reflect.Value{v, reflect.ValueOf(ok)}, nil 31 | } 32 | 33 | var r reflect.Value 34 | switch x.Kind() { 35 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 36 | r, err = evalUnaryIntExpr(x, unary.Op) 37 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 38 | r, err = evalUnaryUintExpr(x, unary.Op) 39 | case reflect.Float32, reflect.Float64: 40 | r, err = evalUnaryFloatExpr(x, unary.Op) 41 | case reflect.Complex64, reflect.Complex128: 42 | r, err = evalUnaryComplexExpr(x, unary.Op) 43 | case reflect.Bool: 44 | r, err = evalUnaryBoolExpr(x, unary.Op) 45 | default: 46 | panic("eval: impossible unary op " + unary.Op.String()) 47 | } 48 | return []reflect.Value{r}, err 49 | } 50 | 51 | func evalUnaryIntExpr(x reflect.Value, op token.Token) (reflect.Value, error) { 52 | var r int64 53 | var err error 54 | 55 | xx := x.Int() 56 | switch op { 57 | case token.ADD: r = +xx 58 | case token.SUB: r = -xx 59 | case token.XOR: r = ^xx 60 | default: 61 | panic("eval: impossible unary op " + op.String()) 62 | } 63 | return reflect.ValueOf(r).Convert(x.Type()), err 64 | } 65 | 66 | func evalUnaryUintExpr(x reflect.Value, op token.Token) (reflect.Value, error) { 67 | var err error 68 | var r uint64 69 | 70 | xx := x.Uint() 71 | switch op { 72 | case token.ADD: r = +xx 73 | case token.SUB: r = -xx 74 | case token.XOR: r = ^xx 75 | default: 76 | panic("eval: impossible unary op " + op.String()) 77 | } 78 | return reflect.ValueOf(r).Convert(x.Type()), err 79 | } 80 | 81 | func evalUnaryFloatExpr(x reflect.Value, op token.Token) (reflect.Value, error) { 82 | var err error 83 | var r float64 84 | 85 | xx := x.Float() 86 | switch op { 87 | case token.ADD: r = + xx 88 | case token.SUB: r = - xx 89 | default: 90 | panic("eval: impossible unary op " + op.String()) 91 | } 92 | return reflect.ValueOf(r).Convert(x.Type()), err 93 | } 94 | 95 | func evalUnaryComplexExpr(x reflect.Value, op token.Token) (reflect.Value, error) { 96 | var err error 97 | var r complex128 98 | 99 | xx := x.Complex() 100 | switch op { 101 | case token.ADD: r = +xx 102 | case token.SUB: r = -xx 103 | default: 104 | panic("eval: impossible unary op " + op.String()) 105 | } 106 | return reflect.ValueOf(r).Convert(x.Type()), err 107 | } 108 | 109 | func evalUnaryBoolExpr(x reflect.Value, op token.Token) (reflect.Value, error) { 110 | var err error 111 | var r bool 112 | 113 | xx := x.Bool() 114 | switch op { 115 | case token.NOT: r = !xx 116 | default: 117 | panic("eval: impossible unary op " + op.String()) 118 | } 119 | return reflect.ValueOf(r).Convert(x.Type()), err 120 | } 121 | -------------------------------------------------------------------------------- /evalunaryexpr_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIntUnaryOps(t *testing.T) { 8 | env := MakeSimpleEnv() 9 | 10 | // FIXME: find out what's wrong 11 | if t == nil { 12 | expectResult(t, "+1", env, +1) 13 | expectResult(t, "-1", env, -1) 14 | } 15 | } 16 | 17 | func TestUintUnaryOps(t *testing.T) { 18 | env := MakeSimpleEnv() 19 | // FIXME: find out what's wrong 20 | if t == nil { 21 | expectResult(t, "uint64(+12)", env, uint64(+12)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example_eval_test.go: -------------------------------------------------------------------------------- 1 | package eval_test 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "go/parser" 7 | "github.com/0xfaded/eval" 8 | ) 9 | 10 | const constant1 = "A constant" 11 | 12 | //makeEnv creates an environment to use in eval 13 | func makeEnv() *eval.SimpleEnv { 14 | env := eval.MakeSimpleEnv() 15 | env.Consts["constant1"] = reflect.ValueOf(constant1) 16 | var1 := 1 17 | env.Vars["var1"] = reflect.ValueOf(&var1) 18 | env.Funcs["add"] = reflect.ValueOf(func(a, b int) int { return a + b }) 19 | fmtPkg := eval.MakeSimpleEnv() 20 | fmtPkg.Funcs["Sprintf"] = reflect.ValueOf(fmt.Sprintf) 21 | env.Pkgs["fmt"] = fmtPkg 22 | return env 23 | 24 | } 25 | 26 | func ExampleSimple() { 27 | result, panik, compileErrs := eval.Eval(`append([]int{1,2}[:1], map[int]int{1:2}[1])[1]`) 28 | _ = panik 29 | _ = compileErrs 30 | fmt.Printf("%+v\n", result[0].Interface()) 31 | } 32 | 33 | func ExampleWithEnvironment() { 34 | env := makeEnv() // Create evaluation environment 35 | result, panik, compileErrs := eval.EvalEnv(`fmt.Sprintf("%s %d", constant1, add(var1, 1) + 1)`, env) 36 | _ = panik 37 | _ = compileErrs 38 | fmt.Printf("%+v\n", result[0].Interface()) 39 | } 40 | 41 | // ExpectResult check the evaluation of a string with an expected result. 42 | // More importantly though, does the steps to evaluate a string: 43 | // 1. Parse expression using parser.ParseExpr (go/parser) 44 | // 2. Type check expression using evalCheckExpr (0xfaded/eval) 45 | // 3. run eval.EvalExpr (0xfaded/eval) 46 | func ExampleFullApi() { 47 | expr := `fmt.Sprintf("%s %d", constant1, add(var1, 1) + 1)` 48 | env := makeEnv() // Create evaluation environment 49 | if e, err := parser.ParseExpr(expr); err != nil { 50 | fmt.Printf("Failed to parse expression '%s' (%v)\n", expr, err) 51 | } else if cexpr, errs := eval.CheckExpr(e, env); len(errs) != 0 { 52 | fmt.Printf("Error checking expression '%s' (%v)\n", expr, errs) 53 | } else if results, err := eval.EvalExpr(cexpr, env); err != nil { 54 | fmt.Printf("Panic evaluating expression '%s' (%v)\n", expr, err) 55 | } else { 56 | fmt.Printf("Expression '%s' yielded '%+v'\n", expr, results[0].Interface()) 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /fakecheck.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | ) 7 | 8 | // convert an ast.Expr to an Expr without actually checking it. This 9 | // is useful for avoiding special cases in error messages. 10 | func fakeCheckExpr(expr ast.Expr, env Env) Expr { 11 | if expr == nil { 12 | return nil 13 | } 14 | switch expr := expr.(type) { 15 | case *ast.BadExpr: 16 | return &BadExpr{BadExpr: expr} 17 | case *ast.Ident: 18 | return &Ident{Ident: expr} 19 | case *ast.Ellipsis: 20 | return &Ellipsis{Ellipsis: expr} 21 | case *ast.BasicLit: 22 | return &BasicLit{BasicLit: expr} 23 | case *ast.FuncLit: 24 | return &FuncLit{FuncLit: expr} 25 | case *ast.CompositeLit: 26 | c := &CompositeLit{CompositeLit: expr} 27 | if expr.Elts != nil { 28 | c.Elts = make([]Expr, len(expr.Elts)) 29 | for i := range c.Elts { 30 | c.Elts[i] = fakeCheckExpr(expr.Elts[i], env) 31 | } 32 | } 33 | return c 34 | case *ast.ParenExpr: 35 | p := &ParenExpr{ParenExpr: expr} 36 | p.X = fakeCheckExpr(expr.X, env) 37 | return p 38 | case *ast.SelectorExpr: 39 | s := &SelectorExpr{SelectorExpr: expr} 40 | s.X = fakeCheckExpr(expr.X, env) 41 | s.Sel = &Ident{Ident: expr.Sel} 42 | return s 43 | case *ast.IndexExpr: 44 | i := &IndexExpr{IndexExpr: expr} 45 | i.X = fakeCheckExpr(expr.X, env) 46 | i.Index = fakeCheckExpr(expr.Index, env) 47 | return i 48 | case *ast.SliceExpr: 49 | s := &SliceExpr{SliceExpr: expr} 50 | s.X = fakeCheckExpr(expr.X, env) 51 | if s.Low != nil { 52 | s.Low = fakeCheckExpr(expr.Low, env) 53 | } 54 | if s.High != nil { 55 | s.High = fakeCheckExpr(expr.High, env) 56 | } 57 | // TODO[crc] go 1.2 introduces the [::] notation. Add after upgrade 58 | //if s.Max != nil { 59 | //s.Max = fakeCheckExpr(s.Max, env) 60 | //} 61 | return s 62 | case *ast.TypeAssertExpr: 63 | a := &TypeAssertExpr{TypeAssertExpr: expr} 64 | a.X = fakeCheckExpr(a.X, env) 65 | return a 66 | case *ast.CallExpr: 67 | c := &CallExpr{CallExpr: expr} 68 | if ident, ok := expr.Fun.(*ast.Ident); ok { 69 | if _, ok := builtinFuncs[ident.Name]; ok { 70 | c.isBuiltin = true 71 | } 72 | } 73 | if !c.isBuiltin { 74 | if _, t, isType, _ := checkType(expr.Fun, env); isType { 75 | c.isTypeConversion = true 76 | c.knownType = knownType{t} 77 | } 78 | } 79 | c.Fun = fakeCheckExpr(expr.Fun, env) 80 | if expr.Args != nil { 81 | c.Args = make([]Expr, len(expr.Args)) 82 | for i := range c.Args { 83 | c.Args[i] = fakeCheckExpr(expr.Args[i], env) 84 | } 85 | } 86 | return c 87 | case *ast.StarExpr: 88 | s := &StarExpr{StarExpr: expr} 89 | s.X = fakeCheckExpr(expr.X, env) 90 | return s 91 | case *ast.UnaryExpr: 92 | u := &UnaryExpr{UnaryExpr: expr} 93 | u.X = fakeCheckExpr(expr.X, env) 94 | return u 95 | case *ast.BinaryExpr: 96 | b := &BinaryExpr{BinaryExpr: expr} 97 | b.X = fakeCheckExpr(expr.X, env) 98 | b.Y = fakeCheckExpr(expr.Y, env) 99 | return b 100 | case *ast.KeyValueExpr: 101 | kv := &KeyValueExpr{KeyValueExpr: expr} 102 | kv.Key = fakeCheckExpr(expr.Key, env) 103 | kv.Value = fakeCheckExpr(expr.Value, env) 104 | return kv 105 | 106 | // Types 107 | case *ast.ArrayType: 108 | arrayT := &ArrayType{ArrayType: expr} 109 | arrayT.Len = fakeCheckExpr(expr.Len, env) 110 | arrayT.Elt = fakeCheckExpr(expr.Elt, env) 111 | return arrayT 112 | case *ast.StructType: 113 | structT := &StructType{StructType: expr} 114 | return structT 115 | case *ast.FuncType: 116 | funcT := &FuncType{FuncType: expr} 117 | return funcT 118 | case *ast.InterfaceType: 119 | interfaceT := &InterfaceType{InterfaceType: expr} 120 | return interfaceT 121 | case *ast.MapType: 122 | mapT := &MapType{MapType: expr} 123 | mapT.Key = fakeCheckExpr(expr.Key, env) 124 | mapT.Value = fakeCheckExpr(expr.Value, env) 125 | return mapT 126 | case *ast.ChanType: 127 | chanT := &ChanType{ChanType: expr} 128 | chanT.Value = fakeCheckExpr(expr.Value, env) 129 | return chanT 130 | 131 | default: 132 | panic(fmt.Sprintf("fakeCheckExpr(%T)", expr)) 133 | } 134 | } 135 | 136 | // Remove the const value from an Expr. If the Expr is not const, do nothing. Otherwise, 137 | // return a non const clone. 138 | func unconstNode(expr Expr) Expr { 139 | // Special case for non-const builtin calls which must have their args unconsted 140 | if e, ok := expr.(*CallExpr); ok && (e.IsConst() || e.isBuiltin) { 141 | u := new(CallExpr) 142 | *u = *e 143 | u.constValue = constValue{} 144 | if u.isBuiltin { 145 | u.CallExpr = new(ast.CallExpr) 146 | *u.CallExpr = *e.CallExpr 147 | u.Args = make([]Expr, len(e.Args)) 148 | for i := range e.Args { 149 | if e.Args[i].IsConst(); ok { 150 | u.Args[i] = unconstNode(e.Args[i]) 151 | } else { 152 | u.Args[i] = e.Args[i] 153 | } 154 | } 155 | } 156 | return u 157 | } 158 | 159 | if !expr.IsConst() { 160 | return expr 161 | } 162 | switch e := expr.(type) { 163 | case *Ident: 164 | u := new(Ident) 165 | *u = *e 166 | u.constValue = constValue{} 167 | return u 168 | case *BasicLit: 169 | u := new(BasicLit) 170 | *u = *e 171 | u.constValue = constValue{} 172 | return u 173 | case *SelectorExpr: 174 | u := new(SelectorExpr) 175 | *u = *e 176 | u.constValue = constValue{} 177 | return u 178 | case *IndexExpr: 179 | u := new(IndexExpr) 180 | *u = *e 181 | u.constValue = constValue{} 182 | return u 183 | case *UnaryExpr: 184 | u := new(UnaryExpr) 185 | *u = *e 186 | u.constValue = constValue{} 187 | return u 188 | case *BinaryExpr: 189 | u := new(BinaryExpr) 190 | *u = *e 191 | u.constValue = constValue{} 192 | return u 193 | default: 194 | panic("eval: impossible. non-const node IsConst() returned true") 195 | } 196 | } 197 | 198 | // shorthand for unconstNode 199 | func uc(expr Expr) Expr { 200 | return unconstNode(expr) 201 | } 202 | -------------------------------------------------------------------------------- /helper_for_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | // Utilities for other tests live here 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | "reflect" 9 | 10 | "go/ast" 11 | "go/parser" 12 | ) 13 | 14 | func getResults(t *testing.T, expr string, env Env) []reflect.Value { 15 | if e, err := parser.ParseExpr(expr); err != nil { 16 | t.Fatalf("Failed to parse expression '%s' (%v)", expr, err) 17 | } else if aexpr, errs := CheckExpr(e, env); errs != nil { 18 | t.Fatalf("Failed to check expression '%s' (%v)", expr, errs) 19 | } else if results, err := EvalExpr(aexpr, env); err != nil { 20 | t.Fatalf("Error evaluating expression '%s' (%v)", expr, err) 21 | } else { 22 | return results 23 | } 24 | return nil 25 | } 26 | 27 | func expectResult(t *testing.T, expr string, env Env, expected interface{}) { 28 | expectResults(t, expr, env, expected) 29 | } 30 | 31 | func expectResults(t *testing.T, expr string, env Env, expected ...interface{}) { 32 | results := getResults(t, expr, env) 33 | if nil == results { 34 | if expected != nil { 35 | t.Fatalf("Expression '%s' is nil but expected '%+v'", expr, expected) 36 | } 37 | return 38 | } else if expected == nil { 39 | t.Fatalf("Expression '%s'expected is '%+v', expected to be nil", expr, results) 40 | } 41 | resultsi := make([]interface{}, len(results)) 42 | for i, result := range results { 43 | resultsi[i] = result.Interface() 44 | } 45 | if !reflect.DeepEqual(resultsi, expected) { 46 | t.Fatalf("Expression '%s' yielded '%+v', expected '%+v'", expr, resultsi, expected) 47 | } 48 | } 49 | 50 | func expectPanic(t *testing.T, expr string, env Env, panicString string) { 51 | if e, err := parser.ParseExpr(expr); err != nil { 52 | t.Fatalf("Failed to parse expression '%s' (%v)", expr, err) 53 | } else if aexpr, errs := CheckExpr(e, env); errs != nil { 54 | t.Fatalf("Failed to check expression '%s' (%v)", expr, errs) 55 | } else if _, err := EvalExpr(aexpr, env); err == nil { 56 | t.Fatalf("Expected expression '%s' to panic", expr) 57 | } else if err.Error() != panicString { 58 | t.Fatalf("Panic `%s` != Expected `%s`", err.Error(), panicString) 59 | } 60 | } 61 | 62 | func expectConst(t *testing.T, expr string, env Env, expected interface{}, expectedType reflect.Type) { 63 | if e, err := parser.ParseExpr(expr); err != nil { 64 | t.Fatalf("Failed to parse expression '%s' (%v)", expr, err) 65 | } else if aexpr, errs := CheckExpr(e, env); errs != nil { 66 | t.Fatalf("Failed to check expression '%s' (%v)", expr, errs) 67 | } else if !aexpr.IsConst() { 68 | t.Fatalf("Expression '%s' did not yield a const node(%+v)", expr, aexpr) 69 | } else if expectedNumber, ok := expected.(*ConstNumber); ok { 70 | if actual, ok2 := aexpr.Const().Interface().(*ConstNumber); !ok2 { 71 | t.Fatalf("Expression '%s' yielded '%v', expected '%v'", expr, aexpr.Const(), expected) 72 | } else if !actual.Value.Equals(&expectedNumber.Value) { 73 | t.Fatalf("Expression '%s' yielded '%v', expected '%v'", expr, actual, expected) 74 | } else if len(aexpr.KnownType()) == 0 { 75 | t.Fatalf("Expression '%s' expected to have type '%v'", expr, expectedType) 76 | } else if actual := aexpr.KnownType()[0]; !typesEqual(expectedType, actual) { 77 | t.Fatalf("Expression '%s' has type '%v', expected '%v'", expr, actual, expectedType) 78 | } 79 | } else { 80 | if actual := aexpr.Const().Interface(); !reflect.DeepEqual(actual, expected) { 81 | t.Fatalf("Expression '%s' yielded '%+v', expected '%+v'", expr, actual, expected) 82 | } else if len(aexpr.KnownType()) == 0 { 83 | t.Fatalf("Expression '%s' expected to have type '%v'", expr, expectedType) 84 | } else if actual := aexpr.KnownType()[0]; !typesEqual(expectedType, actual) { 85 | t.Fatalf("Expression '%s' has type '%v', expected '%v'", expr, aexpr.KnownType()[0], expectedType) 86 | } 87 | } 88 | } 89 | 90 | func expectType(t *testing.T, expr string, env Env, expectedType reflect.Type) { 91 | if e, err := parser.ParseExpr(expr); err != nil { 92 | t.Fatalf("Failed to parse expression '%s' (%v)", expr, err) 93 | } else if aexpr, errs := CheckExpr(e, env); errs != nil { 94 | t.Fatalf("Failed to check expression '%s' (%v)", expr, errs) 95 | } else if aexpr.IsConst() { 96 | t.Fatalf("Expression '%s' yielded a const node(%+v)", expr, aexpr) 97 | } else if len(aexpr.KnownType()) != 1 { 98 | t.Fatalf("Expression '%s' expected to have single type '%v'", expr, expectedType) 99 | } else if actual := aexpr.KnownType()[0]; !typesEqual(expectedType, actual) { 100 | t.Fatalf("Expression '%s' has type '%v', expected '%v'", expr, aexpr.KnownType()[0], expectedType) 101 | } 102 | } 103 | func expectCheckError(t *testing.T, expr string, env Env, errorString ...string) { 104 | var errs []error 105 | if s, err := ParseStmt(expr); err != nil { 106 | t.Fatalf("Failed to parse expression '%s' (%v)", expr, err) 107 | } else if e, ok := s.(*ast.ExprStmt); ok { 108 | _, errs = CheckExpr(e.X, env) 109 | } else { 110 | _, errs = checkStmt(s, env, checkCtx{}) 111 | } 112 | if errs != nil { 113 | var i int 114 | out := "\n" 115 | ok := true 116 | for i = 0; i < len(errorString); i += 1 { 117 | if i >= len(errs) { 118 | out += fmt.Sprintf("%d. Expected `%v` missing\n", i, errorString[i]) 119 | ok = false 120 | } else if errorString[i] == errs[i].Error() { 121 | out += fmt.Sprintf("%d. Expected `%v` == `%v`\n", i, errorString[i], errs[i]) 122 | } else { 123 | out += fmt.Sprintf("%d. Expected `%v` != `%v`\n", i, errorString[i], errs[i]) 124 | ok = false 125 | } 126 | } 127 | for ; i < len(errs); i += 1 { 128 | out += fmt.Sprintf("%d. Unexpected `%v`\n", i, errs[i]) 129 | ok = false 130 | } 131 | if !ok { 132 | t.Fatalf("%sWrong check errors for expression '%s'", out, expr) 133 | } 134 | } else { 135 | for i, s := range errorString { 136 | t.Logf("%d. Expected `%v` missing\n", i, s) 137 | } 138 | t.Fatalf("Missing check errors for expression '%s'", expr ) 139 | } 140 | } 141 | 142 | func expectInterp(t *testing.T, stmt string, env Env) { 143 | if s, err := ParseStmt(stmt); err != nil { 144 | t.Fatalf("Failed to parse stmt '%s' (%v)", stmt, err) 145 | } else if c, errs := checkStmt(s, env, checkCtx{}); errs != nil { 146 | t.Logf("Failed to check stmt '%s'", stmt) 147 | for _, err := range errs { 148 | t.Logf("\t%v", err) 149 | } 150 | t.FailNow() 151 | } else if _, panik := InterpStmt(c, env); panik != nil { 152 | t.Fatalf("Statement '%s' panicked with %v", stmt, panik) 153 | } 154 | } 155 | 156 | func typesEqual(expected, actual reflect.Type) bool { 157 | var unwrapped reflect.Type 158 | switch t := actual.(type) { 159 | case Rune: 160 | unwrapped = t.Type 161 | default: 162 | unwrapped = actual 163 | } 164 | return reflect.DeepEqual(expected, unwrapped) 165 | } 166 | 167 | -------------------------------------------------------------------------------- /inspect.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sort" 7 | "strconv" 8 | "unicode" 9 | ) 10 | 11 | // Inspect prints a reflect.Value the way you would enter it. 12 | // Some like this should really be part of the reflect package. 13 | func Inspect(val reflect.Value) string { 14 | 15 | if val.CanInterface() { 16 | if s, ok := val.Interface().(fmt.Stringer); ok { 17 | return s.String() 18 | } 19 | } 20 | switch val.Kind() { 21 | case reflect.String: 22 | return strconv.QuoteToASCII(val.String()) 23 | case reflect.Slice, reflect.Array: 24 | if val.Len() == 0 { 25 | return "[]" 26 | } 27 | sep := "{" 28 | str := "" 29 | for i:=0; i < val.Len(); i++ { 30 | str += sep 31 | sep = ", " 32 | str += InspectShort(val.Index(i)) 33 | } 34 | str += "}" 35 | return str 36 | 37 | case reflect.Struct: 38 | if val.Type() == untypedNilType { 39 | return "nil" 40 | } 41 | n := val.NumField() 42 | str := val.Type().String() 43 | if n == 0 { 44 | return str + "{}" 45 | } 46 | sep := "{\n\t" 47 | unexported := false 48 | for i := 0; i < n; i++ { 49 | name := val.Type().Field(i).Name 50 | if unicode.IsLower([]rune(name)[0]) { 51 | unexported = true 52 | continue 53 | } 54 | field := val.Field(i) 55 | str += fmt.Sprintf("%s%s: %s", sep, name, InspectShort(field)) 56 | sep = ",\n\t" 57 | } 58 | if unexported { 59 | str += fmt.Sprintf("\n\t// unexported fields") 60 | } 61 | str += "\n}" 62 | return str 63 | 64 | case reflect.Map: 65 | str := val.Type().String() 66 | if val.Len() == 0 { 67 | return str + "{}" 68 | } 69 | sep := " {\n\t" 70 | keys := sortMapKeys(val.MapKeys()) 71 | for _, k := range keys { 72 | v := val.MapIndex(k) 73 | str += fmt.Sprintf("%s%s: %s", sep, InspectShort(k), InspectShort(v)) 74 | sep = ",\n\t" 75 | } 76 | str += "\n}" 77 | return str 78 | 79 | case reflect.Ptr: 80 | // Internal const numbers 81 | i := val.Interface() 82 | if cn, ok := i.(*ConstNumber); ok { 83 | return fmt.Sprint(cn) 84 | } else if val.IsNil() { 85 | return "nil" 86 | } else { 87 | return "&" + InspectShort(reflect.Indirect(val)) 88 | } 89 | case reflect.Interface: 90 | if val.IsNil() { 91 | return "nil" 92 | } else { 93 | return fmt.Sprintf("%s.(%v)", InspectShort(val.Elem()), val.Type()) 94 | } 95 | default: 96 | // FIXME: add more Kinds as folks are annoyed with the output of 97 | // the below: 98 | if val.CanInterface() { 99 | return fmt.Sprintf("%v", val.Interface()) 100 | } else { 101 | return fmt.Sprintf("<%v>", val.Type()) 102 | } 103 | } 104 | } 105 | 106 | // Returns type{...} for composite lits 107 | func InspectShort(val reflect.Value) string { 108 | switch val.Kind() { 109 | case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map: 110 | return fmt.Sprintf("%v{...}", val.Type()) 111 | default: 112 | return Inspect(val) 113 | } 114 | } 115 | 116 | type mapKeys []reflect.Value 117 | func sortMapKeys(keys []reflect.Value) []reflect.Value{ 118 | ks := mapKeys(keys) 119 | sort.Sort(ks) 120 | return ks 121 | } 122 | 123 | func (keys mapKeys) Len() int { 124 | return len(keys) 125 | } 126 | 127 | func (keys mapKeys) Swap(i, j int) { 128 | keys[i], keys[j] = keys[j], keys[i] 129 | } 130 | 131 | func (keys mapKeys) Less(i, j int) bool { 132 | x, y := keys[i], keys[j] 133 | switch x.Type().Kind() { 134 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 135 | return x.Int() < y.Int() 136 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 137 | return x.Uint() < y.Uint() 138 | case reflect.Float32, reflect.Float64: 139 | return x.Float() < y.Float() 140 | case reflect.Complex64, reflect.Complex128: 141 | return real(x.Complex()) < real(y.Complex()) 142 | case reflect.String: 143 | return x.String() < y.String() 144 | case reflect.Bool: 145 | return x.Bool() 146 | default: 147 | return i < j 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /interpstmt.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "go/token" 7 | ) 8 | 9 | type State struct { 10 | Last Stmt 11 | Env Env 12 | } 13 | 14 | func InterpStmt(stmt Stmt, env Env) (last *State, err error) { 15 | switch s := stmt.(type) { 16 | case nil: 17 | case *AssignStmt: 18 | if len(s.Rhs) == 1 { 19 | rs, err := evalTypedExpr(s.Rhs[0], s.types, env) 20 | if err != nil { 21 | if _, ok := err.(PanicInterfaceConversion); !ok || len(s.types) != 2 { 22 | return nil, err 23 | } 24 | } 25 | for i, lhs := range s.Lhs { 26 | if name, ok := s.newNames[i]; !ok { 27 | assign(lhs, rs[i], env) 28 | } else if name != "_" { 29 | v := hackedNew(s.types[i]) 30 | v.Elem().Set(rs[i]) 31 | env.AddVar(name, v) 32 | } 33 | } 34 | } else { 35 | for i, lhs := range s.Lhs { 36 | r, err := evalTypedExpr(s.Rhs[i], s.types[i:i+1], env) 37 | if err != nil { 38 | return nil, err 39 | } 40 | if name, ok := s.newNames[i]; !ok { 41 | assign(lhs, r[0], env) 42 | } else if name != "_" { 43 | v := hackedNew(s.types[i]) 44 | v.Elem().Set(r[0]) 45 | env.AddVar(name, v) 46 | } 47 | } 48 | } 49 | case *BranchStmt: 50 | return &State{s, env}, nil 51 | case *BlockStmt: 52 | return interpBlock(s.List, env) 53 | case *CaseClause: 54 | return interpBlock(s.Body, env) 55 | case *EmptyStmt: 56 | return nil, nil 57 | case *ExprStmt: 58 | _, err := EvalExpr(s.X, env) 59 | return nil, err 60 | case *IfStmt: 61 | env = env.PushScope() 62 | if _ , err = InterpStmt(s.Init, env); err != nil { 63 | return nil, err 64 | } else if rs, err := EvalExpr(s.Cond, env); err != nil { 65 | return nil, err 66 | } else if rs[0].Bool() { 67 | return InterpStmt(s.Body, env) 68 | } else { 69 | return InterpStmt(s.Else, env) 70 | } 71 | case *ForStmt: 72 | env = env.PushScope() 73 | if _, err = InterpStmt(s.Init, env); err != nil { 74 | return nil, err 75 | } 76 | for { 77 | if s.Cond != nil { 78 | if rs, err := EvalExpr(s.Cond, env); err != nil { 79 | return nil, err 80 | } else if !rs[0].Bool() { 81 | break 82 | } 83 | } 84 | if last, err = InterpStmt(s.Body, env); err != nil { 85 | return last, err 86 | } 87 | if last != nil { 88 | if branch, ok := last.Last.(*BranchStmt); ok { 89 | // Are we the target of this branch? 90 | if branch.Label == nil || branch.Label.Name == s.label { 91 | last = nil 92 | if branch.Tok == token.CONTINUE { 93 | goto cont 94 | } 95 | } 96 | } 97 | return last, nil 98 | } 99 | cont: 100 | if _, err = InterpStmt(s.Post, env); err != nil { 101 | return nil, err 102 | } 103 | } 104 | case *LabeledStmt: 105 | return InterpStmt(s.Stmt, env) 106 | case *ReturnStmt: 107 | return &State{s, env}, nil 108 | case *SwitchStmt: 109 | env = env.PushScope() 110 | t := knownType{s.tagT} 111 | if _, err := InterpStmt(s.Init, env); err != nil { 112 | return nil, err 113 | } 114 | tag, err := evalTypedExpr(s.Tag, t, env) 115 | if err != nil { 116 | return nil, err 117 | } 118 | env = env.PushScope() 119 | for _, stmt := range s.Body.List { 120 | clause := stmt.(*CaseClause) 121 | for _, expr := range clause.List { 122 | if sw, err := evalTypedExpr(expr, t, env); err != nil { 123 | return nil, err 124 | } else if eq, err := equal(tag[0], sw[0]); err != nil { 125 | return nil, err 126 | } else if eq { 127 | return InterpStmt(clause, env) 128 | } 129 | } 130 | } 131 | return InterpStmt(s.def, env) 132 | 133 | case *TypeSwitchStmt: 134 | env = env.PushScope() 135 | if _, err = InterpStmt(s.Init, env); err != nil { 136 | return nil, err 137 | } 138 | 139 | x, err := EvalExpr(s.Tag(), env) 140 | if err != nil { 141 | return nil, err 142 | } 143 | // interface.elem() 144 | dynamicX := x[0].Elem() 145 | dynamicT := dynamicX.Type() 146 | 147 | env = env.PushScope() 148 | if name := s.Name(); name != "" { 149 | // dynamicX may not be addressable 150 | x := reflect.New(dynamicT) 151 | x.Elem().Set(dynamicX) 152 | env.AddVar(name, x) 153 | } 154 | 155 | for _, stmt := range s.Body.List { 156 | clause := stmt.(*CaseClause) 157 | for _, expr := range clause.List { 158 | t := expr.KnownType()[0] 159 | if t.Kind() == reflect.Interface { 160 | if dynamicT.Implements(t) { 161 | return InterpStmt(clause, env) 162 | } 163 | } else if t == dynamicT { 164 | return InterpStmt(clause, env) 165 | } 166 | } 167 | } 168 | return InterpStmt(s.def, env) 169 | 170 | default: 171 | panic(dytc(fmt.Sprintf("Unsupported statement %T", s))) 172 | } 173 | return nil, nil 174 | } 175 | 176 | func assign(lhs Expr, rhs reflect.Value, env Env) error { 177 | lhs = skipSuperfluousParens(lhs) 178 | // Always evaluate even if we are doing a map index assign. There are some nasty 179 | // corner cases with map index comparibility that is best left not reimplemented. 180 | if l, err := evalTypedExpr(lhs, lhs.KnownType(), env); err != nil { 181 | return err 182 | } else if index, ok := lhs.(*IndexExpr); ok && index.X.KnownType()[0].Kind() == reflect.Map { 183 | mT := index.X.KnownType()[0] 184 | // known to succeed from above 185 | m, _ := evalTypedExpr(index.X, knownType{mT}, env) 186 | k, _ := evalTypedExpr(index.Index, knownType{mT.Elem()}, env) 187 | m[0].SetMapIndex(k[0], rhs) 188 | } else { 189 | l[0].Set(rhs) 190 | } 191 | return nil 192 | } 193 | 194 | func interpBlock(list []Stmt, env Env) (last *State, err error) { 195 | for i := 0; i < len(list); i += 1 { 196 | if last, err = InterpStmt(list[i], env); err != nil { 197 | return last, err 198 | } else if last != nil { 199 | if last, i = branch(list, last, env); last != nil { 200 | break 201 | } 202 | } 203 | } 204 | return last, nil 205 | } 206 | 207 | // Really naive implementation which simply scans block for the branch target 208 | func branch(list []Stmt, last *State, env Env) (*State, int) { 209 | branch, ok := last.Last.(*BranchStmt) 210 | if !ok { 211 | return last, 0 212 | } 213 | 214 | // breaks should go to the next stmt, goto should go to the labeled stmt 215 | brk := 0 216 | if branch.Tok == token.GOTO { 217 | brk = -1 218 | } 219 | for i, stmt := range list { 220 | switch s := stmt.(type) { 221 | case *LabeledStmt: 222 | if branch.Label != nil && branch.Label.Name == s.Label.Name { 223 | return nil, i+brk 224 | } 225 | // TODO[crc] add SelectStmt and RangeStmt here when implemented 226 | case *ForStmt, *SwitchStmt, *TypeSwitchStmt: 227 | if branch.Label == nil { 228 | return nil, i+brk 229 | } 230 | } 231 | } 232 | return last, 0 233 | } 234 | -------------------------------------------------------------------------------- /panics.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type PanicUser reflect.Value 9 | type PanicDivideByZero struct {} 10 | type PanicInvalidDereference struct {} 11 | type PanicIndexOutOfBounds struct {} 12 | type PanicSliceOutOfBounds struct {} 13 | type PanicInterfaceConversion struct { 14 | // type of type assert operand 15 | xT reflect.Type 16 | 17 | // target assertion type 18 | aT reflect.Type 19 | 20 | // the dynamic type of operand. nil for interface to interface assertions 21 | dynamicT reflect.Type 22 | } 23 | type PanicUncomparableType struct { 24 | dynamicT reflect.Type 25 | } 26 | type PanicUnhashableType struct { 27 | dynamicT reflect.Type 28 | } 29 | 30 | func (p PanicUser) Error() string { 31 | return fmt.Sprint(reflect.Value(p).Interface()) 32 | } 33 | 34 | func (err PanicDivideByZero) Error() string { 35 | return "runtime error: integer divide by zero" 36 | } 37 | 38 | func (err PanicInvalidDereference) Error() string { 39 | return "runtime error: invalid memory address or nil pointer dereference" 40 | } 41 | 42 | func (err PanicIndexOutOfBounds) Error() string { 43 | return "runtime error: index out of range" 44 | } 45 | 46 | func (err PanicSliceOutOfBounds) Error() string { 47 | return "runtime error: slice bounds out of range" 48 | } 49 | 50 | func (err PanicInterfaceConversion) Error() string { 51 | if err.xT == nil { 52 | return fmt.Sprintf("interface conversion: nil is not %v", err.aT) 53 | } else if err.dynamicT == nil { 54 | var missingMethod string 55 | numMethod := err.aT.NumMethod() 56 | for i := 0; i < numMethod; i += 1 { 57 | missingMethod = err.aT.Method(i).Name 58 | if _, ok := err.xT.MethodByName(missingMethod); !ok { 59 | break 60 | } 61 | } 62 | 63 | return fmt.Sprintf("interface conversion: %v is not %v: missing method %s", 64 | err.xT, err.aT, missingMethod) 65 | } else { 66 | return fmt.Sprintf("interface conversion: %v is %v, not %v", 67 | err.xT, err.dynamicT, err.aT) 68 | } 69 | } 70 | 71 | func (err PanicUncomparableType) Error() string { 72 | return fmt.Sprintf("runtime error: comparing uncomparable type %v", err.dynamicT) 73 | } 74 | 75 | func (err PanicUnhashableType) Error() string { 76 | return fmt.Sprintf("runtime error: hash of unhashable type %v", err.dynamicT) 77 | } 78 | -------------------------------------------------------------------------------- /runetype.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | type Rune struct { 8 | reflect.Type 9 | } 10 | 11 | func (Rune) Name() string { 12 | return "rune" 13 | } 14 | 15 | func (Rune) String() string { 16 | return "rune" 17 | } 18 | 19 | var RuneType = Rune{reflect.TypeOf(rune(0))} 20 | -------------------------------------------------------------------------------- /testgen/.gitignore: -------------------------------------------------------------------------------- 1 | *_gen 2 | -------------------------------------------------------------------------------- /testgen/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: *gen.go clean 2 | 3 | *gen.go: 4 | $(eval OUT:=$(patsubst %.go,%,$@)) 5 | go build -o $(OUT) main.go common.go $@ 6 | ./$(OUT) > ../$(patsubst %.go,%_test.go,$@) 7 | 8 | default: %gen.go 9 | 10 | clean: 11 | rm *_gen 12 | -------------------------------------------------------------------------------- /testgen/checkassignstmt_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "text/template" 6 | "github.com/0xfaded/go-testgen" 7 | ) 8 | 9 | type Test struct{} 10 | 11 | var comment = template.Must(template.New("Comment").Parse( 12 | `// Test {{ .Stmt.Name }} 13 | `)) 14 | 15 | var body = template.Must(template.New("Body").Parse( 16 | ` env := MakeSimpleEnv() 17 | f := func() (int, int) { return 1, 1 } 18 | env.Vars["f"] = reflect.ValueOf(&f) 19 | expectCheckError(t, `+"`{{ .Stmt.Value }}`"+`, env,{{ range .Errors }} 20 | `+"`{{ . }}`"+`,{{ end }} 21 | ) 22 | `)) 23 | 24 | func (*Test) Package() string { 25 | return "eval" 26 | } 27 | 28 | func (*Test) Prefix() string { 29 | return "CheckAssignStmt" 30 | } 31 | 32 | func (*Test) Imports() map[string]string { 33 | return map[string]string{"reflect": ""} 34 | } 35 | 36 | func (*Test) Dimensions() []testgen.Dimension { 37 | stmts := []testgen.Element{ 38 | {"TooMany", "_, _ = 1, 2, 3"}, 39 | {"TooFew", "_, _ = 1"}, 40 | {"NoNewIdents1", "f := nil"}, 41 | {"NoNewIdents2", "f, _ := nil, 1"}, 42 | {"UnderscoreNil", "_ = nil"}, 43 | // this case is actually detected by the parser 44 | //{"NonName", "a.b, _ := 1, 1"}, 45 | {"Unaddressable", "1 = 1"}, 46 | {"Unaddressable2", "1, 2 = 1, 2"}, 47 | {"ToNil", "nil = 1"}, 48 | {"Mistyped1", "f = true"}, 49 | {"Mistyped2", "f, f = true, false"}, 50 | } 51 | 52 | return []testgen.Dimension{stmts} 53 | } 54 | 55 | func (*Test) Globals(w io.Writer) error { 56 | return nil 57 | } 58 | 59 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 60 | vars := map[string] interface{} { 61 | "Stmt": elts[0], 62 | } 63 | 64 | return comment.Execute(w, vars) 65 | } 66 | 67 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 68 | stmt := elts[0] 69 | compileErrs, err := compileVoidExprWithDefs(stmt.Value.(string), "f := func() (int, int) { return 1, 1 }") 70 | if err != nil { 71 | return err 72 | } 73 | 74 | vars := map[string] interface{} { 75 | "Stmt": stmt, 76 | "Errors": compileErrs, 77 | } 78 | 79 | return body.Execute(w, &vars) 80 | } 81 | 82 | -------------------------------------------------------------------------------- /testgen/checkbinaryexpr_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | "go/token" 8 | "github.com/0xfaded/go-testgen" 9 | ) 10 | 11 | type Test struct{} 12 | 13 | var comment = template.Must(template.New("Comment").Parse( 14 | `// Test {{ .Lhs.Name }} {{ .Op.Value }} {{ .Rhs.Name }} 15 | `)) 16 | 17 | var body = template.Must(template.New("Body").Parse( 18 | ` env := MakeSimpleEnv() 19 | {{ if .Errors }} 20 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 21 | `+"`{{ . }}`"+`,{{ end }} 22 | ) 23 | {{ else }} 24 | expectConst(t, `+"`{{ .Expr }}`"+`, env, {{ .NewConstType }}({{ .Expr }}), {{ .ResultType }}){{ end }} 25 | `)) 26 | 27 | func (*Test) Package() string { 28 | return "eval" 29 | } 30 | 31 | func (*Test) Prefix() string { 32 | return "CheckBinaryExpr" 33 | } 34 | 35 | func (*Test) Imports() map[string]string { 36 | return nil 37 | } 38 | 39 | func (*Test) Dimensions() []testgen.Dimension { 40 | // All numeric values have been chosen to be a power of two, for good reason :) 41 | types := []testgen.Element{ 42 | {"Int", "4"}, 43 | {"Rune", "'@'"}, 44 | {"Float", "2.0"}, 45 | {"Complex", "8.0i"}, 46 | {"Bool", "true"}, 47 | {"String", `"abc"`}, 48 | {"Nil", "nil"}, 49 | } 50 | ops := []testgen.Element{ 51 | {"Add", token.ADD}, 52 | {"Sub", token.SUB}, 53 | {"Mul", token.MUL}, 54 | {"Quo", token.QUO}, 55 | {"Rem", token.REM}, 56 | {"And", token.AND}, 57 | {"Or", token.OR}, 58 | {"Xor", token.XOR}, 59 | {"AndNot", token.AND_NOT}, 60 | {"Eql", token.EQL}, 61 | {"Neq", token.NEQ}, 62 | {"Leq", token.LEQ}, 63 | {"Geq", token.GEQ}, 64 | {"Lss", token.LSS}, 65 | {"Gtr", token.GTR}, 66 | {"Rhl", token.SHR}, 67 | } 68 | return []testgen.Dimension{ 69 | types, 70 | ops, 71 | types, 72 | } 73 | } 74 | 75 | func (*Test) Globals(w io.Writer) error { 76 | return nil 77 | } 78 | 79 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 80 | vars := map[string] interface{} { 81 | "Lhs": elts[0], 82 | "Op": elts[1], 83 | "Rhs": elts[2], 84 | } 85 | 86 | return comment.Execute(w, vars) 87 | } 88 | 89 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 90 | lhs := elts[0].Name 91 | op := elts[1].Value.(token.Token) 92 | rhs := elts[2].Name 93 | 94 | expr := fmt.Sprintf("%v %v %v", elts[0].Value, op, elts[2].Value) 95 | compileErrs, err := compileExpr(expr) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | var newConstType string 101 | var resultType string 102 | 103 | switch lhs { 104 | case "Int": 105 | switch rhs { 106 | case "Int": 107 | newConstType = "NewConstInt64" 108 | resultType = "ConstInt" 109 | case "Rune": 110 | newConstType = "NewConstRune" 111 | resultType = "ConstRune" 112 | case "Float": 113 | newConstType = "NewConstFloat64" 114 | resultType = "ConstFloat" 115 | case "Complex": 116 | newConstType = "NewConstComplex128" 117 | resultType = "ConstComplex" 118 | } 119 | case "Rune": 120 | switch rhs { 121 | case "Int", "Rune": 122 | newConstType = "NewConstRune" 123 | resultType = "ConstRune" 124 | case "Float": 125 | newConstType = "NewConstFloat64" 126 | resultType = "ConstFloat" 127 | case "Complex": 128 | newConstType = "NewConstComplex128" 129 | resultType = "ConstComplex" 130 | } 131 | case "Float": 132 | switch rhs { 133 | case "Int", "Rune", "Float": 134 | newConstType = "NewConstFloat64" 135 | resultType = "ConstFloat" 136 | case "Complex": 137 | newConstType = "NewConstComplex128" 138 | resultType = "ConstComplex" 139 | } 140 | case "Complex": 141 | switch rhs { 142 | case "Int", "Rune", "Float", "Complex": 143 | newConstType = "NewConstComplex128" 144 | resultType = "ConstComplex" 145 | } 146 | case "String": 147 | switch rhs { 148 | case "String": 149 | resultType = "ConstString" 150 | } 151 | } 152 | 153 | // If its bool op, the result is a bool. Doesn't require a constructor 154 | switch op { 155 | case token.EQL, token.NEQ, token.LEQ, token.GEQ, token.LSS, token.GTR, token.LAND, token.LOR: 156 | newConstType = "" 157 | resultType = "ConstBool" 158 | case token.SHR, token.SHL: 159 | newConstType = "NewConstInt64" 160 | resultType= "ConstInt" 161 | if lhs == rhs && lhs == "Complex" { 162 | // double truncation error lost 163 | compileErrs = append(compileErrs, compileErrs[0]) 164 | } 165 | } 166 | 167 | vars := map[string] interface{} { 168 | "Expr": expr, 169 | "Errors": compileErrs, 170 | "Op": elts[1], 171 | "NewConstType": newConstType, 172 | "ResultType": resultType, 173 | } 174 | 175 | return body.Execute(w, &vars) 176 | } 177 | 178 | -------------------------------------------------------------------------------- /testgen/checkbinaryexpr_nonconst_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "text/template" 8 | "go/token" 9 | "github.com/0xfaded/go-testgen" 10 | ) 11 | 12 | type Test struct{} 13 | 14 | var comment = template.Must(template.New("Comment").Parse( 15 | `// Test {{ .Lhs.Name }} {{ .Op.Value }} {{ .Rhs.Name }} 16 | `)) 17 | 18 | var body = template.Must(template.New("Body").Parse( 19 | ` env := makeCheckBinaryNonConstExprEnv() 20 | {{ .DefA }}; env.Vars["a"] = reflect.ValueOf(&a) 21 | {{ if .DefB }}{{ .DefB }}; env.Vars["b"] = reflect.ValueOf(&b){{ end }}{{ if .Errors }} 22 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 23 | `+"`{{ . }}`"+`,{{ end }} 24 | ) 25 | {{ else }} 26 | expectType(t, `+"`{{ .Expr }}`"+`, env, reflect.TypeOf({{ .Expr }})){{ end }} 27 | `)) 28 | 29 | var typeDefs = ` 30 | type interfaceX interface { x() } 31 | type interfaceY interface { x() } 32 | type interfaceZ interface { z() } 33 | type XinterfaceX int 34 | func (XinterfaceX) x() {} 35 | type arrayT [2]int 36 | type mapT map[int]int 37 | type sliceT []int 38 | type structT struct { 39 | a int 40 | _ []int 41 | } 42 | type structUncompT struct { 43 | a int 44 | b []int 45 | } 46 | ` 47 | var globals = typeDefs + ` 48 | func makeCheckBinaryNonConstExprEnv() *SimpleEnv { 49 | env := MakeSimpleEnv() 50 | env.Types["interfaceX"] = reflect.TypeOf(new(interfaceX)).Elem() 51 | env.Types["interfaceY"] = reflect.TypeOf(new(interfaceY)).Elem() 52 | env.Types["interfaceZ"] = reflect.TypeOf(new(interfaceZ)).Elem() 53 | env.Types["XinterfaceX"] = reflect.TypeOf(XinterfaceX(0)) 54 | env.Types["arrayT"] = reflect.TypeOf(arrayT{}) 55 | env.Types["mapT"] = reflect.TypeOf(mapT{}) 56 | env.Types["sliceT"] = reflect.TypeOf(sliceT{}) 57 | env.Types["structT"] = reflect.TypeOf(structT{}) 58 | env.Types["structUncompT"] = reflect.TypeOf(structUncompT{}) 59 | return env 60 | } 61 | ` 62 | 63 | func (*Test) Package() string { 64 | return "eval" 65 | } 66 | 67 | func (*Test) Prefix() string { 68 | return "CheckBinaryNonConstExpr" 69 | } 70 | 71 | func (*Test) Imports() map[string]string { 72 | return map[string]string { "reflect": "" } 73 | } 74 | 75 | func (*Test) Globals(w io.Writer) error { 76 | _, err := w.Write([]byte(globals)) 77 | return err 78 | } 79 | 80 | func (*Test) Dimensions() []testgen.Dimension { 81 | rhs := []testgen.Element{ 82 | {"ConstInt", "4"}, 83 | {"ConstRune", "'@'"}, 84 | {"ConstFloat", "2.0"}, 85 | {"ConstComplex", "8.0i"}, 86 | {"ConstBool", "true"}, 87 | {"ConstString", `"abc"`}, 88 | {"ConstNil", "nil"}, 89 | {"Int", "int(1)"}, 90 | {"Float32", "float32(1.5)"}, 91 | {"Complex128", "complex128(1i)"}, 92 | // once in the runtime, runes are int32. This is a wont fix 93 | //{"Rune", "rune('a')"}, 94 | {"String", `string("abc")`}, 95 | {"BoolT", "bool(true)"}, 96 | {"Slice", "sliceT(nil)"}, 97 | {"Array", "arrayT{}"}, 98 | {"Map", "mapT{}"}, 99 | {"XinterfaceX", "XinterfaceX(1)"}, 100 | {"InterfaceX", "interfaceX(nil)"}, 101 | {"InterfaceY", "interfaceY(nil)"}, 102 | {"InterfaceZ", "interfaceZ(nil)"}, 103 | {"Ptr", "(*int)(nil)"}, 104 | {"Struct", "structT{}"}, 105 | {"StructUncomp", "structUncompT{}"}, 106 | } 107 | ops := []testgen.Element{ 108 | {"Add", token.ADD}, 109 | {"And", token.AND}, 110 | {"Rem", token.REM}, 111 | {"Eql", token.EQL}, 112 | {"Gtr", token.GTR}, 113 | {"Shl", token.SHL}, 114 | } 115 | // exclude const types 116 | lhs := rhs[7:] 117 | return []testgen.Dimension{ 118 | lhs, 119 | ops, 120 | rhs, 121 | } 122 | } 123 | 124 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 125 | vars := map[string] interface{} { 126 | "Lhs": elts[0], 127 | "Op": elts[1], 128 | "Rhs": elts[2], 129 | } 130 | 131 | return comment.Execute(w, vars) 132 | } 133 | 134 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 135 | op := elts[1].Value.(token.Token) 136 | a := "a" 137 | adef := "a := " + elts[0].Value.(string) 138 | b := "b" 139 | bdef := "b := " + elts[2].Value.(string) 140 | if strings.HasPrefix(elts[2].Name, "Const") { 141 | b = elts[2].Value.(string) 142 | bdef = "" 143 | } 144 | 145 | expr := fmt.Sprintf("%v %v %v", a, op, b) 146 | defs := adef + "\n" + bdef 147 | 148 | compileErrs, err := compileExprWithDefsAndGlobals(expr, defs, typeDefs) 149 | for i := range compileErrs { 150 | compileErrs[i] = strings.Replace(compileErrs[i], "sliceT", "eval.sliceT", -1) 151 | compileErrs[i] = strings.Replace(compileErrs[i], "arrayT", "eval.arrayT", -1) 152 | compileErrs[i] = strings.Replace(compileErrs[i], "mapT", "eval.mapT", -1) 153 | compileErrs[i] = strings.Replace(compileErrs[i], "interfaceX", "eval.interfaceX", -1) 154 | compileErrs[i] = strings.Replace(compileErrs[i], "Xeval.interfaceX", "eval.XinterfaceX", -1) 155 | compileErrs[i] = strings.Replace(compileErrs[i], "interfaceY", "eval.interfaceY", -1) 156 | compileErrs[i] = strings.Replace(compileErrs[i], "interfaceZ", "eval.interfaceZ", -1) 157 | compileErrs[i] = strings.Replace(compileErrs[i], "structT", "eval.structT", -1) 158 | compileErrs[i] = strings.Replace(compileErrs[i], "structUncompT", "eval.structUncompT", -1) 159 | } 160 | if err != nil { 161 | return err 162 | } 163 | 164 | vars := map[string] interface{} { 165 | "DefA": adef, 166 | "DefB": bdef, 167 | "Expr": expr, 168 | "Errors": compileErrs, 169 | "Op": elts[1], 170 | } 171 | 172 | return body.Execute(w, &vars) 173 | } 174 | 175 | -------------------------------------------------------------------------------- /testgen/checkbinaryexpr_typed_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "text/template" 8 | "go/token" 9 | "github.com/0xfaded/go-testgen" 10 | ) 11 | 12 | type Test struct{} 13 | 14 | var comment = template.Must(template.New("Comment").Parse( 15 | `// Test {{ .Lhs.Name }} {{ .Op.Value }} {{ .Rhs.Name }} 16 | `)) 17 | 18 | var body = template.Must(template.New("Body").Parse( 19 | ` env := MakeSimpleEnv() 20 | {{ if .Errors }} 21 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 22 | `+"`{{ . }}`"+`,{{ end }} 23 | ) 24 | {{ else }} 25 | expectConst(t, `+"`{{ .Expr }}`"+`, env, {{ .Expr }}, reflect.TypeOf({{ .Expr }})){{ end }} 26 | `)) 27 | 28 | func (*Test) Package() string { 29 | return "eval" 30 | } 31 | 32 | func (*Test) Prefix() string { 33 | return "CheckBinaryTypedExpr" 34 | } 35 | 36 | func (*Test) Imports() map[string]string { 37 | return map[string]string { "reflect": "" } 38 | } 39 | 40 | func (*Test) Dimensions() []testgen.Dimension { 41 | lhs := []testgen.Element{ 42 | {"Int8", "int8(0x7f)"}, 43 | {"Int16", "int16(0x7fff)"}, 44 | {"Int32", "int32(0x7fffffff)"}, 45 | {"Int64", "int64(0x7fffffffffffffff)"}, 46 | {"Uint8", "uint8(0xff)"}, 47 | {"Uint16", "uint16(0xffff)"}, 48 | {"Uint32", "uint32(0xffffffff)"}, 49 | {"Uint64", "uint64(0xffffffffffffffff)"}, 50 | {"Float32", "float32(0xffffffff)"}, 51 | {"Float64", "float64(0xffffffff)"}, 52 | {"Complex64", "complex64(0xffffffff + 0xffffffff * 1i)"}, 53 | {"Complex128", "complex128(0xffffffff + 0xffffffff * 1i)"}, 54 | {"Rune32", "rune(0x7fffffff)"}, 55 | {"StringT", `string("abc")`}, 56 | {"BoolT", "bool(true)"}, 57 | } 58 | ops := []testgen.Element{ 59 | {"Add", token.ADD}, 60 | {"Sub", token.SUB}, 61 | {"And", token.AND}, 62 | {"Rem", token.REM}, 63 | {"Eql", token.EQL}, 64 | {"Gtr", token.GTR}, 65 | {"Shl", token.SHL}, 66 | } 67 | rhs := []testgen.Element{ 68 | {"Int", "4"}, 69 | {"Rune", "'@'"}, 70 | {"Float", "2.0"}, 71 | {"Complex", "8.0i"}, 72 | {"Bool", "true"}, 73 | {"String", `"abc"`}, 74 | {"Nil", "nil"}, 75 | {"Int8", "int8(0x7f)"}, 76 | {"Int16", "int16(0x7fff)"}, 77 | {"Int32", "int32(0x7fffffff)"}, 78 | {"Int64", "int64(0x7fffffffffffffff)"}, 79 | {"Uint8", "uint8(0xff)"}, 80 | {"Uint16", "uint16(0xffff)"}, 81 | {"Uint32", "uint32(0xffffffff)"}, 82 | {"Uint64", "uint64(0xffffffffffffffff)"}, 83 | {"Float32", "float32(0xffffffff)"}, 84 | {"Float64", "float64(0xffffffff)"}, 85 | {"Complex64", "complex64(0xffffffff + 0xffffffff * 1i)"}, 86 | {"Complex128", "complex128(0xffffffff + 0xffffffff * 1i)"}, 87 | {"Rune32", "rune(0x7fffffff)"}, 88 | {"StringT", `string("abc")`}, 89 | {"BoolT", "bool(true)"}, 90 | } 91 | return []testgen.Dimension{ 92 | lhs, 93 | ops, 94 | rhs, 95 | } 96 | } 97 | 98 | func (*Test) Globals(w io.Writer) error { 99 | return nil 100 | } 101 | 102 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 103 | vars := map[string] interface{} { 104 | "Lhs": elts[0], 105 | "Op": elts[1], 106 | "Rhs": elts[2], 107 | } 108 | 109 | return comment.Execute(w, vars) 110 | } 111 | 112 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 113 | op := elts[1].Value.(token.Token) 114 | 115 | expr := fmt.Sprintf("%v %v %v", elts[0].Value, op, elts[2].Value) 116 | compileErrs, err := compileExpr(expr) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | // TODO Fix for bad complex number formatting in gc. This has been 122 | // addressed in gc 1.3. Remove this after release. 123 | buggy := "4.29497e+094.29497e+09i" 124 | fix := "4.29497e+09+4.29497e+09i" 125 | for i := range compileErrs { 126 | compileErrs[i] = strings.Replace(compileErrs[i], buggy, fix, -1) 127 | } 128 | 129 | vars := map[string] interface{} { 130 | "Expr": expr, 131 | "Errors": compileErrs, 132 | "Op": elts[1], 133 | } 134 | 135 | return body.Execute(w, &vars) 136 | } 137 | 138 | -------------------------------------------------------------------------------- /testgen/checkbuiltin_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "text/template" 6 | "github.com/0xfaded/go-testgen" 7 | ) 8 | 9 | type Test struct{} 10 | 11 | var comment = template.Must(template.New("Comment").Parse( 12 | `// Test {{ .Comment }} 13 | `)) 14 | 15 | var body = template.Must(template.New("Body").Parse( 16 | ` env := MakeSimpleEnv() 17 | {{ if .Errors }}{{ if .TestErrs }} 18 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 19 | `+"`{{ . }}`"+`,{{ end }} 20 | ){{ else }} _ = env{{ end }} 21 | {{ else }} {{ if .TestSuccess }}{{ if .ExpectConst }}expectConst(t, `+"`{{ .Expr }}`"+`, env, {{ .Expr }}, reflect.TypeOf({{ .Expr}})) 22 | {{ else }}expectType(t, `+"`{{ .Expr }}`"+`, env, reflect.TypeOf({{ .Expr }})){{ end }}{{ else }}_ = env{{ end }}{{ end }} 23 | `)) 24 | 25 | func (*Test) Package() string { 26 | return "eval" 27 | } 28 | 29 | func (*Test) Prefix() string { 30 | return "CheckBuiltin" 31 | } 32 | 33 | func (*Test) Imports() map[string]string { 34 | return map[string]string { "reflect": "" } 35 | } 36 | 37 | func (*Test) Dimensions() []testgen.Dimension { 38 | builtins := []testgen.Element{ 39 | {"Complex", "complex"}, 40 | {"Real", "real"}, 41 | {"Imag", "imag"}, 42 | {"New", "new"}, 43 | //{"Make", "make"}, 44 | {"Len", "len"}, 45 | {"Cap", "cap"}, 46 | {"Append", "append"}, 47 | {"Copy", "copy"}, 48 | {"Delete", "delete"}, 49 | //{"Panic", "panic"}, 50 | } 51 | arg0 := []testgen.Element{ 52 | {"X", ""}, 53 | {"Int", "1"}, 54 | {"Float32", "float32(1)"}, 55 | {"String", `"abc"`}, 56 | {"Nil", "nil"}, 57 | {"Float", "1.5"}, 58 | {"Slice", "[]int{}"}, 59 | {"Map", "map[int]int{}"}, 60 | {"Type", "int"}, 61 | {"MakeType", "map[int]int"}, 62 | } 63 | arg1 := append(arg0, testgen.Element{"Double", "1, 1"}, testgen.Element{"Ellipsis", "[]int{1,2}..."}) 64 | return []testgen.Dimension{ 65 | builtins, 66 | arg0, 67 | arg1, 68 | } 69 | } 70 | 71 | func (*Test) Globals(w io.Writer) error { 72 | return nil 73 | } 74 | 75 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 76 | builtin := elts[0].Name 77 | sep := "(" 78 | for _, elt := range elts[1:] { 79 | if elt.Value != "" { 80 | builtin += sep + elt.Value.(string) 81 | sep = ", " 82 | } 83 | } 84 | if sep == "(" { 85 | builtin += "(" 86 | } 87 | builtin += ")" 88 | 89 | vars := map[string] interface{} { 90 | "Comment": builtin, 91 | } 92 | 93 | return comment.Execute(w, vars) 94 | } 95 | 96 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 97 | expr := elts[0].Value.(string) 98 | sep := "(" 99 | for _, elt := range elts[1:] { 100 | if elt.Value != "" { 101 | expr += sep + elt.Value.(string) 102 | sep = ", " 103 | } 104 | } 105 | if sep == "(" { 106 | expr += "(" 107 | } 108 | expr += ")" 109 | 110 | f := elts[0].Name 111 | x := elts[1].Name 112 | y := elts[2].Name 113 | 114 | var compileErrs []string 115 | var err error 116 | if f == "Delete" { 117 | compileErrs, err = compileVoidExpr(expr) 118 | } else { 119 | compileErrs, err = compileExpr(expr) 120 | } 121 | if err != nil { 122 | return err 123 | } 124 | 125 | testErrs := true 126 | if f == "Complex" || f == "Append" || f == "Copy" || f == "Delete" { 127 | if len(compileErrs) == 1 && (x == "Type" || x == "MakeType") { 128 | if y == "Type" { 129 | compileErrs = append(compileErrs, "type int is not an expression") 130 | } else if y == "MakeType" { 131 | compileErrs = append(compileErrs, "type map[int]int is not an expression") 132 | } 133 | } 134 | if f == "Copy" { 135 | if x == "Nil" && y == "Nil" { 136 | compileErrs = append(compileErrs[:1], append([]string{compileErrs[0]}, compileErrs[1:]...)...) 137 | } 138 | if x == "Slice" && y == "Nil" { 139 | // http://code.google.com/p/go/issues/detail?id=7310 140 | testErrs = false 141 | } 142 | } 143 | } 144 | 145 | expectConst := false 146 | switch f { 147 | case "Complex", "Real", "Imag": 148 | expectConst = true 149 | case "Len", "Cap": 150 | z := x 151 | if z == "X" { 152 | z = y 153 | } 154 | switch z { 155 | case "String": 156 | expectConst = true 157 | } 158 | } 159 | 160 | vars := map[string] interface{} { 161 | "Expr": expr, 162 | "Errors": compileErrs, 163 | "TestErrs": testErrs, 164 | "TestSuccess": f != "Delete", 165 | "ExpectConst": expectConst, 166 | } 167 | 168 | return body.Execute(w, &vars) 169 | } 170 | 171 | -------------------------------------------------------------------------------- /testgen/checkcallexpr_const_conv_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | "github.com/0xfaded/go-testgen" 8 | ) 9 | 10 | type Test struct{} 11 | 12 | var comment = template.Must(template.New("Comment").Parse( 13 | `// Test {{ .Type.Value }}({{ .Value.Value }}) 14 | `)) 15 | 16 | var body = template.Must(template.New("Body").Parse( 17 | ` env := MakeSimpleEnv() 18 | {{ if .Errors }}{{ if .TestErrs }} 19 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 20 | `+"`{{ . }}`"+`,{{ end }} 21 | ){{ else }} _ = env{{ end }} 22 | {{ else }} 23 | expectConst(t, `+"`{{ .Expr }}`"+`, env, {{ .Expr }}, reflect.TypeOf({{ .Expr }})){{ end }} 24 | `)) 25 | 26 | func (*Test) Package() string { 27 | return "eval" 28 | } 29 | 30 | func (*Test) Prefix() string { 31 | return "CheckCallExpr" 32 | } 33 | 34 | func (*Test) Imports() map[string]string { 35 | return map[string]string { "reflect": "" } 36 | } 37 | 38 | func (*Test) Dimensions() []testgen.Dimension { 39 | types := []testgen.Element{ 40 | {"Int8", "int8"}, 41 | {"Int16", "int16"}, 42 | {"Int32", "int32"}, 43 | {"Int64", "int64"}, 44 | {"Uint8", "uint8"}, 45 | {"Uint16", "uint16"}, 46 | {"Uint32", "uint32"}, 47 | {"Uint64", "uint64"}, 48 | {"Float32", "float32"}, 49 | {"Float64", "float64"}, 50 | {"Complex64", "complex64"}, 51 | {"Complex128", "complex128"}, 52 | {"Rune", "rune"}, 53 | {"Bool", "bool"}, 54 | {"String", "string"}, 55 | {"Nil", "nil"}, 56 | } 57 | values := []testgen.Element{ 58 | {"From7bits", "0x7f"}, 59 | {"From8bits", "0xff"}, 60 | {"From15bits", "0x7fff"}, 61 | {"From16bits", "0xffff"}, 62 | {"From31bits", "0x7fffffff"}, 63 | {"From32bits", "0xffffffff"}, 64 | {"From63bits", "0x7fffffffffffffff"}, 65 | {"From64bits", "0xffffffffffffffff"}, 66 | {"FromRune", "'d'"}, 67 | {"FromWideRune", "'日'"}, 68 | {"FromFloatingInt", "1.0"}, 69 | {"FromFloating", "1.5"}, 70 | {"FromComplexInt", "1.0+0i"}, 71 | {"FromComplexFloat", "1.5+0i"}, 72 | {"FromComplex", "1.5+1.5i"}, 73 | {"FromBool", "true"}, 74 | {"FromString", `"abc"`}, 75 | {"FromNil", `nil`}, 76 | } 77 | return []testgen.Dimension{ 78 | types, 79 | values, 80 | } 81 | } 82 | 83 | func (*Test) Globals(w io.Writer) error { 84 | return nil 85 | } 86 | 87 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 88 | vars := map[string] interface{} { 89 | "Type": elts[0], 90 | "Value": elts[1], 91 | } 92 | 93 | return comment.Execute(w, vars) 94 | } 95 | 96 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 97 | expr := fmt.Sprintf("%v(%v)", elts[0].Value, elts[1].Value) 98 | 99 | compileErrs, err := compileExpr(expr) 100 | if err != nil { 101 | return err 102 | } 103 | 104 | // https://github.com/0xfaded/go-interactive/issues/20 105 | testErrs := true 106 | if elts[0].Name == "Bool" { 107 | testErrs = false 108 | } 109 | 110 | vars := map[string] interface{} { 111 | "Expr": expr, 112 | "Errors": compileErrs, 113 | "TestErrs": testErrs, 114 | } 115 | 116 | return body.Execute(w, &vars) 117 | } 118 | 119 | -------------------------------------------------------------------------------- /testgen/checkcallexpr_func_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "text/template" 7 | "github.com/0xfaded/go-testgen" 8 | ) 9 | 10 | type Test struct{} 11 | 12 | var comment = template.Must(template.New("Comment").Parse( 13 | `// Test {{ .Comment }} 14 | `)) 15 | 16 | var defs string = 17 | ` type I int 18 | is := []int{1, 2, 3} 19 | f := func(int, bool) int { return 1 } 20 | ft := func(I, bool) int { return 1 } 21 | v1 := func(...int) int { return 1 } 22 | v2 := func(int, ...int) int { return 1 } 23 | vt := func(I, ...I) int { return 1 } 24 | e := func() {} 25 | s := func() int { return 1} 26 | m := func() (int, int) { return 1, 1 } 27 | mt := func() (int, I) { return 1, 1 } 28 | ` 29 | var underscores string = 30 | ` _ = is 31 | _ = f 32 | _ = ft 33 | _ = v1 34 | _ = v2 35 | _ = vt 36 | _ = e 37 | _ = s 38 | _ = m 39 | _ = mt 40 | ` 41 | var body = template.Must(template.New("Body").Parse( 42 | defs + ` 43 | 44 | env := MakeSimpleEnv() 45 | env.Types["I"] = reflect.TypeOf(I(0)) 46 | env.Vars["is"] = reflect.ValueOf(&is) 47 | env.Funcs["f"] = reflect.ValueOf(f) 48 | env.Funcs["ft"] = reflect.ValueOf(ft) 49 | env.Funcs["v1"] = reflect.ValueOf(v1) 50 | env.Funcs["v2"] = reflect.ValueOf(v2) 51 | env.Funcs["vt"] = reflect.ValueOf(vt) 52 | env.Funcs["e"] = reflect.ValueOf(e) 53 | env.Funcs["s"] = reflect.ValueOf(s) 54 | env.Funcs["m"] = reflect.ValueOf(m) 55 | env.Funcs["mt"] = reflect.ValueOf(mt) 56 | 57 | {{ if .Errors }}{{ if .TestErrs }} 58 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 59 | `+"`{{ . }}`"+`,{{ end }} 60 | ){{ else }} _ = env{{ end }} 61 | {{ else }} 62 | expectType(t, `+"`{{ .Expr }}`"+`, env, reflect.TypeOf({{ .Expr }})){{ end }} 63 | `)) 64 | 65 | func (*Test) Package() string { 66 | return "eval" 67 | } 68 | 69 | func (*Test) Prefix() string { 70 | return "CheckCallExpr" 71 | } 72 | 73 | func (*Test) Imports() map[string]string { 74 | return map[string]string { "reflect": "" } 75 | } 76 | 77 | func (*Test) Dimensions() []testgen.Dimension { 78 | funs := []testgen.Element{ 79 | {"NoArg", "s"}, 80 | {"Fixed", "f"}, 81 | {"FixedTyped", "ft"}, 82 | {"Variadic1", "v1"}, 83 | {"Variadic2", "v2"}, 84 | {"VariadicTyped", "vt"}, 85 | } 86 | arg := []testgen.Element{ 87 | {"X", ""}, 88 | {"Int", "1"}, 89 | {"Float", "1.5"}, 90 | {"Bool", "true"}, 91 | {"IntTyped", "I(1)"}, 92 | {"Ints", "is..."}, 93 | {"EmptyFunc", "e()"}, 94 | {"SingleFunc", "s()"}, 95 | {"MultiFunc", "m()"}, 96 | {"MultiFuncMixedTypes", "mt()"}, 97 | } 98 | return []testgen.Dimension{ 99 | funs, 100 | arg, 101 | arg, 102 | } 103 | } 104 | 105 | func (*Test) Globals(w io.Writer) error { 106 | return nil 107 | } 108 | 109 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 110 | fun := elts[0].Name 111 | sep := "(" 112 | for _, elt := range elts[1:] { 113 | if elt.Value != "" { 114 | fun += sep + elt.Value.(string) 115 | sep = ", " 116 | } 117 | } 118 | if sep == "(" { 119 | fun += "(" 120 | } 121 | fun += ")" 122 | 123 | vars := map[string] interface{} { 124 | "Comment": fun, 125 | } 126 | 127 | return comment.Execute(w, vars) 128 | } 129 | 130 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 131 | expr := elts[0].Value.(string) 132 | sep := "(" 133 | for _, elt := range elts[1:] { 134 | if elt.Value != "" { 135 | expr += sep + elt.Value.(string) 136 | sep = ", " 137 | } 138 | } 139 | if sep == "(" { 140 | expr += "(" 141 | } 142 | expr += ")" 143 | 144 | compileErrs, err := compileExprWithDefs(expr, defs + underscores) 145 | if err != nil { 146 | return err 147 | } 148 | 149 | testErrs := true 150 | for i := range compileErrs { 151 | if strings.Index(compileErrs[i], "syntax error") != -1 { 152 | testErrs = false 153 | } 154 | compileErrs[i] = strings.Replace(compileErrs[i], "I", "eval.I", -1) 155 | 156 | // TODO[crc] The below fixes a bug in error messages generated by gc 1.1.2 157 | // Remove this once errors are generated by gc 1.2 158 | compileErrs[i] = strings.Replace(compileErrs[i], 159 | "as type []int in argument to v", 160 | "as type int in argument to v", -1, 161 | ) 162 | compileErrs[i] = strings.Replace(compileErrs[i], 163 | "as type []eval.I in argument to v", 164 | "as type eval.I in argument to v", -1, 165 | ) 166 | } 167 | 168 | // gc is stripping duplicate errors on variadic functions. This is understandable 169 | // but we want to catch all errors. 170 | n0, n1, n2 := elts[0].Name, elts[1].Name, elts[2].Name 171 | if len(compileErrs) > 0 && n0[:3] == "Var" && ((n1 == n2 && (n1 == "Float" || n1 == "Bool" || n1 == "IntTyped" || n1 == "SingleFunc")) || (n1 == "X" && n2 == "MultiFunc" || n2 == "X" && n1 == "MultiFunc")) { 172 | compileErrs = append(compileErrs[:1], append([]string{compileErrs[0]}, compileErrs[1:]...)...) 173 | } 174 | // Duplicates of these errors get stripped on all functions 175 | if n1 == n2 && (n1 == "EmptyFunc" || n1 == "MultiFunc" || n1 == "MultiFuncMixedTypes") { 176 | compileErrs = append(compileErrs[:1], append([]string{compileErrs[0]}, compileErrs[1:]...)...) 177 | } 178 | 179 | vars := map[string] interface{} { 180 | "Expr": expr, 181 | "Errors": compileErrs, 182 | "TestErrs": testErrs, 183 | } 184 | 185 | return body.Execute(w, &vars) 186 | } 187 | 188 | -------------------------------------------------------------------------------- /testgen/checkcompositelit_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "text/template" 7 | "github.com/0xfaded/go-testgen" 8 | ) 9 | 10 | type Test struct{} 11 | 12 | var comment = template.Must(template.New("Comment").Parse( 13 | `// Test {{ .Comment }} 14 | `)) 15 | 16 | var defs = 17 | ` type a1 [1]int 18 | type a2 [2]int 19 | type s1 struct{ a int } 20 | type s2 struct{ a, b int } 21 | ` 22 | var body = template.Must(template.New("Body").Parse(defs + 23 | ` env := MakeSimpleEnv() 24 | env.Types["a1"] = reflect.TypeOf(a1{}) 25 | env.Types["a2"] = reflect.TypeOf(a2{}) 26 | env.Types["s1"] = reflect.TypeOf(s1{}) 27 | env.Types["s2"] = reflect.TypeOf(s2{}) 28 | 29 | {{ if .Errors }}{{ if .TestErrs }} 30 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 31 | `+"`{{ . }}`"+`,{{ end }} 32 | ){{ else }} _ = env{{ end }} 33 | {{ else }} 34 | expectType(t, `+"`{{ .Expr }}`"+`, env, reflect.TypeOf({{ .Expr }})){{ end }} 35 | `)) 36 | 37 | func (*Test) Package() string { 38 | return "eval" 39 | } 40 | 41 | func (*Test) Prefix() string { 42 | return "CheckCompositeLitExpr" 43 | } 44 | 45 | func (*Test) Imports() map[string]string { 46 | return map[string]string { "reflect": "" } 47 | } 48 | 49 | func (*Test) Dimensions() []testgen.Dimension { 50 | funs := []testgen.Element{ 51 | {"A1", "a1"}, 52 | {"A2", "a2"}, 53 | {"S1", "s1"}, 54 | {"S2", "s2"}, 55 | {"Slice", "[]int"}, 56 | {"Map", "map[int] int"}, 57 | } 58 | arg0 := []testgen.Element{ 59 | {"X", ""}, 60 | {"Int", "1"}, 61 | {"Nil", "nil"}, 62 | {"Bool", "true"}, 63 | {"IntKInt", "1: 1"}, 64 | {"IntKBool", "1: true"}, 65 | {"FloatKInt", "float32(1.4): 1"}, 66 | {"AKInt", "a: 1"}, 67 | {"AKBool", "a: true"}, 68 | {"BKInt", "b: 1"}, 69 | {"CKInt", "c: 1"}, 70 | } 71 | arg1 := []testgen.Element{ 72 | {"X", ""}, 73 | {"Int", "1"}, 74 | {"String", "\"a\""}, 75 | {"Nil", "nil"}, 76 | {"IntKInt", "1: 1"}, 77 | {"IntKString", "1: \"a\""}, 78 | {"FloatKInt", "float32(1.5): 1"}, 79 | {"AKInt", "a: 1"}, 80 | {"AKString", "a: \"a\""}, 81 | {"BKInt", "b: 1"}, 82 | {"CKInt", "c: 1"}, 83 | } 84 | return []testgen.Dimension{ 85 | funs, 86 | arg0, 87 | arg1, 88 | } 89 | } 90 | 91 | func (*Test) Globals(w io.Writer) error { 92 | return nil 93 | } 94 | 95 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 96 | fun := elts[0].Name 97 | sep := "{" 98 | for _, elt := range elts[1:] { 99 | if elt.Value != "" { 100 | fun += sep + elt.Value.(string) 101 | sep = ", " 102 | } 103 | } 104 | if sep == "{" { 105 | fun += "{" 106 | } 107 | fun += "}" 108 | 109 | vars := map[string] interface{} { 110 | "Comment": fun, 111 | } 112 | 113 | return comment.Execute(w, vars) 114 | } 115 | 116 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 117 | expr := elts[0].Value.(string) 118 | sep := "{" 119 | for _, elt := range elts[1:] { 120 | if elt.Value != "" { 121 | expr += sep + elt.Value.(string) 122 | sep = ", " 123 | } 124 | } 125 | if sep == "{" { 126 | expr += "{" 127 | } 128 | expr += "}" 129 | 130 | compileErrs, err := compileExprWithDefs(expr, defs) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | n0, n1, n2 := elts[0].Name, elts[1].Name, elts[2].Name 136 | n1HasKey := !(n1 == "X" || n1 == "Int" || n1 == "String" || n1 == "Bool" || n1 == "Nil") 137 | n2HasKey := !(n2 == "X" || n2 == "Int" || n2 == "String" || n2 == "Bool" || n2 == "Nil") 138 | seenBadArrayIndex := false 139 | testErrs := true 140 | for i := 0; i < len(compileErrs); i += 1 { 141 | if strings.Index(compileErrs[i], "syntax error") != -1 { 142 | testErrs = false 143 | } 144 | compileErrs[i] = strings.Replace(compileErrs[i], "a1", "eval.a1", -1) 145 | compileErrs[i] = strings.Replace(compileErrs[i], "a2", "eval.a2", -1) 146 | compileErrs[i] = strings.Replace(compileErrs[i], "s1", "eval.s1", -1) 147 | compileErrs[i] = strings.Replace(compileErrs[i], "s2", "eval.s2", -1) 148 | 149 | // TODO[crc] Fix bugs for gc 1.2 bugs. Remove after upgrade to 1.3 150 | if n0 == "A2" { 151 | compileErrs[i] = strings.Replace(compileErrs[i], "[0:0]", "[0:2]", -1) 152 | } else if n0 == "A1" { 153 | compileErrs[i] = strings.Replace(compileErrs[i], "[0:0]", "[0:1]", -1) 154 | } 155 | 156 | // Fix for bug http://code.google.com/p/go/issues/detail?id=7153 157 | if !n2HasKey && strings.HasPrefix(compileErrs[i], "array index must be non-") { 158 | if seenBadArrayIndex { 159 | // remove the error 160 | compileErrs = append(compileErrs[:i], compileErrs[i+1:]...) 161 | i -= 1 162 | } 163 | seenBadArrayIndex = true 164 | } 165 | } 166 | 167 | // gc strips duplicate errors. This is understandable but we want to catch all errors. 168 | if len(compileErrs) > 0 && (n0 == "S1" || n0 == "S2") && 169 | strings.HasPrefix(n1, "IntK") && strings.HasPrefix(n2, "IntK") { 170 | compileErrs = append(compileErrs[:1], append([]string{compileErrs[0]}, compileErrs[1:]...)...) 171 | } 172 | if len(compileErrs) == 1 && strings.HasPrefix(compileErrs[0], "unknown") && n1 == n2 { 173 | // duplicate `unknown eval.XX field 'X' in struct literal` 174 | compileErrs = append(compileErrs, compileErrs[0]) 175 | } 176 | if len(compileErrs) == 1 && n1 == "Nil" && n2 == "Nil" { 177 | // duplicate `cannot use nil as type int in field value` 178 | compileErrs = append(compileErrs, compileErrs[0]) 179 | } 180 | if len(compileErrs) == 1 && 181 | compileErrs[0] == "array index must be non-negative integer constant" && 182 | n1HasKey && n2HasKey && 183 | n1 != "IntKInt" && n1 != "IntKBool" && n2 != "IntKInt" && n2 != "IntKBool" { 184 | compileErrs = append(compileErrs, compileErrs[0]) 185 | } 186 | if len(compileErrs) == 2 && 187 | strings.HasPrefix(compileErrs[0], "undefined") && 188 | compileErrs[1] == "array index must be non-negative integer constant" && 189 | n1HasKey && n2HasKey && 190 | n1 != "IntKInt" && n1 != "IntKBool" && n2 != "IntKInt" && n2 != "IntKBool" { 191 | compileErrs = append(compileErrs, compileErrs[1]) 192 | } 193 | if len(compileErrs) > 0 && n0 == "Map" && !n1HasKey && !n2HasKey && 194 | n1 != "X" && n2 != "X" && !(n1 == "Nil" && n2 == "Nil") { 195 | compileErrs = append(compileErrs, compileErrs[0]) 196 | } 197 | if len(compileErrs) == 1 && n0 == "Map" && n1HasKey && n2HasKey && 198 | strings.HasPrefix(compileErrs[0], "undefined") && 199 | n1[0] == n2[0] { 200 | 201 | compileErrs = append(compileErrs[:1], append([]string{compileErrs[0]}, compileErrs[1:]...)...) 202 | } 203 | if n0 == "Map" && n1 == "AKInt" && n2 == "AKString" { 204 | compileErrs = append(compileErrs[:1], append([]string{compileErrs[0]}, compileErrs[1:]...)...) 205 | } 206 | vars := map[string] interface{} { 207 | "Expr": expr, 208 | "Errors": compileErrs, 209 | "TestErrs": testErrs, 210 | } 211 | 212 | return body.Execute(w, &vars) 213 | } 214 | 215 | -------------------------------------------------------------------------------- /testgen/checkstarexpr_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | "github.com/0xfaded/go-testgen" 8 | ) 9 | 10 | type Test struct{} 11 | 12 | var comment = template.Must(template.New("Comment").Parse( 13 | `// Test {{ .Star.Value }} 14 | `)) 15 | 16 | var defs = 17 | ` 18 | a := 1 19 | b := &a 20 | _ = b 21 | ` 22 | var body = template.Must(template.New("Body").Parse(defs + 23 | ` env := MakeSimpleEnv() 24 | env.Vars["a"] = reflect.ValueOf(&a) 25 | env.Vars["b"] = reflect.ValueOf(&b) 26 | {{ if .Errors }} 27 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 28 | `+"`{{ . }}`"+`,{{ end }} 29 | ) 30 | {{ else }} 31 | expectType(t, `+"`{{ .Expr }}`"+`, env, reflect.TypeOf({{ .Expr }})){{ end }} 32 | `)) 33 | 34 | func (*Test) Package() string { 35 | return "eval" 36 | } 37 | 38 | func (*Test) Prefix() string { 39 | return "CheckStarExpr" 40 | } 41 | 42 | func (*Test) Imports() map[string]string { 43 | return map[string]string { "reflect": "" } 44 | } 45 | 46 | func (*Test) Dimensions() []testgen.Dimension { 47 | stars := []testgen.Element{ 48 | {"A", "a"}, 49 | {"B", "b"}, 50 | {"AtA", "&a"}, 51 | {"AtB", "&b"}, 52 | {"Int", "int(1)"}, 53 | {"Number", "1.4"}, 54 | {"Rune", "'a'"}, 55 | {"Bool", "true"}, 56 | {"String", `"a"`}, 57 | {"Nil", "nil"}, 58 | {"StarB", "*b"}, 59 | } 60 | return []testgen.Dimension{ 61 | stars, 62 | } 63 | } 64 | 65 | func (*Test) Globals(w io.Writer) error { 66 | return nil 67 | } 68 | 69 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 70 | vars := map[string] interface{} { 71 | "Star": elts[0], 72 | } 73 | 74 | return comment.Execute(w, vars) 75 | } 76 | 77 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 78 | expr := fmt.Sprintf("*%v", elts[0].Value) 79 | 80 | compileErrs, err := compileExprWithDefs(expr, defs) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | vars := map[string] interface{} { 86 | "Expr": expr, 87 | "Errors": compileErrs, 88 | } 89 | 90 | return body.Execute(w, &vars) 91 | } 92 | 93 | -------------------------------------------------------------------------------- /testgen/checkunaryexpr_addr_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | "github.com/0xfaded/go-testgen" 8 | ) 9 | 10 | type Test struct{} 11 | 12 | var comment = template.Must(template.New("Comment").Parse( 13 | `// Test {{ .Amp.Value }} 14 | `)) 15 | 16 | var defs = 17 | ` 18 | a := 1 19 | b := &a 20 | _ = b 21 | ` 22 | var body = template.Must(template.New("Body").Parse(defs + 23 | ` env := MakeSimpleEnv() 24 | env.Vars["a"] = reflect.ValueOf(&a) 25 | {{ if .Errors }} 26 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 27 | `+"`{{ . }}`"+`,{{ end }} 28 | ) 29 | {{ else }} 30 | expectType(t, `+"`{{ .Expr }}`"+`, env, reflect.TypeOf({{ .Expr }})){{ end }} 31 | `)) 32 | 33 | func (*Test) Package() string { 34 | return "eval" 35 | } 36 | 37 | func (*Test) Prefix() string { 38 | return "CheckAddrExpr" 39 | } 40 | 41 | func (*Test) Imports() map[string]string { 42 | return map[string]string { "reflect": "" } 43 | } 44 | 45 | func (*Test) Dimensions() []testgen.Dimension { 46 | stars := []testgen.Element{ 47 | {"A", "a"}, 48 | {"Int", "int(1)"}, 49 | {"Number", "1.4"}, 50 | {"Rune", "'a'"}, 51 | {"Bool", "true"}, 52 | {"String", `"a"`}, 53 | {"Nil", "nil"}, 54 | {"AtA", " &a"}, 55 | {"StarB", " *a"}, 56 | {"Slice", "[]int{1}"}, 57 | {"SliceElt", "[]int{1}[0]"}, 58 | } 59 | return []testgen.Dimension{ 60 | stars, 61 | } 62 | } 63 | 64 | func (*Test) Globals(w io.Writer) error { 65 | return nil 66 | } 67 | 68 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 69 | vars := map[string] interface{} { 70 | "Amp": elts[0], 71 | } 72 | 73 | return comment.Execute(w, vars) 74 | } 75 | 76 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 77 | expr := fmt.Sprintf("&%v", elts[0].Value) 78 | 79 | compileErrs, err := compileExprWithDefs(expr, defs) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | vars := map[string] interface{} { 85 | "Expr": expr, 86 | "Errors": compileErrs, 87 | } 88 | 89 | return body.Execute(w, &vars) 90 | } 91 | 92 | -------------------------------------------------------------------------------- /testgen/checkunaryexpr_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | "go/token" 8 | "github.com/0xfaded/go-testgen" 9 | ) 10 | 11 | type Test struct{} 12 | 13 | var comment = template.Must(template.New("Comment").Parse( 14 | `// Test {{ .Op.Value }} {{ .Rhs.Name }} 15 | `)) 16 | 17 | var body = template.Must(template.New("Body").Parse( 18 | ` env := MakeSimpleEnv() 19 | {{ if .Errors }} 20 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 21 | `+"`{{ . }}`"+`,{{ end }} 22 | ) 23 | {{ else }} 24 | expectConst(t, `+"`{{ .Expr }}`"+`, env, {{ .NewConstType }}({{ .Expr }}), {{ .ResultType }}){{ end }} 25 | `)) 26 | 27 | func (*Test) Package() string { 28 | return "eval" 29 | } 30 | 31 | func (*Test) Prefix() string { 32 | return "CheckUnaryExpr" 33 | } 34 | 35 | func (*Test) Imports() map[string]string { 36 | return nil 37 | } 38 | 39 | func (*Test) Dimensions() []testgen.Dimension { 40 | // All numeric values have been chosen to be a power of two, for good reason :) 41 | types := []testgen.Element{ 42 | {"Int", "4"}, 43 | {"Rune", "'@'"}, 44 | {"Float", "2.0"}, 45 | {"Complex", "8.0i"}, 46 | {"Bool", "true"}, 47 | {"String", `"abc"`}, 48 | {"Nil", "nil"}, 49 | } 50 | ops := []testgen.Element{ 51 | {"Add", token.ADD}, 52 | {"Sub", token.SUB}, 53 | {"Xor", token.XOR}, 54 | {"Not", token.NOT}, 55 | } 56 | return []testgen.Dimension{ 57 | ops, 58 | types, 59 | } 60 | } 61 | 62 | func (*Test) Globals(w io.Writer) error { 63 | return nil 64 | } 65 | 66 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 67 | vars := map[string] interface{} { 68 | "Op": elts[0], 69 | "Rhs": elts[1], 70 | } 71 | 72 | return comment.Execute(w, vars) 73 | } 74 | 75 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 76 | op := elts[0].Value.(token.Token) 77 | rhs := elts[1].Name 78 | 79 | expr := fmt.Sprintf("%v %v", op, elts[1].Value) 80 | compileErrs, err := compileExpr(expr) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | var newConstType string 86 | var resultType string 87 | 88 | switch rhs { 89 | case "Int": 90 | newConstType = "NewConstInt64" 91 | resultType = "ConstInt" 92 | case "Rune": 93 | newConstType = "NewConstRune" 94 | resultType = "ConstRune" 95 | case "Float": 96 | newConstType = "NewConstFloat64" 97 | resultType = "ConstFloat" 98 | case "Complex": 99 | newConstType = "NewConstComplex128" 100 | resultType = "ConstComplex" 101 | case "Bool": 102 | resultType = "ConstBool" 103 | } 104 | 105 | vars := map[string] interface{} { 106 | "Expr": expr, 107 | "Errors": compileErrs, 108 | "Op": elts[1], 109 | "NewConstType": newConstType, 110 | "ResultType": resultType, 111 | } 112 | 113 | return body.Execute(w, &vars) 114 | } 115 | 116 | -------------------------------------------------------------------------------- /testgen/checkunaryexpr_typed_gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "text/template" 7 | "go/token" 8 | "github.com/0xfaded/go-testgen" 9 | ) 10 | 11 | 12 | type Test struct{} 13 | 14 | var comment = template.Must(template.New("Comment").Parse( 15 | `// Test {{ .Op.Value }} {{ .Rhs.Name }} 16 | `)) 17 | 18 | var body = template.Must(template.New("Body").Parse( 19 | ` env := MakeSimpleEnv() 20 | {{ if .Errors }} 21 | expectCheckError(t, `+"`{{ .Expr }}`"+`, env,{{ range .Errors }} 22 | `+"`{{ . }}`"+`,{{ end }} 23 | ) 24 | {{ else }} 25 | expectConst(t, `+"`{{ .Expr }}`"+`, env, {{ .Expr }}, reflect.TypeOf({{ .Expr }})){{ end }} 26 | `)) 27 | 28 | func (*Test) Package() string { 29 | return "eval" 30 | } 31 | 32 | func (*Test) Prefix() string { 33 | return "CheckUnaryTypedExpr" 34 | } 35 | 36 | func (*Test) Imports() map[string]string { 37 | return map[string]string {"reflect": ""} 38 | } 39 | 40 | func (*Test) Dimensions() []testgen.Dimension { 41 | // All numeric values have been chosen to be a power of two, for good reason :) 42 | types := []testgen.Element{ 43 | {"Int32", "int32(4)"}, 44 | //TODO[crc] go1.1 doesnt pick this up. Add after upgrade to 1.2 45 | // {"int32Overflow", "int32(-0x80000000)"}, 46 | {"Float64", "float64(2)"}, 47 | {"Complex128", "complex128(8i)"}, 48 | {"Bool", "bool(true)"}, 49 | {"String", `string("abc")`}, 50 | } 51 | ops := []testgen.Element{ 52 | {"Add", token.ADD}, 53 | {"Sub", token.SUB}, 54 | {"Xor", token.XOR}, 55 | {"Not", token.NOT}, 56 | } 57 | return []testgen.Dimension{ 58 | ops, 59 | types, 60 | } 61 | } 62 | 63 | func (*Test) Globals(w io.Writer) error { 64 | return nil 65 | } 66 | 67 | func (*Test) Comment(w io.Writer, elts ...testgen.Element) error { 68 | vars := map[string] interface{} { 69 | "Op": elts[0], 70 | "Rhs": elts[1], 71 | } 72 | 73 | return comment.Execute(w, vars) 74 | } 75 | 76 | func (*Test) Body(w io.Writer, elts ...testgen.Element) error { 77 | op := elts[0].Value.(token.Token) 78 | 79 | expr := fmt.Sprintf("%v %v", op, elts[1].Value) 80 | compileErrs, err := compileExpr(expr) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | vars := map[string] interface{} { 86 | "Expr": expr, 87 | "Errors": compileErrs, 88 | "Op": elts[1], 89 | } 90 | 91 | return body.Execute(w, &vars) 92 | } 93 | 94 | -------------------------------------------------------------------------------- /testgen/common.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "go/build" 6 | "fmt" 7 | 8 | "io" 9 | "io/ioutil" 10 | 11 | "strings" 12 | 13 | "os" 14 | "os/exec" 15 | ) 16 | 17 | func compileExpr(expr string) (compileErrors []string, err error) { 18 | return compileExprWithDefs(expr, "") 19 | } 20 | 21 | func compileExprWithDefs(expr, defs string) (compileErrors []string, err error) { 22 | return compileExprWithDefsAndGlobals(expr, defs, "") 23 | } 24 | 25 | func compileExprWithDefsAndGlobals(expr, defs, globals string) (compileErrors []string, err error) { 26 | body := `package main 27 | 28 | %s 29 | 30 | func main() { 31 | %s 32 | (func(...interface{}) {})(%s) 33 | } 34 | ` 35 | return compile(expr, defs, globals, body) 36 | } 37 | 38 | func compileVoidExpr(expr string) (compileErrors []string, err error) { 39 | return compileVoidExprWithDefs(expr, "") 40 | } 41 | 42 | func compileVoidExprWithDefs(expr, defs string) (compileErrors []string, err error) { 43 | return compileVoidExprWithDefsAndGlobals(expr, defs, "") 44 | } 45 | 46 | func compileVoidExprWithDefsAndGlobals(expr, defs, globals string) (compileErrors []string, err error) { 47 | body := `package main 48 | 49 | %s 50 | 51 | func main() { 52 | %s 53 | %s 54 | } 55 | ` 56 | return compile(expr, defs, globals, body) 57 | } 58 | 59 | func compile(expr, defs, globals, body string) (compileErrors []string, err error) { 60 | f, err := ioutil.TempFile("/tmp", "testgen") 61 | if err != nil { 62 | return nil, err 63 | } 64 | defer os.Remove(f.Name()) 65 | 66 | _, err = fmt.Fprintf(f, body, globals, defs, expr); 67 | 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | // -e prints all errors 73 | cmd := exec.Command(build.ToolDir + "/8g", "-e", "-o", "/dev/null", f.Name()) 74 | stdout, err := cmd.StdoutPipe() 75 | if err != nil { 76 | return nil, err 77 | } 78 | if err := cmd.Start(); err != nil { 79 | return nil, err 80 | } 81 | buf := bufio.NewReader(stdout) 82 | 83 | line, rerr := buf.ReadString('\n') 84 | for rerr == nil { 85 | if strings.Index(line, ": ") != -1 { 86 | // Remove filename prefix 87 | s := strings.SplitN(line, ": ", 2)[1] 88 | // Remove trailing \n 89 | s = s[:len(s)-1] 90 | compileErrors = append(compileErrors, s) 91 | } 92 | line, rerr = buf.ReadString('\n') 93 | } 94 | if rerr != io.EOF { 95 | return nil, rerr 96 | } else { 97 | return compileErrors, nil 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /testgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "github.com/0xfaded/go-testgen" 7 | ) 8 | 9 | func main() { 10 | if err := testgen.Generate(&Test{}, os.Stdout); err != nil { 11 | panic(fmt.Sprintf("Test generation failed %v\n", err)) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /untypednil.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | type UntypedNil struct {} 4 | func (UntypedNil) String() string { 5 | return "nil" 6 | } 7 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | 7 | "go/parser" 8 | ) 9 | 10 | func errorPosEqual(a, b []string) bool { 11 | if len(a) != len(b) { return false } 12 | for i, aVal := range a { 13 | if aVal != b[i] { return false } 14 | } 15 | return true 16 | } 17 | 18 | func TestFormatErrorPos(t *testing.T) { 19 | source := `split(os.Args ", )")` 20 | errmsg := `1:15: expected ')', found 'STRING' ", "` 21 | results := FormatErrorPos(source, errmsg) 22 | expect := []string { source, "--------------^" } 23 | if !errorPosEqual(expect, results) { 24 | t.Fatalf("Expected %v, got %v", expect, results) 25 | } 26 | 27 | source = "`" 28 | errmsg = `1:1: string not terminated` 29 | results = FormatErrorPos(source, errmsg) 30 | expect = []string { source, "^" } 31 | 32 | source = "y(" 33 | errmsg = `1:3: expected ')', found 'EOF'` 34 | results = FormatErrorPos(source, errmsg) 35 | expect = []string { source, "--^" } 36 | 37 | } 38 | 39 | func TestIsPkgAddressable(t *testing.T) { 40 | env := MakeSimpleEnv() 41 | env.Pkgs["a"] = MakeSimpleEnv() 42 | env.Pkgs["a"].(*SimpleEnv).Vars["v"] = reflect.ValueOf(new(int)) 43 | env.Pkgs["a"].(*SimpleEnv).Funcs["f"] = reflect.ValueOf(func() {}) 44 | v, _ := parser.ParseExpr("a.v") 45 | vv, _ := CheckExpr(v, env) 46 | if !isAddressable(vv) { 47 | t.Fatalf("expected package var 'a.v' to be addressible") 48 | } 49 | 50 | f, _ := parser.ParseExpr("a.f") 51 | ff, _ := CheckExpr(f, env) 52 | if isAddressable(ff) { 53 | t.Fatalf("expected package func 'a.f' to be unaddressible") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /walk.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "go/ast" 5 | ) 6 | 7 | type exprVisitor interface { 8 | visit(expr Expr) bool 9 | } 10 | 11 | func walk(expr ast.Expr, visitor exprVisitor) { 12 | if expr == nil { 13 | return 14 | } 15 | switch expr := expr.(type) { 16 | case *BadExpr: 17 | visitor.visit(expr) 18 | case *Ident: 19 | visitor.visit(expr) 20 | case *Ellipsis: 21 | visitor.visit(expr) 22 | case *BasicLit: 23 | visitor.visit(expr) 24 | case *FuncLit: 25 | visitor.visit(expr) 26 | case *CompositeLit: 27 | if visitor.visit(expr) { 28 | for _, elt := range expr.Elts { 29 | walk(elt, visitor) 30 | } 31 | } 32 | case *ParenExpr: 33 | if visitor.visit(expr) { 34 | walk(expr.X, visitor) 35 | } 36 | case *SelectorExpr: 37 | if visitor.visit(expr) { 38 | walk(expr.X, visitor) 39 | } 40 | case *IndexExpr: 41 | if visitor.visit(expr) { 42 | walk(expr.Index, visitor) 43 | walk(expr.X, visitor) 44 | } 45 | case *SliceExpr: 46 | if visitor.visit(expr) { 47 | walk(expr.Low, visitor) 48 | walk(expr.High, visitor) 49 | // TODO[crc] go 1.2 introduces the [::] notation. Add after upgrade 50 | // walk(expr.Max, visitor) 51 | } 52 | case *TypeAssertExpr: 53 | if visitor.visit(expr) { 54 | walk(expr.X, visitor) 55 | } 56 | case *CallExpr: 57 | if visitor.visit(expr) { 58 | for _, arg := range expr.Args { 59 | walk(arg, visitor) 60 | } 61 | } 62 | case *StarExpr: 63 | if visitor.visit(expr) { 64 | walk(expr.X, visitor) 65 | } 66 | case *UnaryExpr: 67 | if visitor.visit(expr) { 68 | walk(expr.X, visitor) 69 | } 70 | case *BinaryExpr: 71 | if visitor.visit(expr) { 72 | walk(expr.X, visitor) 73 | walk(expr.Y, visitor) 74 | } 75 | case *KeyValueExpr: 76 | if visitor.visit(expr) { 77 | walk(expr.Key, visitor) 78 | walk(expr.Value, visitor) 79 | } 80 | } 81 | } 82 | --------------------------------------------------------------------------------