-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
149 lines (132 loc) · 3.74 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package lexer
import (
"bytes"
"errors"
"fmt"
"io"
"unicode"
)
// eof represents the end of input
var eof = rune(0)
// isWhitespace determines if the character should be considered as whitespace
func isWhitespace(ch rune) bool {
return unicode.IsSpace(ch)
// return ch == ' ' || ch == '\t' || ch == '\n'
}
// isLetter determines if the character is a letter
func isLetter(ch rune) bool {
return unicode.IsLetter(ch)
// return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
}
// isDigit returns true if the rune is a digit.
func isDigit(ch rune) bool {
// return (ch >= '0' && ch <= '9')
return unicode.IsNumber(ch)
}
// isIdentChar returns true if the rune can be used in an unquoted identifier.
func isIdentChar(ch rune) bool { return isLetter(ch) || isDigit(ch) || ch == '_' }
// isIdentFirstChar returns true if the rune can be used as the first char in an unquoted identifer.
func isIdentFirstChar(ch rune) bool { return isLetter(ch) }
var errBadString = errors.New("bad string")
var errBadEscape = errors.New("bad escape")
var errBadRegex = errors.New("bad regex")
var errInvalidIdentifier = errors.New("invalid identifier")
// IsRegexOp returns true if the operator accepts a regex operand.
func IsRegexOp(t Token) bool {
return (t == EQREGEX || t == NEQREGEX)
}
func ScanDelimited(r io.RuneScanner, start, end rune, escapes map[rune]rune, escapesPassThru bool) ([]byte, error) {
// Scan start delimiter.
if ch, _, err := r.ReadRune(); err != nil {
return nil, err
} else if ch != start {
return nil, fmt.Errorf("expected %s; found %s", string(start), string(ch))
}
var buf bytes.Buffer
for {
ch0, _, err := r.ReadRune()
if ch0 == end {
return buf.Bytes(), nil
} else if err != nil {
return buf.Bytes(), err
} else if ch0 == '\n' {
return nil, errors.New("delimited text contains new line")
} else if ch0 == '\\' {
// If the next character is an escape then write the escaped char.
// If it's not a valid escape then return an error.
ch1, _, err := r.ReadRune()
if err != nil {
return nil, err
}
c, ok := escapes[ch1]
if !ok {
if escapesPassThru {
// Unread ch1 (char after the \)
_ = r.UnreadRune()
// Write ch0 (\) to the output buffer.
_, _ = buf.WriteRune(ch0)
continue
} else {
buf.Reset()
_, _ = buf.WriteRune(ch0)
_, _ = buf.WriteRune(ch1)
return buf.Bytes(), errBadEscape
}
}
_, _ = buf.WriteRune(c)
} else {
_, _ = buf.WriteRune(ch0)
}
}
}
// ScanString reads a quoted string from a rune reader.
func ScanString(r io.RuneScanner) (string, error) {
ending, _, err := r.ReadRune()
if err != nil {
return "", errBadString
}
var buf bytes.Buffer
for {
ch0, _, err := r.ReadRune()
if ch0 == ending {
return buf.String(), nil
} else if err != nil || ch0 == '\n' {
return buf.String(), errBadString
} else if ch0 == '\\' {
// If the next character is an escape then write the escaped char.
// If it's not a valid escape then return an error.
ch1, _, _ := r.ReadRune()
if ch1 == 'n' {
_, _ = buf.WriteRune('\n')
} else if ch1 == '\\' {
_, _ = buf.WriteRune('\\')
} else if ch1 == '"' {
_, _ = buf.WriteRune('"')
} else if ch1 == '\'' {
_, _ = buf.WriteRune('\'')
} else {
return string(ch0) + string(ch1), errBadEscape
}
} else {
_, _ = buf.WriteRune(ch0)
}
}
}
// ScanBareIdent reads bare identifier from a rune reader.
func ScanBareIdent(r io.RuneScanner) string {
// Read every ident character into the buffer.
// Non-ident characters and EOF will cause the loop to exit.
var buf bytes.Buffer
for {
ch, _, err := r.ReadRune()
if err != nil {
break
} else if !isIdentChar(ch) {
r.UnreadRune()
break
} else {
_, _ = buf.WriteRune(ch)
}
}
return buf.String()
}