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  }