github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_block.go (about) 1 package expressions 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/lmorg/murex/lang" 10 fn "github.com/lmorg/murex/lang/expressions/functions" 11 "github.com/lmorg/murex/lang/types" 12 "github.com/lmorg/murex/utils/consts" 13 ) 14 15 func init() { 16 lang.ParseExpression = ExpressionParser 17 lang.ParseStatementParameters = StatementParametersParser 18 } 19 20 // ExpressionParser is intended to be called from other parsers as a way of 21 // embedding this expressions library into other language syntaxes. This 22 // function just parses the expression and returns the end of the expression. 23 func ExpressionParser(expression []rune, offset int, exec bool) (int, error) { 24 tree := NewParser(nil, expression, offset) 25 26 err := tree.parseExpression(exec, false) 27 if err != nil { 28 return 0, err 29 } 30 31 err = tree.validateExpression(exec) 32 if err != nil { 33 return 0, err 34 } 35 36 return tree.charPos, nil 37 } 38 39 // StatementParametersParser is intended to be called from other parsers as a 40 // way of parsing function parameters 41 func StatementParametersParser(expression []rune, p *lang.Process) (string, []string, error) { 42 if p.Name.String() == lang.ExpressionFunctionName { 43 return p.Name.String(), []string{string(p.Parameters.PreParsed[0])}, nil 44 } 45 46 tree := NewParser(nil, expression, 0) 47 tree.p = p 48 err := tree.ParseStatement(true) 49 if err != nil { 50 return "", nil, err 51 } 52 53 return tree.statement.String(), tree.statement.Parameters(), nil 54 } 55 56 func NewParser(p *lang.Process, expression []rune, offset int) *ParserT { 57 tree := new(ParserT) 58 tree.expression = expression 59 tree.p = p 60 tree.charOffset = offset 61 return tree 62 } 63 64 func (tree *ParserT) preParser() (int, error) { 65 expErr := tree.parseExpression(false, false) 66 if expErr == nil { 67 // if successful parse, then also validate. 68 // no point validating if the parser has already failed 69 expErr = tree.validateExpression(false) 70 } 71 72 if expErr == nil { 73 return tree.charPos, nil 74 } 75 76 stErr := tree.ParseStatement(false) 77 if stErr != nil { 78 return 0, stErr 79 } 80 81 stErr = tree.statement.validate() 82 83 if len(tree.statement.command) == 0 { 84 return 0, errors.New("you cannot have zero length commands") 85 } 86 87 if stErr == nil && !tree.statement.asStatement && 88 len(tree.statement.parameters) > 0 && len(tree.statement.parameters[0]) > 0 && tree.statement.parameters[0][0] == '=' { 89 // i _still_ think this is probably an expression 90 return 0, expErr 91 } 92 93 return tree.charPos, nil 94 95 } 96 97 var expressionFunctionName = []rune(lang.ExpressionFunctionName) 98 99 func (blk *BlockT) append(tree *ParserT, this fn.Property, next fn.Property) error { 100 switch { 101 102 case tree == nil && blk.nextProperty.FollowOnFn(): 103 exprRune, exprPos := cropCodeInErrMsg(blk.expression, blk.charPos) 104 return fmt.Errorf("invalid syntax at %d. Unexpected pipeline continuation token:\n> %s\n> %s\n> these tokens: %s\n> shouldn't follow: %s", 105 blk.charPos, 106 string(exprRune), strings.Repeat(" ", exprPos)+"^", 107 next.Decompose(), 108 fn.Property(fn.P_PIPE_OUT|fn.P_PIPE_ERR|fn.P_LOGIC_AND|fn.P_LOGIC_OR).Decompose()) 109 110 case len(blk.Functions) > 0 && tree == nil && next.FollowOnFn(): 111 exprRune, exprPos := cropCodeInErrMsg(blk.expression, blk.charPos) 112 return fmt.Errorf("invalid syntax at %d. Semi-colon or line break preceding a pipeline continuation token:\n> %s\n> %s\n> these tokens: %s\n> shouldn't follow: %s", 113 blk.charPos, 114 string(exprRune), strings.Repeat(" ", exprPos)+"^", 115 this.Decompose(), 116 fn.Property(fn.P_NEW_CHAIN|fn.P_LOGIC_AND|fn.P_LOGIC_OR).Decompose()) 117 118 case tree == nil: 119 // do nothing 120 121 case tree.statement == nil: 122 if tree.charPos+1 >= len(tree.expression) { 123 tree.charPos = len(tree.expression) - 1 124 } 125 blk.Functions = append(blk.Functions, fn.FunctionT{ 126 Raw: tree.expression[:tree.charPos+1], 127 Command: expressionFunctionName, 128 Parameters: [][]rune{tree.expression[:tree.charPos+1]}, 129 Properties: blk.nextProperty | this, 130 LineN: blk.lineN + tree.GetLineN(), 131 ColumnN: tree.GetColumnN(), 132 }) 133 134 default: 135 blk.Functions = append(blk.Functions, fn.FunctionT{ 136 Raw: tree.expression[:tree.charPos+1], 137 Command: tree.statement.command, 138 Parameters: tree.statement.parameters, 139 NamedPipes: tree.statement.namedPipes, 140 Cast: tree.statement.cast, 141 Properties: blk.nextProperty | this, 142 LineN: blk.lineN + tree.GetLineN(), 143 ColumnN: tree.GetColumnN(), 144 }) 145 146 } 147 148 blk.nextProperty = next 149 return nil 150 } 151 152 var formatGeneric = []rune("format " + types.Generic) 153 154 func (blk *BlockT) ParseBlock() error { 155 var tree *ParserT 156 157 for ; blk.charPos < len(blk.expression); blk.charPos++ { 158 r := blk.expression[blk.charPos] 159 160 switch r { 161 case ' ', '\t', '\r': 162 continue 163 164 case '\n': 165 if err := blk.append(tree, 0, fn.P_NEW_CHAIN); err != nil { 166 return err 167 } 168 tree = nil 169 blk.lineN++ 170 blk.offset = blk.charPos 171 continue 172 173 case '#': 174 comment := NewParser(nil, blk.expression[blk.charPos:], 0) 175 comment.parseComment() 176 blk.charPos += comment.charPos 177 178 case '/': 179 switch { 180 case blk.nextChar() == '#': 181 comment := NewParser(nil, blk.expression[blk.charPos:], 0) 182 if err := comment.parseCommentMultiLine(); err != nil { 183 return err 184 } 185 blk.charPos += comment.charPos 186 default: 187 tree = NewParser(nil, blk.expression[blk.charPos:], blk.charPos-1) 188 newPos, err := tree.preParser() 189 if err != nil { 190 return err 191 } 192 blk.charPos += newPos 193 } 194 195 case ';': 196 if err := blk.append(tree, 0, fn.P_NEW_CHAIN); err != nil { 197 return err 198 } 199 tree = nil 200 201 case '&': 202 switch { 203 case blk.nextChar() == '&': 204 blk.charPos++ 205 if err := blk.append(tree, 0, fn.P_NEW_CHAIN|fn.P_FOLLOW_ON|fn.P_LOGIC_AND); err != nil { 206 return err 207 } 208 tree = nil 209 case tree == nil: 210 tree = NewParser(nil, blk.expression[blk.charPos:], 0) 211 newPos, err := tree.preParser() 212 if err != nil { 213 return err 214 } 215 blk.charPos += newPos 216 default: 217 blk.panic('&', '&') 218 } 219 220 case '|': 221 if blk.nextChar() == '|' { 222 blk.charPos++ 223 if err := blk.append(tree, 0, fn.P_NEW_CHAIN|fn.P_FOLLOW_ON|fn.P_LOGIC_OR); err != nil { 224 return err 225 } 226 tree = nil 227 228 } else { 229 if err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil { 230 return err 231 } 232 tree = nil 233 } 234 235 case '-': 236 switch { 237 case blk.nextChar() == '>': 238 blk.charPos++ 239 if err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil { 240 return err 241 } 242 tree = nil 243 case tree == nil: 244 tree = NewParser(nil, blk.expression[blk.charPos:], 0) 245 newPos, err := tree.preParser() 246 if err != nil { 247 return err 248 } 249 blk.charPos += newPos 250 default: 251 blk.panic('-', '>') 252 } 253 254 case '?': 255 message := fmt.Sprintf("!!! WARNING: The operator `?` has been deprecated and will be removed in the next release\n!!! : Line: %d\n!!! : Column: %d\n", 256 blk.lineN, blk.charPos) 257 os.Stderr.WriteString(message) 258 259 if err := blk.append(tree, fn.P_PIPE_ERR, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil { 260 return err 261 } 262 tree = nil 263 264 case '=': 265 switch { 266 case blk.nextChar() == '>': 267 blk.charPos++ 268 if err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil { 269 return err 270 } 271 tree = nil 272 format := NewParser(nil, formatGeneric, 0) 273 _, _ = format.preParser() 274 if err := blk.append(format, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil { 275 return err 276 } 277 278 case tree == nil: 279 tree = NewParser(nil, blk.expression[blk.charPos:], 0) 280 newPos, err := tree.preParser() 281 if err != nil { 282 return err 283 } 284 blk.charPos += newPos 285 default: 286 blk.panic('=', '>') 287 } 288 289 case '>': 290 switch { 291 case blk.nextChar() == '>': 292 /*if len(blk.Functions) > 0 && 293 len(blk.Functions[len(blk.Functions)-1].Raw) == 0 && 294 !blk.Functions[len(blk.Functions)-1].Properties.Method() { 295 panic("ugh") 296 }*/ 297 298 err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD) 299 if err != nil { 300 return err 301 } 302 tree, err = blk.parseStatementWithKnownCommand('>', '>') 303 if err != nil { 304 return err 305 } 306 default: 307 tree = NewParser(nil, blk.expression[blk.charPos:], blk.charPos-1) 308 newPos, err := tree.preParser() 309 if err != nil { 310 return err 311 } 312 blk.charPos += newPos 313 } 314 315 default: 316 tree = NewParser(nil, blk.expression[blk.charPos:], blk.charPos-1) 317 newPos, err := tree.preParser() 318 if err != nil { 319 return err 320 } 321 blk.charPos += newPos 322 323 } 324 325 } 326 327 if blk.charPos >= len(blk.expression) { 328 if err := blk.append(tree, 0, 0); err != nil { 329 return err 330 } 331 } 332 333 return nil 334 } 335 336 func (blk *BlockT) parseStatementWithKnownCommand(command ...rune) (*ParserT, error) { 337 tree := NewParser(nil, blk.expression[blk.charPos:], 0) 338 tree.statement = new(StatementT) 339 tree.statement.command = command 340 tree.charPos = len(command) 341 err := tree.parseStatement(false) 342 if err != nil { 343 return nil, err 344 } 345 blk.charPos += tree.charPos 346 return tree, nil 347 } 348 349 func (blk *BlockT) panic(found rune, follows rune) { 350 msg := "unexpected parser error: '%s' found" 351 if follows == 0 { 352 panic(fmt.Sprintf(msg+". "+consts.IssueTrackerURL, string(found))) 353 } 354 355 msg += " but no '%s' follows" 356 panic(fmt.Sprintf(msg+". "+consts.IssueTrackerURL, string(found), string(follows))) 357 }