github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_expression.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/expressions/symbols" 8 ) 9 10 func (tree *ParserT) parseExpression(exec, incLogicalOps bool) error { 11 for ; tree.charPos < len(tree.expression); tree.charPos++ { 12 r := tree.expression[tree.charPos] 13 switch r { 14 case '#': 15 tree.charPos-- 16 return nil 17 18 case ' ', '\t', '\r': 19 // whitespace. do nothing 20 21 case '\n': 22 if len(tree.ast) == 0 { 23 // do nothing if just empty lines 24 tree.crLf() 25 continue 26 } 27 tree.charPos-- 28 return nil 29 30 case ';': 31 // end expression 32 tree.charPos-- 33 return nil 34 35 case '?': 36 switch tree.nextChar() { 37 case '?': 38 // elvis 39 tree.appendAst(symbols.NullCoalescing) 40 tree.charPos++ 41 case ':': 42 // elvis 43 tree.appendAst(symbols.Elvis) 44 tree.charPos++ 45 default: 46 // end expression 47 tree.charPos-- 48 return nil 49 } 50 51 case '|': 52 if incLogicalOps && tree.nextChar() == '|' { 53 // equals 54 tree.appendAst(symbols.LogicalOr) 55 tree.charPos++ 56 } else { 57 // end expression 58 tree.charPos-- 59 return nil 60 } 61 62 case '&': 63 if tree.nextChar() == '&' { 64 if incLogicalOps { 65 // equals 66 tree.appendAst(symbols.LogicalAnd) 67 tree.charPos++ 68 continue 69 } else { 70 // end expression 71 tree.charPos-- 72 return nil 73 } 74 } 75 tree.appendAst(symbols.Unexpected, r) 76 raiseError(tree.expression, nil, tree.charPos, errMessage[symbols.Unexpected]) 77 78 case '=': 79 switch tree.nextChar() { 80 case '=': 81 // equals 82 tree.appendAst(symbols.EqualTo) 83 tree.charPos++ 84 case '~': 85 // regexp 86 tree.appendAst(symbols.Regexp) 87 tree.charPos++ 88 case '>': 89 // generic pipe 90 tree.charPos-- 91 return nil 92 default: 93 // assign value 94 tree.appendAst(symbols.Assign) 95 } 96 97 case ':': 98 switch tree.nextChar() { 99 case '=': 100 // update variable 101 tree.appendAst(symbols.AssignUpdate) 102 tree.charPos++ 103 default: 104 // less than 105 tree.appendAst(symbols.LessThan) 106 } 107 108 case '!': 109 switch tree.nextChar() { 110 case '=': 111 // not equal 112 tree.appendAst(symbols.NotEqualTo) 113 tree.charPos++ 114 case '~': 115 // not regexp 116 tree.appendAst(symbols.NotRegexp) 117 tree.charPos++ 118 case '!': 119 // not like 120 tree.appendAst(symbols.NotLike) 121 tree.charPos++ 122 default: 123 // might be a function 124 if !isBareChar(tree.nextChar()) { 125 // unexpected symbol 126 tree.appendAst(symbols.Unexpected) 127 } 128 value := tree.parseBareword() 129 if len(tree.expression) <= tree.charPos || tree.expression[tree.charPos] != '(' { 130 tree.appendAst(symbols.Unexpected) 131 continue 132 } 133 runes, fn, err := tree.parseFunction(exec, value, varAsValue) 134 if err != nil { 135 return err 136 } 137 dt := primitives.NewFunction(fn) 138 tree.appendAstWithPrimitive(symbols.Calculated, dt, runes...) 139 } 140 141 case '~': 142 switch tree.nextChar() { 143 case '~': 144 // like 145 tree.appendAst(symbols.Like) 146 tree.charPos++ 147 case '>': 148 // merge into 149 tree.appendAst(symbols.MergeInto) 150 tree.charPos++ 151 default: 152 // tilde 153 /*tree.appendAstWithPrimitive(symbols.Calculated, &primitives.DataType{ 154 Primitive: primitives.String, 155 Value: tree.parseVarTilde(exec), 156 })*/ 157 tree.appendAstWithPrimitive(symbols.Calculated, primitives.NewPrimitive( 158 primitives.String, 159 tree.parseVarTilde(exec), 160 )) 161 } 162 163 case '>': 164 switch tree.nextChar() { 165 case '=': 166 // greater than or equal to 167 tree.appendAst(symbols.GreaterThanOrEqual) 168 tree.charPos++ 169 case '>': 170 // redirect (append) 171 tree.charPos-- 172 return nil 173 default: 174 // greater than 175 tree.appendAst(symbols.GreaterThan) 176 } 177 178 case '<': 179 switch tree.nextChar() { 180 case '=': 181 // less than or equal to 182 tree.appendAst(symbols.LessThanOrEqual) 183 tree.charPos++ 184 case '~': 185 // assign and merge 186 tree.appendAst(symbols.AssignAndMerge) 187 tree.charPos++ 188 default: 189 // less than 190 tree.appendAst(symbols.LessThan) 191 } 192 193 case '(': 194 // create sub expression 195 tree.charPos++ 196 branch := NewParser(tree.p, tree.expression[tree.charPos:], 0) 197 branch.charOffset = tree.charPos + tree.charOffset 198 branch.subExp = true 199 err := branch.parseExpression(exec, true) 200 if err != nil { 201 return err 202 } 203 tree.charPos += branch.charPos - 1 204 if exec { 205 dt, err := branch.executeExpr() 206 if err != nil { 207 return err 208 } 209 val, err := dt.GetValue() 210 if err != nil { 211 return err 212 } 213 tree.appendAstWithPrimitive(symbols.Exp(val.Primitive), dt) 214 } else { 215 tree.appendAst(symbols.SubExpressionBegin) 216 } 217 218 case ')': 219 tree.charPos++ 220 switch { 221 case tree.subExp: 222 // end sub expression 223 return nil 224 default: 225 raiseError(tree.expression, nil, tree.charPos, errMessage[symbols.SubExpressionEnd]) 226 } 227 228 case '%': 229 switch tree.nextChar() { 230 case '[': 231 tree.charPos++ 232 err := tree.createArrayAst(exec) 233 if err != nil { 234 return err 235 } 236 case '{': 237 tree.charPos++ 238 err := tree.createObjectAst(exec) 239 if err != nil { 240 return err 241 } 242 case '(': 243 tree.charPos++ 244 err := tree.createStringAst('(', ')', exec) 245 if err != nil { 246 return err 247 } 248 default: 249 tree.appendAst(symbols.Unexpected, r) 250 raiseError(tree.expression, nil, tree.charPos, errMessage[symbols.Unexpected]) 251 } 252 253 case '[': 254 switch tree.nextChar() { 255 case '{': 256 runes, v, mxDt, err := tree.parseLambdaStatement(exec, '$') 257 if err != nil { 258 return err 259 } 260 /*tree.appendAstWithPrimitive(symbols.Calculated, primitives.NewPrimitive( 261 primitives.Array, v), runes...)*/ 262 tree.appendAstWithPrimitive(symbols.Calculated, primitives.NewScalar(mxDt, v), runes...) 263 default: 264 if !exec { 265 return raiseError(tree.expression, nil, tree.charPos, fmt.Sprintf("%s '%s' (%d)", 266 errMessage[symbols.Unexpected], string(r), r)) 267 } 268 tree.charPos++ 269 tree.appendAst(symbols.Unexpected, r) 270 } 271 272 case ']': 273 // end JSON array 274 tree.appendAst(symbols.ArrayEnd, r) 275 276 case '}': 277 // end JSON object 278 tree.appendAst(symbols.ObjectEnd, r) 279 280 case '\'', '`': 281 // start string / end string 282 value, err := tree.parseString(r, r, exec) 283 if err != nil { 284 return err 285 } 286 tree.appendAst(symbols.QuoteSingle, value...) 287 tree.charPos++ 288 289 case '"': 290 // start string / end string 291 value, err := tree.parseString(r, r, exec) 292 if err != nil { 293 return err 294 } 295 tree.appendAst(symbols.QuoteDouble, value...) 296 tree.charPos++ 297 298 case '$': 299 next := tree.nextChar() 300 switch { 301 case next == '{': 302 runes, fn, err := tree.parseSubShell(exec, r, varAsValue) 303 if err != nil { 304 return err 305 } 306 dt := primitives.NewFunction(fn) 307 tree.appendAstWithPrimitive(symbols.Calculated, dt, runes...) 308 case next == 0: 309 tree.appendAst(symbols.Unexpected, r) 310 default: 311 // scalar 312 runes, v, mxDt, fn, err := tree.parseVarScalarExpr(exec, false, varAsValue) 313 if err != nil { 314 return raiseError(tree.expression, nil, tree.charPos, fmt.Sprintf("%s: '%s'", 315 err.Error(), string(r))) 316 } 317 if !exec { 318 dt := primitives.NewScalar(mxDt, v) 319 tree.appendAstWithPrimitive(symbols.Scalar, dt, runes...) 320 } else { 321 dt := primitives.NewFunction(fn) 322 tree.appendAstWithPrimitive(symbols.Scalar, dt, runes...) 323 } 324 } 325 326 case '@': // TODO: test me please! 327 next := tree.nextChar() 328 switch { 329 case next == '{': 330 // subshell 331 runes, fn, err := tree.parseSubShell(exec, r, varAsValue) 332 if err != nil { 333 return err 334 } 335 dt := primitives.NewFunction(fn) 336 tree.appendAstWithPrimitive(symbols.Calculated, dt, runes...) 337 case next == '[': 338 // range (this needs to be a statement) 339 return raiseError(tree.expression, nil, tree.charPos, fmt.Sprintf("%s: '%s'", 340 errMessage[symbols.Unexpected], string(r))) 341 case isBareChar(next): 342 // start array 343 runes, v, err := tree.parseVarArray(exec) 344 if err != nil { 345 return err 346 } 347 tree.appendAstWithPrimitive(symbols.Calculated, primitives.NewPrimitive(primitives.Array, v), runes...) 348 default: 349 if !exec { 350 return raiseError(tree.expression, nil, tree.charPos, fmt.Sprintf("%s: '%s'", 351 errMessage[symbols.Unexpected], string(r))) 352 } 353 tree.appendAst(symbols.Unexpected, r) 354 } 355 356 case '+': 357 switch tree.nextChar() { 358 case '=': 359 // equal add 360 tree.appendAst(symbols.AssignAndAdd) 361 tree.charPos++ 362 default: 363 // add (+append) 364 tree.appendAst(symbols.Add) 365 } 366 367 case '-': 368 c := tree.nextChar() 369 switch { 370 case c == '=': 371 // equal subtract 372 tree.appendAst(symbols.AssignAndSubtract) 373 tree.charPos++ 374 case c >= '0' && '9' >= c: 375 if len(tree.ast) == 0 || tree.ast[len(tree.ast)-1].key > symbols.Operations { 376 // number 377 value := tree.parseNumber() 378 tree.appendAst(symbols.Number, value...) 379 tree.charPos-- 380 } else { 381 // subtract 382 tree.appendAst(symbols.Subtract) 383 } 384 case c == '>': 385 // arrow pipe 386 tree.charPos-- 387 return nil 388 default: 389 tree.appendAst(symbols.Subtract) 390 // invalid hyphen 391 //tree.appendAst(symbols.InvalidHyphen) 392 } 393 394 case '*': 395 switch tree.nextChar() { 396 case '=': 397 // equal multiply 398 tree.appendAst(symbols.AssignAndMultiply) 399 tree.charPos++ 400 default: 401 // multiply 402 tree.appendAst(symbols.Multiply) 403 } 404 405 case '/': 406 switch tree.nextChar() { 407 case '=': 408 // equal divide 409 tree.appendAst(symbols.AssignAndDivide) 410 tree.charPos++ 411 case '#': 412 // multi-line comment 413 if err := tree.parseCommentMultiLine(); err != nil { 414 return err 415 } 416 default: 417 // divide 418 tree.appendAst(symbols.Divide) 419 } 420 421 default: 422 switch { 423 case r >= '0' && '9' >= r: 424 // number 425 value := tree.parseNumber() 426 tree.appendAst(symbols.Number, value...) 427 tree.charPos-- 428 429 case isBareChar(r): 430 // bareword 431 value := tree.parseBareword() 432 switch string(value) { 433 case "true", "false": 434 tree.appendAst(symbols.Boolean, value...) 435 case "null": 436 tree.appendAst(symbols.Null, value...) 437 default: 438 if len(tree.expression) > tree.charPos && tree.expression[tree.charPos] == '(' { 439 runes, fn, err := tree.parseFunction(exec, value, varAsValue) 440 if err != nil { 441 return err 442 } 443 dt := primitives.NewFunction(fn) 444 tree.appendAstWithPrimitive(symbols.Calculated, dt, runes...) 445 } else { 446 tree.appendAst(symbols.Bareword, value...) 447 } 448 } 449 tree.charPos-- 450 451 default: 452 if !exec { 453 return raiseError(tree.expression, nil, tree.charPos, fmt.Sprintf("%s '%s' (%d)", 454 errMessage[symbols.Unexpected], string(r), r)) 455 } 456 tree.charPos++ 457 tree.appendAst(symbols.Unexpected, r) 458 } 459 } 460 } 461 462 if tree.charPos >= len(tree.expression)-1 || tree.expression[tree.charPos] == 0 { 463 tree.charPos-- 464 } 465 466 return nil 467 }