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  }