github.com/cowsed/Parser@v0.0.0-20211216032244-48b10019d380/parser.go (about)

     1  package parser
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  //ElementType is the type of each element
    10  type ElementType int
    11  
    12  //Types of elements
    13  const (
    14  	OperatorType ElementType = iota
    15  	NumberType
    16  	VariableType
    17  	LeftParenType
    18  	RightParenType
    19  	FunctionType
    20  )
    21  
    22  //Token is the type of token and the value of the token
    23  type Token struct {
    24  	Type  ElementType
    25  	Value string
    26  }
    27  
    28  //ParseExpression parses a string into an executable expression
    29  func ParseExpression(expr string) (Expression, error) {
    30  	tokens, err := tokenize(expr)
    31  
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	postfix, err := makePostFix(tokens)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	e, err := parsePostfix(postfix)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return e, nil
    46  }
    47  
    48  func parsePostfix(tokens []Token) (Expression, error) {
    49  	var PartsStack = NewExpressionStack()
    50  	for i := 0; i < len(tokens); i++ {
    51  		t := tokens[i]
    52  		switch t.Type {
    53  		case NumberType:
    54  			v, err := strconv.ParseFloat(t.Value, 64)
    55  			if err != nil {
    56  				return nil, err
    57  			}
    58  			PartsStack.Push(Constant{
    59  				Value: v,
    60  			})
    61  		case OperatorType:
    62  			switch t.Value {
    63  			case "+":
    64  				A, err := PartsStack.Pop()
    65  				if err != nil {
    66  					return nil, err
    67  				}
    68  				B, err := PartsStack.Pop()
    69  				if err != nil {
    70  					return nil, err
    71  				}
    72  				PartsStack.Push(Adder{
    73  					A: B,
    74  					B: A,
    75  				})
    76  			case "-":
    77  				B, err := PartsStack.Pop()
    78  				if err != nil {
    79  					return nil, err
    80  				}
    81  				A, err := PartsStack.Pop()
    82  				if err != nil {
    83  					return nil, err
    84  				}
    85  				PartsStack.Push(Subtractor{
    86  					A: A,
    87  					B: B,
    88  				})
    89  			case "*":
    90  				B, err := PartsStack.Pop()
    91  				if err != nil {
    92  					return nil, err
    93  				}
    94  				A, err := PartsStack.Pop()
    95  				if err != nil {
    96  					return nil, err
    97  				}
    98  				PartsStack.Push(Multiplier{
    99  					A: A,
   100  					B: B,
   101  				})
   102  			case "/":
   103  				B, err := PartsStack.Pop()
   104  				if err != nil {
   105  					return nil, err
   106  				}
   107  				A, err := PartsStack.Pop()
   108  				if err != nil {
   109  					return nil, err
   110  				}
   111  				PartsStack.Push(Divider{
   112  					A: A,
   113  					B: B,
   114  				})
   115  			case "^":
   116  				B, err := PartsStack.Pop()
   117  				if err != nil {
   118  					return nil, err
   119  				}
   120  				A, err := PartsStack.Pop()
   121  				if err != nil {
   122  					return nil, err
   123  				}
   124  				PartsStack.Push(Powerer{
   125  					Base:     A,
   126  					Exponent: B,
   127  				})
   128  			}
   129  		case VariableType:
   130  			symbol := t.Value
   131  			PartsStack.Push(Variable{
   132  				Symbol: symbol,
   133  			})
   134  		case FunctionType:
   135  			switch t.Value {
   136  			case "cos":
   137  				A, err := PartsStack.Pop()
   138  				if err != nil {
   139  					return nil, err
   140  				}
   141  				PartsStack.Push(Coser{A})
   142  			case "sin":
   143  				A, err := PartsStack.Pop()
   144  				if err != nil {
   145  					return nil, err
   146  				}
   147  				PartsStack.Push(Siner{A})
   148  			case "ln":
   149  				A, err := PartsStack.Pop()
   150  				if err != nil {
   151  					return nil, err
   152  				}
   153  				PartsStack.Push(NaturalLogger{A})
   154  
   155  			}
   156  		}
   157  	}
   158  	return PartsStack.Pop()
   159  }
   160  
   161  func makePostFix(tokens []Token) ([]Token, error) {
   162  	precedence := map[string]int{
   163  		"+": 2,
   164  		"-": 2,
   165  		"*": 3,
   166  		"/": 3,
   167  		"^": 4,
   168  	}
   169  	output := []Token{}
   170  
   171  	operatorStack := NewTokenStack()
   172  	for i := 0; i < len(tokens); i++ {
   173  		if tokens[i].Type == NumberType || tokens[i].Type == VariableType {
   174  
   175  			output = append(output, tokens[i])
   176  		} else if tokens[i].Type == FunctionType {
   177  			operatorStack.Push(tokens[i])
   178  		} else if tokens[i].Type == OperatorType {
   179  
   180  			o1 := tokens[i]
   181  			for {
   182  				// there is an operator o2 other than the left parenthesis at the top
   183  				// of the operator stack, and (o2 has greater precedence than o1
   184  				// or they have the same precedence and o1 is left-associative)
   185  				o2, err := operatorStack.Peek()
   186  				if err != nil { //there is an operator o2
   187  					break
   188  				}
   189  				if o2.Value == "(" { //o2 other than the left parenthesis at the top of the operator stack
   190  					break
   191  				}
   192  				if !(precedence[o2.Value] > precedence[o1.Value] || (precedence[o2.Value] == precedence[o1.Value] && o1.Value != "^")) { // (o2 has greater precedence than o1 or they have the same precedence and o1 is left-associative)
   193  					break
   194  				}
   195  				o2_2, _ := operatorStack.Pop()
   196  				output = append(output, o2_2)
   197  			}
   198  			operatorStack.Push(o1)
   199  		} else if tokens[i].Type == LeftParenType {
   200  			operatorStack.Push(tokens[i])
   201  		} else if tokens[i].Type == RightParenType {
   202  			for {
   203  				o, err := operatorStack.Peek()
   204  				if err != nil {
   205  					return nil, fmt.Errorf("unbalanced parentheses at index %d '%s'. next token: %s", i, tokens[i].Value, o.Value)
   206  				}
   207  				if o.Type != LeftParenType {
   208  					o2, _ := operatorStack.Pop()
   209  					output = append(output, o2)
   210  				} else {
   211  					break
   212  				}
   213  			}
   214  			//{assert there is a left parenthesis at the top of the operator stack}
   215  			o1, err := operatorStack.Peek()
   216  			if err != nil || o1.Value != "(" {
   217  				return nil, fmt.Errorf("should be a left parenthesis here")
   218  			}
   219  			operatorStack.Pop()
   220  			//if there is a function token at the top of the operator stack, then:
   221  			//pop the function from the operator stack into the output queue
   222  			o, err := operatorStack.Peek()
   223  			if err == nil {
   224  				if o.Type == FunctionType {
   225  					o, _ = operatorStack.Pop()
   226  					output = append(output, o)
   227  				}
   228  			}
   229  
   230  		}
   231  	}
   232  	for operatorStack.Len() > 0 {
   233  		o, _ := operatorStack.Pop()
   234  		output = append(output, o)
   235  	}
   236  	return output, nil
   237  }
   238  
   239  func tokenize(s string) ([]Token, error) {
   240  	varParts := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
   241  	functions := []string{"cos", "sin", "ln"}
   242  	numberParts := "1234567890."
   243  	operators := "+-*/^"
   244  	s = strings.ReplaceAll(s, " ", "")
   245  	tokens := []Token{}
   246  
   247  	var MidNumber = false
   248  	var MidVar = false
   249  	var currentTokenVal = ""
   250  	for i := 0; i < len(s); i++ {
   251  		//Error checking
   252  		if MidNumber && MidVar {
   253  			return nil, fmt.Errorf("error at index %d, char: %s; a number and a variable together", i, string(s[i]))
   254  		}
   255  
   256  		//Character to analyze
   257  		var char string = string(s[i])
   258  
   259  		//Is part of a number
   260  		IsNumberPart := strings.ContainsAny(char, numberParts)
   261  		if IsNumberPart {
   262  			if MidVar {
   263  				//Finish variable
   264  				var typeOf = VariableType
   265  				if matchesAny(currentTokenVal, functions) {
   266  					typeOf = FunctionType
   267  				}
   268  				tokens = append(tokens, Token{
   269  					Type:  typeOf,
   270  					Value: currentTokenVal,
   271  				})
   272  				currentTokenVal = ""
   273  				MidVar = false
   274  			}
   275  			currentTokenVal += char
   276  			MidNumber = true
   277  			continue
   278  		}
   279  		//End Number
   280  		if MidNumber && !IsNumberPart {
   281  			//End of the previous number
   282  			tokens = append(tokens, Token{
   283  				Type:  NumberType,
   284  				Value: currentTokenVal,
   285  			})
   286  			currentTokenVal = ""
   287  			MidNumber = false
   288  		}
   289  
   290  		//Is part of a variable
   291  		IsVarPart := strings.ContainsAny(char, varParts)
   292  		if IsVarPart {
   293  			currentTokenVal += char
   294  			MidVar = true
   295  			//Check if var so far matches any functions, if it does MidVar = false token = whatever that funciton is
   296  
   297  			continue
   298  		}
   299  		//End Variable
   300  		if MidVar && !IsVarPart {
   301  			//End of variable name
   302  			var typeOf = VariableType
   303  			if matchesAny(currentTokenVal, functions) {
   304  				typeOf = FunctionType
   305  			}
   306  			tokens = append(tokens, Token{
   307  				Type:  typeOf,
   308  				Value: currentTokenVal,
   309  			})
   310  			currentTokenVal = ""
   311  			MidVar = false
   312  		}
   313  
   314  		IsOperator := strings.ContainsAny(char, operators)
   315  		if IsOperator {
   316  
   317  			currentTokenVal = char
   318  			tokens = append(tokens, Token{
   319  				Type:  OperatorType,
   320  				Value: currentTokenVal,
   321  			})
   322  			currentTokenVal = ""
   323  			continue
   324  		}
   325  
   326  		if char == "(" {
   327  			tokens = append(tokens, Token{
   328  				Type:  LeftParenType,
   329  				Value: "(",
   330  			})
   331  			continue
   332  		}
   333  		if char == ")" {
   334  			tokens = append(tokens, Token{
   335  				Type:  RightParenType,
   336  				Value: ")",
   337  			})
   338  			continue
   339  		}
   340  
   341  	}
   342  	//Check if it was the end and partway through a number
   343  	if MidNumber {
   344  		tokens = append(tokens, Token{
   345  			Type:  NumberType,
   346  			Value: currentTokenVal,
   347  		})
   348  		currentTokenVal = ""
   349  	}
   350  	if MidVar {
   351  		//End of variable name
   352  		tokens = append(tokens, Token{
   353  			Type:  VariableType,
   354  			Value: currentTokenVal,
   355  		})
   356  		currentTokenVal = ""
   357  	}
   358  	return tokens, nil
   359  }
   360  
   361  func matchesAny(s string, substrs []string) bool {
   362  	for i := 0; i < len(substrs); i++ {
   363  		if s == substrs[i] {
   364  			return true
   365  		}
   366  	}
   367  	return false
   368  }