package rdpar import "io" import "unicode" import "fmt" import "strings" import "strconv" type Kind int const ( KindError Kind = -1 KindNone Kind = 0 KindBool Kind = iota + 1 KindInt KindFloat KindWord KindString KindLast ) type Value interface { // Kind() Kind } type Predicate func(c rune) bool type Converter func(s string) Value type Pos struct { Name *string Line int Col int } func (p Pos) String() string { name := "" if p.Name != nil { name = *p.Name } return fmt.Sprintf("%s:%d:%d", name, p.Line, p.Col) } type State struct { Kind Token strings.Builder } type Parser struct { Pos Stack []State io.RuneScanner } func (p *Parser) WrapError(err error) Value { return (fmt.Errorf("%s:%w", p.Pos.String(), err)) } func (p *Parser) Errorf(f string, args ...interface{}) Value { return p.WrapError(fmt.Errorf(f, args...)) } func (p *Parser) Accept() string { res := p.Buffer().String() p.Pop() return res } func (p *Parser) ConvertFloat(s string) Value { f, err := strconv.ParseFloat(s, 64) if err != nil { return p.WrapError(err) } return float64(f) } func (p *Parser) ConvertInt(s string) Value { i, err := strconv.ParseInt(s, 0, 64) if err != nil { return p.WrapError(err) } return int64(i) } func (p *Parser) AcceptConvert(conv Converter) Value { return conv(p.Accept()) } func (p Parser) State() Kind { return p.Stack[len(p.Stack)-1].Kind } func (p Parser) Buffer() *strings.Builder { return &(p.Stack[len(p.Stack)-1].Token) } func (p *Parser) Push(kind Kind) { s := State{Kind: kind} p.Stack = append(p.Stack, s) } func (p *Parser) Switch(kind Kind) { p.Stack[len(p.Stack)-1].Kind = kind } func (p *Parser) Pop() bool { if len(p.Stack) > 0 { p.Stack = p.Stack[0 : len(p.Stack)-1] return false } return true } func (p Parser) AppendToken(r rune) { p.Buffer().WriteRune(r) } func (p *Parser) Read() (r rune, ev Value) { r, _, err := p.ReadRune() if err != nil { return r, p.WrapError(err) } if r == '\n' { p.Line++ p.Col = 0 } p.Col++ return r, nil } func (p *Parser) Unread() { // Do not bother to change pos here p.UnreadRune() } func (p *Parser) Peek() (r rune, ev Value) { r, err := p.Read() p.Unread() return r, err } func (p *Parser) ParseWord(end string) Value { c, err := p.Peek() if err != nil { return err } if !unicode.IsLetter(c) { return nil } p.Push(1) for { c, err = p.Read() if err != nil { return err } if strings.ContainsRune(end, c) { p.Unread() res := p.Accept() return string(res) } else { p.AppendToken(c) } } } func (p *Parser) Skip(in string) (err Value) { for { r, err := p.Read() if err != nil { return err } if !strings.ContainsRune(in, r) { p.Unread() return nil } } } func IsError(v Value) bool { if v == nil { return false } _, ok := v.(error) return ok } func (p *Parser) ParseNum(end string) Value { isFloat := false if ok := p.PeekIs(unicode.IsDigit); ok == nil || IsError(ok) { return ok } p.Push(KindInt) for { r, err := p.Read() if err != nil { return err } if strings.ContainsRune(end, r) { sval := p.Accept() if isFloat { fval, err := strconv.ParseFloat(sval, 64) if err != nil { return p.WrapError(err) } return float64(fval) } else { ival, err := strconv.ParseInt(sval, 0, 64) if err != nil { return p.WrapError(err) } return int64(ival) } } else if strings.ContainsRune("+-", r) { if p.Buffer().Len() > 0 { return p.Errorf("Sign only allowed at beginning of number") } p.AppendToken(r) } else if strings.ContainsRune("0123456789", r) { p.AppendToken(r) } else if r == '.' || r == 'e' { if isFloat { return p.Errorf("Incorrect floating point literal") } p.AppendToken(r) isFloat = true } else { return p.Errorf("Unexpected character in integer.") } } } func (p *Parser) PeekIs(pred func(rune) bool) (ok Value) { c, err := p.Peek() if err != nil { return err } if !pred(c) { return bool(false) } return bool(true) } func (p *Parser) PeekStart(start rune) (ok Value) { c, err := p.Peek() if err != nil { return err } if c != start { return bool(false) } return bool(true) } // uses the default \ escape func (p *Parser) ParseString(start, end rune) Value { escaped := false r, err := p.Peek() if err != nil { return err } if r != start { return nil } r, err = p.Read() // skip quote if err != nil { return err } p.Push(1) for { r, err = p.Read() if err != nil { return err } if r == '\\' { if escaped { p.AppendToken('\\') p.AppendToken('\\') escaped = false } else { escaped = true } } else if escaped { p.AppendToken('\\') p.AppendToken(r) escaped = false } else if r == end { sval := "\"" + p.Accept() + "\"" str, err := strconv.Unquote(sval) if err != nil { return p.WrapError(err) } return string(str) } else { p.AppendToken(r) } } } // Raw string, only the end and the escape can be escaped func (p *Parser) ParseRawString(start, end, esc rune) Value { escaped := false r, err := p.Peek() if err != nil { return err } if r != start { return nil } r, err = p.Read() // skip quote if err != nil { return err } p.Push(1) for { r, err = p.Read() if err != nil { return err } if r == esc { if escaped { p.AppendToken(r) escaped = false } else { escaped = true } } else if r == end { if escaped { p.AppendToken(r) escaped = false } else { return p.Accept() } } else { p.AppendToken(r) } } }