func (np NodeProcedure) Apply(e *Evaluator, argVals *gol.NodeList) (gol.Node, error) { if argVals.Len() != np.Args.Len() { return nil, gol.NodeErrorf(argVals, "Arg mismatch") } f := gol.Frame{} z := np.Args.Zip(argVals) err := z.Foreach(func(n gol.Node) error { pair, ok := n.(*gol.NodePair) if !ok { return gol.NodeErrorf(argVals, "Internal error - zip returns non-pair") } id := pair.Car val := pair.Cdr idStr := id.String() f[idStr] = val return nil }) if err != nil { return nil, err } oldEnv := e.Env defer func() { e.Env = oldEnv }() e.Env = np.Env.WithFrame(f) value, err := e.Eval(np.Body) return value, err }
func (e *Evaluator) Apply(nl *gol.NodeList) (gol.Node, error) { if nl.Len() == 0 { return nil, gol.NodeErrorf(nl, "Empty application") } applicable, ok := nl.First().(NodeApplicable) if !ok { return nil, gol.NodeErrorf(nl, "Can't evaluate list with non-applicable head: %T [%s]", nl.First(), nl) } node, err := applicable.Apply(e, nl.Rest()) if err != nil { return nil, err } return node, nil }
func (gb *GolangBackend) compile(node gol.Node) (string, error) { switch n := node.(type) { case *gol.NodeInt: return gb.compileInt(n) case *gol.NodeString: return gb.compileString(n) case *gol.NodeBool: return gb.compileBool(n) case *gol.NodeProgn: return gb.compileProgn(n) case *gol.NodeList: return gb.compileList(n) case *gol.NodeLet: return gb.compileLet(n) case *gol.NodeIdentifier: return gb.compileIdentifier(n) case *gol.NodeLambda: return gb.compileLambda(n) case *gol.NodeIf: return gb.compileIf(n) case *gol.NodeError: return gb.compileError(n) case *gol.NodeDefine: return gb.compileDefine(n) case *gol.NodeSymbol: return "", gol.NodeErrorf(n, "TODO node type %T", node) case *gol.NodeQuote: return "", gol.NodeErrorf(n, "TODO node type %T", node) case *gol.NodeUnQuote: return "", gol.NodeErrorf(n, "TODO node type %T", node) case *gol.NodeSet: return "", gol.NodeErrorf(n, "TODO node type %T", node) default: return "", gol.NodeErrorf(n, "Unrecognised node type %T", node) } }
func (gb *GolangBackend) infer(n gol.Node, typeEnv typ.Env, depth int) (int, error) { numChanges, err := gb.inferUnwrappedError(n, typeEnv, depth) if err != nil { _, isNodeErr := err.(*gol.NodeError) if !isNodeErr { err = gol.NodeErrorf(n, err.Error()) } } return numChanges, err }
func (gb *GolangBackend) compileList(nl *gol.NodeList) (string, error) { if nl.Len() == 0 { return "", gol.NodeErrorf(nl, "empty application") } switch fst := nl.First().(type) { case *gol.NodeIdentifier: return gb.compileFuncCall(fst, nl.Rest()) case *gol.NodeLambda: return gb.compileLambdaApplication(fst, nl.Rest()) default: return "", fmt.Errorf("Non-applicable in head position: %T", fst) } }
func (e *Evaluator) evalIf(ni *gol.NodeIf) (gol.Node, error) { condition, err := e.Eval(ni.Condition) if err != nil { return nil, err } conditionBool, ok := condition.(*gol.NodeBool) if !ok { return nil, gol.NodeErrorf(ni, "Non-boolean in 'if' condition") } if conditionBool.IsTrue() { return e.Eval(ni.TBranch) } else { return e.Eval(ni.FBranch) } }
func (e *Evaluator) Eval(node gol.Node) (gol.Node, error) { switch n := node.(type) { case *gol.NodeError: return nil, n case *gol.NodeIdentifier: value, err := e.Env.Lookup(n.String()) if err != nil { return nil, gol.NodeErrorf(node, "Failed to find [%s]: %s", n.String(), err.Error()) } return value, nil case *gol.NodeInt: return n, nil case *gol.NodeSymbol: return n, nil case *gol.NodeString: return n, nil case *gol.NodeBool: return n, nil case *gol.NodeQuote: if n.Quasi { e.nesting++ value, err := e.Eval(n.Arg) e.nesting-- return value, err } return n.Arg, nil case *gol.NodeUnQuote: e.nesting-- value, err := e.Eval(n.Arg) e.nesting++ return value, err case *gol.NodeLambda: if e.Quoting() { return e.evalList(n.NodeList) } return e.evalLambda(n) case *gol.NodeList: if e.Quoting() { return e.evalList(n) } return e.evalList(n) case *gol.NodeIf: if e.Quoting() { return e.evalList(n.NodeList) } return e.evalIf(n) case *gol.NodeSet: if e.Quoting() { return e.evalList(n.NodeList) } return e.evalSet(n) case *gol.NodeLet: if e.Quoting() { return e.evalList(n.NodeList) } return e.evalLet(n) case *gol.NodeProgn: if e.Quoting() { return e.evalList(n.NodeList) } return e.evalProgn(n) case *gol.NodeDefine: if e.Quoting() { return e.evalList(n.NodeList) } return e.evalDefine(n) default: return nil, gol.NodeErrorf(n, "Unrecognised node type %T", node) } }
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 }