// Package shlex provides a simple lexical analysis like Unix shell.
package shlex import ( ) var ( ErrNoClosing = errors.New("No closing quotation") ErrNoEscaped = errors.New("No escaped character") ) // Tokenizer is the interface that classifies a token according to // words, whitespaces, quotations, escapes and escaped quotations. type Tokenizer interface { IsWord(rune) bool IsWhitespace(rune) bool IsQuote(rune) bool IsEscape(rune) bool IsEscapedQuote(rune) bool } // DefaultTokenizer implements a simple tokenizer like Unix shell. type DefaultTokenizer struct{} func ( *DefaultTokenizer) ( rune) bool { return == '_' || unicode.IsLetter() || unicode.IsNumber() } func ( *DefaultTokenizer) ( rune) bool { switch { case '\'', '"': return true default: return false } } func ( *DefaultTokenizer) ( rune) bool { return unicode.IsSpace() } func ( *DefaultTokenizer) ( rune) bool { return == '\\' } func ( *DefaultTokenizer) ( rune) bool { return == '"' } // Lexer represents a lexical analyzer. type Lexer struct { reader *bufio.Reader tokenizer Tokenizer posix bool whitespacesplit bool } // NewLexer creates a new Lexer reading from io.Reader. This Lexer // has a DefaultTokenizer according to posix and whitespacesplit // rules. func ( io.Reader, , bool) *Lexer { return &Lexer{ reader: bufio.NewReader(), tokenizer: &DefaultTokenizer{}, posix: , whitespacesplit: , } } // NewLexerString creates a new Lexer reading from a string. This // Lexer has a DefaultTokenizer according to posix and whitespacesplit // rules. func ( string, , bool) *Lexer { return NewLexer(strings.NewReader(), , ) } // Split splits a string according to posix or non-posix rules. func ( string, bool) ([]string, error) { return NewLexerString(, , true).Split() } // SetTokenizer sets a Tokenizer. func ( *Lexer) ( Tokenizer) { .tokenizer = } func ( *Lexer) () ([]string, error) { := make([]string, 0) for { , := .readToken() if != "" { = append(, ) } if == io.EOF { break } else if != nil { return , } } return , nil } func ( *Lexer) () (string, error) { := .tokenizer := "" := false := ' ' := ' ' : for { , , := .reader.ReadRune() if != nil { if .IsQuote() { return , ErrNoClosing } else if .IsEscape() { return , ErrNoEscaped } return , } switch { case .IsWhitespace(): switch { case .IsWhitespace(): break case .posix && .IsEscape(): = 'a' = case .IsWord(): += string() = 'a' case .IsQuote(): if !.posix { += string() } = default: = string() if .whitespacesplit { = 'a' } else if != "" || (.posix && ) { break } } case .IsQuote(): = true switch { case == : if !.posix { += string() break } else { = 'a' } case .posix && .IsEscape() && .IsEscapedQuote(): = = default: += string() } case .IsEscape(): if .IsQuote() && != && != { += string() } += string() = case .IsWord(): switch { case .IsWhitespace(): if != "" || (.posix && ) { break } case .posix && .IsQuote(): = case .posix && .IsEscape(): = 'a' = case .IsWord() || .IsQuote(): += string() default: if .whitespacesplit { += string() } else if != "" { .reader.UnreadRune() break } } } } return , nil }