Commits

Greg Ward  committed 354eb51

types,runtime: factor types.BasicArgs out of runtime.FunctionArgs

Also rename FunctionArgs to RuntimeArgs, since now it just embeds
BasicArgs and adds a pointer to the Runtime object. It exists so that
builtin functions have a back-door to access other runtime stuff,
notably the DAG.

This also lets me ditch StubArgSource: BasicArgs fulfills that role now.

  • Participants
  • Parent commits 0c63c5c

Comments (0)

Files changed (6)

File src/fubsy/runtime/builtins.go

 }
 
 func fn_build(argsource types.ArgSource) (types.FuObject, []error) {
-	rt := argsource.(FunctionArgs).runtime
+	rt := argsource.(RuntimeArgs).runtime
 	args := argsource.Args()
 	targets := rt.nodify(args[0])
 	sources := rt.nodify(args[1])
 
 func fn_FileNode(argsource types.ArgSource) (types.FuObject, []error) {
 	name := argsource.Args()[0].ValueString()
-	graph := argsource.(FunctionArgs).Graph()
+	graph := argsource.(RuntimeArgs).Graph()
 	return dag.MakeFileNode(graph, name), nil
 }
 
 func fn_ActionNode(argsource types.ArgSource) (types.FuObject, []error) {
 	basename := argsource.Args()[0].ValueString()
-	graph := argsource.(FunctionArgs).Graph()
+	graph := argsource.(RuntimeArgs).Graph()
 	return dag.MakeActionNode(graph, basename+":action"), nil
 }

File src/fubsy/runtime/builtins_test.go

 	}
 	defer cleanup2()
 
-	args := FunctionArgs{
-		args: types.MakeFuList(),
+	args := RuntimeArgs{
+		BasicArgs: types.MakeBasicArgs(nil, types.MakeFuList(), nil),
 	}
 
 	result, errs := fn_println(args)
 	rfile.Truncate(0)
 	rfile.Seek(0, 0)
 
-	args.args = types.MakeFuList("hello", "world")
+	args.SetArgs(types.MakeFuList("hello", "world"))
 	fn_println(args)
 	data, err = ioutil.ReadFile("stdout")
 	assert.Nil(t, err)
 	// mkdir() happily accepts an empty argument list, to allow for
 	// cases where a user-defined list becomes the arg list, and it
 	// just happens to be empty
-	args := FunctionArgs{
-		args: []types.FuObject{},
+	pargs := []types.FuObject{}
+	args := RuntimeArgs{
+		BasicArgs: types.MakeBasicArgs(nil, pargs, nil),
 	}
 	result, errs := fn_mkdir(args)
 	assert.Nil(t, result)
 	assert.Equal(t, []string{}, dirContents("."))
 
 	// easiest case: create a single dir
-	args.args = types.MakeFuList("foo")
+	pargs = types.MakeFuList("foo")
+	args.SetArgs(pargs)
 	result, errs = fn_mkdir(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 0, len(errs))
 	assert.True(t, isDir("foo"))
 
 	// create multiple dirs, including "foo" which already exists
-	args.args = types.MakeFuList("meep/meep/meep", "foo", "meep/beep")
+	pargs = types.MakeFuList("meep/meep/meep", "foo", "meep/beep")
+	args.SetArgs(pargs)
 	result, errs = fn_mkdir(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 0, len(errs))
 	// now with an error in the middle of the list (*but* we still
 	// create the other requested dirs!)
 	testutils.TouchFiles("meep/zap")
-	args.args = types.MakeFuList("meep/bap", "meep/zap/zip", "foo/bar")
+	pargs = types.MakeFuList("meep/bap", "meep/zap/zip", "foo/bar")
+	args.SetArgs(pargs)
 	result, errs = fn_mkdir(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 1, len(errs))
 	assert.True(t, isDir("foo/bar"))
 
 	// finally, with multiple errors
-	args.args = append(args.args, types.FuString("meep/zap/blop"))
+	pargs = append(pargs, types.FuString("meep/zap/blop"))
+	args.SetArgs(pargs)
 	result, errs = fn_mkdir(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 2, len(errs))
 	cleanup := testutils.Chtemp()
 	defer cleanup()
 
-	args := FunctionArgs{
-		args: types.MakeFuList(),
+	args := RuntimeArgs{
+		BasicArgs: types.MakeBasicArgs(nil, []types.FuObject{}, nil),
 	}
 
 	// remove() doesn't care about empty arg list (same reason as mkdir())
 	assert.Equal(t, 0, len(errs))
 
 	// remove() ignores non-existent files
-	args.args = types.MakeFuList("foo", "bar/bleep/meep", "qux")
+	args.SetArgs(types.MakeFuList("foo", "bar/bleep/meep", "qux"))
 	result, errs = fn_remove(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 0, len(errs))
 
 	// remove() removes regular files
 	testutils.TouchFiles("foo", "bar/bleep/meep", "bar/bleep/feep", "qux")
-	args.args = types.MakeFuList("foo", "bar/bleep/meep", "bogus")
+	args.SetArgs(types.MakeFuList("foo", "bar/bleep/meep", "bogus"))
 	result, errs = fn_remove(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 0, len(errs))
 
 	// remove() removes files and directories too
 	testutils.TouchFiles("foo", "bar/bleep/meep", "qux")
-	args.args = types.MakeFuList("bogus", "bar", "morebogus", "qux")
+	args.SetArgs(types.MakeFuList("bogus", "bar", "morebogus", "qux"))
 	result, errs = fn_remove(args)
 	assert.Nil(t, result)
 	assert.Equal(t, 0, len(errs))
 	testutils.ChmodRO("bar")
 	defer testutils.ChmodOwnerAll("bar")
 
-	args.args = types.MakeFuList("bar", "qux")
+	args.SetArgs(types.MakeFuList("bar", "qux"))
 	result, errs = fn_remove(args)
 	assert.Nil(t, result)
 	assert.Equal(t, "remove bar/bong: permission denied", errs[0].Error())
 }
 
 func Test_FileNode(t *testing.T) {
-	args := FunctionArgs{
-		runtime: minimalRuntime(),
-		args:    []types.FuObject{types.FuString("a.txt")},
+	args := RuntimeArgs{
+		BasicArgs: types.MakeBasicArgs(nil, types.MakeFuList("a.txt"), nil),
+		runtime:   minimalRuntime(),
 	}
 	node0, errs := fn_FileNode(args)
 	assert.Equal(t, 0, len(errs))
 }
 
 func Test_ActionNode(t *testing.T) {
-	args := FunctionArgs{
-		runtime: minimalRuntime(),
-		args:    []types.FuObject{types.FuString("test/x")},
+	args := RuntimeArgs{
+		BasicArgs: types.MakeBasicArgs(nil, types.MakeFuList("test/x"), nil),
+		runtime:   minimalRuntime(),
 	}
 	node0, errs := fn_ActionNode(args)
 	assert.Equal(t, 0, len(errs))

File src/fubsy/runtime/execute.go

 		result, errs = self.evaluateAdd(expr)
 	case *dsl.ASTFunctionCall:
 		var callable types.FuCallable
-		var args FunctionArgs
+		var args RuntimeArgs
 		callable, args, errs = self.prepareCall(expr)
 		if len(errs) == 0 {
 			result, errs = self.evaluateCall(callable, args, nil)
 }
 
 func (self *Runtime) prepareCall(expr *dsl.ASTFunctionCall) (
-	callable types.FuCallable, args FunctionArgs, errs []error) {
+	callable types.FuCallable, args RuntimeArgs, errs []error) {
 
 	// robj is the receiver object for a method call (foo in foo.x())
 	// value is the callable object (function or method) as a FuObject
 	if len(errs) > 0 {
 		return
 	}
-	args.robj = robj
+	args.SetReceiver(robj)
 
 	callable, ok := value.(types.FuCallable)
 	if !ok {
 			return
 		}
 	}
-	args.args = arglist
+	args.SetArgs(arglist)
 	errs = nil
 	return
 }
 
-func (self *Runtime) expandArgs(args FunctionArgs) (FunctionArgs, []error) {
-	xargs := FunctionArgs{
-		runtime: args.runtime,
-		robj:    args.robj,
-	}
+func (self *Runtime) expandArgs(argsource RuntimeArgs) (RuntimeArgs, []error) {
 	var errs []error
-	xargs.args = make([]types.FuObject, len(args.args))
 	var err error
-	for i, arg := range args.args {
-		xargs.args[i], err = arg.ActionExpand(self.stack, nil)
+
+	// XXX ignoring kwargs
+	args := argsource.Args()
+	xargs := make([]types.FuObject, len(args))
+	for i, arg := range args {
+		xargs[i], err = arg.ActionExpand(self.stack, nil)
 		if err != nil {
 			errs = append(errs, err)
 		}
 	}
-	return xargs, errs
+
+	result := RuntimeArgs{
+		BasicArgs: types.MakeBasicArgs(argsource.Receiver(), xargs, nil),
+		runtime:   argsource.runtime,
+	}
+	return result, errs
 }
 
 func (self *Runtime) evaluateCall(
 	callable types.FuCallable,
-	args FunctionArgs,
+	args RuntimeArgs,
 	precall func(types.FuCallable, types.ArgSource)) (
 	types.FuObject, []error) {
 
 	return self.location.ErrorPrefix() + self.err.Error()
 }
 
-type FunctionArgs struct {
+type RuntimeArgs struct {
+	types.BasicArgs
 	runtime *Runtime
-	robj    types.FuObject
-	args    []types.FuObject
-	kwargs  types.ValueMap
-}
-
-// implement types.ArgSource
-func (self FunctionArgs) Receiver() types.FuObject {
-	return self.robj
-}
-
-func (self FunctionArgs) Args() []types.FuObject {
-	return self.args
-}
-
-func (self FunctionArgs) KeywordArgs() types.ValueMap {
-	panic("kwargs not supported yet")
-	//return self.kwargs
 }
 
 // other methods that might come in handy
-func (self FunctionArgs) Graph() *dag.DAG {
+func (self RuntimeArgs) Graph() *dag.DAG {
 	return self.runtime.dag
 }

File src/fubsy/runtime/execute_test.go

 
 	var astcall *dsl.ASTFunctionCall
 	var callable types.FuCallable
-	var args FunctionArgs
+	var args RuntimeArgs
 	var errs []error
 
 	// correct (no args) call to dummy1()
 	callable, args, errs = rt.prepareCall(astcall)
 	assert.Equal(t, 0, len(errs))
 	assert.Equal(t, dummy1, callable)
-	assert.Equal(t, []types.FuObject{}, args.args)
+	assert.Equal(t, []types.FuObject{}, args.Args())
 
 	// and to dummy2()
 	astcall = dsl.NewASTFunctionCall(dsl.NewASTName("dummy2"), onearg)
 	callable, args, errs = rt.prepareCall(astcall)
 	assert.Equal(t, 0, len(errs))
 	assert.Equal(t, dummy2, callable)
-	assert.Equal(t, []types.FuObject{types.FuString("meep")}, args.args)
+	assert.Equal(t, []types.FuObject{types.FuString("meep")}, args.Args())
 
 	// attempt to call dummy2() incorrectly (1 arg, but it's an undefined name)
 	astcall = dsl.NewASTFunctionCall(
 	bar = types.NewFixedFunction("bar", 1, fn_bar)
 
 	rt := minimalRuntime()
-	args := FunctionArgs{runtime: rt}
+	args := RuntimeArgs{runtime: rt}
 
 	var result types.FuObject
 	var errs []error
 
 	// call foo() correctly (no args)
-	args.args = []types.FuObject{}
+	args.SetArgs([]types.FuObject{})
 	result, errs = rt.evaluateCall(foo, args, nil)
 	assert.Equal(t, types.FuString("foo!"), result)
 	assert.Equal(t, 0, len(errs))
 	assert.Equal(t, []string{"foo"}, calls)
 
 	// call foo() incorrectly (1 arg)
-	args.args = []types.FuObject{types.FuString("meep")}
+	args.SetArgs([]types.FuObject{types.FuString("meep")})
 	result, errs = rt.evaluateCall(foo, args, nil)
 	assert.Equal(t, 1, len(errs))
 	assert.Equal(t,
 	assert.Equal(t, []string{"foo", "bar"}, calls)
 
 	// call bar() incorrectly (no args)
-	args.args = nil
+	args.SetArgs(nil)
 	result, errs = rt.evaluateCall(bar, args, nil)
 	assert.Nil(t, result)
 	assert.Equal(t, 1, len(errs))
 	}
 	foo := types.NewFixedFunction("foo", 1, fn_foo)
 	rt := minimalRuntime()
-	args := FunctionArgs{runtime: rt}
+	args := RuntimeArgs{runtime: rt}
 
 	// call bar() with an arg that needs to be expanded to test that
 	// expansion does *not* happen -- evaluateCall() doesn't know
 	// which phase it's in, so it has to rely on someone else to
 	// ActionExpand() each value in the build phase
-	args.args = []types.FuObject{types.FuString(">$src<")}
+	args.SetArgs([]types.FuObject{types.FuString(">$src<")})
 	result, errs := rt.evaluateCall(foo, args, nil)
 	assert.Equal(t, 1, calls)
 	assert.Equal(t, types.FuString("arg: >$src<"), result)
 
 	// call foo() with that expandable value, and make sure it is
 	// really called with the unexpanded value
-	args.args[0] = val
+	args.SetArgs([]types.FuObject{val})
 	result, errs = rt.evaluateCall(foo, args, nil)
 	assert.Equal(t, 2, calls)
 	assert.Equal(t, types.FuString("arg: val"), result)
 
 	callable, args, errs := rt.prepareCall(astcall)
 	assert.Equal(t, "c", callable.(*types.FuFunction).Name())
-	assert.True(t, args.robj == bobj)
+	assert.True(t, args.Receiver() == bobj)
 	assert.Equal(t, 0, len(errs))
 
 	result, errs := rt.evaluateCall(callable, args, precall)

File src/fubsy/types/callables.go

 	KeywordArgs() ValueMap
 }
 
+type BasicArgs struct {
+	robj   FuObject
+	args   []FuObject
+	kwargs ValueMap
+}
+
 // the inner heart of a function or method, the code that is actually called
 // XXX should we allow multiple return values ([]FuObject)?
 type FuCode func(args ArgSource) (FuObject, []error)
 	}
 	return nil
 }
+
+func MakeBasicArgs(robj FuObject, args []FuObject, kwargs ValueMap) BasicArgs {
+	return BasicArgs{
+		robj:   robj,
+		args:   args,
+		kwargs: kwargs,
+	}
+}
+
+// BasicArgs implements ArgSource
+func (self BasicArgs) Receiver() FuObject {
+	return self.robj
+}
+
+func (self BasicArgs) Args() []FuObject {
+	return self.args
+}
+
+func (self BasicArgs) KeywordArgs() ValueMap {
+	return self.kwargs
+}
+
+// mutation methods for building arg objects -- not in the ArgSource
+// interface, since readers don't need these
+func (self *BasicArgs) SetReceiver(robj FuObject) {
+	self.robj = robj
+}
+
+func (self *BasicArgs) SetArgs(args []FuObject) {
+	self.args = args
+}
+
+func (self *BasicArgs) SetKeywordArgs(kwargs ValueMap) {
+	self.kwargs = kwargs
+}

File src/fubsy/types/callables_test.go

 
 func Test_FuFunction_CheckArgs_fixed(t *testing.T) {
 	val := FuString("a")
-	args := StubArgSource([]FuObject{})
+	args := MakeBasicArgs(nil, []FuObject{}, nil)
 	fn := NewFixedFunction("meep", 0, nil)
 
 	err := fn.CheckArgs(args)
 	assert.Nil(t, err)
 
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Equal(t,
 		"function meep() takes no arguments (got 1)", err.Error())
 
 	fn = NewFixedFunction("foo", 2, nil)
-	args = args[:0]
+	args.args = args.args[:0]
 	err = fn.CheckArgs(args)
 	assert.Equal(t,
 		"function foo() takes exactly 2 arguments (got 0)", err.Error())
 
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Equal(t,
 		"function foo() takes exactly 2 arguments (got 1)", err.Error())
 
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Nil(t, err)
 
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Equal(t,
 		"function foo() takes exactly 2 arguments (got 3)", err.Error())
 func Test_FuFunction_CheckArgs_minmax(t *testing.T) {
 	fn := NewVariadicFunction("bar", 2, 4, nil)
 	val := FuString("a")
-	args := StubArgSource([]FuObject{val})
+	args := MakeBasicArgs(nil, []FuObject{val}, nil)
 	err := fn.CheckArgs(args)
 	assert.Equal(t,
 		"function bar() requires at least 2 arguments (got 1)", err.Error())
 
 	// 2 args are good
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Nil(t, err)
 
 	// 3 args are good
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Nil(t, err)
 
 	// 4 args are good
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Nil(t, err)
 
 	// but 5 args is *right out*
-	args = append(args, val)
+	args.args = append(args.args, val)
 	err = fn.CheckArgs(args)
 	assert.Equal(t,
 		"function bar() takes at most 4 arguments (got 5)", err.Error())
 func Test_FuFunction_CheckArgs_unlimited(t *testing.T) {
 	fn := NewVariadicFunction("println", 0, -1, nil)
 	val := FuString("a")
-	args := StubArgSource([]FuObject{val})
+	args := MakeBasicArgs(nil, []FuObject{val}, nil)
 
 	err := fn.CheckArgs(args)
 	assert.Nil(t, err)
 
-	args = append(args, val, val, val, val)
+	args.args = append(args.args, val, val, val, val)
 	err = fn.CheckArgs(args)
 	assert.Nil(t, err)
 }
 
-type StubArgSource []FuObject
+func Test_BasicArgs(t *testing.T) {
+	var args BasicArgs
+	var _ ArgSource = args
 
-func (self StubArgSource) Receiver() FuObject {
-	return nil
+	args.args = MakeFuList("foo", "bar")
+	assert.Nil(t, args.Receiver())
+	assert.Equal(t, args.args, args.Args())
 }
-
-func (self StubArgSource) Args() []FuObject {
-	return self
-}
-
-func (self StubArgSource) Arg(i int) FuObject {
-	return self[i]
-}
-
-func (self StubArgSource) KeywordArgs() ValueMap {
-	panic("not implemented")
-}
-
-func (self StubArgSource) KeywordArg(name string) (FuObject, bool) {
-	panic("not implemented")
-}