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