func buildIdent(b *builder, ident *lex8.Token) tast.Expr { s := b.scope.Query(ident.Lit) if s == nil { b.Errorf(ident.Pos, "undefined identifier %s", ident.Lit) return nil } b.refSym(s, ident.Pos) t := s.ObjType.(types.T) switch s.Type { case tast.SymVar, tast.SymField: ref := tast.NewAddressableRef(t) return &tast.Ident{ident, ref, s} case tast.SymConst, tast.SymStruct, tast.SymType, tast.SymImport: ref := tast.NewRef(t) return &tast.Ident{ident, ref, s} case tast.SymFunc: if t, ok := t.(*types.Func); ok { if t.MethodFunc == nil { return &tast.Ident{ident, tast.NewRef(t), s} } if b.this == nil { panic("this missing") } ref := &tast.Ref{T: t.MethodFunc, Recv: b.this} return &tast.Ident{ident, ref, s} } return &tast.Ident{ident, tast.NewRef(t), s} default: b.Errorf(ident.Pos, "todo: token type: %s", tast.SymStr(s.Type)) return nil } }
func buildPkgSym( b *builder, m *ast.MemberExpr, pkg *types.Pkg, ) (*tast.Ref, *sym8.Symbol) { sym := findPackageSym(b, m.Sub, pkg) if sym == nil { return nil, nil } if pkg.Lang == "asm8" { switch sym.Type { case asm8.SymVar: return tast.NewRef(types.Uint), sym case asm8.SymFunc: return tast.NewRef(types.VoidFunc), sym } b.Errorf(m.Sub.Pos, "invalid symbol %s in %s: %s", m.Sub.Lit, pkg, asm8.SymStr(sym.Type), ) return nil, nil } t := sym.ObjType.(types.T) switch sym.Type { case tast.SymConst, tast.SymStruct, tast.SymFunc: return tast.NewRef(t), sym case tast.SymVar: return tast.NewAddressableRef(t), sym } b.Errorf(m.Sub.Pos, "bug: invalid symbol %s in %s: %s", m.Sub.Lit, pkg, tast.SymStr(sym.Type), ) return nil, nil }
func binaryOpInt( b *builder, opTok *lex8.Token, A, B tast.Expr, t types.T, ) tast.Expr { op := opTok.Lit switch op { case "+", "-", "*", "&", "|", "^", "%", "/": r := tast.NewRef(t) return &tast.OpExpr{A, opTok, B, r} case "==", "!=", ">", "<", ">=", "<=": r := tast.NewRef(types.Bool) return &tast.OpExpr{A, opTok, B, r} } b.Errorf(opTok.Pos, "%q on ints", op) return nil }
func buildSlicing( b *builder, expr *ast.IndexExpr, array tast.Expr, ) tast.Expr { t := array.R().T et := elementType(t) if et == nil { b.Errorf(expr.Lbrack.Pos, "slicing on neither array nor slice") return nil } var indexStart, indexEnd tast.Expr if expr.Index != nil { indexStart = buildArrayIndex(b, expr.Index, expr.Lbrack.Pos) if indexStart == nil { return nil } } if expr.IndexEnd != nil { indexEnd = buildArrayIndex(b, expr.IndexEnd, expr.Colon.Pos) if indexEnd == nil { return nil } } ref := tast.NewRef(&types.Slice{et}) return &tast.IndexExpr{ Array: array, Index: indexStart, IndexEnd: indexEnd, HasColon: true, Ref: ref, } }
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 buildConstMember(b *builder, m *ast.MemberExpr) tast.Expr { obj := b.buildConstExpr(m.Expr) if obj == nil { return nil } ref := obj.R() if !ref.IsSingle() { b.Errorf(m.Dot.Pos, "%s does not have any member", ref) return nil } if pkg, ok := ref.T.(*types.Pkg); ok { s := findPackageSym(b, m.Sub, pkg) if s == nil { return nil } if s.Type != tast.SymConst { b.Errorf(m.Sub.Pos, "%s.%s is not a const", pkg, m.Sub.Lit) return nil } return &tast.Const{tast.NewRef(s.ObjType.(types.T))} } b.Errorf(m.Dot.Pos, "expect const expression") return nil }
func binaryOpSlice(b *builder, opTok *lex8.Token, A, B tast.Expr) tast.Expr { op := opTok.Lit switch op { case "==", "!=": return &tast.OpExpr{A, opTok, B, tast.NewRef(types.Bool)} } b.Errorf(opTok.Pos, "%q on slices", op) return nil }
func unaryOpBool(b *builder, opTok *lex8.Token, B tast.Expr) tast.Expr { op := opTok.Lit if op == "!" { t := B.R().T return &tast.OpExpr{nil, opTok, B, tast.NewRef(t)} } b.Errorf(opTok.Pos, "invalid operation: %q on boolean", op) return nil }
func binaryOpBool(b *builder, opTok *lex8.Token, A, B tast.Expr) tast.Expr { op := opTok.Lit switch op { case "==", "!=", "&&", "||": r := tast.NewRef(types.Bool) return &tast.OpExpr{A, opTok, B, r} } b.Errorf(opTok.Pos, "%q on bools", op) return nil }
func buildMethod(b *builder, f *pkgFunc) *tast.Func { this := f.recv.pt b.thisType = this if f.f.Recv != nil { // go-like, explicit receiver b.this = tast.NewAddressableRef(this) } else { // inlined b.this = tast.NewRef(this) b.scope.PushTable(f.recv.t.Syms) defer b.scope.Pop() } return buildFunc(b, f) }
func unaryOpInt(b *builder, opTok *lex8.Token, B tast.Expr) tast.Expr { op := opTok.Lit switch op { case "+": return B case "-", "^": t := B.R().T return &tast.OpExpr{nil, opTok, B, tast.NewRef(t)} } b.Errorf(opTok.Pos, "invalid operation: %q on %s", op, B) return nil }
func buildCallLen(b *builder, expr *ast.CallExpr, f tast.Expr) tast.Expr { args := buildExprList(b, expr.Args) if args == nil { return nil } ref := args.R() if !ref.IsSingle() { b.Errorf(expr.Lparen.Pos, "len() takes one argument") return nil } t := ref.T switch t.(type) { case *types.Slice: return &tast.CallExpr{f, args, tast.NewRef(types.Int)} case *types.Array: return &tast.CallExpr{f, args, tast.NewRef(types.Int)} } b.Errorf(expr.Lparen.Pos, "len() does not take %s", t) return nil }
func buildConstIdent(b *builder, ident *lex8.Token) tast.Expr { s := b.scope.Query(ident.Lit) if s == nil { b.Errorf(ident.Pos, "undefined identifier %s", ident.Lit) return nil } b.refSym(s, ident.Pos) t := s.ObjType.(types.T) switch s.Type { case tast.SymConst: ref := tast.NewRef(t) return &tast.Const{ref} case tast.SymStruct, tast.SymType, tast.SymImport: ref := tast.NewRef(t) return &tast.Ident{ident, ref, s} } b.Errorf(ident.Pos, "%s is a %s; expect a const", ident.Lit, tast.SymStr(s.Type), ) return nil }
func refAddress(b *builder, opTok *lex8.Token, B tast.Expr) tast.Expr { op := opTok.Lit opPos := opTok.Pos bref := B.R() if types.IsType(bref.T) || !bref.IsSingle() { b.Errorf(opPos, "%q on %s", op, bref) return nil } else if !bref.Addressable { b.Errorf(opPos, "reading address of non-addressable") return nil } r := tast.NewRef(&types.Pointer{bref.T}) return &tast.OpExpr{nil, opTok, B, r} }
func binaryOpPtr(b *builder, opTok *lex8.Token, A, B tast.Expr) tast.Expr { op := opTok.Lit atyp := A.R().T btyp := B.R().T switch op { case "==", "!=": if types.IsNil(atyp) { A = tast.NewCast(A, btyp) } else if types.IsNil(btyp) { B = tast.NewCast(B, atyp) } return &tast.OpExpr{A, opTok, B, tast.NewRef(types.Bool)} } b.Errorf(opTok.Pos, "%q on pointers", 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 buildMember(b *builder, m *ast.MemberExpr) tast.Expr { obj := b.buildExpr(m.Expr) if obj == nil { return nil } ref := obj.R() if !ref.IsSingle() { b.Errorf(m.Dot.Pos, "%s does not have any member", ref) return nil } t := ref.T if pkg, ok := t.(*types.Pkg); ok { r, sym := buildPkgSym(b, m, pkg) if r == nil { return nil } // TODO: this can be further optimized return &tast.MemberExpr{obj, m.Sub, r, sym} } pt := types.PointerOf(t) var tstruct *types.Struct var ok bool if pt != nil { if tstruct, ok = pt.(*types.Struct); !ok { b.Errorf(m.Dot.Pos, "*%s is not a pointer of struct", t) return nil } } else { if tstruct, ok = t.(*types.Struct); !ok { b.Errorf(m.Dot.Pos, "%s is not a struct", t) return nil } } symTable := tstruct.Syms name := m.Sub.Lit sym := symTable.Query(name) if sym == nil { b.Errorf(m.Sub.Pos, "struct %s has no member named %s", tstruct, name, ) return nil } else if !sym8.IsPublic(name) && sym.Pkg() != b.path { b.Errorf(m.Sub.Pos, "symbol %s is not public", name) return nil } b.refSym(sym, m.Sub.Pos) if sym.Type == tast.SymField { t := sym.ObjType.(types.T) r := tast.NewAddressableRef(t) return &tast.MemberExpr{obj, m.Sub, r, sym} } else if sym.Type == tast.SymFunc { ft := sym.ObjType.(*types.Func) r := tast.NewRef(ft.MethodFunc) r.Recv = ref return &tast.MemberExpr{obj, m.Sub, r, sym} } b.Errorf(m.Sub.Pos, "invalid sym type: %s", tast.SymStr(sym.Type)) return nil }
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 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} }