From 93740d2d153b3da3a1be5707db1400106e3f6491 Mon Sep 17 00:00:00 2001 From: Alexander Kiryukhin Date: Sat, 6 Mar 2021 22:30:32 +0300 Subject: Initial --- example/math_expression/main.go | 54 ++++++++++++++++++++++++++ example/math_expression/rpn.go | 84 ++++++++++++++++++++++++++++++++++++++++ example/math_expression/stack.go | 26 +++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 example/math_expression/main.go create mode 100644 example/math_expression/rpn.go create mode 100644 example/math_expression/stack.go (limited to 'example') diff --git a/example/math_expression/main.go b/example/math_expression/main.go new file mode 100644 index 0000000..35f7c3b --- /dev/null +++ b/example/math_expression/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + + "github.com/neonxp/unilex" +) + +var output []unilex.Lexem = []unilex.Lexem{} +var opPriority = map[string]int{ + "^": 3, + "!": 3, + "*": 2, + "/": 2, + "+": 1, + "-": 1, +} + +func main() { + + l := unilex.New("10 * (20.0 + 30.0)") + + go l.Run(lexExpression) // Start lexer + + // Read infix expression lexems from lexer and convert them to RPN (reverse polish notation) + rpn := infixToRPNotation(l) + fmt.Println("RPN:", rpn) + + // Calculate RPN + result := calculateRPN(rpn) + fmt.Println("Result:", result) +} + +func lexExpression(l *unilex.Lexer) unilex.StateFunc { + l.AcceptWhile(" \t") + l.Ignore() // Ignore whitespaces + + switch { + case l.Accept("("): + l.Emit("LP") + case l.Accept(")"): + l.Emit("RP") + case unilex.ScanNumber(l): + l.Emit("NUMBER") + case l.Accept("+-*/^!"): + l.Emit("OPERATOR") + case l.Peek() == unilex.EOF: + return nil + default: + return l.Errorf("Unexpected symbol") + } + + return lexExpression +} diff --git a/example/math_expression/rpn.go b/example/math_expression/rpn.go new file mode 100644 index 0000000..b65983d --- /dev/null +++ b/example/math_expression/rpn.go @@ -0,0 +1,84 @@ +package main + +// Helper functions to convert infix notation to RPN and calculates expression result. + +import ( + "fmt" + "log" + "strconv" + + "github.com/neonxp/unilex" +) + +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 +} diff --git a/example/math_expression/stack.go b/example/math_expression/stack.go new file mode 100644 index 0000000..2ca8acc --- /dev/null +++ b/example/math_expression/stack.go @@ -0,0 +1,26 @@ +package main + +// Simple lexem stack implementation. + +import "github.com/neonxp/unilex" + +type lexemStack []unilex.Lexem + +func (ls *lexemStack) Head() (l unilex.Lexem) { + if len(*ls) == 0 { + return unilex.Lexem{Type: unilex.LEOF} + } + return (*ls)[len(*ls)-1] +} + +func (ls *lexemStack) Push(l unilex.Lexem) { + *ls = append(*ls, l) +} + +func (ls *lexemStack) Pop() (l unilex.Lexem) { + if len(*ls) == 0 { + return unilex.Lexem{Type: unilex.LEOF} + } + *ls, l = (*ls)[:len(*ls)-1], (*ls)[len(*ls)-1] + return l +} -- cgit v1.2.3