/
lexer.go
138 lines (118 loc) · 2.86 KB
/
lexer.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
package main
import (
"io"
"fmt"
"errors"
"strconv"
"text/scanner"
)
type Token int
const (
// Identifiers
IdentTok Token = iota
// Literals
StringTok
// Keywords
ImportTok
RegTok
LazyTok
ListTok
RootTok
// Punctuation
OpenParenTok
CloseParenTok
CommaTok
PeriodTok
// Sentinals
EOFTok
)
func (self Token) String() string {
switch self {
case IdentTok: return "Ident"
case StringTok: return "String"
case ImportTok: return "Import"
case RegTok: return "Reg"
case LazyTok: return "Lazy"
case ListTok: return "List"
case RootTok: return "Root"
case OpenParenTok: return "OpenParen"
case CloseParenTok: return "CloseParen"
case CommaTok: return "Comma"
case PeriodTok: return "Period"
case EOFTok: return "EOF"
default: panic("Not actually a token value.")
}
}
type Lexer interface {
Next() Token
Current() Token
Value() string
Error() error
}
type lexerStuff struct {
s scanner.Scanner
current Token
value string
error error
}
func identToToken(ident string) Token {
switch ident {
case "import": return ImportTok
case "reg": return RegTok
case "lazy": return LazyTok
case "list": return ListTok
case "root": return RootTok
default: return IdentTok
}
}
var i int
func (self *lexerStuff) Next() Token {
self.value = ""
tok := self.s.Scan()
if self.error != nil {
return EOFTok
}
switch tok {
case scanner.Ident:
value := self.s.TokenText()
self.current = identToToken(value)
if self.current == IdentTok {
self.value = value
}
case scanner.String:
self.value = self.s.TokenText()
self.current = StringTok
case ',': self.current = CommaTok
case '(': self.current = OpenParenTok
case ')': self.current = CloseParenTok
case '.': self.current = PeriodTok
case scanner.EOF:
self.current = EOFTok
default:
self.error = fmt.Errorf("Invalid token %s", strconv.QuoteRune(tok))
self.current = EOFTok
}
return self.current
}
func (self *lexerStuff) Current() Token {
return self.current
}
func (self *lexerStuff) Value() string {
return self.value
}
func (self *lexerStuff) Error() error {
return self.error
}
func NewLexer(reader io.Reader) Lexer {
lex := new(lexerStuff)
lex.s = scanner.Scanner{}
lex.s.Init(reader)
lex.s.Error = func(s *scanner.Scanner, msg string) {
lex.error = errors.New(msg)
}
lex.s.Mode = scanner.ScanIdents |
scanner.ScanStrings |
scanner.ScanComments |
scanner.SkipComments
return lex
}