-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.lua
128 lines (108 loc) · 3.34 KB
/
parser.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
local Stack = require('lib.Stack')
local function table_has(table, value)
for _, val in ipairs(table) do
if val == value then
return true
end
end
return false
end
local function newCommand(command, parent)
local obj = { value = command, next = nil }
if parent then
parent.next = obj
end
return obj
end
local function parse(rawScript)
local commands = {}
local current = 1
local command = nil
local nextToken = string.gmatch(rawScript or '', "[%a_]+")
local ifStack = Stack:new()
local loopStack = Stack:new()
while true do
local token = nextToken()
if not token then
break
end
if table_has({ 'forward', 'backward', 'left', 'right', 'open' },
token) then
command = newCommand(token, command)
commands[current] = command
current = current + 1
elseif table_has({'ifexit', 'ifstart'}, token) then
command = newCommand(token, command)
commands[current] = command
current = current + 1
command.exit = newCommand('endif', command)
commands[current] = command.exit
current = current + 1
command.elseBranch = newCommand('else', nil)
commands[current] = command.elseBranch
command.elseBranch.next = command.exit
current = current + 1
command.thenBranch = newCommand('then', nil)
commands[current] = command.thenBranch
command.thenBranch.next = command.exit
current = current + 1
ifStack:push(command)
command = command.thenBranch
elseif token == 'else' then
local ifcond = ifStack:top()
if not ifcond then
return nil
end
command.next = ifcond.exit
command = ifcond.elseBranch
elseif token == 'endif' then
local ifcond = ifStack:top()
if not ifcond then
return nil
end
ifStack:pop()
if command then
command.next = ifcond.exit
end
command = ifcond.exit
elseif token == 'loop' then
command = newCommand(token, command)
commands[current] = command
current = current + 1
loopStack:push(command)
command.exit = newCommand('endloop', nil)
commands[current] = command.exit
current = current + 1
elseif token == 'endloop' then
local loop = loopStack:top()
if not loop then
return nil
end
if command then
command.next = loop
end
command = loop.exit
loopStack:pop()
elseif token == 'break' then
local loop = loopStack:top()
if not loop then
return nil
end
command = newCommand(token, command)
commands[current] = command
current = current + 1
command.next = loop.exit
command = nil
else -- unknown token
return nil
end
end
if ifStack:top() then
return nil
end
commands.top = commands[1]
return commands
end
return {
parse = parse,
}