|
@@ -0,0 +1,337 @@
|
|
|
+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 := "<stdin>"
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|