Command parsers plan 2
This commit is contained in:
@ -1,86 +1,10 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"github.com/Tnze/go-mc/chat"
|
||||||
"unsafe"
|
"strings"
|
||||||
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CmdSet struct {
|
|
||||||
root int32
|
|
||||||
nodes []Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCmdSet() *CmdSet {
|
|
||||||
return &CmdSet{
|
|
||||||
root: 0,
|
|
||||||
nodes: []Node{
|
|
||||||
{
|
|
||||||
flags: TypeRoot,
|
|
||||||
parser: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdSet) AddNode(n Node) int32 {
|
|
||||||
c.nodes = append(c.nodes, n)
|
|
||||||
return int32(len(c.nodes) - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdSet) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
return pk.Tuple{
|
|
||||||
pk.Array(c.nodes),
|
|
||||||
pk.VarInt(c.root),
|
|
||||||
}.WriteTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdSet) Execute(cmd string) error {
|
|
||||||
for _, child := range c.nodes[c.root].children {
|
|
||||||
if node := c.nodes[child]; node.flags&0x03 == TypeLiteral {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Node struct {
|
|
||||||
flags byte
|
|
||||||
children []int32
|
|
||||||
redirect int32
|
|
||||||
name string
|
|
||||||
parser Parser
|
|
||||||
suggestions string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Node) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
return pk.Tuple{
|
|
||||||
pk.Byte(n.flags),
|
|
||||||
pk.Array(*(*[]pk.VarInt)(unsafe.Pointer(&n.children))),
|
|
||||||
pk.Opt{ // Redirect
|
|
||||||
Has: n.flags&HasRedirect != 0,
|
|
||||||
Field: pk.VarInt(n.redirect),
|
|
||||||
},
|
|
||||||
pk.Opt{ // Name
|
|
||||||
Has: n.flags&0x03 == TypeLiteral || n.flags&0x03 == TypeArgument,
|
|
||||||
Field: pk.String(n.name),
|
|
||||||
},
|
|
||||||
pk.Opt{ // Parser & Properties
|
|
||||||
Has: n.flags&0x03 == TypeArgument,
|
|
||||||
Field: n.parser,
|
|
||||||
},
|
|
||||||
pk.Opt{
|
|
||||||
Has: n.flags&HasSuggestionsType != 0,
|
|
||||||
Field: pk.Identifier(n.suggestions),
|
|
||||||
},
|
|
||||||
}.WriteTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Parser interface {
|
|
||||||
pk.FieldEncoder
|
|
||||||
Parse(cmd string) (token string, value interface{}, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeRoot byte = iota
|
TypeRoot byte = iota
|
||||||
TypeLiteral
|
TypeLiteral
|
||||||
@ -93,3 +17,207 @@ const (
|
|||||||
HasRedirect
|
HasRedirect
|
||||||
HasSuggestionsType
|
HasSuggestionsType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Node interface {
|
||||||
|
Then(nodes ...Node) Node
|
||||||
|
Runnable() HandleFunc
|
||||||
|
Parse(cmd string, store map[string]interface{}) (token string, next Node, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Root struct {
|
||||||
|
children map[string]Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandleFunc func(args map[string]interface{})
|
||||||
|
|
||||||
|
func NewGraph() *Root {
|
||||||
|
return &Root{children: make(map[string]Node)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) Then(nodes ...Node) Node {
|
||||||
|
for _, node := range nodes {
|
||||||
|
l, ok := node.(*Literal)
|
||||||
|
if !ok {
|
||||||
|
panic("you could only add Literal node to Root")
|
||||||
|
}
|
||||||
|
if _, ok := r.children[l.Name]; ok {
|
||||||
|
panic("the Literal(" + l.Name + ") is already exist")
|
||||||
|
}
|
||||||
|
r.children[l.Name] = node
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func Lite(literal string) *Literal {
|
||||||
|
return &Literal{Name: literal}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Arg(name string, parser Parser) *Argument {
|
||||||
|
return &Argument{Name: name, Parser: parser}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) Runnable() HandleFunc {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) Parse(cmd string, _ map[string]interface{}) (token string, next Node, err error) {
|
||||||
|
token, value, err := StringParser(0).Parse(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
child, ok := r.children[value.(string)]
|
||||||
|
if !ok {
|
||||||
|
return "", nil, Error{chat.TranslateMsg("command.unknown.command")}
|
||||||
|
}
|
||||||
|
return "", child, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) Run(cmd string) error {
|
||||||
|
var node Node = r
|
||||||
|
args := make(map[string]interface{})
|
||||||
|
for {
|
||||||
|
token, next, err := node.Parse(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, token))
|
||||||
|
if next == nil || len(cmd) == 0 {
|
||||||
|
f := node.Runnable()
|
||||||
|
if f == nil {
|
||||||
|
return Error{chat.TranslateMsg("command.unknown.command")}
|
||||||
|
}
|
||||||
|
f(args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Children struct {
|
||||||
|
Lite map[string]*Literal
|
||||||
|
Args *Argument
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Children) add(nodes []Node) {
|
||||||
|
for _, node := range nodes {
|
||||||
|
switch node.(type) {
|
||||||
|
case *Literal:
|
||||||
|
if c.Args != nil {
|
||||||
|
panic("this Node already has an Argument Node children, cannot Add Literal Node")
|
||||||
|
}
|
||||||
|
if c.Lite == nil {
|
||||||
|
c.Lite = make(map[string]*Literal)
|
||||||
|
}
|
||||||
|
child := node.(*Literal)
|
||||||
|
c.Lite[child.Name] = child
|
||||||
|
case *Argument:
|
||||||
|
if c.Lite != nil {
|
||||||
|
panic("this Node already has Literal Node children, cannot Add Argument Node")
|
||||||
|
}
|
||||||
|
if c.Args != nil {
|
||||||
|
panic("this Node already set an Argument Node child, cannot be duplicate")
|
||||||
|
}
|
||||||
|
c.Args = node.(*Argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
Handler HandleFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) Handle(f HandleFunc) {
|
||||||
|
if h.Handler != nil {
|
||||||
|
panic("handler func set twice")
|
||||||
|
}
|
||||||
|
h.Handler = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) Runnable() HandleFunc {
|
||||||
|
return h.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
type Literal struct {
|
||||||
|
Name string
|
||||||
|
Children
|
||||||
|
Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Literal) Then(nodes ...Node) Node {
|
||||||
|
l.add(nodes)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Literal) Parse(cmd string, store map[string]interface{}) (token string, next Node, err error) {
|
||||||
|
cmd = strings.TrimPrefix(cmd, l.Name)
|
||||||
|
store[l.Name] = nil
|
||||||
|
if l.Children.Lite != nil {
|
||||||
|
token, value, err := StringParser(0).Parse(strings.TrimSpace(cmd))
|
||||||
|
if err != nil {
|
||||||
|
return token, nil, err
|
||||||
|
}
|
||||||
|
if next, ok := l.Children.Lite[value.(string)]; ok {
|
||||||
|
return l.Name, next, nil
|
||||||
|
}
|
||||||
|
return "", nil, Error{chat.TranslateMsg("command.unknown.command")}
|
||||||
|
} else if l.Children.Args != nil {
|
||||||
|
return l.Name, l.Children.Args, nil
|
||||||
|
} else {
|
||||||
|
return l.Name, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Literal) Handle(f HandleFunc) Node {
|
||||||
|
l.Handler.Handle(f)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
type Argument struct {
|
||||||
|
Name string
|
||||||
|
Children
|
||||||
|
Parser Parser
|
||||||
|
Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Argument) Parse(cmd string, store map[string]interface{}) (token string, next Node, err error) {
|
||||||
|
token, value, err := a.Parser.Parse(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
store[a.Name] = value
|
||||||
|
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, token))
|
||||||
|
|
||||||
|
if a.Children.Lite != nil {
|
||||||
|
_, value, err := StringParser(0).Parse(strings.TrimSpace(cmd))
|
||||||
|
if err != nil {
|
||||||
|
return token, nil, err
|
||||||
|
}
|
||||||
|
if next, ok := a.Children.Lite[value.(string)]; ok {
|
||||||
|
return token, next, nil
|
||||||
|
}
|
||||||
|
return "", nil, Error{chat.TranslateMsg("command.unknown.command")}
|
||||||
|
} else if a.Children.Args != nil {
|
||||||
|
return token, a.Children.Args, nil
|
||||||
|
} else {
|
||||||
|
return token, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Argument) Then(nodes ...Node) Node {
|
||||||
|
a.add(nodes)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Argument) Handle(f HandleFunc) Node {
|
||||||
|
a.Handler.Handle(f)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
msg chat.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return e.msg.String()
|
||||||
|
}
|
||||||
|
52
server/command/command_test.go
Normal file
52
server/command/command_test.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestRoot_Run(t *testing.T) {
|
||||||
|
g := NewGraph().Then(
|
||||||
|
Lite("whitelist").Then(
|
||||||
|
Lite("add").Then(
|
||||||
|
Arg("targets", StringParser(0)).
|
||||||
|
Handle(func(args map[string]interface{}) {
|
||||||
|
t.Logf("whitelist add: %v", args)
|
||||||
|
})),
|
||||||
|
Lite("remove").Then(
|
||||||
|
Arg("targets", StringParser(0)).
|
||||||
|
Handle(func(args map[string]interface{}) {
|
||||||
|
t.Logf("whitelist remove: %v", args)
|
||||||
|
})),
|
||||||
|
Lite("on").Handle(func(args map[string]interface{}) {
|
||||||
|
t.Logf("whitelist on: %v", args)
|
||||||
|
}),
|
||||||
|
Lite("off").Handle(func(args map[string]interface{}) {
|
||||||
|
t.Logf("whitelist off: %v", args)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).(*Root)
|
||||||
|
|
||||||
|
targetA := Arg("targetA", StringParser(0))
|
||||||
|
targetB := Arg("targetB", StringParser(0))
|
||||||
|
targetB.Handle(func(args map[string]interface{}) {
|
||||||
|
t.Logf("tp A <from/to> B parsed: %v", args)
|
||||||
|
})
|
||||||
|
tp := Lite("tp").Then(targetA.Then(
|
||||||
|
Lite("form").Then(targetB),
|
||||||
|
Lite("to").Then(targetB),
|
||||||
|
))
|
||||||
|
g.Then(tp)
|
||||||
|
|
||||||
|
err := g.Run("tp Tnze to Xi_Xi_Mi")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//g2 := NewGraph()
|
||||||
|
//g2.Then(
|
||||||
|
// g.Lite("whitelist").Then(
|
||||||
|
// // using reflect to generate all arguments nodes
|
||||||
|
// g.Lite("add").Then(g.Func(func(player GameProfile) {
|
||||||
|
//
|
||||||
|
// })),
|
||||||
|
// ),
|
||||||
|
//)
|
||||||
|
}
|
Reference in New Issue
Block a user