From 6f157a89188afb75b3e3bcfdc76a6ff4081e0f2a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 3 Jul 2021 03:15:09 +0800 Subject: [PATCH] TestOperator: BinaryOp/UnaryOp --- ast.go | 82 +++++++++++++++++++++++++++++++++-- builtin.gop | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ codebuild.go | 34 ++++++++++++++- package.go | 38 +++++++++++++--- package_test.go | 29 ++++++++++++- 5 files changed, 281 insertions(+), 14 deletions(-) create mode 100644 builtin.gop diff --git a/ast.go b/ast.go index 0a252977..b0bd140b 100644 --- a/ast.go +++ b/ast.go @@ -6,6 +6,7 @@ import ( "go/types" "log" "strconv" + "strings" "github.com/goplus/gox/internal" ) @@ -87,7 +88,8 @@ func toType(typ types.Type) ast.Expr { case *types.Array: return toArrayType(t) } - panic("TODO: toType") + log.Panicln("TODO: toType -", typ) + return nil } func toBasicType(t *types.Basic) ast.Expr { @@ -189,6 +191,11 @@ func toObjectExpr(pkg *Package, v types.Object) ast.Expr { if atPkg == pkg.Types { // at this package return ident(v.Name()) } + if atPkg == pkg.builtin { // at builtin package + if isBuiltinOp(v) { + return toOperatorExpr(v.Name()) + } + } importPkg, ok := pkg.importPkgs[atPkg.Path()] if !ok { log.Panicln("TODO: package not found -", atPkg.Name(), atPkg.Path()) @@ -201,6 +208,52 @@ func toObjectExpr(pkg *Package, v types.Object) ast.Expr { } } +func toOperatorExpr(fullName string) ast.Expr { + if pos := strings.LastIndex(fullName, "_"); pos > 0 { + name := fullName[pos:] + if op, ok := nameToOps[name]; ok { + if op.Arity == 2 { + return &ast.BinaryExpr{Op: op.Tok} + } + return &ast.UnaryExpr{Op: op.Tok} + } + } + log.Panicln("TODO: not a valid operator -", fullName) + return nil +} + +type operator struct { + Tok token.Token + Arity int +} + +var ( + nameToOps = map[string]operator{ + "_Add": {token.ADD, 2}, + "_Sub": {token.SUB, 2}, + "_Mul": {token.MUL, 2}, + "_Quo": {token.QUO, 2}, + "_Rem": {token.REM, 2}, + "_Or": {token.OR, 2}, + "_Xor": {token.XOR, 2}, + "_And": {token.AND, 2}, + "_AndNot": {token.AND_NOT, 2}, + + "_Lsh": {token.SHL, 2}, + "_Rsh": {token.SHR, 2}, + + "_LT": {token.LSS, 2}, + "_LE": {token.LEQ, 2}, + "_GT": {token.GTR, 2}, + "_GE": {token.GEQ, 2}, + "_EQ": {token.EQL, 2}, + "_NE": {token.NEQ, 2}, + + "_Neg": {token.SUB, 1}, + "_Not": {token.XOR, 1}, + } +) + func toFuncCall(fn internal.Elem, args []internal.Elem) internal.Elem { sig, ok := fn.Type.(*types.Signature) if !ok { @@ -231,9 +284,30 @@ func toFuncCall(fn internal.Elem, args []internal.Elem) internal.Elem { } matchFuncArgs(tyArgs, params) } - return internal.Elem{ - Val: &ast.CallExpr{Fun: fn.Val, Args: valArgs}, - Type: sig.Results(), + tyRet := toRetType(sig) + switch t := fn.Val.(type) { + case *ast.BinaryExpr: + t.X, t.Y = valArgs[0], valArgs[1] + return internal.Elem{Val: t, Type: tyRet} + case *ast.UnaryExpr: + t.X = valArgs[0] + return internal.Elem{Val: t, Type: tyRet} + default: + return internal.Elem{ + Val: &ast.CallExpr{Fun: fn.Val, Args: valArgs}, + Type: tyRet, + } + } +} + +func toRetType(sig *types.Signature) types.Type { + switch t := sig.Results(); t.Len() { + case 1: + return t.At(0).Type() + case 0: + return nil + default: + return t } } diff --git a/builtin.gop b/builtin.gop new file mode 100644 index 00000000..1355e8c2 --- /dev/null +++ b/builtin.gop @@ -0,0 +1,112 @@ +/* + Copyright 2021 The GoPlus Authors (goplus.org) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// +build ignore + +// ----------------------------------------------------------------------------- +// type int_type = int, int64, int32, int16, int8, uint, uintptr, uint64, uint32, uint16, uint8 + +func (a int_type) + (b int_type) int_type +func (a int_type) - (b int_type) int_type +func (a int_type) * (b int_type) int_type +func (a int_type) / (b int_type) int_type +func (a int_type) % (b int_type) int_type + +func (a int_type) | (b int_type) int_type +func (a int_type) ^ (b int_type) int_type +func (a int_type) & (b int_type) int_type +func (a int_type) &^ (b int_type) int_type + +func (a int_type) << (n untyped_uint) int_type +func (a int_type) >> (n untyped_uint) int_type + +func (a int_type) < (b int_type) bool +func (a int_type) <= (b int_type) bool +func (a int_type) > (b int_type) bool +func (a int_type) >= (b int_type) bool +func (a int_type) == (b int_type) bool +func (a int_type) != (b int_type) bool + +func -(a int_type) int_type +func ^(a int_type) int_type + +// ----------------------------------------------------------------------------- +// type untyped_int[T], T = untyped_int | int_type + +func (a untyped_int) + (b T) T +func (a untyped_int) - (b T) T +func (a untyped_int) * (b T) T +func (a untyped_int) / (b T) T +func (a untyped_int) % (b T) T + +func (a untyped_int) | (b T) T +func (a untyped_int) ^ (b T) T +func (a untyped_int) & (b T) T +func (a untyped_int) &^ (b T) T + +func (a untyped_int) << (n untyped_uint) untyped_int +func (a untyped_int) >> (n untyped_uint) untyped_int + +func (a untyped_int) < (b T) bool +func (a untyped_int) <= (b T) bool +func (a untyped_int) > (b T) bool +func (a untyped_int) >= (b T) bool +func (a untyped_int) == (b T) bool +func (a untyped_int) != (b T) bool + +func -(a untyped_int) untyped_int +func ^(a untyped_int) untyped_int + +// ----------------------------------------------------------------------------- +// type string + +func (a string) + (b string) string + +func (a string) < (b string) bool +func (a string) <= (b string bool +func (a string) > (b string) bool +func (a string) >= (b string) bool +func (a string) == (b string) bool +func (a string) != (b string) bool + +// ----------------------------------------------------------------------------- +// type float_type = float64, float32 + +func (a float_type) + (b float_type) float_type +func (a float_type) - (b float_type) float_type +func (a float_type) * (b float_type) float_type +func (a float_type) / (b float_type) float_type + +func (a float_type) < (b float_type) bool +func (a float_type) <= (b float_type bool +func (a float_type) > (b float_type) bool +func (a float_type) >= (b float_type) bool +func (a float_type) == (b float_type) bool +func (a float_type) != (b float_type) bool + +func -(a float_type) float_type + +// ----------------------------------------------------------------------------- +// type complex_type = complex128, complex64 + +func (a complex_type) + (b complex_type) complex_type +func (a complex_type) - (b complex_type) complex_type +func (a complex_type) * (b complex_type) complex_type +func (a complex_type) / (b complex_type) complex_type + +func (a complex_type) == (b complex_type) bool +func (a complex_type) != (b complex_type) bool + +func -(a complex_type) complex_type + +// ----------------------------------------------------------------------------- diff --git a/codebuild.go b/codebuild.go index 354542a8..63b8c323 100644 --- a/codebuild.go +++ b/codebuild.go @@ -135,9 +135,41 @@ var ( token.AND: "_And", // & token.OR: "_Or", // | token.XOR: "_Xor", // ^ + token.AND_NOT: "_AndNot", // &^ token.SHL: "_Lsh", // << token.SHR: "_Rsh", // >> - token.AND_NOT: "_AndNot", // &^ + + token.LSS: "_LT", + token.LEQ: "_LE", + token.GTR: "_GT", + token.GEQ: "_GE", + token.EQL: "_EQ", + token.NEQ: "_NE", + } +) + +// UnaryOp func +func (p *CodeBuilder) UnaryOp(op token.Token) *CodeBuilder { + pkg := p.pkg + args := p.stk.GetArgs(1) + if typ, ok := pkg.checkBuiltin(args[0].Type); ok { + name := pkg.prefix.Operator + typ + unaryOps[op] + fn := pkg.builtin.Scope().Lookup(name) + if fn == nil { + panic("TODO: operator not matched") + } + ret := toFuncCall(toObject(pkg, fn), args) + p.stk.Ret(1, ret) + } else { + panic("TODO: UnaryOp") + } + return p +} + +var ( + unaryOps = [...]string{ + token.SUB: "_Neg", + token.XOR: "_Not", } ) diff --git a/package.go b/package.go index ec554fe3..ff69997c 100644 --- a/package.go +++ b/package.go @@ -144,7 +144,7 @@ var ( var ( intBinaryOps = []string{ "_Add", "_Sub", "_Mul", "_Quo", "_Rem", "_Or", "_Xor", "_And", "_AndNot"} - intBooleanOps = []string{ + numStringBooleanOps = []string{ "_LT", "_LE", "_GT", "_GE", "_EQ", "_NE"} intTypes = []types.BasicKind{ types.Int, types.Int64, types.Int32, types.Int16, types.Int8, @@ -171,24 +171,43 @@ func addIntType(builtin *types.Package, typ, untypedUint types.Type, prefix *Nam n := types.NewVar(token.NoPos, builtin, "n", untypedUint) args2 := types.NewTuple(a, n) sig2 := types.NewSignature(nil, args2, ret, false) - gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"Lsh", sig2)) - gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"Rsh", sig2)) + gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"_Lsh", sig2)) + gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"_Rsh", sig2)) // func opPrefix_type_op(a, b type) bool ret3 := types.NewTuple(types.NewVar(token.NoPos, builtin, "", types.Typ[types.Bool])) sig3 := types.NewSignature(nil, args, ret3, false) - for _, op := range intBooleanOps { + for _, op := range numStringBooleanOps { gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+op, sig3)) } // func opPrefix_type_op(a type) type args4 := types.NewTuple(a) sig4 := types.NewSignature(nil, args4, ret, false) - gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"Neg", sig4)) - gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"Not", sig4)) + gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"_Neg", sig4)) + gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"_Not", sig4)) } -func addUntypedType(builtin *types.Package, typ types.Type, prefix *NamePrefix) { +func addStringType(builtin *types.Package, prefix *NamePrefix) { + gbl := builtin.Scope() + typ := types.Typ[types.String] + opPrefix := prefix.Operator + "string" + + a := types.NewVar(token.NoPos, builtin, "a", typ) + b := types.NewVar(token.NoPos, builtin, "b", typ) + args := types.NewTuple(a, b) + ret := types.NewTuple(types.NewVar(token.NoPos, builtin, "", typ)) + sig := types.NewSignature(nil, args, ret, false) + + // func opPrefix_type_op(a, b type) type + gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+"_Add", sig)) + + // func opPrefix_type_op(a, b type) bool + ret3 := types.NewTuple(types.NewVar(token.NoPos, builtin, "", types.Typ[types.Bool])) + sig3 := types.NewSignature(nil, args, ret3, false) + for _, op := range numStringBooleanOps { + gbl.Insert(types.NewFunc(token.NoPos, builtin, opPrefix+op, sig3)) + } } func newBuiltinDefault(prefix *NamePrefix) *types.Package { @@ -197,6 +216,7 @@ func newBuiltinDefault(prefix *NamePrefix) *types.Package { for _, intTy := range intTypes { addIntType(builtin, types.Typ[intTy], untypedUint, prefix) } + addStringType(builtin, prefix) return builtin } @@ -210,4 +230,8 @@ func defaultCheckBuiltinType(typ types.Type) (name string, is bool) { return } +func isBuiltinOp(v types.Object) bool { + return v.Pos() == token.NoPos +} + // ---------------------------------------------------------------------------- diff --git a/package_test.go b/package_test.go index 56af4e9a..ef1f6984 100644 --- a/package_test.go +++ b/package_test.go @@ -155,12 +155,37 @@ func main() { `) } -func TestBinaryOp(t *testing.T) { +func TestOperator(t *testing.T) { + var a, b, c, d *gox.Var + pkg := gox.NewPackage("", "main", nil) + pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg). + NewVar("a", &a).NewVar("b", &b).NewVar("c", &c).NewVar("d", &d). + VarRef(a).Val("Hi").Assign(1).EndStmt(). + VarRef(b).Val(a).Val("!").BinaryOp(token.ADD).Assign(1).EndStmt(). + VarRef(c).Val(&ast.BasicLit{Kind: token.INT, Value: "13"}).Assign(1).EndStmt(). + VarRef(d).Val(c).UnaryOp(token.SUB).Assign(1).EndStmt(). + End() + domTest(t, pkg, `package main + +func main() { + var a string + var b string + var c int + var d int + a = "Hi" + b = a + "!" + c = 13 + d = -c +} +`) +} + +func _TestBinaryOpUntyped(t *testing.T) { var a *gox.Var pkg := gox.NewPackage("", "main", nil) pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg). NewVar("a", &a). - Val("Hi").Val("!").BinaryOp(token.ADD).EndStmt(). + VarRef(a).Val("Hi").Val("!").BinaryOp(token.ADD).Assign(1).EndStmt(). End() domTest(t, pkg, `package main