1// ================================================================
2// Methods for built-in functions
3// ================================================================
4
5package cst
6
7import (
8	"errors"
9	"fmt"
10
11	"miller/src/dsl"
12	"miller/src/lib"
13	"miller/src/runtime"
14	"miller/src/types"
15)
16
17// ----------------------------------------------------------------
18func (this *RootNode) BuildBuiltinFunctionCallsiteNode(
19	astNode *dsl.ASTNode,
20) (IEvaluable, error) {
21	lib.InternalCodingErrorIf(
22		astNode.Type != dsl.NodeTypeFunctionCallsite &&
23			astNode.Type != dsl.NodeTypeOperator,
24	)
25	lib.InternalCodingErrorIf(astNode.Token == nil)
26	lib.InternalCodingErrorIf(astNode.Children == nil)
27
28	functionName := string(astNode.Token.Lit)
29
30	builtinFunctionInfo := BuiltinFunctionManagerInstance.LookUp(functionName)
31	if builtinFunctionInfo != nil {
32		if builtinFunctionInfo.hasMultipleArities { // E.g. "+" and "-"
33			return this.BuildMultipleArityFunctionCallsiteNode(astNode, builtinFunctionInfo)
34		} else if builtinFunctionInfo.zaryFunc != nil {
35			return this.BuildZaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
36		} else if builtinFunctionInfo.unaryFunc != nil {
37			return this.BuildUnaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
38		} else if builtinFunctionInfo.contextualUnaryFunc != nil {
39			return this.BuildContextualUnaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
40		} else if builtinFunctionInfo.binaryFunc != nil {
41			return this.BuildBinaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
42		} else if builtinFunctionInfo.ternaryFunc != nil {
43			return this.BuildTernaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
44		} else if builtinFunctionInfo.variadicFunc != nil {
45			return this.BuildVariadicFunctionCallsiteNode(astNode, builtinFunctionInfo)
46		} else {
47			return nil, errors.New(
48				"CST BuildFunctionCallsiteNode: builtin function not implemented yet: " +
49					functionName,
50			)
51		}
52	}
53
54	return nil, nil // not found
55}
56
57// ----------------------------------------------------------------
58func (this *RootNode) BuildMultipleArityFunctionCallsiteNode(
59	astNode *dsl.ASTNode,
60	builtinFunctionInfo *BuiltinFunctionInfo,
61) (IEvaluable, error) {
62	callsiteArity := len(astNode.Children)
63	if callsiteArity == 1 && builtinFunctionInfo.unaryFunc != nil {
64		return this.BuildUnaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
65	}
66	if callsiteArity == 2 && builtinFunctionInfo.binaryFunc != nil {
67		return this.BuildBinaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
68	}
69	if callsiteArity == 3 && builtinFunctionInfo.ternaryFunc != nil {
70		return this.BuildTernaryFunctionCallsiteNode(astNode, builtinFunctionInfo)
71	}
72
73	return nil, errors.New(
74		fmt.Sprintf(
75			"CST BuildMultipleArityFunctionCallsiteNode: function name not found: " +
76				builtinFunctionInfo.name,
77		),
78	)
79}
80
81// ----------------------------------------------------------------
82type ZaryFunctionCallsiteNode struct {
83	zaryFunc types.ZaryFunc
84}
85
86func (this *RootNode) BuildZaryFunctionCallsiteNode(
87	astNode *dsl.ASTNode,
88	builtinFunctionInfo *BuiltinFunctionInfo,
89) (IEvaluable, error) {
90	callsiteArity := len(astNode.Children)
91	expectedArity := 0
92	if callsiteArity != expectedArity {
93		return nil, errors.New(
94			fmt.Sprintf(
95				"Miller: function %s invoked with %d argument%s; expected %d",
96				builtinFunctionInfo.name,
97				callsiteArity,
98				lib.Plural(callsiteArity),
99				expectedArity,
100			),
101		)
102	}
103
104	return &ZaryFunctionCallsiteNode{
105		zaryFunc: builtinFunctionInfo.zaryFunc,
106	}, nil
107}
108
109func (this *ZaryFunctionCallsiteNode) Evaluate(
110	state *runtime.State,
111) *types.Mlrval {
112	return this.zaryFunc()
113}
114
115// ----------------------------------------------------------------
116type UnaryFunctionCallsiteNode struct {
117	unaryFunc  types.UnaryFunc
118	evaluable1 IEvaluable
119}
120
121func (this *RootNode) BuildUnaryFunctionCallsiteNode(
122	astNode *dsl.ASTNode,
123	builtinFunctionInfo *BuiltinFunctionInfo,
124) (IEvaluable, error) {
125	callsiteArity := len(astNode.Children)
126	expectedArity := 1
127	if callsiteArity != expectedArity {
128		return nil, errors.New(
129			fmt.Sprintf(
130				"Miller: function %s invoked with %d argument%s; expected %d",
131				builtinFunctionInfo.name,
132				callsiteArity,
133				lib.Plural(callsiteArity),
134				expectedArity,
135			),
136		)
137	}
138
139	evaluable1, err := this.BuildEvaluableNode(astNode.Children[0])
140	if err != nil {
141		return nil, err
142	}
143
144	return &UnaryFunctionCallsiteNode{
145		unaryFunc:  builtinFunctionInfo.unaryFunc,
146		evaluable1: evaluable1,
147	}, nil
148}
149
150func (this *UnaryFunctionCallsiteNode) Evaluate(
151	state *runtime.State,
152) *types.Mlrval {
153	return this.unaryFunc(this.evaluable1.Evaluate(state))
154}
155
156// ----------------------------------------------------------------
157type ContextualUnaryFunctionCallsiteNode struct {
158	contextualUnaryFunc types.ContextualUnaryFunc
159	evaluable1          IEvaluable
160}
161
162func (this *RootNode) BuildContextualUnaryFunctionCallsiteNode(
163	astNode *dsl.ASTNode,
164	builtinFunctionInfo *BuiltinFunctionInfo,
165) (IEvaluable, error) {
166	callsiteArity := len(astNode.Children)
167	expectedArity := 1
168	if callsiteArity != expectedArity {
169		return nil, errors.New(
170			fmt.Sprintf(
171				"Miller: function %s invoked with %d argument%s; expected %d",
172				builtinFunctionInfo.name,
173				callsiteArity,
174				lib.Plural(callsiteArity),
175				expectedArity,
176			),
177		)
178	}
179
180	evaluable1, err := this.BuildEvaluableNode(astNode.Children[0])
181	if err != nil {
182		return nil, err
183	}
184
185	return &ContextualUnaryFunctionCallsiteNode{
186		contextualUnaryFunc: builtinFunctionInfo.contextualUnaryFunc,
187		evaluable1:          evaluable1,
188	}, nil
189}
190
191func (this *ContextualUnaryFunctionCallsiteNode) Evaluate(
192	state *runtime.State,
193) *types.Mlrval {
194	return this.contextualUnaryFunc(this.evaluable1.Evaluate(state), state.Context)
195}
196
197// ----------------------------------------------------------------
198type BinaryFunctionCallsiteNode struct {
199	binaryFunc types.BinaryFunc
200	evaluable1 IEvaluable
201	evaluable2 IEvaluable
202}
203
204func (this *RootNode) BuildBinaryFunctionCallsiteNode(
205	astNode *dsl.ASTNode,
206	builtinFunctionInfo *BuiltinFunctionInfo,
207) (IEvaluable, error) {
208	callsiteArity := len(astNode.Children)
209	expectedArity := 2
210	if callsiteArity != expectedArity {
211		return nil, errors.New(
212			fmt.Sprintf(
213				"Miller: function %s invoked with %d argument%s; expected %d",
214				builtinFunctionInfo.name,
215				callsiteArity,
216				lib.Plural(callsiteArity),
217				expectedArity,
218			),
219		)
220	}
221
222	evaluable1, err := this.BuildEvaluableNode(astNode.Children[0])
223	if err != nil {
224		return nil, err
225	}
226	evaluable2, err := this.BuildEvaluableNode(astNode.Children[1])
227	if err != nil {
228		return nil, err
229	}
230
231	// Special short-circuiting cases
232	if builtinFunctionInfo.name == "&&" {
233		return this.BuildLogicalANDOperatorNode(
234			evaluable1,
235			evaluable2,
236		), nil
237	}
238	if builtinFunctionInfo.name == "||" {
239		return this.BuildLogicalOROperatorNode(
240			evaluable1,
241			evaluable2,
242		), nil
243	}
244	if builtinFunctionInfo.name == "??" {
245		return this.BuildAbsentCoalesceOperatorNode(
246			evaluable1,
247			evaluable2,
248		), nil
249	}
250	if builtinFunctionInfo.name == "???" {
251		return this.BuildEmptyCoalesceOperatorNode(
252			evaluable1,
253			evaluable2,
254		), nil
255	}
256
257	return &BinaryFunctionCallsiteNode{
258		binaryFunc: builtinFunctionInfo.binaryFunc,
259		evaluable1: evaluable1,
260		evaluable2: evaluable2,
261	}, nil
262}
263
264func (this *BinaryFunctionCallsiteNode) Evaluate(
265	state *runtime.State,
266) *types.Mlrval {
267	return this.binaryFunc(
268		this.evaluable1.Evaluate(state),
269		this.evaluable2.Evaluate(state),
270	)
271}
272
273// ----------------------------------------------------------------
274type TernaryFunctionCallsiteNode struct {
275	ternaryFunc types.TernaryFunc
276	evaluable1  IEvaluable
277	evaluable2  IEvaluable
278	evaluable3  IEvaluable
279}
280
281func (this *RootNode) BuildTernaryFunctionCallsiteNode(
282	astNode *dsl.ASTNode,
283	builtinFunctionInfo *BuiltinFunctionInfo,
284) (IEvaluable, error) {
285	callsiteArity := len(astNode.Children)
286	expectedArity := 3
287	if callsiteArity != expectedArity {
288		return nil, errors.New(
289			fmt.Sprintf(
290				"Miller: function %s invoked with %d argument%s; expected %d",
291				builtinFunctionInfo.name,
292				callsiteArity,
293				lib.Plural(callsiteArity),
294				expectedArity,
295			),
296		)
297	}
298
299	evaluable1, err := this.BuildEvaluableNode(astNode.Children[0])
300	if err != nil {
301		return nil, err
302	}
303	evaluable2, err := this.BuildEvaluableNode(astNode.Children[1])
304	if err != nil {
305		return nil, err
306	}
307	evaluable3, err := this.BuildEvaluableNode(astNode.Children[2])
308	if err != nil {
309		return nil, err
310	}
311
312	// Special short-circuiting case
313	if builtinFunctionInfo.name == "?:" {
314		return this.BuildStandardTernaryOperatorNode(
315			evaluable1,
316			evaluable2,
317			evaluable3,
318		), nil
319	}
320
321	return &TernaryFunctionCallsiteNode{
322		ternaryFunc: builtinFunctionInfo.ternaryFunc,
323		evaluable1:  evaluable1,
324		evaluable2:  evaluable2,
325		evaluable3:  evaluable3,
326	}, nil
327}
328
329func (this *TernaryFunctionCallsiteNode) Evaluate(
330	state *runtime.State,
331) *types.Mlrval {
332	return this.ternaryFunc(
333		this.evaluable1.Evaluate(state),
334		this.evaluable2.Evaluate(state),
335		this.evaluable3.Evaluate(state),
336	)
337}
338
339// ----------------------------------------------------------------
340type VariadicFunctionCallsiteNode struct {
341	variadicFunc types.VariadicFunc
342	evaluables   []IEvaluable
343}
344
345func (this *RootNode) BuildVariadicFunctionCallsiteNode(
346	astNode *dsl.ASTNode,
347	builtinFunctionInfo *BuiltinFunctionInfo,
348) (IEvaluable, error) {
349	lib.InternalCodingErrorIf(astNode.Children == nil)
350	evaluables := make([]IEvaluable, len(astNode.Children))
351
352	callsiteArity := len(astNode.Children)
353	if callsiteArity < builtinFunctionInfo.minimumVariadicArity {
354		return nil, errors.New(
355			fmt.Sprintf(
356				"Miller: function %s takes minimum argument count %d; got %d.\n",
357				builtinFunctionInfo.name,
358				builtinFunctionInfo.minimumVariadicArity,
359				callsiteArity,
360			),
361		)
362	}
363
364	var err error = nil
365	for i, astChildNode := range astNode.Children {
366		evaluables[i], err = this.BuildEvaluableNode(astChildNode)
367		if err != nil {
368			return nil, err
369		}
370	}
371	return &VariadicFunctionCallsiteNode{
372		variadicFunc: builtinFunctionInfo.variadicFunc,
373		evaluables:   evaluables,
374	}, nil
375}
376
377func (this *VariadicFunctionCallsiteNode) Evaluate(
378	state *runtime.State,
379) *types.Mlrval {
380	args := make([]*types.Mlrval, len(this.evaluables))
381	for i, _ := range this.evaluables {
382		args[i] = this.evaluables[i].Evaluate(state)
383	}
384	return this.variadicFunc(args)
385}
386
387// ================================================================
388type LogicalANDOperatorNode struct {
389	a, b IEvaluable
390}
391
392func (this *RootNode) BuildLogicalANDOperatorNode(a, b IEvaluable) *LogicalANDOperatorNode {
393	return &LogicalANDOperatorNode{
394		a: a,
395		b: b,
396	}
397}
398
399// This is different from most of the evaluator functions in that it does
400// short-circuiting: since is logical AND, the second argument is not evaluated
401// if the first argument is false.
402//
403// Disposition matrix:
404//
405//       {
406//a      b  ERROR   ABSENT  EMPTY  STRING INT    FLOAT  BOOL
407//ERROR  :  {ERROR, ERROR,  ERROR, ERROR, ERROR, ERROR, ERROR},
408//ABSENT :  {ERROR, absent, ERROR, ERROR, ERROR, ERROR, absent},
409//EMPTY  :  {ERROR, ERROR,  ERROR, ERROR, ERROR, ERROR, ERROR},
410//STRING :  {ERROR, ERROR,  ERROR, ERROR, ERROR, ERROR, ERROR},
411//INT    :  {ERROR, ERROR,  ERROR, ERROR, ERROR, ERROR, ERROR},
412//FLOAT  :  {ERROR, ERROR,  ERROR, ERROR, ERROR, ERROR, ERROR},
413//BOOL   :  {ERROR, absent, ERROR, ERROR, ERROR, ERROR, a&&b},
414//       }
415//
416// which without the all-error rows/columns reduces to
417//
418//       {
419//a      b  ABSENT   BOOL
420//ABSENT :  {absent, absent},
421//BOOL   :  {absent, a&&b},
422//       }
423//
424// So:
425// * Evaluate a
426// * If a is not absent or bool: return error
427// * If a is absent: return absent
428// * If a is false: return a
429// * Now a is boolean true
430// * Evaluate b
431// * If b is not absent or bool: return error
432// * If b is absent: return absent
433// * Return a && b
434
435func (this *LogicalANDOperatorNode) Evaluate(
436	state *runtime.State,
437) *types.Mlrval {
438	aout := this.a.Evaluate(state)
439	atype := aout.GetType()
440	if !(atype == types.MT_ABSENT || atype == types.MT_BOOL) {
441		return types.MLRVAL_ERROR
442	}
443	if atype == types.MT_ABSENT {
444		return types.MLRVAL_ABSENT
445	}
446	if aout.IsFalse() {
447		// This means false && bogus type evaluates to true, which is sad but
448		// which we MUST do in order to not violate the short-circuiting
449		// property.  We would have to evaluate b to know if it were error or
450		// not.
451		return aout
452	}
453
454	bout := this.b.Evaluate(state)
455	btype := bout.GetType()
456	if !(btype == types.MT_ABSENT || btype == types.MT_BOOL) {
457		return types.MLRVAL_ERROR
458	}
459	if btype == types.MT_ABSENT {
460		return types.MLRVAL_ABSENT
461	}
462
463	return types.MlrvalLogicalAND(aout, bout)
464}
465
466// ================================================================
467type LogicalOROperatorNode struct {
468	a, b IEvaluable
469}
470
471func (this *RootNode) BuildLogicalOROperatorNode(a, b IEvaluable) *LogicalOROperatorNode {
472	return &LogicalOROperatorNode{
473		a: a,
474		b: b,
475	}
476}
477
478// This is different from most of the evaluator functions in that it does
479// short-circuiting: since is logical OR, the second argument is not evaluated
480// if the first argument is false.
481//
482// See the disposition-matrix discussion for LogicalANDOperator.
483func (this *LogicalOROperatorNode) Evaluate(
484	state *runtime.State,
485) *types.Mlrval {
486	aout := this.a.Evaluate(state)
487	atype := aout.GetType()
488	if !(atype == types.MT_ABSENT || atype == types.MT_BOOL) {
489		return types.MLRVAL_ERROR
490	}
491	if atype == types.MT_ABSENT {
492		return types.MLRVAL_ABSENT
493	}
494	if aout.IsTrue() {
495		// This means true || bogus type evaluates to true, which is sad but
496		// which we MUST do in order to not violate the short-circuiting
497		// property.  We would have to evaluate b to know if it were error or
498		// not.
499		return aout
500	}
501
502	bout := this.b.Evaluate(state)
503	btype := bout.GetType()
504	if !(btype == types.MT_ABSENT || btype == types.MT_BOOL) {
505		return types.MLRVAL_ERROR
506	}
507	if btype == types.MT_ABSENT {
508		return types.MLRVAL_ABSENT
509	}
510	return types.MlrvalLogicalOR(aout, bout)
511}
512
513// ================================================================
514// a ?? b evaluates to b only when a is absent. Example: '$foo ?? 0' when the
515// current record has no field $foo.
516type AbsentCoalesceOperatorNode struct{ a, b IEvaluable }
517
518func (this *RootNode) BuildAbsentCoalesceOperatorNode(a, b IEvaluable) *AbsentCoalesceOperatorNode {
519	return &AbsentCoalesceOperatorNode{a: a, b: b}
520}
521
522// This is different from most of the evaluator functions in that it does
523// short-circuiting: the second argument is not evaluated if the first
524// argument is not absent.
525func (this *AbsentCoalesceOperatorNode) Evaluate(
526	state *runtime.State,
527) *types.Mlrval {
528	aout := this.a.Evaluate(state)
529	if aout.GetType() != types.MT_ABSENT {
530		return aout
531	}
532
533	return this.b.Evaluate(state)
534}
535
536// ================================================================
537// a ?? b evaluates to b only when a is absent or empty. Example: '$foo ?? 0'
538// when the current record has no field $foo, or when $foo is empty..
539type EmptyCoalesceOperatorNode struct{ a, b IEvaluable }
540
541func (this *RootNode) BuildEmptyCoalesceOperatorNode(a, b IEvaluable) *EmptyCoalesceOperatorNode {
542	return &EmptyCoalesceOperatorNode{a: a, b: b}
543}
544
545// This is different from most of the evaluator functions in that it does
546// short-circuiting: the second argument is not evaluated if the first
547// argument is not absent.
548func (this *EmptyCoalesceOperatorNode) Evaluate(
549	state *runtime.State,
550) *types.Mlrval {
551	aout := this.a.Evaluate(state)
552	atype := aout.GetType()
553	if atype == types.MT_ABSENT || atype == types.MT_VOID || (atype == types.MT_STRING && aout.String() == "") {
554		return this.b.Evaluate(state)
555	} else {
556		return aout
557	}
558}
559
560// ================================================================
561type StandardTernaryOperatorNode struct{ a, b, c IEvaluable }
562
563func (this *RootNode) BuildStandardTernaryOperatorNode(a, b, c IEvaluable) *StandardTernaryOperatorNode {
564	return &StandardTernaryOperatorNode{a: a, b: b, c: c}
565}
566func (this *StandardTernaryOperatorNode) Evaluate(
567	state *runtime.State,
568) *types.Mlrval {
569	aout := this.a.Evaluate(state)
570
571	boolValue, isBool := aout.GetBoolValue()
572	if !isBool {
573		return types.MLRVAL_ERROR
574	}
575
576	// Short-circuit: defer evaluation unless needed
577	if boolValue == true {
578		return this.b.Evaluate(state)
579	} else {
580		return this.c.Evaluate(state)
581	}
582}
583
584// ================================================================
585// The function-manager logic is designed to make it easy to implement a large
586// number of functions/operators with a small number of keystrokes. The general
587// paradigm is evaluate the arguments, then invoke the function/operator.
588//
589// For some, such as the binary operators "&&" and "||", and the ternary
590// operator "?:", there is short-circuiting logic wherein one argument may not
591// be evaluated depending on another's value. These functions are placeholders
592// for the function-manager lookup table to indicate the arity of the function,
593// even though at runtime these functions should not get invoked.
594
595func BinaryShortCircuitPlaceholder(input1, input2 *types.Mlrval) *types.Mlrval {
596	lib.InternalCodingErrorPanic("Short-circuting was not correctly implemented")
597	return types.MLRVAL_ERROR // not reached
598}
599
600func TernaryShortCircuitPlaceholder(input1, input2, input3 *types.Mlrval) *types.Mlrval {
601	lib.InternalCodingErrorPanic("Short-circuting was not correctly implemented")
602	return types.MLRVAL_ERROR // not reached
603}
604