package ws import ( "errors" "io" "strings" ) type Command struct { Command string Args []string } type parser struct { cmd *Command r *strings.Reader arg string args []string } type parserQuote rune const ( parserQuoteSingle parserQuote = '\'' parserQuoteDouble parserQuote = '"' ) type parserBracket rune const ( parserBracketCurly parserBracket = '{' parserBracketSquare parserBracket = '[' parserBracketTriangle parserBracket = '<' ) var parserClosingBracket = map[parserBracket]rune{ parserBracketCurly: '}', parserBracketSquare: ']', parserBracketTriangle: '>', } func parseCommand(msg string) (*Command, error) { cmd := &Command{} r := strings.NewReader(msg) parser := &parser{cmd: cmd, r: r} return cmd, parser.parse() } func (p *parser) parse() error { err := p.stateBase() if err != nil { return err } p.args = append(p.args, p.arg) filteredArgs := []string{} for _, arg := range p.args { if arg != "" { filteredArgs = append(filteredArgs, arg) } } if len(p.args) >= 1 { p.cmd.Command = p.args[0] } if len(p.args) >= 2 { p.cmd.Args = p.args[1:] } return nil } func (p *parser) consume() (rune, bool, error) { b := make([]byte, 1) _, err := p.r.Read(b) if err == io.EOF { return 0, true, nil } if err != nil { return 0, false, err } return rune(b[0]), false, nil } func (p *parser) push(r rune) { p.arg += string(r) } func (p *parser) flush() { p.args = append(p.args, p.arg) p.arg = "" } func (p *parser) stateBase() error { for { ch, eof, err := p.consume() if err != nil { return err } if eof { return nil } switch ch { case '\\': err := p.stateEscape() if err != nil { return err } case rune(parserQuoteDouble): fallthrough case rune(parserQuoteSingle): err := p.stateQuote(parserQuote(ch)) if err != nil { return err } // special case, if the arg starts with a { then parse it with rough object mode case rune(parserBracketCurly): fallthrough case rune(parserBracketSquare): fallthrough case rune(parserBracketTriangle): if p.arg != "" { p.push(ch) } else { err := p.stateBracket(parserBracket(ch)) if err != nil { return err } } case '\n': fallthrough case '\t': fallthrough case ' ': p.flush() default: p.push(ch) } } } func (p *parser) stateQuote(quote parserQuote) error { for { ch, eof, err := p.consume() if err != nil { return err } if eof { return errors.New("missing closing quote") } switch ch { case '\\': err := p.stateEscape() if err != nil { return err } case rune(quote): return nil default: p.push(ch) } } } func (p *parser) stateBracket(bracket parserBracket) error { p.push(rune(bracket)) for { ch, eof, err := p.consume() if err != nil { return err } if eof { return errors.New("missing closing bracket") } switch ch { case '\\': err := p.stateEscape() if err != nil { return err } case rune(parserQuoteDouble): fallthrough case rune(parserQuoteSingle): p.push(ch) err := p.stateQuote(parserQuote(ch)) if err != nil { return err } p.push(ch) case rune(parserBracketCurly): fallthrough case rune(parserBracketSquare): fallthrough case rune(parserBracketTriangle): err := p.stateBracket(parserBracket(ch)) if err != nil { return err } case parserClosingBracket[bracket]: p.push(ch) return nil default: p.push(ch) } } } func (p *parser) stateEscape() error { ch, eof, err := p.consume() if err != nil { return err } if eof { return errors.New("invalid escape sequence before end of file") } switch ch { case 'n': p.arg += "\n" case 't': p.arg += "\t" default: p.push(ch) } return nil }