lgame.lua
· 1.5 KiB · Lua
原始文件
Playground
local Space = require("lspace")
local enemies = {}
local r, f, l, box, tuples, rules = Space()
local function p(...)
local args = {...}
return function() print(table.unpack(args)) end
end
function espawn(vars)
print("Spawning "..vars.enemy.." at "..vars.x..", "..vars.y)
table.insert(enemies, {t=vars.enemy, x=vars.x,y=vars.y})
end
f('play level 1')
r('play level 1').so(
p"Started Level 1!",
'spawn a flyer at 60 10',
'spawn a flyer at 60 20',
'spawn a flyer at 60 30',
'playing level 1'
)
r('spawn a $enemy at $x $y').so(espawn)
r('spawn an $enemy at $x $y').so(espawn)
r('playing level 1', 'all enemies are dead').so(p'LEVEL 2', 'play level 2')
r('there are $some enemies').so(
function(v)
-- print("some", v.some, type(v.some))
if v.some == "0" then f('all enemies are dead') end
end
)
r('an enemy died').so(
function(_) print('ded') f('there are '.. #enemies..' enemies') end
)
r('a','b','c').so(p'a,b&c')
f('a')
f('b')
f('letters '..l('a', 'b', 'c'))
f('c')
r('a', 'b').so(p'a&b',"a and b")
r('MYVARR $vals').so(function(vars) print(table.unpack(vars.vals)) end)
box('MYVARR', {1,2,3,4})
print("***************************")
for _, t in ipairs(tuples) do
print(t, table.unpack(t))
end
print("***************************")
function kill()
table.remove(enemies, 1)
f('an enemy died')
end
kill()
kill()
kill()
print("***************************")
for _, t in ipairs(tuples) do
print(table.unpack(t))
end
1 | local Space = require("lspace") |
2 | local enemies = {} |
3 | local r, f, l, box, tuples, rules = Space() |
4 | |
5 | local function p(...) |
6 | local args = {...} |
7 | return function() print(table.unpack(args)) end |
8 | end |
9 | |
10 | function espawn(vars) |
11 | print("Spawning "..vars.enemy.." at "..vars.x..", "..vars.y) |
12 | table.insert(enemies, {t=vars.enemy, x=vars.x,y=vars.y}) |
13 | end |
14 | |
15 | f('play level 1') |
16 | r('play level 1').so( |
17 | p"Started Level 1!", |
18 | 'spawn a flyer at 60 10', |
19 | 'spawn a flyer at 60 20', |
20 | 'spawn a flyer at 60 30', |
21 | 'playing level 1' |
22 | ) |
23 | r('spawn a $enemy at $x $y').so(espawn) |
24 | r('spawn an $enemy at $x $y').so(espawn) |
25 | r('playing level 1', 'all enemies are dead').so(p'LEVEL 2', 'play level 2') |
26 | r('there are $some enemies').so( |
27 | function(v) |
28 | -- print("some", v.some, type(v.some)) |
29 | if v.some == "0" then f('all enemies are dead') end |
30 | end |
31 | ) |
32 | r('an enemy died').so( |
33 | function(_) print('ded') f('there are '.. #enemies..' enemies') end |
34 | ) |
35 | |
36 | r('a','b','c').so(p'a,b&c') |
37 | f('a') |
38 | f('b') |
39 | f('letters '..l('a', 'b', 'c')) |
40 | f('c') |
41 | r('a', 'b').so(p'a&b',"a and b") |
42 | r('MYVARR $vals').so(function(vars) print(table.unpack(vars.vals)) end) |
43 | box('MYVARR', {1,2,3,4}) |
44 | |
45 | print("***************************") |
46 | for _, t in ipairs(tuples) do |
47 | print(t, table.unpack(t)) |
48 | end |
49 | |
50 | print("***************************") |
51 | function kill() |
52 | table.remove(enemies, 1) |
53 | f('an enemy died') |
54 | end |
55 | |
56 | kill() |
57 | kill() |
58 | kill() |
59 | |
60 | print("***************************") |
61 | |
62 | for _, t in ipairs(tuples) do |
63 | print(table.unpack(t)) |
64 | end |
65 |
lspace.lua
· 6.6 KiB · Lua
原始文件
Playground
local symbIdx = 1
local function log(...)
local toLog = {}
for i,v in ipairs({...}) do
print(i, tostring(v))
toLog[i] = tostring(v)
end
print(table.unpack(toLog))
end
local function getenv(env, k)
if env.vars[k] then
return env.vars[k]
elseif env._parent then
return getenv(env._parent, k)
else
return nil
end
end
local function setenv(env, k, v)
env.vars[k] = v
end
local envmt = { __index = getenv }
local function proxy(env)
local ret = {}
local mt = {}
function mt:__index(k)
return getenv(env, k)
end
setmetatable(ret, mt)
return ret
end
local varmt = { __tostring = function(s) return "var="..s.var end }
local litmt = { __tostring = function(s) return "lit="..s.lit end }
local function env(parent)
local ret = { _parent = parent, vars = {} }
-- setmetatable(ret, envmt)
return ret
end
local function LSpace()
local tuples = {}
local toDel = {}
local rules = {}
function genSym() local ret = "_"..symbIdx symbIdx = symbIdx + 1 return ret end
function add(rule) table.insert(rules, rule) end
function matchPhrases(todos, phrases, pidx, vars, tuples)
-- print("PIDX", pidx, #phrases)
local anyMatch = false
local phrase = phrases[pidx]
for ti=#tuples, 1,-1 do
local rowVars = env(vars)
local tuple = tuples[ti]
-- log("Testing Tuple", table.unpack(tuple))
if not tuple then
goto nomatch
end
if #phrase ~= #tuple then
-- print("len nomatch")
goto nomatch
end
if toDel[ti] then
goto nomatch
end
for i=1,#tuple do
local t, w = tuple[i], phrase[i]
if w.lit and t ~= w.lit then
-- print("lit ", t, w, "nomatch")
goto nomatch
end
if w.var then
local v = getenv(rowVars, w.var)
if v and v ~= t then
-- print("var ", t, w, "nomatch")
goto nomatch
elseif not v then
setenv(rowVars, w.var, t)
end
end
end
if #phrases == pidx then
-- print("YO!")
--table.remove(tuples, ti)
toDel[ti] = true
anyMatch = true
for _,todo in ipairs(todos) do
todo(proxy(rowVars))
-- table.insert(matches, ti)
end
elseif pidx < #phrases then
-- print("OY!")
if matchPhrases(todos, phrases, pidx+1, env(rowVars), tuples) then
toDel[ti]=true
anyMatch=true
end
end
::nomatch::
end
return anyMatch
end
function match(rules, tuples)
for _, rule in ipairs(rules) do
local vars = env()
local matched = true
local tuple = tuples[ti]
--local matchIds =
matchPhrases(rule.consequents, rule.phrases, 1, vars, tuples)
local removing = {}
for k in pairs(toDel) do
table.insert(removing, k)
toDel[k] = nil
end
-- print("matched",table.unpack(matchIds))
table.sort(removing)
for i=#removing,1,-1 do
table.remove(tuples, removing[i])
end
end
end
function makeRuleStr(r)
local rule = { }
for w in r:gmatch("%S+") do
if w:match("^%$") then
local p = { var=w:sub(2) }
setmetatable(p, varmt)
table.insert(rule, p)
else
local p = { lit=w }
setmetatable(p, litmt)
table.insert(rule, p)
end
end
return rule
end
function list(...)
local t, vars, parts = {}, {}, {...}
local prevSym = genSym()
local headSym = prevSym
for i,p in ipairs(parts) do
if type(p) == "string" then
t = {}
t[#t+1] = prevSym
for w in p:gmatch("%S+") do
if w:match("^%$") then
vars[w] = vars[w] or genSym()
t[#t+1] = vars[w]
else t[#t+1] = w end
end
prevSym = genSym()
t[#t+1] = prevSym
table.insert(tuples, t)
end
end
t[#t] = nil
match(rules, tuples)
return headSym
end
function box(n, t)
local sym = genSym()
table.insert(tuples, { n, t })
match(rules, tuples)
return n.." "..sym
end
function fact(...)
local t, vars, parts = {}, {}, {...}
for i,p in ipairs(parts) do
if type(p) == "string" then
for w in p:gmatch("%S+") do
if w:match("^%$") then
vars[w] = vars[w] or genSym()
t[#t+1] = vars[w]
else t[#t+1] = w end
end
table.insert(tuples, t)
t = {}
end
end
match(rules, tuples)
end
function rule(...)
local ruleDef = {
phrases = {},
consequents = {},
}
local ruleTemps = {...}
for i,r in ipairs(ruleTemps) do
if type(r) == "string" then
table.insert(ruleDef.phrases, makeRuleStr(r))
else
error("Cannot make rule out of nonstring")
end
end
function so(...)
local consequents = {...}
for i, c in ipairs(consequents) do
if type(c) == "string" then
table.insert(ruleDef.consequents, function(vars)
fact(c:gsub("(%$%S+)", function(var)
vars[var] = vars[var] or genSym()
return vars[var]
end))
end)
elseif type(c) == "function" then
table.insert(ruleDef.consequents, c)
else
error("Cannot add a consequent that isn't a string or function!")
end
end
add(ruleDef)
match(rules, tuples)
end
return { so=so }
end
return rule, fact, list, box, tuples, rules
end
return LSpace
1 | local symbIdx = 1 |
2 | |
3 | local function log(...) |
4 | local toLog = {} |
5 | for i,v in ipairs({...}) do |
6 | print(i, tostring(v)) |
7 | toLog[i] = tostring(v) |
8 | end |
9 | print(table.unpack(toLog)) |
10 | end |
11 | |
12 | local function getenv(env, k) |
13 | if env.vars[k] then |
14 | return env.vars[k] |
15 | elseif env._parent then |
16 | return getenv(env._parent, k) |
17 | else |
18 | return nil |
19 | end |
20 | end |
21 | |
22 | local function setenv(env, k, v) |
23 | env.vars[k] = v |
24 | end |
25 | |
26 | local envmt = { __index = getenv } |
27 | |
28 | local function proxy(env) |
29 | local ret = {} |
30 | local mt = {} |
31 | function mt:__index(k) |
32 | return getenv(env, k) |
33 | end |
34 | setmetatable(ret, mt) |
35 | return ret |
36 | end |
37 | |
38 | local varmt = { __tostring = function(s) return "var="..s.var end } |
39 | local litmt = { __tostring = function(s) return "lit="..s.lit end } |
40 | |
41 | local function env(parent) |
42 | local ret = { _parent = parent, vars = {} } |
43 | -- setmetatable(ret, envmt) |
44 | return ret |
45 | end |
46 | |
47 | local function LSpace() |
48 | local tuples = {} |
49 | local toDel = {} |
50 | local rules = {} |
51 | |
52 | function genSym() local ret = "_"..symbIdx symbIdx = symbIdx + 1 return ret end |
53 | |
54 | function add(rule) table.insert(rules, rule) end |
55 | |
56 | function matchPhrases(todos, phrases, pidx, vars, tuples) |
57 | -- print("PIDX", pidx, #phrases) |
58 | local anyMatch = false |
59 | local phrase = phrases[pidx] |
60 | for ti=#tuples, 1,-1 do |
61 | local rowVars = env(vars) |
62 | local tuple = tuples[ti] |
63 | -- log("Testing Tuple", table.unpack(tuple)) |
64 | if not tuple then |
65 | goto nomatch |
66 | end |
67 | if #phrase ~= #tuple then |
68 | -- print("len nomatch") |
69 | goto nomatch |
70 | end |
71 | if toDel[ti] then |
72 | goto nomatch |
73 | end |
74 | for i=1,#tuple do |
75 | local t, w = tuple[i], phrase[i] |
76 | if w.lit and t ~= w.lit then |
77 | -- print("lit ", t, w, "nomatch") |
78 | goto nomatch |
79 | end |
80 | if w.var then |
81 | local v = getenv(rowVars, w.var) |
82 | if v and v ~= t then |
83 | -- print("var ", t, w, "nomatch") |
84 | goto nomatch |
85 | elseif not v then |
86 | setenv(rowVars, w.var, t) |
87 | end |
88 | end |
89 | end |
90 | if #phrases == pidx then |
91 | -- print("YO!") |
92 | --table.remove(tuples, ti) |
93 | toDel[ti] = true |
94 | anyMatch = true |
95 | for _,todo in ipairs(todos) do |
96 | todo(proxy(rowVars)) |
97 | -- table.insert(matches, ti) |
98 | end |
99 | elseif pidx < #phrases then |
100 | -- print("OY!") |
101 | if matchPhrases(todos, phrases, pidx+1, env(rowVars), tuples) then |
102 | toDel[ti]=true |
103 | anyMatch=true |
104 | end |
105 | end |
106 | ::nomatch:: |
107 | end |
108 | return anyMatch |
109 | end |
110 | |
111 | function match(rules, tuples) |
112 | for _, rule in ipairs(rules) do |
113 | local vars = env() |
114 | local matched = true |
115 | local tuple = tuples[ti] |
116 | --local matchIds = |
117 | matchPhrases(rule.consequents, rule.phrases, 1, vars, tuples) |
118 | local removing = {} |
119 | for k in pairs(toDel) do |
120 | table.insert(removing, k) |
121 | toDel[k] = nil |
122 | end |
123 | -- print("matched",table.unpack(matchIds)) |
124 | table.sort(removing) |
125 | for i=#removing,1,-1 do |
126 | table.remove(tuples, removing[i]) |
127 | end |
128 | end |
129 | end |
130 | |
131 | function makeRuleStr(r) |
132 | local rule = { } |
133 | for w in r:gmatch("%S+") do |
134 | if w:match("^%$") then |
135 | local p = { var=w:sub(2) } |
136 | setmetatable(p, varmt) |
137 | table.insert(rule, p) |
138 | else |
139 | local p = { lit=w } |
140 | setmetatable(p, litmt) |
141 | table.insert(rule, p) |
142 | end |
143 | end |
144 | return rule |
145 | end |
146 | |
147 | function list(...) |
148 | local t, vars, parts = {}, {}, {...} |
149 | local prevSym = genSym() |
150 | local headSym = prevSym |
151 | for i,p in ipairs(parts) do |
152 | if type(p) == "string" then |
153 | t = {} |
154 | t[#t+1] = prevSym |
155 | for w in p:gmatch("%S+") do |
156 | if w:match("^%$") then |
157 | vars[w] = vars[w] or genSym() |
158 | t[#t+1] = vars[w] |
159 | else t[#t+1] = w end |
160 | end |
161 | prevSym = genSym() |
162 | t[#t+1] = prevSym |
163 | table.insert(tuples, t) |
164 | end |
165 | end |
166 | t[#t] = nil |
167 | match(rules, tuples) |
168 | return headSym |
169 | end |
170 | |
171 | function box(n, t) |
172 | local sym = genSym() |
173 | table.insert(tuples, { n, t }) |
174 | match(rules, tuples) |
175 | return n.." "..sym |
176 | end |
177 | |
178 | function fact(...) |
179 | local t, vars, parts = {}, {}, {...} |
180 | for i,p in ipairs(parts) do |
181 | if type(p) == "string" then |
182 | for w in p:gmatch("%S+") do |
183 | if w:match("^%$") then |
184 | vars[w] = vars[w] or genSym() |
185 | t[#t+1] = vars[w] |
186 | else t[#t+1] = w end |
187 | end |
188 | table.insert(tuples, t) |
189 | t = {} |
190 | end |
191 | end |
192 | match(rules, tuples) |
193 | end |
194 | |
195 | function rule(...) |
196 | local ruleDef = { |
197 | phrases = {}, |
198 | consequents = {}, |
199 | } |
200 | local ruleTemps = {...} |
201 | for i,r in ipairs(ruleTemps) do |
202 | if type(r) == "string" then |
203 | table.insert(ruleDef.phrases, makeRuleStr(r)) |
204 | else |
205 | error("Cannot make rule out of nonstring") |
206 | end |
207 | end |
208 | |
209 | function so(...) |
210 | local consequents = {...} |
211 | for i, c in ipairs(consequents) do |
212 | if type(c) == "string" then |
213 | table.insert(ruleDef.consequents, function(vars) |
214 | fact(c:gsub("(%$%S+)", function(var) |
215 | vars[var] = vars[var] or genSym() |
216 | return vars[var] |
217 | end)) |
218 | end) |
219 | elseif type(c) == "function" then |
220 | table.insert(ruleDef.consequents, c) |
221 | else |
222 | error("Cannot add a consequent that isn't a string or function!") |
223 | end |
224 | end |
225 | add(ruleDef) |
226 | match(rules, tuples) |
227 | end |
228 | return { so=so } |
229 | end |
230 | |
231 | return rule, fact, list, box, tuples, rules |
232 | end |
233 | |
234 | return LSpace |
235 |