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