1// ================================================================
2// CST build/execute for subroutine nodes.
3//
4// Subroutines can't be used as rvalues; their invocation must be the entire
5// statement. Nonetheless, their name-resolution, argument/parameter binding,
6// etc. are very similar to functions.
7// ================================================================
8
9package cst
10
11import (
12	"miller/src/dsl"
13	"miller/src/lib"
14)
15
16// ----------------------------------------------------------------
17// Subroutine lookup:
18//
19// * Unlike for functions, There are no built-in subroutines -- the only ones
20//   that exist are user-defined.
21// * Try UDS lookup (i.e. the UDS has been defined before being called)
22// * Absent a match there:
23//   o Make a UDS-placeholder node with present signature but nil function-pointer
24//   o Append that node to CST to-be-resolved list
25//   o On a next pass, we will walk that list resolving against all encountered
26//     UDS definitions. (It will be an error then if it's still unresolvable.)
27
28func (this *RootNode) BuildSubroutineCallsiteNode(astNode *dsl.ASTNode) (IExecutable, error) {
29	lib.InternalCodingErrorIf(
30		astNode.Type != dsl.NodeTypeSubroutineCallsite &&
31			astNode.Type != dsl.NodeTypeOperator,
32	)
33	lib.InternalCodingErrorIf(astNode.Token == nil)
34	lib.InternalCodingErrorIf(astNode.Children == nil)
35
36	subroutineName := string(astNode.Token.Lit)
37
38	//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
39	// Look for a user-defined subroutine with the given name.
40
41	callsiteArity := len(astNode.Children)
42	uds, err := this.udsManager.LookUp(subroutineName, callsiteArity)
43	if err != nil {
44		return nil, err
45	}
46
47	// AST snippet for 'call s($x, $y)':
48	//
49	// * statement block
50	//     * subroutine callsite "call"
51	//         * direct field value "x"
52	//         * direct field value "y"
53	//
54	// Here we need to make an array of our arguments at the callsite, to be
55	// paired up with the parameters within he subroutine definition at runtime.
56	argumentNodes := make([]IEvaluable, callsiteArity)
57	for i, argumentASTNode := range astNode.Children {
58		argumentNode, err := this.BuildEvaluableNode(argumentASTNode)
59		if err != nil {
60			return nil, err
61		}
62		argumentNodes[i] = argumentNode
63	}
64
65	if uds == nil {
66		// Mark this as unresolved for an after-pass to see if a UDS with this
67		// name/arity has been defined farther down in the DSL expression after
68		// this callsite. This happens example when a subroutine is called before
69		// it's defined.
70		uds = NewUnresolvedUDS(subroutineName, callsiteArity)
71		udsCallsiteNode := NewUDSCallsite(argumentNodes, uds)
72		this.rememberUnresolvedSubroutineCallsite(udsCallsiteNode)
73		return udsCallsiteNode, nil
74	} else {
75		udsCallsiteNode := NewUDSCallsite(argumentNodes, uds)
76		return udsCallsiteNode, nil
77	}
78}
79