From ab1049d237cd51f2b4d8bfc6750ed5260010dbfe Mon Sep 17 00:00:00 2001 From: Dustin Pianalto Date: Tue, 25 Aug 2020 21:59:09 -0800 Subject: [PATCH] Add initial files --- rpnConvert.go | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ rpnParser.go | 95 ++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 rpnConvert.go create mode 100644 rpnParser.go diff --git a/rpnConvert.go b/rpnConvert.go new file mode 100644 index 0000000..53f0a40 --- /dev/null +++ b/rpnConvert.go @@ -0,0 +1,156 @@ +package rpnparse + +import ( + "fmt" + "strconv" + "strings" +) + +type Operator struct { + Token string + Precedence int + Association string +} + +func (o Operator) HasHigherPrecedence(t Operator) bool { + return o.Precedence < t.Precedence // lower number is higher precedence +} + +func (o Operator) HasEqualPrecedence(t Operator) bool { + return o.Precedence == t.Precedence +} + +func (o Operator) IsLeftAssociative() bool { + return o.Association == "left" +} + +var operators = map[string]Operator{ + "+": Operator{ + Token: "+", + Precedence: 4, + Association: "left", + }, + "-": Operator{ + Token: "-", + Precedence: 4, + Association: "left", + }, + "*": Operator{ + Token: "*", + Precedence: 3, + Association: "left", + }, + "/": Operator{ + Token: "/", + Precedence: 3, + Association: "left", + }, + "%": Operator{ + Token: "%", + Precedence: 3, + Association: "left", + }, + "(": Operator{ + Token: "(", + Precedence: 1, + Association: "left", + }, + ")": Operator{ + Token: ")", + Precedence: 1, + Association: "left", + }, +} + +type Stack []Operator + +func (s *Stack) IsEmpty() bool { + return len(*s) == 0 +} + +func (s *Stack) Push(op Operator) { + *s = append(*s, op) +} + +func (s *Stack) Pop() (Operator, bool) { + if s.IsEmpty() { + return Operator{}, false + } + index := len(*s) - 1 + element := (*s)[index] + *s = (*s)[:index] + return element, true +} + +func (s *Stack) Top() Operator { + if s.IsEmpty() { + return Operator{} + } + return (*s)[len(*s)-1] +} + +func GenerateRPN(tokens []string) (string, error) { + output := "" + s := Stack{} + for _, token := range tokens { + err := processToken(token, &s, &output) + if err != nil { + return "", err + } + } + for !s.IsEmpty() { + ele, _ := s.Pop() + output += " " + ele.Token + } + + return strings.TrimSpace(output), nil +} + +func processToken(t string, s *Stack, o *string) error { + if _, err := strconv.Atoi(t); err == nil { + *o += " " + t + return nil + } else if op, ok := operators[t]; ok { + if op.Token == "(" { + s.Push(op) + } else if op.Token == ")" { + if s.IsEmpty() { + return fmt.Errorf("mismatched parentheses") + } + for s.Top().Token != "(" { + if ele, ok := s.Pop(); ok { + *o += " " + ele.Token + } else { + return fmt.Errorf("mismatched parentheses") + } + if s.IsEmpty() { + break + } + } + s.Pop() // Pop and discard the ( + } else if !s.IsEmpty() { + for { + if (s.Top().HasHigherPrecedence(op) || + (s.Top().HasEqualPrecedence(op) && + op.IsLeftAssociative())) && + s.Top().Token != "(" { + if ele, ok := s.Pop(); ok { + *o += " " + ele.Token + if s.IsEmpty() { + break + } + continue + } else { + break + } + } + break + } + s.Push(op) + } else { + s.Push(op) + } + return nil + } + return fmt.Errorf("invalid character %s", t) +} diff --git a/rpnParser.go b/rpnParser.go new file mode 100644 index 0000000..cc651c4 --- /dev/null +++ b/rpnParser.go @@ -0,0 +1,95 @@ +package rpnparse + +import ( + "errors" + "fmt" + "math" + "strconv" +) + +type FStack []float64 + +func (s *FStack) IsEmpty() bool { + return len(*s) == 0 +} + +func (s *FStack) Push(op float64) { + *s = append(*s, op) +} + +func (s *FStack) Pop() (float64, bool) { + if s.IsEmpty() { + return 0, false + } + index := len(*s) - 1 + element := (*s)[index] + *s = (*s)[:index] + return element, true +} + +func (s *FStack) 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 *FStack) Top() float64 { + if s.IsEmpty() { + return 0 + } + return (*s)[len(*s)-1] +} + +func ParseRPN(args []string) (float64, error) { + s := FStack{} + 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") +}