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

     1  package expressions
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/lmorg/murex/lang"
     8  	"github.com/lmorg/murex/lang/expressions/primitives"
     9  	"github.com/lmorg/murex/lang/expressions/symbols"
    10  	"github.com/lmorg/murex/lang/types"
    11  	"github.com/lmorg/murex/utils"
    12  )
    13  
    14  func (tree *ParserT) createArrayAst(exec bool) error {
    15  	// create JSON array
    16  	_, dt, err := tree.parseArray(exec)
    17  	if err != nil {
    18  		return err
    19  	}
    20  	tree.appendAstWithPrimitive(symbols.ArrayBegin, dt)
    21  	tree.charPos++
    22  	return nil
    23  }
    24  
    25  func (tree *ParserT) parseArray(exec bool) ([]rune, *primitives.DataType, error) {
    26  	var (
    27  		start = tree.charPos
    28  		//value = make([]rune, 0, len(tree.expression)-tree.charPos)
    29  		slice []interface{}
    30  	)
    31  
    32  	// check if valid mkarray
    33  	dt, pos, err := tree.parseArrayMaker(exec)
    34  	if err != nil {
    35  		return nil, nil, err
    36  	}
    37  	if dt != nil {
    38  		tree.charPos--
    39  		return nil, dt, nil
    40  	}
    41  	tree.charPos = pos
    42  
    43  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
    44  		r := tree.expression[tree.charPos]
    45  
    46  		switch r {
    47  		case '#':
    48  			tree.parseComment()
    49  
    50  		case '/':
    51  			// multiline comment
    52  			if tree.nextChar() == '#' {
    53  				if err := tree.parseCommentMultiLine(); err != nil {
    54  					return nil, nil, err
    55  				}
    56  			} else {
    57  				// string
    58  				value := tree.parseArrayBareword()
    59  				slice = append(slice, formatArrayValue(value))
    60  			}
    61  
    62  		case '\'', '"':
    63  			// quoted string
    64  			value, err := tree.parseString(r, r, exec)
    65  			if err != nil {
    66  				return nil, nil, err
    67  			}
    68  			slice = append(slice, string(value))
    69  			tree.charPos++
    70  
    71  		case '%':
    72  			switch tree.nextChar() {
    73  			case '[', '{':
    74  				// do nothing because action covered in the next iteration
    75  			case '(':
    76  				// start nested string
    77  				tree.charPos++
    78  				value, err := tree.parseParenthesis(exec)
    79  				if err != nil {
    80  					return nil, nil, err
    81  				}
    82  				slice = append(slice, string(value))
    83  			default:
    84  				return nil, nil, fmt.Errorf("'%%' token should be followed with '[', '{', or '(', instead got '%s'", string(r))
    85  			}
    86  
    87  		case '[':
    88  			// start nested array
    89  			_, dt, err := tree.parseArray(exec)
    90  			if err != nil {
    91  				return nil, nil, err
    92  			}
    93  			v, err := dt.GetValue()
    94  			if err != nil {
    95  				return nil, nil, err
    96  			}
    97  			slice = append(slice, v.Value)
    98  			tree.charPos++
    99  
   100  		case ']':
   101  			// end array
   102  			goto endArray
   103  
   104  		case '{':
   105  			// start nested object
   106  			_, dt, err := tree.parseObject(exec)
   107  			if err != nil {
   108  				return nil, nil, err
   109  			}
   110  			v, err := dt.GetValue()
   111  			if err != nil {
   112  				return nil, nil, err
   113  			}
   114  			slice = append(slice, v.Value)
   115  			tree.charPos++
   116  
   117  		case '$':
   118  			// inline scalar
   119  			switch tree.nextChar() {
   120  			case '{':
   121  				r, fn, err := tree.parseSubShell(exec, r, varAsValue)
   122  				if err != nil {
   123  					return nil, nil, err
   124  				}
   125  				if exec {
   126  					v, err := fn()
   127  					if err != nil {
   128  						return nil, nil, err
   129  					}
   130  					slice = append(slice, v.Value)
   131  				} else {
   132  					slice = append(slice, string(r))
   133  				}
   134  
   135  			default:
   136  				_, v, _, err := tree.parseVarScalar(exec, exec, varAsValue)
   137  				if err != nil {
   138  					return nil, nil, err
   139  				}
   140  				slice = append(slice, v)
   141  			}
   142  
   143  		case '~':
   144  			// tilde
   145  			slice = append(slice, tree.parseVarTilde(exec))
   146  
   147  		case '@':
   148  			// inline array
   149  			var (
   150  				name []rune
   151  				v    interface{}
   152  			)
   153  			switch tree.nextChar() {
   154  			case '{':
   155  				r, fn, err := tree.parseSubShell(exec, r, varAsValue)
   156  				if err != nil {
   157  					return nil, nil, err
   158  				}
   159  				if exec {
   160  					val, err := fn()
   161  					v = val.Value
   162  					if err != nil {
   163  						return nil, nil, err
   164  					}
   165  				} else {
   166  					v = string(r)
   167  				}
   168  			default:
   169  				name, v, err = tree.parseVarArray(exec)
   170  				if err != nil {
   171  					return nil, nil, err
   172  				}
   173  				//tree.charPos++
   174  			}
   175  			switch t := v.(type) {
   176  			case nil:
   177  				slice = append(slice, t)
   178  			case []interface{}:
   179  				slice = append(slice, t...)
   180  			case []string, []float64, []int:
   181  				slice = append(slice, v.([]interface{})...)
   182  			case string:
   183  				slice = append(slice, t)
   184  			default:
   185  				return nil, nil, raiseError(tree.expression, nil, tree.charPos,
   186  					fmt.Sprintf(
   187  						"cannot expand %T into an array type\nVariable name: @%s",
   188  						t, string(name)))
   189  			}
   190  
   191  		case '\n':
   192  			tree.crLf()
   193  			//fallthrough
   194  
   195  		case ',', ' ', '\t', '\r':
   196  			// do nothing
   197  
   198  		default:
   199  			value := tree.parseArrayBareword()
   200  			v, err := types.ConvertGoType(value, types.Number)
   201  			if err == nil {
   202  				// is a number
   203  				slice = append(slice, v)
   204  			} else {
   205  				// is a string
   206  				slice = append(slice, formatArrayValue(value))
   207  			}
   208  		}
   209  	}
   210  
   211  	return nil, nil, raiseError(tree.expression, nil, tree.charPos,
   212  		"missing closing square bracket ']'")
   213  
   214  endArray:
   215  	value := tree.expression[start:tree.charPos]
   216  	tree.charPos--
   217  	dt = primitives.NewPrimitive(primitives.Array, slice)
   218  	return value, dt, nil
   219  }
   220  
   221  func (tree *ParserT) parseArrayBareword() []rune {
   222  	var start = tree.charPos
   223  
   224  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
   225  		r := tree.expression[tree.charPos]
   226  
   227  		switch r {
   228  		case ',', ' ', '\t', '\r', '\n', '[', ']', '{', '}', ':', '$', '~', '@', '"', '\'', '%':
   229  			goto endArrayBareword
   230  		case '/':
   231  			if tree.nextChar() == '*' {
   232  				goto endArrayBareword
   233  			}
   234  		}
   235  	}
   236  
   237  endArrayBareword:
   238  	value := tree.expression[start:tree.charPos]
   239  	tree.charPos--
   240  	return value
   241  }
   242  
   243  func formatArrayValue(value []rune) interface{} {
   244  	s := string(value)
   245  	switch s {
   246  	case "true":
   247  		return true
   248  	case "false":
   249  		return false
   250  	case "null":
   251  		return nil
   252  	default:
   253  		return s
   254  	}
   255  }
   256  
   257  func (tree *ParserT) parseArrayMaker(exec bool) (*primitives.DataType, int, error) {
   258  	start := tree.charPos
   259  	var (
   260  		mkarray     bool
   261  		brackets    int = 1
   262  		twoBrackets bool
   263  	)
   264  
   265  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
   266  		r := tree.expression[tree.charPos]
   267  
   268  		switch r {
   269  		case '\'', '"', '(', '{', '%':
   270  			return nil, start, nil
   271  
   272  		case '.':
   273  			if tree.nextChar() == '.' {
   274  				tree.charPos++
   275  				mkarray = true
   276  			}
   277  
   278  		case '[':
   279  			brackets++
   280  			if brackets == 3 {
   281  				return nil, start, nil
   282  			}
   283  			if brackets == 2 {
   284  				twoBrackets = true
   285  			}
   286  
   287  		case ']':
   288  			brackets--
   289  			if brackets == 0 {
   290  				goto endParseArrayMaker
   291  			}
   292  
   293  		}
   294  	}
   295  
   296  	return nil, start, raiseError(
   297  		tree.expression, nil, tree.charPos, "missing closing bracket `]` ")
   298  
   299  endParseArrayMaker:
   300  	if !mkarray {
   301  		return nil, start, nil
   302  	}
   303  
   304  	if !exec {
   305  		return primitives.NewPrimitive(primitives.Array, make([]any, 0)), tree.charPos, nil
   306  	}
   307  
   308  	var block []rune
   309  	if twoBrackets {
   310  		block = append([]rune{'j', 'a', ':', ' '}, tree.expression[start+1:tree.charPos]...)
   311  	} else {
   312  		block = append([]rune{'j', 'a', ':', ' ', '['}, tree.expression[start+1:tree.charPos]...)
   313  		block = append(block, ']')
   314  	}
   315  
   316  	fork := tree.p.Fork(lang.F_NO_STDIN | lang.F_CREATE_STDERR | lang.F_CREATE_STDOUT)
   317  	_, err := fork.Execute(block)
   318  	if err != nil {
   319  		return nil, start, err
   320  	}
   321  
   322  	b, err := fork.Stderr.ReadAll()
   323  	if err != nil {
   324  		return nil, start, err
   325  	}
   326  	if len(b) > 0 {
   327  		b = utils.CrLfTrim(b)
   328  		return nil, start, errors.New(string(b))
   329  	}
   330  
   331  	var slice []interface{}
   332  	err = fork.Stdout.ReadArrayWithType(tree.p.Context, func(v interface{}, _ string) {
   333  		slice = append(slice, v)
   334  	})
   335  
   336  	if err != nil {
   337  		return nil, start, err
   338  	}
   339  
   340  	dt := primitives.NewPrimitive(primitives.Array, slice)
   341  	return dt, tree.charPos, nil
   342  }