don't overallocate so much during the read stage
[lisp-parkour.git] / lexer.lua
blob8cc90f9342abeafdf8965dde8e584f621477f26f
1 require'lpeg'
2 local l = lpeg
3 local P, S, R, V, Cmt = l.P, l.S, l.R, l.V, l.Cmt
5 -- Adapted from scintillua's lexer.lua:
6 -- Copyright 2006-2017 Mitchell mitchell.att.foicica.com.
8 local M = {}
10 M.any = P(1)
11 M.alpha = R('AZ', 'az')
12 M.digit = R('09')
13 M.alnum = R('AZ', 'az', '09')
14 M.xdigit = R('09', 'AF', 'af')
16 M.dec_num = M.digit^1
17 M.hex_num = '0' * S('xX') * M.xdigit^1
18 M.oct_num = '0' * R('07')^1
19 M.integer = S('+-')^-1 * (M.hex_num + M.oct_num + M.dec_num)
20 M.float = S('+-')^-1 *
21 ((M.digit^0 * '.' * M.digit^1 + M.digit^1 * '.' * M.digit^0) *
22 (S('eE') * S('+-')^-1 * M.digit^1)^-1 +
23 (M.digit^1 * S('eE') * S('+-')^-1 * M.digit^1))
25 function M.delimited_range(chars, single_line, no_escape, balanced)
26 local s = chars:sub(1, 1)
27 local e = #chars == 2 and chars:sub(2, 2) or s
28 local range
29 local b = balanced and s or ''
30 local n = single_line and '\n' or ''
31 if no_escape then
32 local invalid = S(e..n..b)
33 range = M.any - invalid
34 else
35 local invalid = S(e..n..b) + '\\'
36 range = M.any - invalid + '\\' * M.any
37 end
38 if balanced and s ~= e then
39 return P{s * (range + V(1))^0 * e}
40 else
41 return s * range^0 * P(e)^-1
42 end
43 end
45 function M.word_match(words, word_chars)
46 local word_list = {}
47 for i = 1, #words do
48 word_list[words[i]] = true
49 end
50 local chars = M.alnum + '_'
51 if word_chars then chars = chars + S(word_chars) end
52 return Cmt(chars^1, function(_, index, word)
53 return word_list[word] and index or nil
54 end)
55 end
57 return M