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  }