goctl added
This commit is contained in:
182
tools/goctl/api/parser/basestate.go
Normal file
182
tools/goctl/api/parser/basestate.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
startState = iota
|
||||
attrNameState
|
||||
attrValueState
|
||||
attrColonState
|
||||
multilineState
|
||||
)
|
||||
|
||||
type baseState struct {
|
||||
r *bufio.Reader
|
||||
lineNumber *int
|
||||
}
|
||||
|
||||
func newBaseState(r *bufio.Reader, lineNumber *int) *baseState {
|
||||
return &baseState{
|
||||
r: r,
|
||||
lineNumber: lineNumber,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *baseState) parseProperties() (map[string]string, error) {
|
||||
var r = s.r
|
||||
var attributes = make(map[string]string)
|
||||
var builder strings.Builder
|
||||
var key string
|
||||
var st = startState
|
||||
|
||||
for {
|
||||
ch, err := s.read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch st {
|
||||
case startState:
|
||||
switch {
|
||||
case isNewline(ch):
|
||||
return nil, fmt.Errorf("%q should be on the same line with %q", leftParenthesis, infoDirective)
|
||||
case isSpace(ch):
|
||||
continue
|
||||
case ch == leftParenthesis:
|
||||
st = attrNameState
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected char %q after %q", ch, infoDirective)
|
||||
}
|
||||
case attrNameState:
|
||||
switch {
|
||||
case isNewline(ch):
|
||||
if builder.Len() > 0 {
|
||||
return nil, fmt.Errorf("unexpected newline after %q", builder.String())
|
||||
}
|
||||
case isLetterDigit(ch):
|
||||
builder.WriteRune(ch)
|
||||
case isSpace(ch):
|
||||
if builder.Len() > 0 {
|
||||
key = builder.String()
|
||||
builder.Reset()
|
||||
st = attrColonState
|
||||
}
|
||||
case ch == colon:
|
||||
if builder.Len() == 0 {
|
||||
return nil, fmt.Errorf("unexpected leading %q", ch)
|
||||
}
|
||||
key = builder.String()
|
||||
builder.Reset()
|
||||
st = attrValueState
|
||||
case ch == rightParenthesis:
|
||||
return attributes, nil
|
||||
}
|
||||
case attrColonState:
|
||||
switch {
|
||||
case isSpace(ch):
|
||||
continue
|
||||
case ch == colon:
|
||||
st = attrValueState
|
||||
default:
|
||||
return nil, fmt.Errorf("bad char %q after %q in %q", ch, key, infoDirective)
|
||||
}
|
||||
case attrValueState:
|
||||
switch {
|
||||
case ch == multilineBeginTag:
|
||||
if builder.Len() > 0 {
|
||||
return nil, fmt.Errorf("%q before %q", builder.String(), multilineBeginTag)
|
||||
} else {
|
||||
st = multilineState
|
||||
}
|
||||
case isSpace(ch):
|
||||
if builder.Len() > 0 {
|
||||
builder.WriteRune(ch)
|
||||
}
|
||||
case isNewline(ch):
|
||||
attributes[key] = builder.String()
|
||||
builder.Reset()
|
||||
st = attrNameState
|
||||
case ch == rightParenthesis:
|
||||
attributes[key] = builder.String()
|
||||
builder.Reset()
|
||||
return attributes, nil
|
||||
default:
|
||||
builder.WriteRune(ch)
|
||||
}
|
||||
case multilineState:
|
||||
switch {
|
||||
case ch == multilineEndTag:
|
||||
attributes[key] = builder.String()
|
||||
builder.Reset()
|
||||
st = attrNameState
|
||||
case isNewline(ch):
|
||||
var multipleNewlines bool
|
||||
loopAfterNewline:
|
||||
for {
|
||||
next, err := read(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isSpace(next):
|
||||
continue
|
||||
case isNewline(next):
|
||||
multipleNewlines = true
|
||||
default:
|
||||
if err := unread(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break loopAfterNewline
|
||||
}
|
||||
}
|
||||
|
||||
if multipleNewlines {
|
||||
fmt.Fprintln(&builder)
|
||||
} else {
|
||||
builder.WriteByte(' ')
|
||||
}
|
||||
case ch == rightParenthesis:
|
||||
if builder.Len() > 0 {
|
||||
attributes[key] = builder.String()
|
||||
builder.Reset()
|
||||
}
|
||||
return attributes, nil
|
||||
default:
|
||||
builder.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *baseState) read() (rune, error) {
|
||||
value, err := read(s.r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if isNewline(value) {
|
||||
*s.lineNumber++
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (s *baseState) readLine() (string, error) {
|
||||
line, _, err := s.r.ReadLine()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
*s.lineNumber++
|
||||
return string(line), nil
|
||||
}
|
||||
|
||||
func (s *baseState) skipSpaces() error {
|
||||
return skipSpaces(s.r)
|
||||
}
|
||||
|
||||
func (s *baseState) unread() error {
|
||||
return unread(s.r)
|
||||
}
|
||||
Reference in New Issue
Block a user