-
Notifications
You must be signed in to change notification settings - Fork 31
/
items.go
164 lines (141 loc) · 3.41 KB
/
items.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package uci
import (
"bytes"
"fmt"
)
// item represents a lexeme (token)
//
// https://talks.golang.org/2011/lex.slide#8
type item struct {
typ itemType
val string
pos int
}
type OptionType int
const (
TypeOption OptionType = iota // option is not a list
TypeList // option is a list
)
// MarshalJSON implements encoding/json.Marshaler.
func (ot OptionType) MarshalJSON() ([]byte, error) {
switch ot {
case TypeOption:
return []byte(`"option"`), nil
case TypeList:
return []byte(`"list"`), nil
default:
return nil, ErrUnknownOptionType{Type: fmt.Sprintf("!OptionType(%02x)", ot)}
}
}
// UnmarshalJSON implements encoding/json.Unmarshaler.
func (ot *OptionType) UnmarshalJSON(b []byte) error {
if len(b) == 0 || bytes.Equal(b, []byte("null")) || bytes.Equal(b, []byte("\"option\"")) {
*ot = TypeOption
return nil
}
if bytes.Equal(b, []byte(`"list"`)) {
*ot = TypeList
return nil
}
return ErrUnknownOptionType{Type: string(b)}
}
// itemType defines the kind of lexed item
//
// https://talks.golang.org/2011/lex.slide#9
type itemType int
// These items define the UCI language.
const (
itemError itemType = iota // error occurred; item.val is text of error
itemBOF // begin of file; lexing starts here
itemEOF // end of file; lexing ends here
itemPackage // package keyword
itemConfig // config keyword
itemOption // option keyword
itemList // list keyword
itemIdent // identifier string
itemString // quoted string
)
func (t itemType) String() string {
switch t {
case itemError:
return "Error"
case itemBOF:
return "BOF"
case itemEOF:
return "EOF"
case itemPackage:
return "Package"
case itemConfig:
return "Config"
case itemOption:
return "Option"
case itemList:
return "List"
case itemIdent:
return "Ident"
case itemString:
return "String"
}
return fmt.Sprintf("%%itemType(%d)", int(t))
}
// keyword represents a special marker of the input: each (trimmed,
// non-empty) line of the input must start with a keywords.
type keyword string
// these are the recognized keywords.
const (
kwPackage = keyword("package")
kwConfig = keyword("config")
kwOption = keyword("option")
kwList = keyword("list")
)
// String implements fmt.Stringer interface. Useful for debugging
//
// https://talks.golang.org/2011/lex.slide#11
func (i item) String() string {
if i.pos < 0 {
if i.typ != itemError && len(i.val) > 25 {
return fmt.Sprintf("(%s %.25q...)", i.typ, i.val)
}
return fmt.Sprintf("(%s %q)", i.typ, i.val)
}
if i.typ != itemError && len(i.val) > 25 {
return fmt.Sprintf("(%s %.25q... %d)", i.typ, i.val, i.pos)
}
return fmt.Sprintf("(%s %q %d)", i.typ, i.val, i.pos)
}
type scanFn func(*scanner) scanFn
type scanToken int
const (
tokError scanToken = iota + 1
tokEOF
tokPackage // item-seq: (package, string)
tokSection // item-seq: (config, ident, maybe string)
tokOption // item-seq: (option, ident, string)
tokList // item-seq: (list, ident, string)
)
func (t scanToken) String() string {
switch t {
case tokEOF:
return "eof"
case tokError:
return "error"
case tokPackage:
return "package"
case tokSection:
return "config"
case tokOption:
return "option"
case tokList:
return "list"
case scanToken(0):
return "[not initialized]"
}
return fmt.Sprintf("%%scanToken(%d)", int(t))
}
type token struct {
typ scanToken
items []item
}
func (t token) String() string {
return fmt.Sprintf("%s%s", t.typ, t.items)
}