File: verm.lua

package info (click to toggle)
vcmi 1.6.5%2Bdfsg-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid, trixie
  • size: 32,060 kB
  • sloc: cpp: 238,971; python: 265; sh: 224; xml: 157; ansic: 78; objc: 61; makefile: 49
file content (375 lines) | stat: -rw-r--r-- 7,372 bytes parent folder | download | duplicates (2)
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375

local _G=_G
local ipairs=ipairs
local select=select
local pairs=pairs
local type = type
local unpack=unpack
local logError = logError

local DATA = DATA

--/////////////////////////

local function table_print (tt, done)
  done = done or {}
  if type(tt) == "table" then
    local sb = {}
    table.insert(sb, "{");
    for key, value in pairs (tt) do
      if type (value) == "table" and not done [value] then
        done [value] = true
        table.insert(sb, key .. ":{");
        table.insert(sb, table_print (value, done))
        table.insert(sb, "}");
      else
        table.insert(sb, string.format(
            "%s:\"%s\"", tostring (key), tostring(value)))
       end
       table.insert(sb, ",");
    end
    table.insert(sb, "}");

    m = getmetatable(tt);
    if m and m.__index then
		table.insert(sb, table_print (m.__index, done));
    end

    return table.concat(sb)
  else
    return tt .. ""
  end
end

local function to_string( tbl )
    if  "nil"       == type( tbl ) then
        return tostring(nil)
    elseif  "table" == type( tbl ) then
        return table_print(tbl)
    elseif  "string" == type( tbl ) then
        return tbl
    else
        return tostring(tbl)
    end
end

--/////////////////////////

local VERM = {}

local function createEnv(parent, current)
	return setmetatable(
		current or {},
		{
			__parent = parent,
			__index = parent,
			__newindex = function(t, k ,v)
				if type(k) ~= "string" then
					error("String key for env. table required, but got:"..to_string(k))
				end

				local function setOnFirstHit(t, k, v)
					local vv = rawget(t, k)
					if vv~= nil then rawset(t, k, v); return true end

					local m = getmetatable(t)

					if not m then return false end --assume top

					local p = m.__parent

					if not p then
						return false
					else
						return setOnFirstHit(p, k, v)
					end
				end

				if not setOnFirstHit(t, k, v) then
					rawset(t, k, v)
				end
			end
		}
	)
end

local function isNIL(v)
	return (type(v) == "table") and (next(v) == nil)
end

local function prognForm(e, ...)
	--eval each argument, return last result

	local argc = select('#',...)

	if argc == 0 then return {}	end

	for n = 1, argc - 1 do
		VERM:eval(e, (select(n,...)))
	end

	return VERM:eval(e, (select(argc,...)))
end

local function lambdaOrMacro(e, isMacro, args, ...)

	--TODO: get rid of pack-unpack
	local body = {...}
	local oldEnv = e

	local ret = function(e, ...)

	-- we need a function that have parameters with names from `args` table
	-- pack parameters from '...' and bind to new environment

		local newEnv = createEnv(oldEnv, {})

		for i, v in ipairs(args) do
			local p = select(i,...)
			if isMacro then
				newEnv[v] = p
			else
				newEnv[v] = VERM:evalValue(e, p)
			end
		end
		if isMacro then
			local buffer = {}
			for _, v in ipairs(body) do
				table.insert(buffer, (VERM:eval(newEnv, v)))
			end
			return prognForm(newEnv, unpack(buffer))
		else
			return prognForm(newEnv, unpack(body))
		end

	end

	return ret
end

local function lambdaForm(e, args, ...)
	return lambdaOrMacro(e, false, args,  ...)
end

local function defunForm(e, name, args, ...)
	local ret = lambdaOrMacro(e, false, args, ...)
	e[name] = ret
	return ret
end

local function defmacroForm(e, name, args, ...)
	local ret = lambdaOrMacro(e, true, args, ...)
	e[name] = ret
	return ret
end

local function backquoteEval(e, v)
	if isNIL(v) then
		return v
	elseif type(v) == "table" then
		local car = v[1]

		if car == "," then
			return VERM:evalValue(e, v[2])
		else
			local ret = {}

			for _, v in ipairs(v) do
				table.insert(ret, (backquoteEval(e, v)))
			end
			return ret
		end
	else
		return v
	end
end

local specialForms =
{
	["<"] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		if lhs < rhs then
			return lhs
		else
			return {}
		end
	end,
	["<="] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		if lhs <= rhs then
			return lhs
		else
			return {}
		end
	end,
	[">"] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		if lhs > rhs then
			return lhs
		else
			return {}
		end
	end,
	[">="] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		if lhs >= rhs then
			return lhs
		else
			return {}
		end
	end,
	["="] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		if lhs == rhs then
			return lhs
		else
			return {}
		end
	end,
	["+"] = function(e, ...)
		local ret = 0
		for n=1,select('#',...) do
			local v = VERM:evalValue(e, (select(n,...)))
			ret = ret + v
		end
		return ret
	end,
	["*"] = function(e, ...)
		local ret = 1
		for n=1,select('#',...) do
			local v = VERM:evalValue(e, (select(n,...)))
			ret = ret * v
		end
		return ret
	end,
	["-"] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		return lhs - rhs
	end,
	["/"] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		return lhs / rhs
	end,
	["%"] = function(e, lhs, rhs)
		lhs = VERM:evalValue(e, lhs)
		rhs = VERM:evalValue(e, rhs)
		return lhs % rhs
	end,
--	["comma-unlist"] = function(e, ...) end,
	["`"] = backquoteEval,
--	["get-func"] = function(e, ...) end,
	["'"] = function(e, v)
		return v
	end,
	["if"] = function(e, cond, v1, v2)
		cond = VERM:evalValue(e, cond)

		if isNIL(cond) then
			return VERM:evalValue(e, v2)
		else
			return VERM:evalValue(e, v1)
		end
	end,
--	["set"] = function(e, ...) end,
--	["setf"] = function(e, ...) end,
	["setq"] = function(e, name, value)
		e[name] = VERM:evalValue(e, value)
	end,
	["lambda"] = lambdaForm,
	["defun"] = defunForm,
	["progn"] = prognForm,
	["defmacro"] = defmacroForm,
	["do"] = function(e, cond, body)
		local c = VERM:eval(e, cond)
		while not isNIL(c) do
			VERM:eval(e, body)
			c = VERM:eval(e, cond)
		end
		return {}
	end,
	["car"] = function(e, list)
		list = VERM:eval(e, list)
		return list[1] or {}
	end,
	["cdr"] = function(e, list)
		list = VERM:eval(e, list)
		local ret = {}
		for i, v in ipairs(list) do
			if i > 1 then
				table.insert(ret, v)
			end
		end

		return ret
	end,
	["list"] = function(e, ...)
		local ret = {}
		for n=1,select('#',...) do
			local v = VERM:evalValue(e, (select(n,...)))
			table.insert(ret, v)
		end

		return ret
	end,
	["setq-erm"] = function(e, var, varIndex, value)
		local v = VERM:evalValue(e, value)
		DATA.ERM[var][tostring(VERM:evalValue(e, varIndex))] = v
		return v
	end,
}

function VERM:evalValue(e, v)
	if isNIL(v) then
		return v
	elseif type(v) == "table" then
		return self:eval(e, v)
	elseif type(v) == "string" then
		return e[v]
	elseif type(v) == "function" then
		error("evalValue do not accept functions")
	else
		return v
    end
end

function VERM:eval(e, t)
	if type(t) ~= "table" then
		logError("Not valid form: ".. to_string(t))
		return {}
	end

	local car = t[1]
	local origCar = car

	if type(car) == "string" then
		car = e[car]
	end

	if type(car) == "table" then
		car = self:eval(e, car)
	end

	if type(car) == "function" then
		return car(e, unpack(t,2))
	else
		logError(to_string(t) .. " is not callable. Car()="..to_string(car))
		logError("Env:"..to_string(e))
		return {}
	end
end

function VERM:E(line)
	self:eval(self.topEnv, line)
end

VERM.topEnv = createEnv(specialForms)

return VERM