github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_statement.go (about) 1 package expressions 2 3 import ( 4 "fmt" 5 6 "github.com/lmorg/murex/lang/expressions/primitives" 7 "github.com/lmorg/murex/lang/types" 8 "github.com/lmorg/murex/utils/consts" 9 ) 10 11 func appendToParam(tree *ParserT, r ...rune) { 12 tree.statement.paramTemp = append(tree.statement.paramTemp, r...) 13 } 14 15 var namedPipeFn = []rune(consts.NamedPipeProcName) 16 17 func (tree *ParserT) parseStatement(exec bool) error { 18 var escape bool 19 20 for ; tree.charPos < len(tree.expression); tree.charPos++ { 21 r := tree.expression[tree.charPos] 22 23 if escape { 24 if r == '\n' { 25 tree.crLf() 26 if err := tree.nextParameter(); err != nil { 27 return err 28 } 29 escape = false 30 continue 31 } 32 33 if !exec { 34 appendToParam(tree, '\\', r) 35 escape = false 36 if (r == ' ' || r == '\t') && tree.nextChar() == '#' { 37 tree.statement.ignoreCrLf = true 38 } 39 continue 40 } 41 42 switch r { 43 case ' ', '\t': 44 if tree.nextChar() == '#' { 45 tree.statement.ignoreCrLf = true 46 } else { 47 appendToParam(tree, r) 48 } 49 case 's': 50 appendToParam(tree, ' ') 51 case 't': 52 appendToParam(tree, '\t') 53 case 'r': 54 appendToParam(tree, '\r') 55 case 'n': 56 appendToParam(tree, '\n') 57 case '\r': 58 continue 59 default: 60 appendToParam(tree, r) 61 } 62 escape = false 63 continue 64 } 65 66 switch r { 67 case '#': 68 tree.statement.validFunction = false 69 tree.parseComment() 70 71 case '/': 72 if tree.nextChar() == '#' { 73 if err := tree.parseCommentMultiLine(); err != nil { 74 return err 75 } 76 } else { 77 appendToParam(tree, r) 78 } 79 80 case '\\': 81 tree.statement.validFunction = false 82 escape = true 83 84 case ' ', '\t', '\r': 85 // whitespace. do nothing 86 if err := tree.nextParameter(); err != nil { 87 return err 88 } 89 90 case '\n': 91 // '\' escaped used at end of line 92 tree.crLf() 93 if tree.statement.ignoreCrLf { 94 tree.statement.ignoreCrLf = false 95 if err := tree.nextParameter(); err != nil { 96 return err 97 } 98 continue 99 } 100 // ignore empty lines while in the statement parser 101 if len(tree.statement.command) > 0 || len(tree.statement.paramTemp) > 0 { 102 err := tree.nextParameter() 103 tree.charPos-- 104 return err 105 } 106 107 case '*': 108 tree.statement.possibleGlob = exec 109 tree.statement.validFunction = false 110 appendToParam(tree, r) 111 112 case '?': 113 prev := tree.prevChar() 114 next := tree.nextChar() 115 if prev != ' ' && prev != '\t' && 116 next != ' ' && next != '\t' { 117 tree.statement.possibleGlob = exec 118 tree.statement.validFunction = false 119 appendToParam(tree, r) 120 continue 121 } 122 fallthrough 123 124 case ';', '|': 125 // end expression 126 err := tree.nextParameter() 127 tree.charPos-- 128 return err 129 130 case '&': 131 if tree.nextChar() == '&' { 132 err := tree.nextParameter() 133 tree.charPos-- 134 return err 135 } 136 tree.statement.validFunction = false 137 appendToParam(tree, r) 138 139 case ':': 140 tree.statement.validFunction = false 141 if err := processStatementColon(tree, exec); err != nil { 142 return err 143 } 144 145 case '=': 146 tree.statement.validFunction = false 147 switch tree.nextChar() { 148 case '>': 149 // generic pipe 150 err := tree.nextParameter() 151 tree.charPos-- 152 return err 153 default: 154 // assign value 155 appendToParam(tree, r) 156 } 157 158 case '~': 159 // tilde 160 tree.statement.validFunction = false 161 appendToParam(tree, []rune(tree.parseVarTilde(exec))...) 162 163 case '<': 164 tree.statement.validFunction = false 165 switch { 166 case len(tree.statement.paramTemp) > 0: 167 appendToParam(tree, r) 168 case len(tree.statement.command) == 0: 169 // check if named pipe 170 value := tree.parseNamedPipe() 171 if len(value) == 0 { 172 appendToParam(tree, r) 173 } else { 174 tree.statement.command = namedPipeFn 175 tree.statement.paramTemp = value 176 if err := tree.nextParameter(); err != nil { 177 return err 178 } 179 } 180 case len(tree.statement.parameters) == 0: 181 // check if named pipe 182 value := tree.parseNamedPipe() 183 if len(value) == 0 { 184 appendToParam(tree, r) 185 } else { 186 tree.statement.namedPipes = append(tree.statement.namedPipes, string(value)) 187 } 188 default: 189 appendToParam(tree, r) 190 } 191 192 case '>': 193 tree.statement.validFunction = false 194 switch tree.nextChar() { 195 case '>': 196 // redirect (append) 197 if len(tree.statement.command) == 0 && len(tree.statement.paramTemp) == 0 { 198 appendToParam(tree, r, r) 199 tree.charPos++ 200 if err := tree.nextParameter(); err != nil { 201 return err 202 } 203 } else { 204 if len(tree.statement.paramTemp) > 0 { 205 tree.statement.paramTemp = tree.statement.paramTemp[:len(tree.statement.paramTemp)-2] 206 if err := tree.nextParameter(); err != nil { 207 return err 208 } 209 } 210 tree.charPos-- 211 return nil 212 } 213 default: 214 appendToParam(tree, r) 215 } 216 217 case '(': 218 prev := tree.prevChar() 219 switch { 220 case len(tree.statement.command) == 0 && len(tree.statement.paramTemp) == 0: 221 // command (deprecated) 222 appendToParam(tree, r) 223 if err := tree.nextParameter(); err != nil { 224 return err 225 } 226 227 case prev == ' ', prev == '\t': 228 // parenthesis quotes 229 if exec { 230 pos := tree.charPos 231 expr, err := tree.parseParenthesis(false) 232 if err != nil { 233 return err 234 } 235 dt, err := ExecuteExpr(tree.p, expr) 236 if err == nil { 237 // parenthesis is an expression 238 v, err := dt.GetValue() 239 if err != nil { 240 return err 241 } 242 r, err := v.Marshal() 243 if err != nil { 244 return fmt.Errorf("cannot marshal output of inlined expression in `%s`: %s", 245 string(tree.statement.command), err.Error()) 246 } 247 appendToParam(tree, r...) 248 continue 249 } 250 tree.charPos = pos 251 } 252 // parenthesis is a string (deprecated) 253 value, err := tree.parseParenthesis(exec) 254 if err != nil { 255 return err 256 } 257 appendToParam(tree, value...) 258 259 case tree.statement.validFunction: 260 // function(parameters...) 261 value, fn, err := tree.parseFunction(exec, tree.statement.paramTemp, varAsString) 262 if err != nil { 263 return err 264 } 265 tree.statement.paramTemp = nil 266 if exec { 267 val, err := fn() 268 if err != nil { 269 return err 270 } 271 appendToParam(tree, []rune(val.Value.(string))...) 272 } else { 273 appendToParam(tree, value...) 274 } 275 tree.charPos-- 276 if err := tree.nextParameter(); err != nil { 277 return err 278 } 279 default: 280 tree.statement.validFunction = false 281 appendToParam(tree, r) 282 } 283 284 case '%': 285 tree.statement.validFunction = false 286 if !exec { 287 appendToParam(tree, '%') 288 } 289 switch tree.nextChar() { 290 case '[': 291 // JSON array 292 err := processStatementFromExpr(tree, tree.parseArray, exec) 293 if err != nil { 294 return err 295 } 296 case '{': 297 // JSON object 298 err := processStatementFromExpr(tree, tree.parseObject, exec) 299 if err != nil { 300 return err 301 } 302 // i don't know why I need the next 4 lines, but tests fail without it 303 tree.charPos++ 304 if !exec { 305 appendToParam(tree, '}') 306 } 307 case '(': 308 // string 309 tree.charPos++ 310 value, err := tree.parseParenthesis(exec) 311 if err != nil { 312 return err 313 } 314 appendToParam(tree, value...) 315 default: 316 appendToParam(tree, r) 317 } 318 319 case '{': 320 tree.statement.validFunction = false 321 // block literal 322 value, err := tree.parseBlockQuote() 323 if err != nil { 324 return err 325 } 326 // was this the start of a parameter... 327 var nextParam bool 328 if len(tree.statement.paramTemp) == 0 && tree.tokeniseCurlyBrace() { 329 nextParam = true 330 } 331 appendToParam(tree, value...) 332 // ...if so lets create a new parameter 333 if nextParam { 334 if err := tree.nextParameter(); err != nil { 335 return err 336 } 337 } 338 339 case '[': 340 tree.statement.validFunction = false 341 switch { 342 //case len(tree.statement.command) == 1 && tree.prevChar() != '!': 343 // kjhkjhkjhkjhkjhkh 344 case len(tree.statement.command) > 0 || len(tree.statement.paramTemp) > 0: 345 appendToParam(tree, r) 346 case tree.nextChar() == '[': 347 // element 348 appendToParam(tree, '[', '[') 349 tree.charPos++ 350 if err := tree.nextParameter(); err != nil { 351 return err 352 } 353 default: 354 // index 355 appendToParam(tree, r) 356 if err := tree.nextParameter(); err != nil { 357 return err 358 } 359 } 360 361 case '}': 362 return raiseError(tree.expression, nil, tree.charPos, 363 "unexpected closing bracket '}'") 364 365 case '\'', '"': 366 tree.statement.validFunction = false 367 value, err := tree.parseString(r, r, exec) 368 if err != nil { 369 return err 370 } 371 appendToParam(tree, value...) 372 tree.statement.canHaveZeroLenStr = true 373 tree.charPos++ 374 375 case '`': 376 tree.statement.validFunction = false 377 value, err := tree.parseBackTick(r, exec) 378 if err != nil { 379 return err 380 } 381 appendToParam(tree, value...) 382 tree.charPos++ 383 384 case '$': 385 tree.statement.validFunction = false 386 switch { 387 case tree.nextChar() == '{': 388 // subshell 389 value, fn, err := tree.parseSubShell(exec, r, varAsString) 390 if err != nil { 391 return err 392 } 393 if exec { 394 val, err := fn() 395 if err != nil { 396 return err 397 } 398 appendToParam(tree, []rune(val.Value.(string))...) 399 tree.statement.canHaveZeroLenStr = true 400 } else { 401 appendToParam(tree, value...) 402 } 403 default: 404 // start scalar 405 var tokenise bool 406 tokenise = tree.tokeniseScalar() 407 execScalar := exec && tokenise 408 value, v, _, err := tree.parseVarScalar(execScalar, execScalar, varAsString) 409 if err != nil { 410 return raiseError(tree.expression, nil, tree.charPos, err.Error()) 411 } 412 switch { 413 case execScalar: 414 appendToParam(tree, []rune(v.(string))...) 415 tree.statement.canHaveZeroLenStr = true 416 case !tokenise: 417 appendToParam(tree, value[1:]...) 418 default: 419 appendToParam(tree, value...) 420 } 421 } 422 423 case '@': 424 tree.statement.validFunction = false 425 prev := tree.prevChar() 426 next := tree.nextChar() 427 switch { 428 case prev != ' ' && prev != '\t' && prev != 0: 429 appendToParam(tree, r) 430 case next == '{': 431 // subshell 432 if err := tree.nextParameter(); err != nil { 433 return err 434 } 435 value, fn, err := tree.parseSubShell(exec, r, varAsString) 436 if err != nil { 437 return err 438 } 439 var v any 440 if exec { 441 val, err := fn() 442 if err != nil { 443 return err 444 } 445 v = val.Value 446 } 447 processStatementArrays(tree, value, v, exec) 448 case next == '[' && len(tree.statement.command) == 0 && len(tree.statement.paramTemp) == 0: 449 // @[ command 450 appendToParam(tree, '@', '[') 451 tree.charPos++ 452 if err := tree.nextParameter(); err != nil { 453 return err 454 } 455 case isBareChar(tree.nextChar()): 456 // start scalar 457 if err := tree.nextParameter(); err != nil { 458 return err 459 } 460 value, v, err := tree.parseVarArray(exec) 461 if err != nil { 462 return err 463 } 464 if exec { 465 processStatementArrays(tree, value, v, exec) 466 } else { 467 appendToParam(tree, value...) 468 } 469 default: 470 appendToParam(tree, r) 471 } 472 473 case '-': 474 next := tree.nextChar() 475 switch { 476 case next == '>': 477 err := tree.nextParameter() 478 tree.charPos-- 479 return err 480 default: 481 // assign value 482 appendToParam(tree, r) 483 } 484 485 default: 486 // assign value 487 if !isBareChar(r) || r == '!' { 488 tree.statement.validFunction = false 489 } 490 appendToParam(tree, r) 491 } 492 } 493 494 err := tree.nextParameter() 495 tree.charPos-- 496 return err 497 } 498 499 func processStatementArrays(tree *ParserT, value []rune, v interface{}, exec bool) error { 500 if exec { 501 switch t := v.(type) { 502 case []string: 503 for i := range t { 504 value = []rune(t[i]) 505 appendToParam(tree, value...) 506 if err := tree.nextParameter(); err != nil { 507 return err 508 } 509 } 510 case [][]rune: 511 for i := range t { 512 value = t[i] 513 appendToParam(tree, value...) 514 if err := tree.nextParameter(); err != nil { 515 return err 516 } 517 } 518 case [][]byte: 519 for i := range t { 520 value = []rune(string(t[i])) 521 appendToParam(tree, value...) 522 if err := tree.nextParameter(); err != nil { 523 return err 524 } 525 } 526 case []interface{}: 527 for i := range t { 528 s, err := types.ConvertGoType(t[i], types.String) 529 if err != nil { 530 return err 531 } 532 value = []rune(s.(string)) 533 appendToParam(tree, value...) 534 if err := tree.nextParameter(); err != nil { 535 return err 536 } 537 } 538 case string: 539 appendToParam(tree, []rune(value)...) 540 if err := tree.nextParameter(); err != nil { 541 return err 542 } 543 default: 544 s, err := types.ConvertGoType(t, types.String) 545 if err != nil { 546 return err 547 } 548 value = []rune(s.(string)) 549 appendToParam(tree, value...) 550 } 551 552 } else { 553 appendToParam(tree, value...) 554 if err := tree.nextParameter(); err != nil { 555 return err 556 } 557 } 558 return tree.nextParameter() 559 } 560 561 func processStatementColon(tree *ParserT, exec bool) error { 562 tree.statement.asStatement = true 563 564 switch { 565 case len(tree.statement.command) == 0: 566 if len(tree.statement.paramTemp) > 0 { 567 // is a command 568 if !exec { 569 appendToParam(tree, ':') 570 } 571 return tree.nextParameter() 572 } else { 573 // is a cast 574 tree.charPos++ 575 tree.statement.cast = tree.parseBareword() 576 } 577 default: 578 // is a value 579 appendToParam(tree, ':') 580 } 581 582 return nil 583 } 584 585 type parserMethodT func(bool) ([]rune, *primitives.DataType, error) 586 587 func processStatementFromExpr(tree *ParserT, method parserMethodT, exec bool) error { 588 tree.charPos++ 589 value, dt, err := method(exec) 590 if err != nil { 591 return err 592 } 593 594 if exec { 595 val, err := dt.GetValue() 596 if err != nil { 597 return err 598 } 599 value, err = val.Marshal() 600 if err != nil { 601 return err 602 } 603 appendToParam(tree, value...) 604 } else { 605 appendToParam(tree, value...) 606 } 607 608 if exec { 609 tree.charPos++ 610 } 611 return nil 612 }