github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/mkarray/mkarray.go (about) 1 package mkarray 2 3 import ( 4 "fmt" 5 6 "github.com/lmorg/murex/lang" 7 "github.com/lmorg/murex/lang/stdio" 8 "github.com/lmorg/murex/lang/types" 9 ) 10 11 func init() { 12 lang.DefineFunction("a", cmdA, types.String) 13 lang.DefineFunction("ja", cmdJa, types.Json) 14 lang.DefineFunction("ta", cmdTa, types.WriteArray) 15 } 16 17 const ( 18 astTypeString = iota 19 astTypeOpen 20 astTypeClose 21 astTypeSeparator 22 astTypeRange 23 ) 24 25 type ast struct { 26 Data []byte 27 Type int 28 } 29 30 type arrayT struct { 31 p *lang.Process 32 expression []byte 33 dataType string 34 writer stdio.ArrayWriter 35 groups [][]ast 36 } 37 38 func cmdA(p *lang.Process) error { 39 return mkArray(p, types.String) 40 } 41 42 func cmdJa(p *lang.Process) error { 43 return mkArray(p, types.Json) 44 } 45 46 func cmdTa(p *lang.Process) error { 47 dataType, err := p.Parameters.String(0) 48 if err != nil { 49 return err 50 } 51 52 params := p.Parameters.StringArray()[1:] 53 p.Parameters.DefineParsed(params) 54 55 return mkArray(p, dataType) 56 } 57 58 // echo %[1..3] 59 // a: [1..10] -> ... 60 // ja: [1..10] -> ... 61 // ta: dataType [1..10] -> ... 62 func mkArray(p *lang.Process, dataType string) error { 63 p.Stdout.SetDataType(dataType) 64 65 a := new(arrayT) 66 a.p = p 67 a.expression = p.Parameters.ByteAll() 68 a.dataType = dataType 69 70 ok, err := a.isNumberArray() 71 if ok || err != nil { 72 return err 73 } 74 75 return a.isStringArray() 76 } 77 78 func (a *arrayT) parseExpression() error { 79 var ( 80 escaped, open, dots bool 81 nodes = make([]ast, 1) 82 node = &nodes[0] 83 ) 84 85 for i, b := range a.expression { 86 switch b { 87 case '\\': 88 dots = false 89 if escaped { 90 node.Data = append(node.Data, b) 91 } 92 escaped = !escaped 93 94 case ',': 95 dots = false 96 if escaped { 97 node.Data = append(node.Data, b) 98 escaped = !escaped 99 continue 100 } 101 nodes = append(nodes, 102 ast{ 103 Data: []byte{','}, 104 Type: astTypeSeparator, 105 }, 106 ast{}, 107 ) 108 node = &nodes[len(nodes)-1] 109 110 case '[': 111 dots = false 112 if escaped { 113 node.Data = append(node.Data, b) 114 escaped = !escaped 115 continue 116 } 117 if open { 118 return fmt.Errorf("cannot open bracket (char %d) inside of open bracket.\nIf you wanted to print the bracket then please escape it: `\\[``", i) 119 } 120 open = true 121 nodes = append(nodes, 122 ast{ 123 Data: []byte{'['}, 124 Type: astTypeOpen, 125 }, 126 ast{}, 127 ) 128 node = &nodes[len(nodes)-1] 129 130 case ']': 131 dots = false 132 if escaped { 133 node.Data = append(node.Data, b) 134 escaped = !escaped 135 continue 136 } 137 if !open { 138 return fmt.Errorf("cannot close bracket (char %d) with an open bracket.\nIf you wanted to print the bracket then please escape it: `\\]``", i) 139 } 140 open = false 141 nodes = append(nodes, 142 ast{ 143 Data: []byte{']'}, 144 Type: astTypeClose, 145 }, 146 ast{}, 147 ) 148 node = &nodes[len(nodes)-1] 149 150 case '.': 151 if open { 152 if dots { 153 node.Type = astTypeRange 154 } 155 dots = !dots 156 } 157 node.Data = append(node.Data, b) 158 159 default: 160 dots = false 161 escaped = false 162 node.Data = append(node.Data, b) 163 } 164 } 165 166 if open { 167 return fmt.Errorf("missing closing square bracket, ']', in: %s", string(a.expression)) 168 } 169 170 // Group the parameters to handle recursive matching 171 a.groups = make([][]ast, 1) 172 for i := range nodes { 173 switch nodes[i].Type { 174 case astTypeOpen: 175 open = true 176 a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i]) 177 178 case astTypeClose: 179 open = false 180 a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i]) 181 182 case astTypeSeparator: 183 if open { 184 a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i]) 185 } else { 186 a.groups = append(a.groups, []ast{}) 187 } 188 189 default: 190 a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i]) 191 } 192 } 193 194 return nil 195 }