z_mirror/luapls

git clone https://git.raiguard.me/z_mirror/luapls

Caleb HeuerFix infinite loop in readString when encountering an EOF8be9468

main
6.0 KiB346 linesraw
1package lexer
2
3import (
4	"strings"
5	"unicode"
6	"unicode/utf8"
7
8	"github.com/raiguard/luapls/lua/token"
9)
10
11const digits string = "0123456789"
12const hexDigits string = digits + "abcdefABCDEF"
13const whitespace string = " \t\r\n"
14
15type Lexer struct {
16	input string // the string being scanned.
17	start int    // start position of this token.
18	pos   int    // current position in the input.
19	width int    // width of last rune read.
20
21	lineBreaks []int
22}
23
24func New(input string) *Lexer {
25	return &Lexer{input: input, pos: 0, lineBreaks: []int{}}
26}
27
28func (l *Lexer) Next() token.Token {
29	l.ignore()
30
31	if l.acceptRun(" \t") {
32		return l.makeToken(token.WHITESPACE)
33	}
34	if l.acceptRun("\r\n") {
35		return l.makeToken(token.WHITESPACE)
36	}
37
38	tok := token.INVALID
39
40	switch r := l.read(); r {
41	case 0: // EOF
42		tok = token.EOF
43	case '=':
44		if l.accept("=") {
45			tok = token.EQUAL
46		} else {
47			tok = token.ASSIGN
48		}
49	case '^':
50		tok = token.POW
51	case '>':
52		if l.accept("=") {
53			tok = token.GEQ
54		} else {
55			tok = token.GT
56		}
57	case '<':
58		if l.accept("=") {
59			tok = token.LEQ
60		} else {
61			tok = token.LT
62		}
63	case '#':
64		tok = token.LEN
65	case '-':
66		if l.accept("-") {
67			tok = token.COMMENT
68			l.readComment()
69		} else {
70			tok = token.MINUS
71		}
72	case '%':
73		tok = token.MOD
74	case '+':
75		tok = token.PLUS
76	case '/':
77		tok = token.SLASH
78	case '*':
79		tok = token.MUL
80	case '~':
81		if l.accept("=") {
82			tok = token.NEQ
83		}
84	case '(':
85		tok = token.LPAREN
86	case ')':
87		tok = token.RPAREN
88	case '[':
89		if l.readRawString() {
90			tok = token.RAWSTRING
91		} else {
92			tok = token.LBRACK
93		}
94	case ']':
95		tok = token.RBRACK
96	case '{':
97		tok = token.LBRACE
98	case '}':
99		tok = token.RBRACE
100	case ':':
101		if l.accept(":") {
102			tok = token.LABEL
103		} else {
104			tok = token.COLON
105		}
106	case ',':
107		tok = token.COMMA
108	case '.':
109		if l.accept(".") {
110			if l.accept(".") {
111				tok = token.VARARG
112			} else {
113				tok = token.CONCAT
114			}
115		} else if unicode.IsDigit(l.peek()) {
116			l.backup()
117			l.readNumber()
118			tok = token.NUMBER
119		} else {
120			tok = token.DOT
121		}
122	case ';':
123		tok = token.SEMICOLON
124	case '\'', '"':
125		if l.readString(r) {
126			tok = token.STRING
127		}
128	case '@':
129		if l.readIdentifier() {
130			if reserved, ok := token.Reserved[l.input[l.start:l.pos]]; ok {
131				tok = reserved
132			}
133		}
134	default:
135		if unicode.IsDigit(r) {
136			l.backup()
137			if l.readNumber() {
138				tok = token.NUMBER
139			} else {
140				l.ignore()
141			}
142		} else if l.readIdentifier() {
143			if reserved, ok := token.Reserved[l.input[l.start:l.pos]]; ok {
144				tok = reserved
145			} else {
146				tok = token.IDENT
147			}
148		} else {
149			// TODO: Make this unreachable
150			l.read() // Always make progress
151		}
152	}
153
154	return l.makeToken(tok)
155}
156
157func (l *Lexer) GetLineBreaks() []int {
158	return l.lineBreaks
159}
160
161func Run(input string) ([]token.Token, []int) {
162	l := New(input)
163	tokens := []token.Token{}
164	for {
165		tok := l.Next()
166		tokens = append(tokens, tok)
167		if tok.Type == token.EOF {
168			break
169		}
170	}
171	return tokens, l.lineBreaks
172}
173
174func (l *Lexer) makeToken(typ token.TokenType) token.Token {
175	return token.Token{
176		Type:    typ,
177		Literal: l.input[l.start:l.pos],
178		Pos:     l.start,
179	}
180}
181
182// read returns the next rune in the input.
183func (l *Lexer) read() (r rune) {
184	if l.pos >= len(l.input) {
185		l.width = 0
186		return 0
187	}
188	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
189	if r == '\n' && (len(l.lineBreaks) == 0 || l.lineBreaks[len(l.lineBreaks)-1] != l.pos) {
190		l.lineBreaks = append(l.lineBreaks, l.pos)
191	}
192	l.pos += l.width
193	return r
194}
195
196// ignore skips over the pending input before this point.
197func (l *Lexer) ignore() {
198	l.start = l.pos
199}
200
201// backup steps back one rune.
202// Can only be called once per call of next.
203func (l *Lexer) backup() {
204	l.pos -= l.width
205}
206
207// peek returns but does not consume the next rune in the input.
208func (l *Lexer) peek() rune {
209	rune := l.read()
210	l.backup()
211	return rune
212}
213
214// accept consumes the next rune if it is from the valid set.
215func (l *Lexer) accept(valid string) bool {
216	if strings.IndexRune(valid, l.read()) >= 0 {
217		return true
218	}
219	l.backup()
220	return false
221}
222
223// acceptRun accepts a run of runes from the valid set.
224func (l *Lexer) acceptRun(valid string) bool {
225	success := false
226	for l.accept(valid) {
227		success = true
228	}
229	return success
230}
231
232// acceptNot consumes the next rune if it is from the valid set.
233func (l *Lexer) acceptNot(valid string) bool {
234	r := l.read()
235	if r != 0 && strings.IndexRune(valid, r) == -1 {
236		return true
237	}
238	l.backup()
239	return false
240}
241
242// acceptNotRun acceptNots a run of runes from the valid set.
243func (l *Lexer) acceptNotRun(valid string) {
244	for l.acceptNot(valid) {
245	}
246}
247
248// TODO: Type annotations
249func (l *Lexer) readComment() {
250	if l.accept("[") && l.readRawString() {
251		return
252	}
253	for l.acceptNot("\n") {
254	}
255	return
256}
257
258func (l *Lexer) readNumber() bool {
259	useDigits := digits
260	if l.accept("0") && l.accept("xX") {
261		useDigits = hexDigits
262	}
263	l.acceptRun(useDigits)
264
265	if l.accept(".") {
266		l.acceptRun(useDigits)
267	}
268
269	if l.accept("eE") {
270		l.acceptRun(digits)
271		l.accept("+-")
272		l.acceptRun(digits)
273	} else if l.accept("pP") {
274		l.acceptRun(hexDigits)
275		l.accept("+-")
276		l.acceptRun(hexDigits)
277	}
278
279	return true
280}
281
282func (l *Lexer) readIdentifier() bool {
283	for isIdentifier(l.read()) {
284	}
285	l.backup()
286	return true
287}
288
289func (l *Lexer) readString(quote rune) bool {
290	for {
291		if l.accept(string(quote)) {
292			break
293		}
294		if l.accept("\n") || l.peek() == 0 {
295			// TODO: Lexing error
296			return false
297		}
298		if l.accept("\\") {
299			if l.accept("z") {
300				l.acceptRun(whitespace)
301				continue
302			}
303			if l.accept(string(quote)) || l.accept("abfnrtvz\r\n") {
304				continue
305			}
306		}
307		l.read()
308	}
309	return true
310}
311
312func (l *Lexer) readRawString() bool {
313	if !l.accept("[=") {
314		return false
315	}
316	l.backup()
317	level := 0
318	for l.accept("=") {
319		level++
320	}
321	if !l.accept("[") {
322		return false
323	}
324	for {
325		l.acceptNotRun("]")
326		if !l.accept("]") {
327			return false
328		}
329		thisLevel := 0
330		for l.accept("=") {
331			thisLevel++
332		}
333		if !l.accept("]") {
334			continue
335		}
336		if thisLevel == level {
337			break
338		}
339		l.backup()
340	}
341	return true
342}
343
344func isIdentifier(rune rune) bool {
345	return unicode.IsLetter(rune) || unicode.IsDigit(rune) || rune == '_'
346}