aboutsummaryrefslogblamecommitdiff
path: root/example/math_expression/rpn.go
blob: 84de59cfb4a29249f4c3536e0d10eae1f9d3b2a6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

                 











                                                                                      








                                







































































                                                                                                                               
// +build example

package main

// Helper functions to convert infix notation to RPN and calculates expression result.

import (
	"fmt"
	"log"
	"strconv"

	"github.com/neonxp/unilex"
)

var opPriority = map[string]int{
	"^": 3,
	"!": 3,
	"*": 2,
	"/": 2,
	"+": 1,
	"-": 1,
}

func infixToRPNotation(l *unilex.Lexer) []unilex.Lexem {
	output := []unilex.Lexem{}
	stack := lexemStack{}
parseLoop:
	for ll := range l.Output { // Read lexems from Lexer output channel, convert starts as soon as first lexems scanned!
		fmt.Printf("Lexem: %+v\n", ll)

		switch {
		case ll.Type == "NUMBER", ll.Type == "OPERATOR" && ll.Value == "!":
			output = append(output, ll)
		case ll.Type == "LP":
			stack.Push(ll)
		case ll.Type == "RP":
			for {
				cl := stack.Pop()
				if cl.Type == "LP" {
					break
				}
				if cl.Type == unilex.LEOF {
					log.Fatalf("No pair for parenthesis at %d", ll.Start)
				}
				output = append(output, cl)
			}
		case ll.Type == "OPERATOR":
			for {
				if stack.Head().Type == "OPERATOR" && (opPriority[stack.Head().Value] > opPriority[ll.Value]) {
					output = append(output, stack.Pop())
					continue
				}
				break
			}
			stack.Push(ll)
		case ll.Type == unilex.LEOF:
			break parseLoop
		}
	}

	for stack.Head().Type != unilex.LEOF {
		output = append(output, stack.Pop())
	}

	return output
}

func calculateRPN(rpnLexems []unilex.Lexem) string {
	stack := lexemStack{}
	for _, op := range rpnLexems {
		if op.Type == "NUMBER" {
			stack.Push(op)
		} else {
			switch op.Value {
			case "+":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: "NUMBER", Value: strconv.FormatFloat(a2+a1, 'f', -1, 64)})
			case "-":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: "NUMBER", Value: strconv.FormatFloat(a2-a1, 'f', -1, 64)})
			case "*":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: "NUMBER", Value: strconv.FormatFloat(a2*a1, 'f', -1, 64)})
			case "/":
				a1, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				a2, _ := strconv.ParseFloat(stack.Pop().Value, 64)
				stack.Push(unilex.Lexem{Type: "NUMBER", Value: strconv.FormatFloat(a2/a1, 'f', -1, 64)})
			}
		}
	}
	return stack.Head().Value
}