Merge branch 'master' into development
This commit is contained in:
commit
23c0434ed3
@ -2,8 +2,11 @@ package exts
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dustinpianalto/disgoman"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/rpnparse"
|
||||||
)
|
)
|
||||||
|
|
||||||
func interleave(ctx disgoman.Context, args []string) {
|
func interleave(ctx disgoman.Context, args []string) {
|
||||||
@ -47,3 +50,35 @@ func deinterleave(ctx disgoman.Context, args []string) {
|
|||||||
ctx.Send(fmt.Sprintf("(%v, %v)", x, y))
|
ctx.Send(fmt.Sprintf("(%v, %v)", x, y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateRPNCommand(ctx disgoman.Context, args []string) {
|
||||||
|
rpn, err := rpnparse.GenerateRPN(args)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Send(rpn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRPNCommand(ctx disgoman.Context, args []string) {
|
||||||
|
res, err := rpnparse.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 := rpnparse.GenerateRPN(args)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := rpnparse.ParseRPN(strings.Split(rpn, " "))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Send(fmt.Sprintf("The result is: %v", res))
|
||||||
|
}
|
||||||
|
|||||||
@ -204,4 +204,30 @@ func AddCommandHandlers(h *disgoman.CommandManager) {
|
|||||||
RequiredPermissions: disgoman.PermissionManageServer,
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
Invoke: getPuzzleChannel,
|
Invoke: getPuzzleChannel,
|
||||||
})
|
})
|
||||||
|
Name: "RPN",
|
||||||
|
Aliases: []string{"rpn"},
|
||||||
|
Description: "Convert infix to rpn",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: generateRPNCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "ParseRPN",
|
||||||
|
Aliases: []string{"PRPN", "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", "infix"},
|
||||||
|
Description: "Solve infix equation and return the result",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: solveCommand,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
156
djpianalto.com/goff/utils/rpn.go
Normal file
156
djpianalto.com/goff/utils/rpn.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
95
djpianalto.com/goff/utils/rpnParser.go
Normal file
95
djpianalto.com/goff/utils/rpnParser.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
1
go.mod
1
go.mod
@ -7,6 +7,7 @@ require (
|
|||||||
github.com/dustinpianalto/disgoman v0.0.10
|
github.com/dustinpianalto/disgoman v0.0.10
|
||||||
github.com/emersion/go-imap v1.0.5
|
github.com/emersion/go-imap v1.0.5
|
||||||
github.com/emersion/go-message v0.12.0
|
github.com/emersion/go-message v0.12.0
|
||||||
|
github.com/dustinpianalto/rpnparse v1.0.1
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/lib/pq v1.3.0
|
github.com/lib/pq v1.3.0
|
||||||
github.com/olebedev/when v0.0.0-20190311101825-c3b538a97254
|
github.com/olebedev/when v0.0.0-20190311101825-c3b538a97254
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user