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 }