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 }