func binaryOpConst(b *builder, op string, A, B *ref) *ref { va, _ := types.NumConst(A.Type()) vb, _ := types.NumConst(B.Type()) br := func(b bool) *ref { if b { return refTrue } return refFalse } switch op { case "==": return br(va == vb) case "!=": return br(va != vb) case ">": return br(va > vb) case "<": return br(va < vb) case ">=": return br(va >= vb) case "<=": return br(va <= vb) } panic("bug") }
func opAssign(b *builder, dest, src tast.Expr, op *lex8.Token) tast.Stmt { destRef := dest.R() srcRef := src.R() if !destRef.IsSingle() || !srcRef.IsSingle() { b.Errorf(op.Pos, "%s %s %s", destRef, op.Lit, srcRef) return nil } else if !destRef.Addressable { b.Errorf(op.Pos, "assign to non-addressable") return nil } opLit := parseAssignOp(op.Lit) destType := destRef.Type() srcType := srcRef.Type() if opLit == ">>" || opLit == "<<" { if v, ok := types.NumConst(srcType); ok { src = constCast(b, op.Pos, v, src, types.Uint) if src == nil { return nil } srcRef = src.R() srcType = types.Uint } if !canShift(b, destType, srcType, op.Pos, opLit) { return nil } return &tast.AssignStmt{dest, op, src} } if v, ok := types.NumConst(srcType); ok { src = constCast(b, op.Pos, v, src, destType) if src == nil { return nil } srcRef = src.R() srcType = destType } if ok, t := types.SameBasic(destType, srcType); ok { switch t { case types.Int, types.Int8, types.Uint, types.Uint8: return &tast.AssignStmt{dest, op, src} } } b.Errorf(op.Pos, "invalid %s %s %s", destType, opLit, srcType) return nil }
// allocPrepare checks if the provided types are all allocable, and insert // implicit type casts if needed. Only literay expression list needs alloc // prepare. func allocPrepare( b *builder, toks []*lex8.Token, lst *tast.ExprList, ) *tast.ExprList { ret := tast.NewExprList() for i, tok := range toks { e := lst.Exprs[i] t := e.Type() if types.IsNil(t) { b.Errorf(tok.Pos, "cannot infer type from nil for %q", tok.Lit) return nil } if v, ok := types.NumConst(t); ok { e = constCastInt(b, tok.Pos, v, e) if e == nil { return nil } } if !types.IsAllocable(t) { b.Errorf(tok.Pos, "cannot allocate for %s", t) return nil } ret.Append(e) } return ret }
func unaryOpConst(b *builder, opTok *lex8.Token, B tast.Expr) tast.Expr { op := opTok.Lit bref := B.R() if !bref.IsSingle() { b.Errorf(opTok.Pos, "invalid operation: %q on %s", op, bref) return nil } v, ok := types.NumConst(bref.T) if !ok { // TODO: support type const b.Errorf(opTok.Pos, "typed const operation not implemented") return nil } switch op { case "+": return B // just shortcut this case "-": return &tast.Const{tast.NewRef(types.NewNumber(-v))} } b.Errorf(opTok.Pos, "invalid operation: %q on %s", op, B) return nil }
func buildCast(b *builder, expr *ast.CallExpr, t types.T) tast.Expr { pos := expr.Lparen.Pos args := buildExprList(b, expr.Args) if args == nil { return nil } ref := args.R() if !ref.IsSingle() { b.Errorf(pos, "cannot convert %s to %s", ref, t) return nil } srcType := ref.T if c, ok := srcType.(*types.Const); ok { if v, ok := types.NumConst(srcType); ok && types.IsInteger(t) { return constCast(b, pos, v, args, t) } srcType = c.Type // using the underlying type } if types.IsInteger(t) && types.IsInteger(srcType) { return tast.NewCast(args, t) } if regSizeCastable(t, srcType) { return tast.NewCast(args, t) } b.Errorf(pos, "cannot convert from %s to %s", srcType, t) return nil }
func buildConst(b *builder, c *tast.Const) *ref { if _, ok := types.NumConst(c.T); ok { // untyped consts are just types. return newRef(c.T, nil) } if t, ok := c.T.(types.Basic); ok { v := c.ConstValue.(int64) switch t { case types.Int, types.Uint: return newRef(c.T, ir.Num(uint32(v))) case types.Int8, types.Uint8, types.Bool: return newRef(c.T, ir.Byt(uint8(v))) default: panic("other basic types not supported yet") } } if c.T == types.String { s := c.ConstValue.(string) ret := b.newTemp(c.T) b.b.Arith(ret.IR(), nil, "makeStr", b.p.NewString(s)) return ret } panic("other const types not supported") }
func checkArrayIndex(b *builder, index tast.Expr, pos *lex8.Pos) tast.Expr { t := index.R().T if v, ok := types.NumConst(t); ok { if v < 0 { b.Errorf(pos, "array index is negative: %d", v) return nil } return constCastInt(b, pos, v, index) } if !types.IsInteger(t) { b.Errorf(pos, "index must be an integer") return nil } return index }
func assign(b *builder, dest, src tast.Expr, op *lex8.Token) tast.Stmt { destRef := dest.R() srcRef := src.R() ndest := destRef.Len() nsrc := srcRef.Len() if ndest != nsrc { b.Errorf(op.Pos, "cannot assign %s to %s", nsrc, ndest) return nil } for i := 0; i < ndest; i++ { r := destRef.At(i) if !r.Addressable { b.Errorf(op.Pos, "assigning to non-addressable") return nil } destType := r.Type() srcType := srcRef.At(i).Type() if !types.CanAssign(destType, srcType) { b.Errorf(op.Pos, "cannot assign %s to %s", srcType, destType) return nil } } // insert casting if needed if srcList, ok := tast.MakeExprList(src); ok { newList := tast.NewExprList() for i, e := range srcList.Exprs { t := e.Type() if types.IsNil(t) { e = tast.NewCast(e, destRef.At(i).Type()) } else if v, ok := types.NumConst(t); ok { e = constCast(b, nil, v, e, destRef.At(i).Type()) if e == nil { panic("bug") } } newList.Append(e) } src = newList } return &tast.AssignStmt{dest, op, src} }
func varDeclPrepare( b *builder, toks []*lex8.Token, lst *tast.ExprList, t types.T, ) *tast.ExprList { ret := tast.NewExprList() for i, tok := range toks { e := lst.Exprs[i] etype := e.Type() if types.IsNil(etype) { e = tast.NewCast(e, t) } else if v, ok := types.NumConst(etype); ok { e = constCast(b, tok.Pos, v, e, t) if e == nil { return nil } } ret.Append(e) } return ret }
func buildArrayType(b *builder, expr *ast.ArrayTypeExpr) types.T { t := buildType(b, expr.Type) if t == nil { return nil } if expr.Len == nil { // slice return &types.Slice{t} } // array n := b.buildConstExpr(expr.Len) if n == nil { return nil } ntype := n.R().T c, ok := ntype.(*types.Const) if !ok { // might be true, false, or other builtin consts b.Errorf(ast.ExprPos(expr), "array index is not a constant") return nil } if v, ok := types.NumConst(ntype); ok { if v < 0 { b.Errorf(ast.ExprPos(expr), "array index is negative: %d", c.Value, ) return nil } else if !types.InRange(v, types.Int) { b.Errorf(ast.ExprPos(expr), "index out of range of int32") return nil } return &types.Array{T: t, N: int32(v)} } // TODO: support typed const b.Errorf(ast.ExprPos(expr), "typed const not implemented yet") return nil }
func buildConst(b *builder, c *tast.Const) *ref { if _, ok := types.NumConst(c.T); ok { // untyped consts are just types. return newRef(c.T, nil) } if t, ok := c.T.(types.Basic); ok { v := c.ConstValue.(int64) return newRef(c.T, constNumIr(v, t)) } if c.T == types.String { s := c.ConstValue.(string) ret := b.newTemp(c.T) b.b.Arith(ret.IR(), nil, "makeStr", b.p.NewString(s)) return ret } if t, ok := c.T.(*types.Slice); ok { if bt, ok := t.T.(types.Basic); ok { switch bt { case types.Int, types.Uint, types.Int8, types.Uint8, types.Bool: bs := c.ConstValue.([]byte) ret := b.newTemp(t) ref := b.p.NewHeapDat(bs, bt.Size(), bt.RegSizeAlign()) b.b.Arith(ret.IR(), nil, "makeDat", ref) return ret default: panic("other const slices not supported") } } else { panic("not basic type") } } panic("other const types not supported") }
func buildCast(b *builder, from *ref, t types.T) *ref { srcType := from.Type() ret := b.newTemp(t) if types.IsNil(srcType) { size := t.Size() if size == arch8.RegSize { return newRef(t, ir.Num(0)) } if _, ok := t.(*types.Slice); !ok { panic("bug") } ret := b.newTemp(t) b.b.Zero(ret.IR()) return ret } if c, ok := srcType.(*types.Const); ok { if v, ok := types.NumConst(srcType); ok && types.IsInteger(t) { return newRef(t, constNumIr(v, t)) } // TODO: we do not support typed const right? // so why need this line? srcType = c.Type // using the underlying type } if types.IsInteger(t) && types.IsInteger(srcType) { b.b.Arith(ret.IR(), nil, "cast", from.IR()) return ret } if regSizeCastable(t, srcType) { b.b.Arith(ret.IR(), nil, "", from.IR()) return ret } panic("bug") }
func buildReturnStmt(b *builder, stmt *ast.ReturnStmt) tast.Stmt { pos := stmt.Kw.Pos if stmt.Exprs == nil { if b.retType == nil || b.retNamed { return &tast.ReturnStmt{} } b.Errorf(pos, "expects return %s", fmt8.Join(b.retType, ",")) return nil } if b.retType == nil { b.Errorf(pos, "function expects no return value") return nil } src := b.buildExpr(stmt.Exprs) if src == nil { return nil } srcRef := src.R() nret := len(b.retType) nsrc := srcRef.Len() if nret != nsrc { b.Errorf(pos, "expect (%s), returning (%s)", fmt8.Join(b.retType, ","), srcRef, ) return nil } for i := 0; i < nret; i++ { t := b.retType[i] srcType := srcRef.At(i).Type() if !types.CanAssign(t, srcType) { b.Errorf(pos, "expect (%s), returning (%s)", fmt8.Join(b.retType, ","), srcRef, ) return nil } } // insert implicit type casts if srcList, ok := tast.MakeExprList(src); ok { newList := tast.NewExprList() for i, e := range srcList.Exprs { t := e.Type() if types.IsNil(t) { e = tast.NewCast(e, b.retType[i]) } else if v, ok := types.NumConst(t); ok { e = constCast(b, nil, v, e, b.retType[i]) if e == nil { panic("bug") } } newList.Append(e) } src = newList } return &tast.ReturnStmt{src} }
func binaryOpConst(b *builder, opTok *lex8.Token, A, B tast.Expr) tast.Expr { op := opTok.Lit aref := A.R() bref := B.R() if aref.List != nil || bref.List != nil { b.Errorf(opTok.Pos, "invalid %s %q %s", aref.T, op, bref.T) return nil } va, oka := types.NumConst(aref.T) vb, okb := types.NumConst(bref.T) if !(oka && okb) { b.Errorf(opTok.Pos, "non-numeric consts ops not implemented") return nil } r := func(v int64) tast.Expr { return &tast.Const{tast.NewRef(types.NewNumber(v))} } switch op { case "+": return r(va + vb) case "-": return r(va - vb) case "*": return r(va * vb) case "&": return r(va & vb) case "|": return r(va | vb) case "^": return r(va ^ vb) case "%": if vb == 0 { b.Errorf(opTok.Pos, "modular by zero") return nil } return r(va % vb) case "/": if vb == 0 { b.Errorf(opTok.Pos, "divide by zero") return nil } return r(va / vb) case "==", "!=", ">", "<", ">=", "<=": return &tast.OpExpr{A, opTok, B, tast.NewRef(types.Bool)} case "<<": if vb < 0 { b.Errorf(opTok.Pos, "shift with negative value", vb) return nil } return r(va << uint64(vb)) case ">>": if vb < 0 { b.Errorf(opTok.Pos, "shift with negative value", vb) return nil } return r(va >> uint64(vb)) } b.Errorf(opTok.Pos, "%q on consts", op) return nil }
func buildCallExpr(b *builder, expr *ast.CallExpr) tast.Expr { f := b.buildExpr(expr.Func) if f == nil { return nil } pos := ast.ExprPos(expr.Func) fref := f.R() if !fref.IsSingle() { b.Errorf(pos, "%s is not callable", fref) return nil } if t, ok := fref.T.(*types.Type); ok { return buildCast(b, expr, t.T) } builtin, ok := fref.T.(*types.BuiltInFunc) if ok { switch builtin.Name { case "len": return buildCallLen(b, expr, f) case "make": return buildCallMake(b, expr, f) } b.Errorf(pos, "builtin %s() not implemented", builtin.Name) return nil } funcType, ok := fref.T.(*types.Func) if !ok { b.Errorf(pos, "function call on non-callable: %s", fref) return nil } args := buildExprList(b, expr.Args) if args == nil { return nil } argsRef := args.R() nargs := argsRef.Len() if nargs != len(funcType.Args) { b.Errorf(ast.ExprPos(expr), "argument expects (%s), got (%s)", fmt8.Join(funcType.Args, ","), args, ) return nil } // type check on each argument for i := 0; i < nargs; i++ { argType := argsRef.At(i).Type() expect := funcType.Args[i].T if !types.CanAssign(expect, argType) { pos := ast.ExprPos(expr.Args.Exprs[i]) b.Errorf(pos, "argument %d expects %s, got %s", i+1, expect, argType, ) return nil } } // insert casting when it is a literal expression list. callArgs, ok := tast.MakeExprList(args) if ok { castedArgs := tast.NewExprList() for i := 0; i < nargs; i++ { argExpr := callArgs.Exprs[i] argType := argExpr.Type() expect := funcType.Args[i].T // insert auto casts for consts if types.IsNil(argType) { castedArgs.Append(tast.NewCast(argExpr, expect)) } else if _, ok := types.NumConst(argType); ok { castedArgs.Append(tast.NewCast(argExpr, expect)) } else { castedArgs.Append(argExpr) } } args = castedArgs } retRef := tast.Void for _, t := range funcType.RetTypes { retRef = tast.AppendRef(retRef, tast.NewRef(t)) } return &tast.CallExpr{f, args, retRef} }
func buildBinaryOpExpr(b *builder, expr *ast.OpExpr) tast.Expr { opTok := expr.Op op := opTok.Lit opPos := opTok.Pos A := b.buildExpr(expr.A) if A == nil { return nil } aref := A.R() if !aref.IsSingle() { b.Errorf(opPos, "%q on %s", op, aref) return nil } atyp := aref.T B := b.buildExpr(expr.B) if B == nil { return nil } bref := B.R() if !bref.IsSingle() { b.Errorf(opPos, "%q on %s", op, bref) return nil } btyp := bref.T if types.IsConst(atyp) && types.IsConst(btyp) { return binaryOpConst(b, opTok, A, B) } if op == ">>" || op == "<<" { if v, ok := types.NumConst(btyp); ok { B = constCast(b, opPos, v, B, types.Uint) if B == nil { return nil } btyp = types.Uint } if v, ok := types.NumConst(atyp); ok { A = constCast(b, opPos, v, A, types.Int) if A == nil { return nil } atyp = types.Int } if !canShift(b, atyp, btyp, opPos, op) { return nil } r := tast.NewRef(atyp) return &tast.OpExpr{A, opTok, B, r} } if v, ok := types.NumConst(atyp); ok { A = constCast(b, opPos, v, A, btyp) if A == nil { return nil } atyp = btyp } else if c, ok := atyp.(*types.Const); ok { atyp = c.Type } if v, ok := types.NumConst(btyp); ok { B = constCast(b, opPos, v, B, atyp) if B == nil { return nil } btyp = atyp } else if c, ok := btyp.(*types.Const); ok { btyp = c.Type } if ok, t := types.SameBasic(atyp, btyp); ok { switch t { case types.Int, types.Int8, types.Uint, types.Uint8: return binaryOpInt(b, opTok, A, B, t) case types.Bool: return binaryOpBool(b, opTok, A, B) case types.Float32: b.Errorf(opPos, "floating point operations not implemented") return nil } } if types.IsNil(atyp) && types.IsNil(btyp) { return binaryOpNil(b, opTok, A, B) } else if types.BothPointer(atyp, btyp) { return binaryOpPtr(b, opTok, A, B) } else if types.BothFuncPointer(atyp, btyp) { return binaryOpPtr(b, opTok, A, B) } else if types.BothSlice(atyp, btyp) { return binaryOpSlice(b, opTok, A, B) } b.Errorf(opPos, "invalid operation of %s %s %s", atyp, op, btyp) if types.IsInteger(atyp) && types.IsInteger(btyp) { switch op { case "+", "-", "*", "&", "|", "^", "%", "/", "==", "!=", ">", "<", ">=", "<=": b.Errorf( opPos, "operation %s needs the same type on both sides", op, ) } } return nil }
func buildCallMake(b *builder, expr *ast.CallExpr, f tast.Expr) tast.Expr { args := buildExprList(b, expr.Args) if args == nil { return nil } n := args.R().Len() if n == 0 { b.Errorf(expr.Lparen.Pos, "make() takes at least one argument") return nil } argsList, ok := tast.MakeExprList(args) if !ok { b.Errorf(expr.Lparen.Pos, "make() only takes a literal list") return nil } arg0 := argsList.Exprs[0] t, ok := arg0.R().T.(*types.Type) if !ok { b.Errorf(expr.Lparen.Pos, "make() takes a type as the 1st argument") return nil } switch st := t.T.(type) { case *types.Slice: if n != 3 { b.Errorf(expr.Lparen.Pos, "make() slice takes 3 arguments") return nil } size := argsList.Exprs[1] pos := ast.ExprPos(expr.Args.Exprs[1]) size = checkArrayIndex(b, size, pos) if size == nil { return nil } start := argsList.Exprs[2] startType := start.R().T startPos := ast.ExprPos(expr.Args.Exprs[2]) if v, ok := types.NumConst(startType); ok { start = constCastUint(b, startPos, v, start) if start == nil { return nil } } else if !types.IsBasic(startType, types.Uint) { pt := types.PointerOf(startType) if pt == nil || !types.SameType(pt, st.T) { b.Errorf(startPos, "make() takes an uint or a typed pointer as the 3rd arg", ) return nil } } callArgs := tast.NewExprList() callArgs.Append(arg0) callArgs.Append(size) callArgs.Append(start) r := tast.NewRef(st) return &tast.CallExpr{f, callArgs, r} } b.Errorf(expr.Lparen.Pos, "cannot make() type %s", t.T) return nil }
func buildArrayLit(b *builder, lit *ast.ArrayLiteral) tast.Expr { hold := b.lhsSwap(false) defer b.lhsRestore(hold) if lit.Type.Len != nil { b.Errorf( ast.ExprPos(lit), "array literal with length not supported yet", ) return nil } buf := new(bytes.Buffer) t := buildType(b, lit.Type.Type) if t == nil { return nil } if !types.IsInteger(t) { pos := ast.ExprPos(lit.Type.Type) b.Errorf(pos, "array literal must be integer type") return nil } bt := t.(types.Basic) if lit.Exprs != nil { for _, expr := range lit.Exprs.Exprs { n := b.buildConstExpr(expr) if n == nil { return nil } ntype := n.R().T if _, ok := ntype.(*types.Const); !ok { b.Errorf(ast.ExprPos(expr), "array literal not a constant") return nil } if v, ok := types.NumConst(ntype); ok { if !types.InRange(v, t) { b.Errorf( ast.ExprPos(expr), "constant out of range of %s", t, ) return nil } switch bt { case types.Int, types.Uint: var bs [4]byte binary.LittleEndian.PutUint32(bs[:], uint32(v)) buf.Write(bs[:]) case types.Int8, types.Uint8: buf.Write([]byte{byte(v)}) default: panic("not integer") } } } } ref := tast.NewConstRef(&types.Slice{bt}, buf.Bytes()) return &tast.Const{ref} }