From 1ddc4d4c3643b118b7919b5fb3bd0b2762d518f9 Mon Sep 17 00:00:00 2001 From: Dustin Pianalto Date: Tue, 25 Aug 2020 21:03:50 -0800 Subject: [PATCH] Add rpn parser and infix solver --- djpianalto.com/goff/exts/fun.go | 27 +++++++- djpianalto.com/goff/exts/init.go | 25 ++++++- djpianalto.com/goff/utils/rpnParser.go | 95 ++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 djpianalto.com/goff/utils/rpnParser.go diff --git a/djpianalto.com/goff/exts/fun.go b/djpianalto.com/goff/exts/fun.go index f1abd04..c133a88 100644 --- a/djpianalto.com/goff/exts/fun.go +++ b/djpianalto.com/goff/exts/fun.go @@ -3,6 +3,7 @@ package exts import ( "fmt" "strconv" + "strings" "djpianalto.com/goff/djpianalto.com/goff/utils" "github.com/dustinpianalto/disgoman" @@ -50,10 +51,34 @@ func deinterleave(ctx disgoman.Context, args []string) { } } -func rpn(ctx disgoman.Context, args []string) { +func generateRPNCommand(ctx disgoman.Context, args []string) { rpn, err := utils.GenerateRPN(args) if err != nil { ctx.Send(err.Error()) + return } ctx.Send(rpn) } + +func parseRPNCommand(ctx disgoman.Context, args []string) { + res, err := utils.ParseRPN(args) + if err != nil { + ctx.Send(err.Error()) + return + } + ctx.Send(fmt.Sprintf("The result is: %v", res)) +} + +func solveCommand(ctx disgoman.Context, args []string) { + rpn, err := utils.GenerateRPN(args) + if err != nil { + ctx.Send(err.Error()) + return + } + res, err := utils.ParseRPN(strings.Split(rpn, " ")) + if err != nil { + ctx.Send(err.Error()) + return + } + ctx.Send(fmt.Sprintf("The result is: %v", res)) +} diff --git a/djpianalto.com/goff/exts/init.go b/djpianalto.com/goff/exts/init.go index 919d2a2..bc11dcc 100644 --- a/djpianalto.com/goff/exts/init.go +++ b/djpianalto.com/goff/exts/init.go @@ -187,12 +187,31 @@ func AddCommandHandlers(h *disgoman.CommandManager) { Invoke: deinterleave, }) _ = h.AddCommand(&disgoman.Command{ - Name: "rpn", - Aliases: []string{}, + Name: "RPN", + Aliases: []string{"rpn"}, Description: "Convert infix to rpn", OwnerOnly: false, Hidden: false, RequiredPermissions: 0, - Invoke: rpn, + Invoke: generateRPNCommand, }) + _ = h.AddCommand(&disgoman.Command{ + Name: "ParseRPN", + Aliases: []string{'PRPN'}, + Description: "Parse RPN string and return the result", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: parseRPNCommand, + }) + _ = h.AddCommand(&disgoman.Command{ + Name: "solve", + Aliases: []string{"math"}, + Description: "Solve infix equation and return the result", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: solveCommand, + }) + } diff --git a/djpianalto.com/goff/utils/rpnParser.go b/djpianalto.com/goff/utils/rpnParser.go new file mode 100644 index 0000000..778a5e7 --- /dev/null +++ b/djpianalto.com/goff/utils/rpnParser.go @@ -0,0 +1,95 @@ +package utils + +import ( + "errors" + "fmt" + "math" + "strconv" +) + +type Stack []float64 + +func (s *Stack) IsEmpty() bool { + return len(*s) == 0 +} + +func (s *Stack) Push(op float64) { + *s = append(*s, op) +} + +func (s *Stack) Pop() (float64, bool) { + if s.IsEmpty() { + return 0, false + } + index := len(*s) - 1 + element := (*s)[index] + *s = (*s)[:index] + return element, true +} + +func (s *Stack) PopTwo() (float64, float64, bool) { + if s.IsEmpty() || len(*s) < 2 { + return 0, 0, false + } + index := len(*s) - 1 + b := (*s)[index] + a := (*s)[index-1] + *s = (*s)[:index-1] + return a, b, true + +} + +func (s *Stack) Top() float64 { + if s.IsEmpty() { + return 0 + } + return (*s)[len(*s)-1] +} + +func ParseRPN(args []string) (float64, error) { + s := Stack{} + for _, token := range args { + switch token { + case "+": + if a, b, ok := s.PopTwo(); ok { + s.Push(a + b) + } else { + return 0, fmt.Errorf("not enough operands on stack for +: %v", s) + } + case "-": + if a, b, ok := s.PopTwo(); ok { + s.Push(a - b) + } else { + return 0, fmt.Errorf("not enough operands on stack for -: %v", s) + } + case "*": + if a, b, ok := s.PopTwo(); ok { + s.Push(a * b) + } else { + return 0, fmt.Errorf("not enough operands on stack for *: %v", s) + } + case "/": + if a, b, ok := s.PopTwo(); ok { + s.Push(a / b) + } else { + return 0, fmt.Errorf("not enough operands on stack for /: %v", s) + } + case "%": + if a, b, ok := s.PopTwo(); ok { + s.Push(math.Mod(a, b)) + } else { + return 0, fmt.Errorf("not enough operands on stack for %: %v", s) + } + default: + f, err := strconv.ParseFloat(token, 64) + if err != nil { + return 0, err + } + s.Push(f) + } + } + if res, ok := s.Pop(); ok { + return res, nil + } + return 0, errors.New("no result") +}