github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_quotes.go (about) 1 package expressions 2 3 import ( 4 "fmt" 5 6 "github.com/lmorg/murex/lang/expressions/symbols" 7 "github.com/lmorg/murex/utils/ansi" 8 ) 9 10 func (tree *ParserT) createStringAst(qStart, qEnd rune, exec bool) error { 11 // create JSON dict 12 value, err := tree.parseString(qStart, qEnd, exec) 13 if err != nil { 14 return err 15 } 16 tree.appendAst(symbols.QuoteParenthesis, value...) 17 tree.charPos++ 18 return nil 19 } 20 21 func (tree *ParserT) parseParenthesis(exec bool) ([]rune, error) { 22 value, err := tree.parseString('(', ')', exec) 23 if err != nil { 24 return nil, err 25 } 26 27 tree.charPos++ 28 29 if exec { 30 value = []rune(ansi.ExpandConsts(string(value))) 31 } 32 33 return value, nil 34 } 35 36 func (tree *ParserT) parseString(qStart, qEnd rune, exec bool) ([]rune, error) { 37 if exec && qStart != '\'' { 38 return tree.parseStringInfix(qEnd, exec) 39 } 40 41 var value []rune 42 43 if !exec { 44 value = []rune{qStart} 45 } 46 47 for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ { 48 r := tree.expression[tree.charPos] 49 50 switch { 51 case r == '(' && qEnd == ')': 52 v, err := tree.parseParenthesis(exec) 53 if err != nil { 54 return nil, err 55 } 56 value = append(value, v...) 57 58 case r == '\n': 59 value = append(value, r) 60 tree.crLf() 61 62 case r == qEnd: 63 // end quote 64 goto endString 65 66 default: 67 // string 68 value = append(value, r) 69 } 70 } 71 72 return value, raiseError( 73 tree.expression, nil, tree.charPos, fmt.Sprintf( 74 "missing closing quote (%s)", 75 string([]rune{qEnd}))) 76 77 endString: 78 tree.charPos-- 79 if !exec { 80 value = append(value, qEnd) 81 } 82 83 return value, nil 84 } 85 86 func (tree *ParserT) parseStringInfix(qEnd rune, exec bool) ([]rune, error) { 87 var ( 88 value []rune 89 escaped bool 90 ) 91 92 for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ { 93 r := tree.expression[tree.charPos] 94 95 switch { 96 case escaped: 97 switch r { 98 case 's': 99 value = append(value, ' ') 100 case 't': 101 value = append(value, '\t') 102 case 'r': 103 value = append(value, '\r') 104 case 'n': 105 value = append(value, '\n') 106 default: 107 value = append(value, r) 108 } 109 // end escape 110 escaped = false 111 112 case r == '\\' && qEnd != ')': 113 // start escape 114 escaped = true 115 116 case r == '\n': 117 value = append(value, r) 118 tree.crLf() 119 120 case r == '$': 121 switch { 122 case tree.nextChar() == '{': 123 // subshell 124 subshell, fn, err := tree.parseSubShell(exec, r, varAsString) 125 if err != nil { 126 return nil, err 127 } 128 if exec { 129 val, err := fn() 130 if err != nil { 131 return nil, err 132 } 133 value = append(value, []rune(val.Value.(string))...) 134 } else { 135 value = append(value, subshell...) 136 } 137 default: 138 // inline scalar 139 scalar, v, _, err := tree.parseVarScalar(exec, exec, varAsString) 140 if err != nil { 141 return nil, err 142 } 143 if exec { 144 value = append(value, []rune(v.(string))...) 145 } else { 146 value = append(value, scalar...) 147 } 148 } 149 150 case r == '~': 151 // tilde 152 tilde := tree.parseVarTilde(exec) 153 value = append(value, []rune(tilde)...) 154 155 case r == '(' && qEnd == ')': 156 v, err := tree.parseParenthesis(exec) 157 if err != nil { 158 return nil, err 159 } 160 value = append(value, '(') 161 value = append(value, v...) 162 value = append(value, ')') 163 164 case r == qEnd: 165 // end quote 166 goto endString 167 168 default: 169 // string 170 value = append(value, r) 171 } 172 } 173 174 return value, raiseError( 175 tree.expression, nil, tree.charPos, fmt.Sprintf( 176 "missing closing quote '%s'", 177 string([]rune{qEnd}))) 178 179 endString: 180 tree.charPos-- 181 return value, nil 182 } 183 184 func (tree *ParserT) parseBackTick(quote rune, exec bool) ([]rune, error) { 185 if exec { 186 quote = '\'' 187 } 188 189 var value []rune 190 191 value = []rune{quote} 192 193 for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ { 194 r := tree.expression[tree.charPos] 195 196 switch r { 197 case '`': 198 // end quote 199 goto endBackTick 200 201 default: 202 // string 203 value = append(value, r) 204 } 205 } 206 207 return value, raiseError( 208 tree.expression, nil, tree.charPos, "missing closing backtick, '`'") 209 210 endBackTick: 211 tree.charPos-- 212 value = append(value, quote) 213 214 return value, nil 215 } 216 217 func (tree *ParserT) parseBlockQuote() ([]rune, error) { 218 start := tree.charPos 219 220 for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ { 221 r := tree.expression[tree.charPos] 222 223 switch r { 224 case '#': 225 tree.parseComment() 226 227 case '\n': 228 tree.crLf() 229 230 case '%': 231 switch tree.nextChar() { 232 case '[': 233 tree.charPos++ 234 _, _, err := tree.parseArray(false) 235 if err != nil { 236 return nil, err 237 } 238 case '{': 239 tree.charPos++ 240 _, _, err := tree.parseObject(false) 241 if err != nil { 242 return nil, err 243 } 244 tree.charPos++ 245 } 246 247 case '\'': 248 _, err := tree.parseString('\'', '\'', false) 249 if err != nil { 250 return nil, err 251 } 252 tree.charPos++ 253 254 case '"': 255 _, err := tree.parseStringInfix('"', false) 256 if err != nil { 257 return nil, err 258 } 259 tree.charPos++ 260 261 case '(': 262 _, err := tree.parseString('(', ')', false) 263 if err != nil { 264 return nil, err 265 } 266 tree.charPos++ 267 268 case '{': 269 _, err := tree.parseBlockQuote() 270 if err != nil { 271 return nil, err 272 } 273 274 case '}': 275 // end quote 276 return tree.expression[start : tree.charPos+1], nil 277 278 default: 279 // nothing to do 280 } 281 } 282 283 return nil, raiseError(tree.expression, nil, tree.charPos, "missing closing brace '}'") 284 } 285 286 func (tree *ParserT) parseNamedPipe() []rune { 287 start := tree.charPos 288 289 for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ { 290 r := tree.expression[tree.charPos] 291 292 if isBareChar(r) || r == '!' || r == ':' || r == '=' || r == '.' { 293 continue 294 } 295 296 if r == '>' { 297 return tree.expression[start+1 : tree.charPos] 298 } 299 300 break 301 } 302 303 tree.charPos = start 304 return nil 305 }