2017-08-08 11:56:14 +02:00
|
|
|
package goparsify
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-08-08 12:34:21 +02:00
|
|
|
"strconv"
|
2017-08-08 11:56:14 +02:00
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
// StringLit matches a quoted string and returns it in .Result. It may contain:
|
|
|
|
// - unicode
|
|
|
|
// - escaped characters, eg \" or \n
|
|
|
|
// - unicode sequences, eg \uBEEF
|
2017-08-08 11:56:14 +02:00
|
|
|
func StringLit(allowedQuotes string) Parser {
|
2017-08-09 13:18:14 +02:00
|
|
|
return NewParser("string literal", func(ps *State) Result {
|
2017-08-08 11:56:14 +02:00
|
|
|
ps.AutoWS()
|
|
|
|
|
|
|
|
if !stringContainsByte(allowedQuotes, ps.Input[ps.Pos]) {
|
|
|
|
ps.ErrorHere(allowedQuotes)
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 11:56:14 +02:00
|
|
|
}
|
|
|
|
quote := ps.Input[ps.Pos]
|
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
var end = ps.Pos + 1
|
2017-08-08 11:56:14 +02:00
|
|
|
|
|
|
|
inputLen := len(ps.Input)
|
|
|
|
var buf *bytes.Buffer
|
|
|
|
|
|
|
|
for end < inputLen {
|
|
|
|
switch ps.Input[end] {
|
|
|
|
case '\\':
|
|
|
|
if end+1 >= inputLen {
|
|
|
|
ps.ErrorHere(string(quote))
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 11:56:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if buf == nil {
|
|
|
|
buf = bytes.NewBufferString(ps.Input[ps.Pos+1 : end])
|
|
|
|
}
|
|
|
|
|
|
|
|
c := ps.Input[end+1]
|
|
|
|
if c == 'u' {
|
|
|
|
if end+6 >= inputLen {
|
2017-08-09 13:18:14 +02:00
|
|
|
ps.Error.expected = "[a-f0-9]{4}"
|
2017-08-08 11:56:14 +02:00
|
|
|
ps.Error.pos = end + 2
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 11:56:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
r, ok := unhex(ps.Input[end+2 : end+6])
|
|
|
|
if !ok {
|
2017-08-09 13:18:14 +02:00
|
|
|
ps.Error.expected = "[a-f0-9]"
|
2017-08-08 11:56:14 +02:00
|
|
|
ps.Error.pos = end + 2
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 11:56:14 +02:00
|
|
|
}
|
|
|
|
buf.WriteRune(r)
|
|
|
|
end += 6
|
|
|
|
} else {
|
|
|
|
buf.WriteByte(c)
|
|
|
|
end += 2
|
|
|
|
}
|
|
|
|
case quote:
|
|
|
|
if buf == nil {
|
|
|
|
result := ps.Input[ps.Pos+1 : end]
|
|
|
|
ps.Pos = end + 1
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{Result: result}
|
2017-08-08 11:56:14 +02:00
|
|
|
}
|
|
|
|
ps.Pos = end + 1
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{Result: buf.String()}
|
2017-08-08 11:56:14 +02:00
|
|
|
default:
|
2017-08-08 12:51:54 +02:00
|
|
|
if buf == nil {
|
|
|
|
if ps.Input[end] < 127 {
|
|
|
|
end++
|
|
|
|
} else {
|
|
|
|
_, w := utf8.DecodeRuneInString(ps.Input[end:])
|
|
|
|
end += w
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r, w := utf8.DecodeRuneInString(ps.Input[end:])
|
|
|
|
end += w
|
2017-08-08 11:56:14 +02:00
|
|
|
buf.WriteRune(r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ps.ErrorHere(string(quote))
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 11:56:14 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
// NumberLit matches a floating point or integer number and returns it as a int64 or float64 in .Result
|
2017-08-08 12:34:21 +02:00
|
|
|
func NumberLit() Parser {
|
2017-08-09 13:18:14 +02:00
|
|
|
return NewParser("number literal", func(ps *State) Result {
|
2017-08-08 12:34:21 +02:00
|
|
|
ps.AutoWS()
|
|
|
|
end := ps.Pos
|
|
|
|
float := false
|
|
|
|
inputLen := len(ps.Input)
|
|
|
|
|
|
|
|
if end < inputLen && (ps.Input[end] == '-' || ps.Input[end] == '+') {
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
|
|
|
|
for end < inputLen && ps.Input[end] >= '0' && ps.Input[end] <= '9' {
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
|
|
|
|
if end < inputLen && ps.Input[end] == '.' {
|
|
|
|
float = true
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
|
|
|
|
for end < inputLen && ps.Input[end] >= '0' && ps.Input[end] <= '9' {
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
|
|
|
|
if end < inputLen && (ps.Input[end] == 'e' || ps.Input[end] == 'E') {
|
|
|
|
end++
|
|
|
|
float = true
|
|
|
|
|
|
|
|
if end < inputLen && (ps.Input[end] == '-' || ps.Input[end] == '+') {
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
|
|
|
|
for end < inputLen && ps.Input[end] >= '0' && ps.Input[end] <= '9' {
|
|
|
|
end++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if end == ps.Pos {
|
|
|
|
ps.ErrorHere("number")
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 12:34:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var result interface{}
|
|
|
|
var err error
|
|
|
|
if float {
|
|
|
|
result, err = strconv.ParseFloat(ps.Input[ps.Pos:end], 10)
|
|
|
|
} else {
|
|
|
|
result, err = strconv.ParseInt(ps.Input[ps.Pos:end], 10, 64)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
ps.ErrorHere("number")
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{}
|
2017-08-08 12:34:21 +02:00
|
|
|
}
|
|
|
|
ps.Pos = end
|
2017-08-09 13:18:14 +02:00
|
|
|
return Result{Result: result}
|
2017-08-08 12:34:21 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-08 11:56:14 +02:00
|
|
|
func stringContainsByte(s string, b byte) bool {
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
if b == s[i] {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func unhex(b string) (v rune, ok bool) {
|
|
|
|
for _, c := range b {
|
|
|
|
v <<= 4
|
|
|
|
switch {
|
|
|
|
case '0' <= c && c <= '9':
|
|
|
|
v |= c - '0'
|
|
|
|
case 'a' <= c && c <= 'f':
|
|
|
|
v |= c - 'a' + 10
|
|
|
|
case 'A' <= c && c <= 'F':
|
|
|
|
v |= c - 'A' + 10
|
|
|
|
default:
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return v, true
|
|
|
|
}
|