func TestGolangTypeString(t *testing.T) { testCases := []struct { testType typ.Type expected string }{ {typ.Int, "int64"}, {typ.String, "string"}, {typ.Bool, "bool"}, {typ.Symbol, "string"}, {typ.NewFunc([]typ.Type{typ.String}, typ.String), "func(string) string"}, {typ.NewFunc([]typ.Type{ typ.String, typ.Int, typ.Bool, }, typ.Int), "func(string,int64,bool) int64"}, } for _, tc := range testCases { golangStr, err := golangStringForType(tc.testType) if err != nil { t.Errorf("Error return [%s]", err) } if golangStr != tc.expected { t.Errorf("Failed [%s]: %s != %s", tc.testType, golangStr, tc.expected) } else { t.Logf("Worked [%s]: %s", tc.testType, golangStr) } } }
func newDefaultTypeEnv() typ.Env { e := typ.NewEnv() ints := []typ.Type{typ.NewVariadic(typ.Int)} anys := []typ.Type{typ.NewVariadic(typ.Any)} f := typ.Frame{ "-": typ.NewFunc(ints, typ.Int), "+": typ.NewFunc(ints, typ.Int), "*": typ.NewFunc(ints, typ.Int), "=": typ.NewFunc(ints, typ.Bool), "display": typ.NewFunc(anys, typ.Void), "void": typ.NewFunc([]typ.Type{}, typ.Void), } return e.WithFrame(f) }
func (gb *GolangBackend) inferUnwrappedError(n gol.Node, typeEnv typ.Env, depth int) (int, error) { numChanges := 0 iprintf := func(format string, args ...interface{}) { indentStr := indentString(depth) s := fmt.Sprintf(format, args...) log.Printf("infer: %d %s%s", depth, indentStr, s) } iprintf("node %p: %s [%T]: %s (%s)\n", n, n, n, n.Type(), typ.ResolveStr(n.Type())) // Remember this to see if we changed it origType := n.Type() switch node := n.(type) { case *gol.NodeProgn: iprintf("progn\n") first := true err := node.NodeList.ForeachLast(func(child gol.Node, last bool) error { if first { // Skip the 'progn' symbol first = false return nil } childChanges, err := gb.infer(child, typeEnv, depth+1) if err != nil { return err } numChanges += childChanges if last { // Type of last child should match that of progn as a whole iprintf("Inferring type %s for child %s (type %s)\n", n.Type(), child, child.Type()) err = child.NodeUnify(n.Type(), typeEnv) if err != nil { return err } } else { // All other children should be void iprintf("Inferring void for %s [%T] %s\n", child, child.Type(), child.Type()) err = child.NodeUnify(typ.Void, typeEnv) if err != nil { return err } iprintf("after infer void for %s [%T] %s\n", child, child.Type(), child.Type()) } changed, err := gb.infer(child, typeEnv, depth+1) if err != nil { return err } numChanges += changed return nil }) if err != nil { return 0, err } case *gol.NodeList: iprintf("nodelist\n") if node.Len() == 0 { return 0, fmt.Errorf("Empty application") } // What types are in the list? var head gol.Node argTypes := make([]typ.Type, 0) first := true node.Foreach(func(child gol.Node) error { childChanges, err := gb.infer(child, typeEnv, depth+1) if err != nil { return err } numChanges += childChanges if first { first = false head = child } else { argTypes = append(argTypes, child.Type()) } return nil }) // What type of function would fit these (and return type)? wantedType := typ.Func{ Args: argTypes, Result: node.Type(), } // Unify that what we have in head position err := head.NodeUnify(wantedType, typeEnv) if err != nil { return 0, err } case *gol.NodeLambda: iprintf("NodeLambda (%s): %s\n", n.String(), typ.ResolveStr(n.Type())) argTypes := make([]typ.Type, node.Args.Len()) i := 0 frame := make(map[string]typ.Type) err := node.Args.Foreach(func(child gol.Node) error { id, ok := child.(*gol.NodeIdentifier) if !ok { return gol.NodeErrorf(n, "non-identifier in lambda args: %s", child.String()) } frame[id.String()] = child.Type() argTypes[i] = child.Type() i++ return nil }) if err != nil { return 0, err } oldEnv := typeEnv defer func() { typeEnv = oldEnv }() typeEnv = typeEnv.WithFrame(frame) childChanges, err := gb.infer(node.Body, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges resultType := node.Body.Type() calcType := typ.NewFunc(argTypes, resultType) err = node.NodeUnify(calcType, typeEnv) if err != nil { return 0, err } case *gol.NodeIdentifier: iprintf("NodeIdentifier (%s)\n", n.String()) newType, err := typeEnv.Lookup(n.String()) if err == nil { iprintf("NodeIdentifier (%s) [%s]\n", n.String(), newType.String()) err = node.NodeUnify(newType, typeEnv) if err != nil { return 0, err } } case *gol.NodeLet: iprintf("NodeLet (%s)\n", n.String()) frame := make(map[string]typ.Type) for k, v := range node.Bindings { frame[k] = v.Type() childChanges, err := gb.infer(v, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges } oldEnv := typeEnv defer func() { typeEnv = oldEnv }() typeEnv = typeEnv.WithFrame(frame) childChanges, err := gb.infer(node.Body, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges err = node.NodeUnify(node.Body.Type(), typeEnv) if err != nil { return 0, err } case *gol.NodeInt: err := node.NodeUnify(typ.Int, typeEnv) if err != nil { return 0, err } case *gol.NodeIf: iprintf("NodeIf (%s)\n", n.String()) childChanges, err := gb.infer(node.Condition, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges childChanges, err = gb.infer(node.TBranch, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges childChanges, err = gb.infer(node.FBranch, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges err = node.Condition.NodeUnify(typ.Bool, typeEnv) if err != nil { return 0, err } err = node.TBranch.NodeUnify(node.Type(), typeEnv) if err != nil { return 0, err } err = node.FBranch.NodeUnify(node.Type(), typeEnv) if err != nil { return 0, err } case *gol.NodeError: case *gol.NodeSymbol: case *gol.NodeString: case *gol.NodeBool: case *gol.NodeDefine: // JB - hack into top level typeEnv.AddTopLevel(node.Symbol.String(), node.Value.Type()) iprintf("NodeDefine (%s)\n", n.String()) childChanges, err := gb.infer(node.Value, typeEnv, depth+1) if err != nil { return 0, err } numChanges += childChanges case *gol.NodePair: // TODO: use an And type here.... // Can leave newType as Any since all Pairs have an inferred type case *gol.NodeQuote: panic("implement") case *gol.NodeUnQuote: panic("implement") case *gol.NodeSet: panic("implement") default: return 0, gol.NodeErrorf(n, "unrecognised/unhandled node type %T", n) } if n.Type() != origType { numChanges++ } iprintf("done\n") return numChanges, nil }