Skip to content

Commit

Permalink
TestOperator: BinaryOp/UnaryOp
Browse files Browse the repository at this point in the history
  • Loading branch information
xushiwei committed Jul 2, 2021
1 parent 40e67a4 commit 6f157a8
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 14 deletions.
82 changes: 78 additions & 4 deletions ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"go/types"
"log"
"strconv"
"strings"

"github.com/goplus/gox/internal"
)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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())
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
}

Expand Down
112 changes: 112 additions & 0 deletions builtin.gop
Original file line number Diff line number Diff line change
@@ -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

// -----------------------------------------------------------------------------
34 changes: 33 additions & 1 deletion codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
)

Expand Down
38 changes: 31 additions & 7 deletions package.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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
}

Expand All @@ -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
}

// ----------------------------------------------------------------------------
29 changes: 27 additions & 2 deletions package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 6f157a8

Please sign in to comment.