github.com/informationsea/shellflow@v0.1.3/flowscript/flowscript_parser.go (about) 1 package flowscript 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "strconv" 8 ) 9 10 /* 11 * Syntax 12 * exp := <factor0> ; <factor0> | <factor0> 13 * factor0 := <factor1> = <factor1> | <factor1> 14 * factor1 := <factor2> + <factor1> | <factor2> - <factor1> | <factor2> 15 * factor2 := <factor3> * <factor2> | <factor3> / <factor2> | <factor3> 16 * factor3 := <array_access> | <function_call> | <string> | <number> | <ident> | ( <exp> ) 17 * array_access_or_array := <ident> [ <exp> ] | <array_access> [ <exp> ] | <array> [ <exp> ] | <array> 18 * array := [ <exp> {, <exp>}* ] 19 * function_call := <ident> ( {<exp> {, <exp>}*}? ) 20 */ 21 22 var numberRegexp = regexp.MustCompile("\\d+") 23 var variableRegexp = regexp.MustCompile("^[a-zA-Z_]\\w*$") 24 var errUnmatched = errors.New("Unmatched") 25 var errFinished = errors.New("Finished") 26 27 type isMatched func(token []byte) bool 28 type convertToType func(token []byte) (eval Evaluable, err error) 29 type parser func(tokenizer *LookAheadScanner) (eval Evaluable, err error) 30 type binaryEvaluableCreator func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) 31 32 func parserHelper(tokenizer *LookAheadScanner, test isMatched, convert convertToType) (eval Evaluable, err error) { 33 token := tokenizer.Bytes() 34 if test(token) { 35 r, e := convert(token) 36 if e != nil { 37 return nil, e 38 } 39 tokenizer.Scan() 40 if te := tokenizer.Err(); te != nil { 41 return nil, te 42 } else { 43 return r, nil 44 } 45 } 46 return nil, errUnmatched 47 } 48 49 func binaryOperatorParserHelper(tokenizer *LookAheadScanner, leftParser parser, rightParser parser, binary map[string]binaryEvaluableCreator) (eval Evaluable, err error) { 50 eval1, err := leftParser(tokenizer) 51 if err == errUnmatched { 52 return nil, errUnmatched 53 } 54 if err == nil { 55 next := tokenizer.Text() 56 creator, ok := binary[next] 57 if ok && len(tokenizer.LookAheadBytes(1)) > 0 { 58 tokenizer.Scan() 59 if te := tokenizer.Err(); te != nil { 60 return nil, te 61 } 62 eval2, err2 := rightParser(tokenizer) 63 if err2 == nil { 64 return creator(eval1, next, eval2) 65 } else { 66 return nil, fmt.Errorf("parse error: %s %s %s", eval1, next, tokenizer.Text()) 67 } 68 } 69 return eval1, nil 70 } 71 return nil, err 72 } 73 74 func ParseAsNumber(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 75 if len(tokenizer.Bytes()) < 1 { 76 return nil, errUnmatched 77 } 78 79 return parserHelper(tokenizer, func(token []byte) bool { 80 return numberRegexp.Match(token) 81 }, func(token []byte) (eval Evaluable, err error) { 82 n, err := strconv.ParseInt(string(token), 10, 32) 83 if err == nil { 84 eval = ValueEvaluable{IntValue{n}} 85 } 86 return 87 }) 88 } 89 90 func ParseAsString(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 91 if len(tokenizer.Bytes()) < 2 { 92 return nil, errUnmatched 93 } 94 95 return parserHelper(tokenizer, func(token []byte) bool { 96 return token[0] == '"' && token[len(token)-1] == '"' 97 }, func(token []byte) (eval Evaluable, err error) { 98 n, err := strconv.Unquote(string(token)) 99 if err == nil { 100 eval = ValueEvaluable{StringValue{n}} 101 } 102 return 103 }) 104 } 105 106 func ParseAsVariable(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 107 return parserHelper(tokenizer, func(token []byte) bool { 108 return variableRegexp.Match(token) 109 }, func(token []byte) (eval Evaluable, err error) { 110 return &Variable{string(token)}, nil 111 }) 112 } 113 114 var parseAsExpMap = map[string]binaryEvaluableCreator{ 115 ";": func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) { 116 return &JoinedExpression{exp1: exp1, exp2: exp2}, nil 117 }, 118 } 119 120 func ParseAsExp(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 121 return binaryOperatorParserHelper(tokenizer, ParseAsFactor0, ParseAsExp, parseAsExpMap) 122 } 123 124 var parseAsFactor0Map = map[string]binaryEvaluableCreator{ 125 "=": func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) { 126 return &AssignExpression{variable: exp1, exp: exp2}, nil 127 }, 128 } 129 130 func ParseAsFactor0(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 131 return binaryOperatorParserHelper(tokenizer, ParseAsFactor1, ParseAsFactor1, parseAsFactor0Map) 132 } 133 134 var parseAsFactor1Map = map[string]binaryEvaluableCreator{ 135 "+": func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) { 136 return &PlusExpression{exp1: exp1, exp2: exp2}, nil 137 }, 138 "-": func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) { 139 return &NumericOperationExpression{exp1: exp1, exp2: exp2, operator: operator}, nil 140 }, 141 } 142 143 func ParseAsFactor1(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 144 return binaryOperatorParserHelper(tokenizer, ParseAsFactor2, ParseAsFactor1, parseAsFactor1Map) 145 } 146 147 var parseAsFactor2Map = map[string]binaryEvaluableCreator{ 148 "*": func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) { 149 return &NumericOperationExpression{exp1: exp1, exp2: exp2, operator: operator}, nil 150 }, 151 "/": func(exp1 Evaluable, operator string, exp2 Evaluable) (eval Evaluable, err error) { 152 return &NumericOperationExpression{exp1: exp1, exp2: exp2, operator: operator}, nil 153 }, 154 } 155 156 func ParseAsFactor2(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 157 return binaryOperatorParserHelper(tokenizer, ParseAsFactor3, ParseAsFactor2, parseAsFactor2Map) 158 } 159 160 func ParseAsFactor3(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 161 token := tokenizer.Text() 162 if token == "(" { 163 tokenizer.Scan() 164 if e := tokenizer.Err(); e != nil { 165 return nil, e 166 } 167 168 eval, err = ParseAsExp(tokenizer) 169 170 endToken := tokenizer.Text() 171 if endToken != ")" { 172 return nil, fmt.Errorf("syntax error: ) is not found: ( %s %s", eval.String(), endToken) 173 } 174 175 tokenizer.Scan() 176 if e := tokenizer.Err(); e != nil { 177 return nil, e 178 } 179 return 180 } 181 182 eval, err = ParseAsArrayAccessOrArray(tokenizer) 183 if err != errUnmatched { 184 return 185 } 186 187 eval, err = ParseAsFunctionCall(tokenizer) 188 if err != errUnmatched { 189 return 190 } 191 192 eval, err = ParseAsString(tokenizer) 193 if err != errUnmatched { 194 return 195 } 196 eval, err = ParseAsNumber(tokenizer) 197 if err != errUnmatched { 198 return 199 } 200 eval, err = ParseAsVariable(tokenizer) 201 if err != errUnmatched { 202 return 203 } 204 205 return nil, errUnmatched 206 } 207 208 func ParseAsArray(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 209 if tokenizer.Text() != "[" { 210 return nil, errUnmatched 211 } 212 tokenizer.Scan() 213 if e := tokenizer.Err(); e != nil { 214 return nil, e 215 } 216 217 var values []Evaluable 218 for { 219 if tokenizer.Text() == "]" { 220 break 221 } 222 current, err := ParseAsExp(tokenizer) 223 if err == errUnmatched { 224 return nil, fmt.Errorf("syntax error: no expression is found: %s", tokenizer.Text()) 225 } 226 if err != nil { 227 return nil, err 228 } 229 values = append(values, current) 230 if tokenizer.Text() != "," { 231 break 232 } 233 tokenizer.Scan() 234 if e := tokenizer.Err(); e != nil { 235 return nil, e 236 } 237 } 238 239 if tokenizer.Text() != "]" { 240 return nil, fmt.Errorf("syntax error: \"]\" is not found: [%s%s ", values, tokenizer.Text()) 241 } 242 tokenizer.Scan() 243 if e := tokenizer.Err(); e != nil { 244 return nil, e 245 } 246 247 return &ArrayExpression{values: values}, nil 248 } 249 250 func ParseAsArrayAccessOrArray(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 251 var array Evaluable 252 err = errUnmatched 253 254 tried := false 255 if tokenizer.Text() == "[" { 256 array, err = ParseAsArray(tokenizer) 257 tried = true 258 } else if variableRegexp.Match(tokenizer.Bytes()) && tokenizer.LookAheadText(1) == "[" { 259 array, err = ParseAsVariable(tokenizer) 260 tried = true 261 } 262 263 if err == nil { 264 var current Evaluable = array 265 for tokenizer.Text() == "[" { 266 tokenizer.Scan() 267 if e := tokenizer.Err(); e != nil { 268 return nil, e 269 } 270 exp, err2 := ParseAsExp(tokenizer) 271 if err2 == errUnmatched { 272 return nil, fmt.Errorf("syntax error no expression is found in a bracket: %s[%s", array.String(), tokenizer.Text()) 273 } else if err2 != nil { 274 return nil, err2 275 } 276 if tokenizer.Text() != "]" { 277 return nil, fmt.Errorf("syntax error \"]\" is not found: %s[%s%s", array.String(), exp.String(), tokenizer.Text()) 278 } 279 tokenizer.Scan() 280 if e := tokenizer.Err(); e != nil { 281 return nil, e 282 } 283 current = &ArrayAccess{Array: current, ArrayIndex: exp} 284 } 285 286 if v, ok := current.(*ArrayAccess); ok { 287 return v, nil 288 } 289 if v, ok := current.(*ArrayExpression); ok { 290 return v, nil 291 } 292 } 293 294 if tried { 295 if err == errUnmatched { 296 return nil, fmt.Errorf("syntax error: %s", tokenizer.Text()) 297 } 298 return nil, err 299 } 300 return nil, errUnmatched 301 } 302 303 func ParseAsFunctionCall(tokenizer *LookAheadScanner) (eval Evaluable, err error) { 304 if !variableRegexp.Match(tokenizer.Bytes()) || tokenizer.LookAheadText(1) != "(" { 305 return nil, errUnmatched 306 } 307 308 function, err := ParseAsVariable(tokenizer) 309 tokenizer.Scan() // skip "(" 310 if e := tokenizer.Err(); e != nil { 311 return nil, e 312 } 313 314 var values []Evaluable 315 for { 316 if tokenizer.Text() == ")" { 317 break 318 } 319 current, err := ParseAsExp(tokenizer) 320 if err == errUnmatched { 321 return nil, fmt.Errorf("syntax error: no expression is found: %s", tokenizer.Text()) 322 } 323 if err != nil { 324 return nil, err 325 } 326 values = append(values, current) 327 if tokenizer.Text() != "," { 328 break 329 } 330 tokenizer.Scan() 331 if e := tokenizer.Err(); e != nil { 332 return nil, e 333 } 334 } 335 336 if tokenizer.Text() != ")" { 337 return nil, fmt.Errorf("syntax error: \")\" is not found: [%s%s ", values, tokenizer.Text()) 338 } 339 tokenizer.Scan() 340 if e := tokenizer.Err(); e != nil { 341 return nil, e 342 } 343 344 return &FunctionCall{function: function, args: values}, nil 345 }