github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_switch.go (about)

     1  package expressions
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  
     7  	"github.com/lmorg/murex/lang"
     8  	"github.com/lmorg/murex/lang/types"
     9  )
    10  
    11  type SwitchT struct {
    12  	Condition  string
    13  	Parameters [][]rune
    14  }
    15  
    16  func (s *SwitchT) ParametersLen() int {
    17  	return len(s.Parameters)
    18  }
    19  
    20  func (s *SwitchT) ParametersString(i int) string {
    21  	return string(s.Parameters[i])
    22  }
    23  
    24  func (s *SwitchT) ParametersAll() []string {
    25  	params := make([]string, len(s.Parameters))
    26  
    27  	for i := range s.Parameters {
    28  		params[i] = string(s.Parameters[i])
    29  	}
    30  
    31  	return params
    32  }
    33  
    34  func (s *SwitchT) ParametersStringAll() string {
    35  	return strings.Join(s.ParametersAll(), " ")
    36  }
    37  
    38  func (s *SwitchT) Block(i int) ([]rune, error) {
    39  	if i > len(s.Parameters)-1 {
    40  		return nil, errors.New("too few parameters")
    41  	}
    42  	if types.IsBlockRune(s.Parameters[i]) {
    43  		return s.Parameters[i], nil
    44  	}
    45  	return nil, errors.New("not a code block")
    46  }
    47  
    48  func ParseSwitch(p *lang.Process, expression []rune) ([]*SwitchT, error) {
    49  	var swt []*SwitchT
    50  
    51  	for i := 0; i < len(expression); i++ {
    52  
    53  		switch expression[i] {
    54  		case ' ', '\t', '\r', '\n', ';':
    55  			continue
    56  
    57  		default:
    58  			tree := NewParser(p, expression[i:], 0)
    59  			tree.statement = new(StatementT)
    60  			newPos, err := tree.parseSwitch()
    61  			if err != nil {
    62  				return nil, err
    63  			}
    64  			if len(tree.statement.command) > 0 || len(tree.statement.parameters) > 0 {
    65  				swt = append(swt, &SwitchT{
    66  					Condition:  tree.statement.String(),
    67  					Parameters: tree.statement.parameters,
    68  				})
    69  				i += newPos
    70  			}
    71  		}
    72  
    73  	}
    74  
    75  	return swt, nil
    76  }
    77  
    78  func (tree *ParserT) parseSwitch() (int, error) {
    79  	var escape bool
    80  
    81  	for ; tree.charPos < len(tree.expression); tree.charPos++ {
    82  		r := tree.expression[tree.charPos]
    83  
    84  		if escape {
    85  			escape = false
    86  			switch r {
    87  			case 's':
    88  				appendToParam(tree, ' ')
    89  			case 't':
    90  				appendToParam(tree, '\t')
    91  			case 'r':
    92  				appendToParam(tree, '\r')
    93  			case 'n':
    94  				appendToParam(tree, '\n')
    95  			default:
    96  				appendToParam(tree, r)
    97  			}
    98  			continue
    99  		}
   100  
   101  		switch r {
   102  		case '#':
   103  			tree.parseComment()
   104  
   105  		case '/':
   106  			if tree.nextChar() == '#' {
   107  				if err := tree.parseCommentMultiLine(); err != nil {
   108  					return 0, err
   109  				}
   110  			} else {
   111  				appendToParam(tree, r)
   112  			}
   113  
   114  		case '\\':
   115  			escape = true
   116  
   117  		case ' ', '\t', '\r':
   118  			// whitespace. do nothing
   119  			if err := tree.nextParameter(); err != nil {
   120  				return 0, err
   121  			}
   122  
   123  		case '\n', ';':
   124  			// ignore empty lines while in the statement parser
   125  			if len(tree.statement.command) > 0 {
   126  				if err := tree.nextParameter(); err != nil {
   127  					return 0, err
   128  				}
   129  				tree.charPos--
   130  				return tree.charPos, nil
   131  			}
   132  
   133  		case ':':
   134  			if err := processStatementColon(tree, true); err != nil {
   135  				return 0, err
   136  			}
   137  
   138  		case '~':
   139  			// tilde
   140  			appendToParam(tree, []rune(tree.parseVarTilde(true))...)
   141  			if err := tree.nextParameter(); err != nil {
   142  				return 0, err
   143  			}
   144  
   145  		case '(':
   146  			if len(tree.statement.command) == 0 && len(tree.statement.paramTemp) == 0 {
   147  				appendToParam(tree, r)
   148  				if err := tree.nextParameter(); err != nil {
   149  					return 0, err
   150  				}
   151  				continue
   152  			}
   153  			prev := tree.prevChar()
   154  			if prev == ' ' || prev == '\t' {
   155  				// quotes
   156  				value, err := tree.parseParenthesis(true)
   157  				if err != nil {
   158  					return 0, err
   159  				}
   160  				appendToParam(tree, value...)
   161  				continue
   162  			}
   163  			appendToParam(tree, r)
   164  
   165  		case '%':
   166  			switch tree.nextChar() {
   167  			case '[':
   168  				// JSON array
   169  				err := processStatementFromExpr(tree, tree.parseArray, true)
   170  				if err != nil {
   171  					return 0, err
   172  				}
   173  			case '{':
   174  				// JSON object
   175  				err := processStatementFromExpr(tree, tree.parseObject, true)
   176  				if err != nil {
   177  					return 0, err
   178  				}
   179  			case '(':
   180  				tree.charPos++
   181  				value, err := tree.parseParenthesis(true)
   182  				if err != nil {
   183  					return 0, err
   184  				}
   185  				appendToParam(tree, value...)
   186  			default:
   187  				appendToParam(tree, r)
   188  			}
   189  
   190  		case '{':
   191  			// block literal
   192  			value, err := tree.parseBlockQuote()
   193  			if err != nil {
   194  				return 0, err
   195  			}
   196  			appendToParam(tree, value...)
   197  
   198  		case '}':
   199  			return 0, raiseError(tree.expression, nil, tree.charPos,
   200  				"unexpected closing bracket '}'")
   201  
   202  		case '\'', '"':
   203  			value, err := tree.parseString(r, r, true)
   204  			if err != nil {
   205  				return 0, err
   206  			}
   207  			appendToParam(tree, value...)
   208  			tree.statement.canHaveZeroLenStr = true
   209  			tree.charPos++
   210  
   211  		case '$':
   212  			switch {
   213  			case tree.nextChar() == '{':
   214  				// subshell
   215  				_, fn, err := tree.parseSubShell(true, r, varAsString)
   216  				if err != nil {
   217  					return 0, err
   218  				}
   219  				val, err := fn()
   220  				if err != nil {
   221  					return 0, err
   222  				}
   223  				appendToParam(tree, []rune(val.Value.(string))...)
   224  				tree.statement.canHaveZeroLenStr = true
   225  			case isBareChar(tree.nextChar()):
   226  				// start scalar
   227  				_, v, _, err := tree.parseVarScalar(true, true, varAsString)
   228  				if err != nil {
   229  					return 0, err
   230  				}
   231  				appendToParam(tree, []rune(v.(string))...)
   232  				tree.statement.canHaveZeroLenStr = true
   233  			default:
   234  				appendToParam(tree, r)
   235  			}
   236  
   237  		case '@':
   238  			prev := tree.prevChar()
   239  			switch {
   240  			case prev != ' ' && prev != '\t':
   241  				appendToParam(tree, r)
   242  			case tree.nextChar() == '{':
   243  				// subshell
   244  				if err := tree.nextParameter(); err != nil {
   245  					return 0, err
   246  				}
   247  				value, fn, err := tree.parseSubShell(true, r, varAsString)
   248  				if err != nil {
   249  					return 0, err
   250  				}
   251  				val, err := fn()
   252  				if err != nil {
   253  					return 0, err
   254  				}
   255  				processStatementArrays(tree, value, val.Value, true)
   256  			case isBareChar(tree.nextChar()):
   257  				// start scalar
   258  				if err := tree.nextParameter(); err != nil {
   259  					return 0, err
   260  				}
   261  				value, v, err := tree.parseVarArray(true)
   262  				if err != nil {
   263  					return 0, err
   264  				}
   265  				processStatementArrays(tree, value, v, true)
   266  			default:
   267  				appendToParam(tree, r)
   268  			}
   269  
   270  		default:
   271  			// assign value
   272  			appendToParam(tree, r)
   273  		}
   274  	}
   275  
   276  	if err := tree.nextParameter(); err != nil {
   277  		return 0, err
   278  	}
   279  	tree.charPos--
   280  	return tree.charPos, nil
   281  }