-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlisp.lua
executable file
·132 lines (113 loc) · 4.82 KB
/
lisp.lua
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
#!/usr/bin/env tarantool
local utf8 = require('utf8')
local utils = require('lisp_utils')
local function parse(input)
local WAIT_ANY = 1
local WAIT_DOUBLE_QUOTE = 2
local state = WAIT_ANY
local ast = setmetatable({}, {__index={position=0}})
local stack = {}
local skip = false
local capture_start = nil
local capture = {}
for position, codepoint in utf8.next, input do
if not skip then
if state == WAIT_ANY then
if codepoint == string.byte('\n')
or codepoint == string.byte('\r')
or codepoint == string.byte(' ')
or codepoint == string.byte('\t') then
if #capture > 0 then
table.insert(ast,
setmetatable({atom = table.concat(capture)},
{__index={position=capture_start}}))
capture = {}
capture_start = nil
end
elseif codepoint == string.byte('(') then
if #capture > 0 then
table.insert(ast, setmetatable({atom = table.concat(capture)},
{__index={position=capture_start}}))
capture = {}
capture_start = nil
end
local inner = setmetatable({}, {__index={position=position}})
table.insert(ast, inner)
table.insert(stack, ast)
ast = inner
elseif codepoint == string.byte(')') then
if #capture > 0 then
table.insert(ast, setmetatable({atom = table.concat(capture)},
{__index={position=capture_start}}))
capture = {}
capture_start = nil
end
if #stack <= 0 then
return nil, 'Unexpected end of list at position %q'
end
ast = stack[#stack]
stack[#stack] = nil
else
if codepoint == string.byte('"') then
state = WAIT_DOUBLE_QUOTE
else
table.insert(capture, utf8.char(codepoint))
capture_start = capture_start or position
end
end
elseif state == WAIT_DOUBLE_QUOTE then
if codepoint == string.byte('\\') then
local oldposition = position
position, codepoint = utf8.next(input, position)
skip = true
if position == nil then
-- TODO error
utils.errorx('Unexpected eof')
end
if codepoint == string.byte('r') then
table.insert(capture, '\r')
capture_start = capture_start or position
elseif codepoint == string.byte('n') then
table.insert(capture, '\n')
capture_start = capture_start or position
elseif codepoint == string.byte('t') then
table.insert(capture, '\t')
capture_start = capture_start or position
elseif codepoint == string.byte('"') then
table.insert(capture, utf8.char(codepoint))
capture_start = capture_start or position
elseif codepoint == string.byte('\\') then
table.insert(capture, utf8.char(codepoint))
capture_start = capture_start or position
else
utils.errorx('Wrong escaping at position %q', oldposition)
end
elseif codepoint == string.byte('"') then
table.insert(ast, setmetatable({atom = table.concat(capture)},
{__index={position=capture_start}}))
capture = {}
capture_start = nil
state = WAIT_ANY
else
table.insert(capture, string.char(codepoint))
capture_start = capture_start or position
end
end
else
skip = false
end
end
if #capture > 0 then
table.insert(ast, setmetatable({atom = table.concat(capture)},
{__index={position=capture_start}}))
capture = {}
capture_start = nil
end
if #stack >= 1 then
utils.errorx('Unexpected eof list is not closed')
end
return ast
end
return {
parse=parse
}