· 6 years ago · Apr 26, 2019, 02:46 PM
1local LIL_VERSION = "0.1"
2
3local LUACEPTION_LEVEL = (os._LUACEPTION_LEVEL or 0) + 1
4
5-- Yields if necessary
6local yield
7
8-- pullQueuedEvent([filter]) - like os.pullEvent but can return events previously queued by yield
9local pullQueuedEvent
10do
11 local event_queue = {}
12 local lastYield
13 local key = "dummy "..tostring({})
14 local function doYield()
15 lastYield = os.clock()
16 os.queueEvent(key)
17 while true do
18 local evt = {os.pullEvent()}
19 if evt[1] ~= key then
20 table.insert(event_queue, evt)
21 while #event_queue > 100 do
22 table.remove(event_queue, 1)
23 end
24 else
25 break
26 end
27 end
28 end
29 function yield()
30 if lastYield < os.clock() - 0.1 then
31 doYield()
32 end
33 end
34 doYield()
35
36 function pullQueuedEvent(filter)
37 if filter ~= nil then
38 while #event_queue > 0 and event_queue[1][1] ~= filter do
39 table.remove(event_queue, 1)
40 end
41 end
42 if #event_queue > 0 then
43 return table.remove(event_queue, 1)
44 else
45 return {os.pullEvent(filter)}
46 end
47 end
48end
49
50local function tokenize(c, locprefix)
51 local tokens = {}
52
53 locprefix = locprefix or "[string]"
54
55 local pos = 1
56
57 local line = 1
58 local char = 1
59
60 local lasttok
61 local function accept(regex)
62 --print(regex, " ", pos)
63 --yield()
64 local r = c:match("^"..regex, pos)
65 if r == nil then return false end
66 lasttok = r
67 pos = pos + #lasttok
68
69 for k=1,#r do
70 if r:sub(k,k) == "\n" then
71 line = line + 1
72 char = 1
73 else
74 char = char + 1
75 end
76 end
77
78 return true
79 end
80
81 local function getContext()
82 return {prefix=locprefix, line=line, char=char}
83 --return c:sub(pos, pos+100)
84 end
85
86 local keywords = {"do","end","function","while","repeat","until","if","then","elseif","then","else","for","in","local","return","break",
87 "nil","false","true",
88 "%.%.%.","==","~=","%.%.","<=",">=","and","or","not",
89 "[-+;.:+*/,=%[%]%(%)%<%>%^%%#{}]"}
90 local function tokenise1()
91 accept("[ \r\n\t]+")
92 if accept("%-%-%[%[") then
93 while not accept("%]%]") do
94 if not accept("[^%]]+") then accept(".") end
95 end
96 return tokenise1()
97 end
98 if accept("%-%-[^\n]*\n") then return tokenise1() end
99 if accept("[a-zA-Z_][a-zA-Z_0-9]*") then
100 for k,v in ipairs(keywords) do
101 if lasttok == v then return v end
102 end
103 return "id"
104 end
105 for k,v in ipairs(keywords) do if accept(v) then return lasttok end end
106 if accept("[0-9]+%.[0-9]*") or accept("[0-9]+") then return "num" end
107 if accept("\"") or accept("%[%[") then
108 local s = ""
109 local long = lasttok == "[["
110 local _end = long and "%]%]" or "\""
111 while not accept(_end) do
112 if accept("\\") then
113 if accept("a") then s=s.."\a"
114 elseif accept("b") then s=s.."\b"
115 elseif accept("f") then s=s.."\f"
116 elseif accept("n") then s=s.."\n"
117 elseif accept("r") then s=s.."\r"
118 elseif accept("t") then s=s.."\t"
119 elseif accept("v") then s=s.."\v"
120 elseif accept("\\") then s=s.."\\"
121 elseif accept("\"") then s=s.."\""
122 elseif accept("'") then s=s.."\'"
123 elseif accept("%[") then s=s.."["
124 elseif accept("%]") then s=s.."]"
125 elseif accept("[0-9][0-9][0-9]") or accept("[0-9][0-9]") or accept("[0-9]") then s=s..string.char(tonumber(lasttok))
126 end
127 elseif accept(long and "[^%]\\]+" or "[^\n\"\\]+") then s=s..lasttok
128 else error("unfinished string")
129 end
130 end
131 lasttok = s
132 return "str"
133 end
134 if pos > #c then lasttok="" return "<eof>" end
135 error("Unknown token near "..c:sub(pos-50,pos+100))
136 return nil
137 end
138
139 while pos <= #c do
140 local t = tokenise1()
141 if t == nil then --[[print(c:sub(pos,pos+100))]] break end
142 table.insert(tokens, {t, lasttok, getContext()})
143 end
144
145 do
146 local f = fs.open("tokens.txt", "w")
147 for k,v in ipairs(tokens) do
148 if v[1] == v[2] then
149 f.write(v[1].."\n")
150 else
151 f.write(v[1].." : "..v[2].."\n")
152 end
153 end
154 f.close()
155 end
156
157 return tokens
158end
159
160local function tokenizeFile(fn)
161 local f = fs.open(fn,"r")
162 local c = f.readAll()
163 f.close()
164 return tokenize(c, fn)
165end
166
167local function parse(tokens)
168 local ntok = 1
169 local tdata, context = nil, tokens[1][3]
170 local function accept(name)
171 if ntok > #tokens then return false end
172 if tokens[ntok][1] == name then
173 ntok = ntok + 1
174 tdata = tokens[ntok-1][2]
175 context = tokens[ntok-1][3]
176 --print("accepted "..name)
177 return true
178 end
179 return false
180 end
181
182 local function peek(name)
183 return ntok <= #tokens and tokens[ntok][1] == name
184 end
185
186 local function expect(name)
187 if not accept(name) then error("expected "..name.." near "..tokens[ntok][3],2) end
188 return tdata
189 end
190
191 local function assert(a, b)
192 if a == nil then error(b.." near "..tokens[ntok][3],2) end
193 return a
194 end
195
196 local block, stat, laststat, funcname, varlist, var, namelist, explist, exp, prefixexp, functioncall, args, function_, funcbody
197 local parlist, tableconstructor, fieldlist, field, fieldsep, binop, unop
198
199 function block()
200 local r = {}
201 local s
202 repeat
203 s = stat()
204 table.insert(r, s)
205 accept(";")
206 until s == nil
207 table.insert(r, laststat())
208 accept(";")
209 return r
210 end
211
212 function stat()
213 local nt = ntok
214 do
215 local vl = varlist()
216 if vl ~= nil then
217 expect("=")
218 local al = explist()
219 if al == nil then return nil end
220 return {"=", vl, al, context=context}
221 end
222 end
223
224 ntok = nt
225 do
226 local fc = prefixexp()
227 if fc ~= nil and (fc[1] == "call" or fc[1] == ":call") then return fc end
228 end
229
230 local ctx = context
231
232 ntok = nt
233 if accept("do") then
234 local b = assert(block(), "expected block")
235 expect("end")
236 return {"block", b}
237 elseif accept("while") then
238 local e = exp()
239 if e == nil then error("expected expression") end
240 expect("do")
241 local b = assert(block(), "expected block")
242 expect("end")
243 return {"while", e, b, context=ctx}
244 elseif accept("repeat") then
245 local b = block()
246 expect("until")
247 local e = exp()
248 if e == nil then error("expected expression") end
249 return {"repeat", b, e, context=ctx}
250 elseif accept("if") then
251 local r = {"if", context=ctx}
252 table.insert(r, assert(exp(), "expected expression"))
253 expect("then")
254 table.insert(r, block())
255 while accept("elseif") do
256 table.insert(r, assert(exp(), "expected expression"))
257 expect("then")
258 table.insert(r, block())
259 end
260 if accept("else") then
261 table.insert(r, block())
262 end
263 expect("end")
264 return r
265 elseif accept("for") then
266 nt = ntok
267 local nl = namelist()
268 if nl == nil or not accept("in") then
269 ntok = nt
270 -- numeric for
271 expect("id")
272 local id = tdata
273 expect("=")
274 local start = assert(exp(), "expected expression")
275 expect(",")
276 local stop = assert(exp(), "expected expression")
277 local step = {"con", 1}
278 if accept(",") then
279 step = assert(exp(), "expected expression")
280 end
281 expect("do")
282 local b = block()
283 expect("end")
284 return {"nfor", id, start, stop, step, b, context=ctx}
285 else
286 -- generic for
287 local el = explist()
288 expect("do")
289 local b = block()
290 expect("end")
291 return {"gfor", nl, el, b, context=ctx}
292 end
293 elseif accept("function") then
294 local fn = assert(funcname(), "expected funcname")
295 local fb = funcbody()
296 return {"funcst", fn, fb, context=ctx}
297 elseif accept("local") then
298 if accept("function") then
299 local id = expect("id")
300 local fb = funcbody()
301 return {"lfunc", id, fb, context=ctx}
302 else
303 local nl, el = assert(namelist(), "expected namelist")
304 if accept("=") then
305 el = assert(explist(), "expected explist")
306 end
307 return {"local", nl, el, context=ctx}
308 end
309 else
310 return nil
311 end
312 end
313
314 function laststat()
315 if accept("return") then
316 local nt = ntok
317 local el = explist()
318 if el == nil then ntok = nt end
319 return {"return", el, context=context}
320 elseif accept("break") then
321 return {"break", context=context}
322 else
323 return nil
324 end
325 end
326
327 function funcname()
328 local n = {".", expect("id")}
329 while accept(".") do
330 table.insert(n, expect("id"))
331 end
332 if accept(":") then
333 n[1] = ":"
334 table.insert(n, expect("id"))
335 end
336 return n
337 end
338
339 function varlist()
340 local l = {}
341 repeat
342 local v = var()
343 if v == nil then
344 return nil
345 end
346 table.insert(l, v)
347 until not accept(",")
348 return l
349 end
350
351 function var()
352 local nt = ntok
353 local pe = prefixexp()
354 if pe == nil or (pe[1] ~= "[]" and pe[1] ~= "." and pe[1] ~= "var") then
355 ntok = nt
356 return nil
357 end
358 return pe
359 end
360
361 function namelist()
362 local l = {expect("id")}
363 while accept(",") do
364 table.insert(l, expect("id"))
365 end
366 return l
367 end
368
369 function explist()
370 local l = {exp()}
371 if l[1] == nil then return {} end
372 while accept(",") do
373 table.insert(l, assert(exp(), "expected expression"))
374 end
375 return l
376 end
377
378 local function exp0()
379 if accept("nil") then return {"con", nil, context=context} end
380 if accept("true") then return {"con", true, context=context} end
381 if accept("false") then return {"con", false, context=context} end
382 if accept("num") then return {"con", tonumber(tdata), context=context} end
383 if accept("str") then return {"con", tdata, context=context} end
384 if accept("...") then return {"...", context=context} end
385 if accept("function") then return {"func", funcbody(), context=context} end
386 if accept("-") then return {"negate", exp0(), context=context} end
387 if accept("not") then return {"not", exp0(), context=context} end
388 if accept("#") then return {"#", exp0(), context=context} end
389 if peek("{") then return {"tcon", tableconstructor(), context=context} end
390 do
391 local nt = ntok
392 local pe = prefixexp()
393 if pe ~= nil then
394 return pe
395 end
396 ntok = nt
397 end
398 if accept("(") then
399 local e = assert(exp(), "expected expression")
400 expect(")")
401 return e
402 end
403 return nil
404 end
405
406 local function exp1()
407 local e = exp0()
408 if e == nil then return nil end
409 -- right associative
410 -- {"^", a, {"^", b, {"^", c, d}}}
411 if accept("^") then
412 e = {"^", e, assert(exp0(), "expected expression"), context=context}
413 local last = e
414 while accept("^") do
415 last[3] = {"^", last[3], assert(exp0(), "expected expression"), context=context}
416 last = last[3]
417 end
418 end
419 return e
420 end
421
422 local function exp1_5()
423 local e = exp1()
424 if e == nil then return nil end
425 while accept("%") do e={"%", e, assert(exp1(), "expected expression"), context=context} end
426 return e
427 end
428
429 local function exp2()
430 local e = exp1_5()
431 if e == nil then return nil end
432 while accept("/") do e={"/", e, assert(exp1_5(), "expected expression"), context=context} end
433 return e
434 end
435
436 local function exp3()
437 local e = exp2()
438 if e == nil then return nil end
439 while accept("*") do e={"*", e, assert(exp2(), "expected expression"), context=context} end
440 return e
441 end
442
443 local function exp4()
444 local e = exp3()
445 if e == nil then return nil end
446 while accept("-") do e={"-", e, assert(exp3(), "expected expression"), context=context} end
447 return e
448 end
449
450 local function exp5()
451 local e = exp4()
452 if e == nil then return nil end
453 while accept("+") do e={"+", e, assert(exp4(), "expected expression"), context=context} end
454 return e
455 end
456
457 local function exp6()
458 local e = exp5()
459 if e == nil then return nil end
460 -- right associative
461 -- {"^", a, {"^", b, {"^", c, d}}}
462 if accept("..") then
463 e = {"..", e, assert(exp5(), "expected expression")}
464 local last = e
465 while accept("..") do
466 last[3] = {"..", last[3], assert(exp5(), "expected expression"), context=context}
467 last = last[3]
468 end
469 end
470 return e
471 end
472
473 local function exp7()
474 local e = exp6()
475 if e == nil then return nil end
476 while accept(">") or accept(">=") or accept("<") or accept("<=") or accept("==") or accept("~=") do
477 e = {tdata, e, assert(exp6(), "expected expression"), context=context}
478 end
479 return e
480 end
481
482 local function exp8()
483 local e = exp7()
484 if e == nil then return nil end
485 while accept("and") do e={"and", e, assert(exp7(), "expected expression"), context=context} end
486 return e
487 end
488
489 local function exp9()
490 local e = exp8()
491 if e == nil then return nil end
492 while accept("or") do e={"or", e, assert(exp8(), "expected expression"), context=context} end
493 return e
494 end
495
496 function exp()
497 return exp9()
498 end
499
500 function prefixexp()
501 -- prefixexp = Name | prefixexp [ exp ] | prefixexp . Name | prefixexp : Name args | prefixexp args | ( exp )
502 local r
503 if accept("id") then
504 r = {"var", tdata, context=context}
505 elseif accept("(") then
506 local e = assert(exp(), "expression expected")
507 expect(")")
508 r = {"()", e, context=context}
509 else
510 return nil
511 end
512 while true do
513 if accept(".") then
514 local e = expect("id")
515 r = {".", r, e, context=context}
516 elseif accept("[") then
517 local e = assert(exp(), "expression expected")
518 expect("]")
519 r = {"[]", r, e, context=context}
520 elseif peek("(") or peek("{") or peek("str") then
521 local e = args()
522 r = {"call", r, e, context=context}
523 elseif accept(":") then
524 local i = expect("id")
525 local e = args()
526 r = {":call", r, i, e, context=context}
527 else
528 break
529 end
530 end
531 return r
532 end
533
534 function funcbody()
535 local p, b
536 expect("(")
537 if not accept(")") then
538 p = parlist()
539 expect(")")
540 else
541 p = {}
542 end
543 b = block()
544 expect("end")
545 return {p, b}
546 end
547
548 function parlist()
549 if accept("...") then return {"..."} end
550 local l = {expect("id")}
551 while accept(",") do
552 if accept("...") then
553 table.insert(l, "...")
554 break
555 else
556 table.insert(l, expect("id"))
557 end
558 end
559 return l
560 end
561
562 function tableconstructor()
563 expect("{")
564 local fl = fieldlist()
565 expect("}")
566 return fl
567 end
568
569 function fieldlist()
570 local fl = {}
571 while true do
572 local f = field()
573 if f == nil then break end
574 table.insert(fl, f)
575 if not accept(";") and not accept(",") then break end
576 end
577 return fl
578 end
579
580 function field()
581 if accept("[") then
582 local e = assert(exp(), "expression expected")
583 expect("]")
584 expect("=")
585 return {e, assert(exp(), "expression expected")}
586 end
587 local nt = ntok
588 if accept("id") then
589 local e = {"con", tdata}
590 if accept("=") then
591 return {e, assert(exp(), "expression expected")}
592 else
593 ntok = nt
594 end
595 end
596 local e = exp()
597 if e == nil then return nil end
598 return {"auto", e}
599 end
600
601 function args()
602 if peek("{") then return {{"tcons", tableconstructor(), context=context}} end
603 if accept("str") then return {{"con", tdata, context=context}} end
604 expect("(")
605 local el = explist()
606 expect(")")
607 return el
608 end
609
610 return block()
611end
612
613local function interpret(ast_list)
614 local DEFAULT_GLOBAL_ENV = {} -- initialized later
615
616 local log
617 local DISABLE_LOG = true
618 local logfile = nil
619 if not DISABLE_LOG then
620 logfile = fs.open("log.txt", "w")
621 log = function(s)
622 logfile.write(s.."\n")
623 end
624 else
625 log = function() end
626 end
627
628 local expr, callfunc, block, stmt, funcdef
629
630 local function assert(test, message, level)
631 if not test then error(message or "assertion failed!", (level or 1)+1) end
632 return test
633 end
634
635 -- Allow throwing of non-string errors
636 local oldError, oldPcall = error, pcall
637 local error, pcall
638 do
639 local err
640 function error(x, level)
641 err = x
642 if (level or 1) == 0 then
643 oldError("exterr", 0)
644 else
645 if type(x) == "table" then x.lualoc = scope.context end
646 oldError("exterr", (level or 1) + 1)
647 end
648 end
649 function pcall(f, ...)
650 local rv = {oldPcall(f, ...)}
651 if rv[1] then
652 -- no error
653 return unpack(rv)
654 end
655 -- error
656 local err2 = rv[2]
657 if err2 and err2:len() >= 6 and err2:sub(-6) == "exterr" then
658 -- thrown with the overridden error()
659 local location = err2:sub(1, -7)
660 if type(err) == "string" then
661 return false, location..err
662 elseif type(err) == "table" then
663 if location ~= "" then err.location = location end
664 return false, err
665 else
666 return false, err
667 end
668 else
669 -- normal error
670 return false, err2
671 end
672 end
673 end
674
675 local scope = {env=DEFAULT_GLOBAL_ENV}
676 local scopeStack = {scope}
677 local function pushScope(sc)
678 scope = sc or setmetatable({context="", vars = setmetatable({}, {__index=scope.vars})}, {__index=scope})
679 table.insert(scopeStack, scope)
680 --log("pushScope "..(#scopeStack-1).." -> "..#scopeStack)
681 end
682 local function popScope()
683 --log("popScope "..(#scopeStack).." -> "..(#scopeStack-1))
684 table.remove(scopeStack, #scopeStack)
685 scope = scopeStack[#scopeStack]
686 end
687 local function addLocal(name, val)
688 if val == nil then error({"interp", "expected two arguments"}, 2) end
689 if val.type == nil then error({"interp", "corrupt table"}, 2) end
690 scope.vars[name] = {val=val}
691 end
692 -- Returns the highest scope of the (n-1)'th function call
693 -- Eg if scopes 5 and 12 correspond to new functions, getStackLevel(2) returns scope 11 and getStackLevel(3) returns scope 4
694 -- getStackLevel(1) always returns the top scope
695 local function getStackLevel(n)
696 if n ~= math.floor(n) or n < 0 then
697 error{"lua", "expected non-negative integer"}
698 end
699 if n == 0 then n = 1 end
700 local pos = #scopeStack
701 for k=2,n do
702 while not pos.fn do
703 pos = pos - 1
704 if pos <= 0 then
705 error{"lua", "invalid level"}
706 end
707 end
708 pos = pos - 1
709 if pos <= 0 then
710 error{"lua", "invalid level"}
711 end
712 end
713 return scopeStack[pos]
714 end
715 local function interp_rawget(t, name)
716 if name == nil or name.type == "nil" then return nil end
717 if t.type == nil then error("corrupt table",2) end
718 if t.type ~= "table" then error({"lua", "attempt to index " .. t.type .. " value"}) end
719 if t.val[name.type] ~= nil then
720 local tt = t.val[name.type]
721 if tt[name.val] then
722 return tt[name.val]
723 else
724 return {type="nil"}
725 end
726 else
727 return {type="nil"}
728 end
729 end
730 local function interp_rawset(t, name, value)
731 if name == nil or name.type == "nil" then error({"lua", "expected table index; got nil"}) end
732 if t.type ~= "table" then error({"lua", "attempt to index " .. t.type .. " value"}, 2) end
733 assert(type(t.val) == "table", "corrupted table", 2)
734 assert(type(name) == "table", "typeof name ~= table", 2)
735 if t.val[name.type] == nil then t.val[name.type] = {} end
736 assert(type(t.val[name.type]) == "table", "corrupted table", 2)
737 if value.type == "nil" then
738 t.val[name.type][name.val] = nil
739 if next(t.val[name.type]) == nil then
740 t.val[name.type] = nil
741 end
742 else
743 t.val[name.type][name.val] = value
744 end
745 end
746 local function getInTable(t, name, prev)
747 prev = prev or {}
748 if prev[t] then error({"lua", "loop in gettable"}) end
749 prev[t] = true
750
751 if type(name) == "string" or type(name)=="number" then name = {type=type(name), val=name} end
752
753 assert(type(t) == "table", "typeof t ~= table")
754 assert(type(name) == "table", "typeof name ~= table")
755 assert(name.type, "corrupt table", 2)
756
757 -- try the table first
758 local rgr = t.type == "table" and interp_rawget(t, name)
759 if rgr and rgr.type ~= "nil" then return rgr end
760
761 -- then try __index
762 local index = t.metatable and interp_rawget(t.metatable, {type="string",val="__index"})
763 if index and index.type ~= "nil" then
764 if index.type == "function" then
765 return expr({"call", {"quote", index}, {{"quote", t}, {"quote", name}}})[1]
766 else
767 return getInTable(index, name, prev)
768 end
769 end
770
771 -- then return nil
772 return {type="nil"}
773 end
774 local function setInTable(t, name, value, prev)
775 prev = prev or {}
776 if prev[t] then error({"lua", "loop in settable"}) end
777 prev[t] = true
778
779 assert(type(t) == "table", "typeof t ~= table", 2)
780 assert(type(name) == "table", "typeof name ~= table", 2)
781
782 if name == nil or name.type == "nil" then error({"lua", "expected table index; got nil"}) end
783
784 -- first, if the key already exists in the table, set it and return
785 if t.type == "table" and interp_rawget(t, name).type ~= "nil" then
786 return interp_rawset(t, name, value)
787 end
788
789 -- then try __newindex
790 local newindex = t.metatable and interp_rawget(t.metatable, {type="string",val="__newindex"})
791 if newindex and newindex.type ~= "nil" then
792 if newindex.type == "function" then
793 return expr({"call", {"quote", newindex}, {{"quote", t}, {"quote", name}, {"quote", value}}})[1]
794 else
795 return setInTable(newindex, name, value, prev)
796 end
797 end
798
799 -- then create a new key if it's a table
800 if t.type == "table" then
801 return interp_rawset(t, name, value)
802 end
803
804 -- then fail
805 error({"lua", "cannot index "..t.type})
806 end
807 local function getValue(name)
808 if type(name) == "string" then
809 local v = scope.vars[name]
810 if v ~= nil then
811 return v.val
812 else
813 return getInTable(scope.env, {type="string",val=name})
814 end
815 elseif type(name) == "table" then
816 if name[1] == "." then
817 if #name == 2 then
818 return setValue(name[2], value)
819 end
820 local t = getValue(name[2])
821 for k=3,#name do
822 t = getInTable(t, {type="string",val=name[2]})
823 end
824 return t
825 elseif name[1] == "var" then
826 return getValue(name[2])
827 else
828 error({"interp", "invalid name operation "..name[1]})
829 end
830 else
831 error({"interp", "invalid name type "..type(name)})
832 end
833 end
834 local function setValue(name, value)
835 if value == nil then value = {type="nil"} end
836 if value.type == nil then error({"interp", "corrupt table"}, 2) end
837 if type(name) == "string" then
838 local v = scope.vars[name]
839 if v ~= nil then
840 v.val = value
841 else
842 setInTable(scope.env, {type="string",val=name}, value)
843 end
844 elseif type(name) == "table" then
845 if name[1] == "." then
846 if #name == 2 then
847 return setValue(name[2], value)
848 end
849 local t = getValue(name[2])
850 for k=3,#name-1 do
851 t = getInTable(t, {type="string",val=name[k]})
852 end
853
854 setInTable(t, {type="string",val=name[#name]}, value)
855 elseif name[1] == "[]" then
856 local tbl = expr(name[2])[1]
857 local key = expr(name[3])[1]
858 setInTable(tbl, key, value)
859 elseif name[1] == "var" then
860 setValue(name[2], value)
861 else
862 error({"interp", "invalid name operation " .. name[1]})
863 end
864 else
865 error({"interp", "invalid name type " .. type(name)})
866 end
867 end
868 local function isTrue(val)
869 if val.type == "nil" or (val.type == "boolean" and val.val == false) then
870 return false
871 else
872 return true
873 end
874 end
875 local function getcomphandler(a, b, event)
876 if a.type ~= b.type then return nil end
877 if a.metatable == nil or b.metatable == nil then return nil end
878 local af = interp_rawget(a.metatable, {type="string",val=event})
879 local bf = interp_rawget(b.metatable, {type="string",val=event})
880 if af == nil or bf == nil or af.type ~= bf.type then return nil end
881 if af.val == bf.val then
882 return af
883 else
884 return nil
885 end
886 end
887 local function explist(l)
888 local r = {}
889 if l == nil then error({"interp","table expected, got nil"},2) end
890 for k,v in ipairs(l) do
891 local sh = #scopeStack
892 for k2,v2 in ipairs(expr(v)) do
893 table.insert(r, v2)
894 end
895 if sh ~= #scopeStack then error({"interp", "scope stack imbalance in expression list, type: "..v[1]}) end
896 end
897 return r
898 end
899
900 local function getmetamethod(obj, name)
901 if obj.metatable == nil then return nil end
902 return obj.metatable.val.string and obj.metatable.val.string[name]
903 end
904
905 local function getbinhandler(a, b, name)
906 return getmetamethod(a, name) or getmetamethod(b, name)
907 end
908
909 local function callmetamethod(obj, name, args, default)
910 local mm = getmetamethod(obj, name)
911 if mm == nil then return default end
912 return callfunc(mm, args)
913 end
914
915 -- initialize DEFAULT_GLOBAL_ENV
916 do
917 local env = {}
918
919 -- helper function
920 -- if o.type ~= t, throw an error, then return t.val
921 -- if o == nil and allowNil, return nil
922 local function checkType(o, t, allowNil)
923 if t == nil then error({"interp", "expected string, got nil"}) end
924 if o == nil or o.type == "nil" and allowNil then return nil end
925 if o == nil then error({"lua", "expected "..t..", got nil"}, 2) end
926 if o.type == nil then error({"interp", "corrupt table"},3) end
927 if o.type ~= t then error({"lua", "expected "..t..", got "..o.type}, 2) end
928 return o.val
929 end
930
931 local function wrapObj(o)
932 if type(o) == "number" or type(o) == "string" or type(o) == "boolean" or type(o) == "nil" then
933 return {type=type(o),val=o}
934 else
935 error({"lua", "invalid type "..type(o).." - expected number, string, boolean or nil"})
936 end
937 end
938 local function unwrapObj(o)
939 if o.type == "number" or o.type == "string" or o.type == "boolean" or o.type == "nil" then
940 return o.val
941 else
942 error({"interp", "invalid type "..o.type.." - expected number, string, boolean or nil"})
943 end
944 end
945
946
947 -- wraps a native function into a virtualized function
948 -- can only handle types: number, string, boolean, nil
949 -- eg: wrapFunc(math.pow)({type="number",val=2},{type="number",val=5}) -> {type="number",val=32}
950 local function wrapFunc(fn)
951 return function(...)
952 local args = {...}
953 for k,v in ipairs(args) do
954 args[k] = unwrapObj(v)
955 end
956 local ret = {fn(unpack(args))}
957 for k,v in pairs(ret) do
958 ret[k] = wrapObj(v)
959 end
960 if #ret == 0 then ret[1] = {type="nil"} end
961 return unpack(ret)
962 end
963 end
964
965 -- like wrapFunc, for functions that return either nil or a list of wrappable types
966 local function wrapListReturningFunc(fn)
967 return function(...)
968 local args = {...}
969 for k,v in pairs(args) do
970 args[k] = unwrapObj(v)
971 end
972 local ok, ret = pcall(fn, unpack(args))
973 if not ok then error({"lua", ret}) end
974 if ret == nil then return {type="nil"} end
975 local r = {type="table",val={number={}}}
976 for k,v in ipairs(ret) do
977 r.val.number[k] = wrapObj(v)
978 end
979 return r
980 end
981 end
982
983 env.math = {
984 huge = {type="number",val=math.huge},
985 pi = {type="number",val=math.pi},
986 }
987
988 for k,v in ipairs({"abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "deg", "exp", "floor", "fmod", "frexp", "ldexp", "log", "log10", "max", "min", "modf", "pow", "rad", "random", "randomseed", "sin", "sinh", "sqrt", "tanh", "tan"}) do
989 env.math[v] = wrapFunc(math[v])
990 end
991
992 env.string = {
993 byte = wrapFunc(string.byte),
994 char = wrapFunc(string.char),
995 find = wrapFunc(string.find),
996 format = wrapFunc(string.format),
997 len = wrapFunc(string.len),
998 lower = wrapFunc(string.lower),
999 match = wrapFunc(string.match),
1000 rep = wrapFunc(string.rep),
1001 reverse = wrapFunc(string.reverse),
1002 sub = wrapFunc(string.sub),
1003 upper = wrapFunc(string.upper),
1004
1005 gmatch = function(str, ptn)
1006 str = checkType(str, "string")
1007 ptn = checkType(ptn, "string")
1008 local f, s, v = string.gmatch(str, ptn)
1009 return {type="function", val=function()
1010 v = f(s, v)
1011 return wrapObj(v)
1012 end}
1013 end,
1014
1015 -- todo: gsub
1016 }
1017
1018 local event_table_map = {}
1019 setmetatable(event_table_map, {__mode="k"})
1020
1021 local function native_pullEventRaw(filter)
1022 filter = checkType(filter, "string", true)
1023 local valid, t
1024 repeat
1025 valid = true
1026 t = pullQueuedEvent()
1027 for k,v in ipairs(t) do
1028 if type(v) == "table" then
1029 if event_table_map[v] then
1030 t[k] = event_table_map[v]
1031 else
1032 -- ignore this event
1033 valid = false
1034 end
1035 else
1036 t[k] = wrapObj(v)
1037 end
1038 end
1039 until valid
1040 if #t == 0 then return {type="nil"} end
1041 return unpack(t)
1042 end
1043
1044 env.os = {
1045 queueEvent = wrapFunc(os.queueEvent),
1046 pullEventRaw = native_pullEventRaw,
1047 getComputerLabel = wrapFunc(os.getComputerLabel),
1048 shutdown = wrapFunc(os.shutdown),
1049 setAlarm = function(time)
1050 local t = os.setAlarm(checkType(time, "number"))
1051 event_table_map[t] = {type="table", val={}}
1052 return event_table_map[t]
1053 end,
1054 clock = wrapFunc(os.clock),
1055 reboot = wrapFunc(os.reboot),
1056 setComputerLabel = wrapFunc(os.setComputerLabel),
1057 time = wrapFunc(os.time),
1058 startTimer = function(delay)
1059 local t = os.startTimer(checkType(delay, "number"))
1060 event_table_map[t] = {type="table", val={}}
1061 return event_table_map[t]
1062 end
1063 }
1064
1065 env.fs = {
1066 list = wrapListReturningFunc(fs.list),
1067 isDir = wrapFunc(fs.isDir),
1068 exists = wrapFunc(fs.exists),
1069 isReadOnly = wrapFunc(fs.isReadOnly),
1070 getName = wrapFunc(fs.getName),
1071 getSize = wrapFunc(fs.getSize),
1072 getDrive = wrapFunc(fs.getDrive),
1073 makeDir = wrapFunc(fs.makeDir),
1074 move = wrapFunc(fs.move),
1075 copy = wrapFunc(fs.copy),
1076 delete = wrapFunc(fs.delete),
1077 combine = wrapFunc(fs.combine),
1078 open = function(path, mode)
1079 path = checkType(path, "string")
1080 mode = checkType(mode, "string")
1081 local native = fs.open(path, mode)
1082 if native == nil then return {type="nil"} end
1083
1084 local rt = {type="table",val={}}
1085 for k,v in pairs(native) do
1086 if type(k) == "string" and type(v) == "function" then
1087 setInTable(rt, wrapObj(k), {type="function",val=wrapFunc(v)})
1088 end
1089 end
1090 return rt
1091 end
1092 }
1093
1094 --[[env.coroutine = {
1095 yield = function(...)
1096 local oss = scopeStack
1097 local rv = {coroutine.yield(...)}
1098 scopeStack = oss
1099 if #rv == 0 then return {type="nil"} end
1100 return unpack(rv)
1101 end,
1102 create = function(fn)
1103 local cr_scopeStack = {scope}
1104 local cr = coroutine.create(function(...)
1105 scopeStack = cr_scopeStack
1106 local rv = {pcall(callfunc, fn, ...)}
1107 if table.remove(rv, 1) then
1108 -- no error
1109 return unpack(rv)
1110 end
1111 -- error
1112 local err = rv[1]
1113 if type(err) ~= "table" or err[1] ~= "lua" then
1114 if type(err) == "table" then
1115 print("Internal error in coroutine: "..err.location..err[2])
1116 else
1117 print("Internal error in coroutine: "..tostring(err))
1118 end
1119 error(err, 0)
1120 end
1121 print("Lua coroutine error: "..err.location..err[2])
1122 error(err, 0)
1123 end)
1124 return {type="thread", val={cr=cr, scopeStack=cr_scopeStack}}
1125 end,
1126 resume = function(c, ...)
1127 local oss = scopeStack
1128 local rv = {coroutine.resume(checkType(c, "thread").cr, ...)}
1129 scopeStack = oss
1130 rv[1] = wrapObj(rv[1])
1131 return unpack(rv)
1132 end,
1133 status = function(c)
1134 return wrapObj(coroutine.status(checkType(c, "thread").cr))
1135 end,
1136 }]]
1137
1138 env.peripheral = {
1139 isPresent = wrapFunc(peripheral.isPresent),
1140 getType = wrapFunc(peripheral.getType),
1141 getMethods = wrapListReturningFunc(peripheral.getMethods),
1142 call = wrapFunc(peripheral.call)
1143 }
1144
1145 env.term = {
1146 setCursorBlink = wrapFunc(term.setCursorBlink),
1147 getSize = wrapFunc(term.getSize),
1148 getCursorPos = wrapFunc(term.getCursorPos),
1149 setCursorPos = wrapFunc(term.setCursorPos),
1150 write = wrapFunc(term.write),
1151 clear = wrapFunc(term.clear),
1152 clearLine = wrapFunc(term.clearLine),
1153 scroll = wrapFunc(term.scroll)
1154 }
1155
1156 env.table = {}
1157
1158 function env.loadstring(str, prefix)
1159 str = checkType(str, "string")
1160 prefix = checkType(prefix, "string", true) or "[string]"
1161 local ok, tokens, ast = pcall(tokenize, str, prefix)
1162 if not ok then error({"lua", tokens}, 2) end
1163 ok, ast = pcall(parse, tokens)
1164 if not ok then error({"lua", ast}, 2) end
1165
1166 return funcdef({{"..."},ast})
1167 end
1168
1169 function env.error(message, level)
1170 message = checkType(message, "string")
1171 level = checkType(level, "number", true) or 1
1172 if level == 0 then
1173 error({"lua", message}, 0)
1174 else
1175 local sc = getStackLevel(level)
1176 error({"lua", sc.context.prefix..":"..sc.context.line..": "..message}, 0)
1177 end
1178 end
1179
1180 function env.rawequal(a, b)
1181 if a == nil or b == nil then return a == b end
1182 return a.type == b.type and a.val == b.val
1183 end
1184
1185 function env.rawget(tbl, key)
1186 return interp_rawget(tbl or {type="nil"}, key or {type="nil"})
1187 end
1188
1189 function env.rawset(tbl, key, value)
1190 interp_rawset(tbl or {type="nil"}, key or {type="nil"}, value or {type="nil"})
1191 return {type="nil"}
1192 end
1193
1194 function env.select(...)
1195 local args = {...}
1196 local n = args[1]
1197 if n == nil then error{"lua", "expected at least one argument"} end
1198 if n.type == "string" and n.val == "#" then
1199 return {type="number", val=(#args-1)}
1200 end
1201 n = checkType(n, "number")
1202 return args[n+1] or {type="nil"}
1203 end
1204
1205 function env.setfenv(fn, tbl)
1206 if fn == nil then fn = {type="nil"} end
1207 checkType(tbl, "table")
1208 if fn.type == "number" then
1209 local scope = getStackLevel(fn.val)
1210 scope.fn.val.scope.env = tbl
1211 else
1212 checkType(fn, "function")
1213 fn.val.scope.env = tbl
1214 end
1215 return fn
1216 end
1217
1218 function env.getfenv(fn)
1219 if fn == nil then fn = {type="nil"} end
1220 if fn.type == "number" then
1221 local scope = getStackLevel(fn.val)
1222 return scope.fn.scope.env
1223 elseif fn.type == "nil" then
1224 return scope.env
1225 else
1226 checkType(fn, "function")
1227 return fn.val.scope.env
1228 end
1229 end
1230
1231 function env.setmetatable(tbl, meta)
1232 tbl = tbl or {type="nil"}
1233 if tbl.type ~= "table" then
1234 error{"lua", "cannot set metatable of "..tbl.type.." value"}
1235 end
1236 if meta and meta.type == "nil" then meta = nil end
1237 tbl.metatable = meta
1238 return {type="nil"}
1239 end
1240
1241 function env.getmetatable(tbl)
1242 return tbl.metatable or {type="nil"}
1243 end
1244
1245 function env.unpack(tbl, start)
1246 checkType(tbl, "table")
1247 start = checkType(start, "number", true)
1248 if #tbl.val.number < (start or 1) then return {type="nil"} end
1249 return unpack(tbl.val.number, start or 1)
1250 end
1251
1252 function env.xpcall(...)
1253 error("todo xpcall")
1254 end
1255
1256 function env.pcall(fn, ...)
1257 local rv = {pcall(callfunc, fn, {...})}
1258 local ok = rv[1]
1259 if ok then
1260 rv[1] = wrapObj(rv[1])
1261 return unpack(rv)
1262 else
1263 if type(rv[2]) ~= "table" then
1264 error(rv[2], 0)
1265 elseif rv[2][1] == "lua" then
1266 return {type="boolean", val=false}, {type="string", val=(rv[2].lualoc or "")..rv[2][2]}
1267 else
1268 error(rv[2], 0)
1269 end
1270 end
1271 end
1272
1273 env.redstone = {}
1274 for _,k in ipairs({"setBundledOutput","getBundledInput","testBundledInput","getInput","getBundledOutput","getOutput","setOutput"}) do
1275 env.redstone[k] = wrapFunc(redstone[k])
1276 end
1277
1278--[[
1279rednet = TODO ??????
1280coroutine = TODO NATIVE
1281 status = status
1282 resume = resume
1283 create = create
1284 yield = yield
1285 wrap = wrap
1286 running = running
1287string
1288 gsub = TODO NATIVE
1289]]
1290
1291 function env.tonumber(a)
1292 if a == nil or a.type ~= "string" then return wrapObj(nil) end
1293 return wrapObj(tonumber(a.val))
1294 end
1295
1296 function env.tostring(a)
1297 if a == nil or a.type == "nil" then return wrapObj("nil") end
1298 local rv = callmetamethod(a, "__tostring", {a}, wrapObj(tostring(a.val)))
1299 return rv
1300 end
1301
1302 function env.read(ch)
1303 return wrapObj(read(unwrapObj(ch)))
1304 end
1305
1306 function env.print(...)
1307 local s = ""
1308 for k,v in ipairs({...}) do
1309 s = s..tostring(v.val)
1310 end
1311 log(s)
1312 return wrapObj(print(s))
1313 end
1314
1315 function env.table.insert(tbl, pos, val)
1316 if tbl == nil then error({"lua", "expected table, got nil"}) end
1317 if tbl.type ~= "table" then error({"lua", "expected table, got "..tbl.type}) end
1318 if val ~= nil then
1319 if pos.type ~= "number" then
1320 error({"lua", "expected number, got "..pos.type})
1321 end
1322 pos = unwrapObj(pos)
1323 else
1324 val, pos = pos, nil
1325 end
1326 tbl.val.number = tbl.val.number or {}
1327 table.insert(tbl.val.number, val)
1328 return {type="nil"}
1329 end
1330
1331 function env.table.remove(tbl, pos)
1332 pos = checkType(pos, "number", true)
1333 checkType(tbl, "table")
1334 table.remove(tbl.val.number, pos)
1335 return {type="nil"}
1336 end
1337
1338 function env.next(tbl, key)
1339 if tbl == nil then tbl = {type="nil"} end
1340 if key == nil then key = {type="nil"} end
1341 if tbl.type == nil then error({"interp", "corrupt table"}, 2) end
1342 if tbl.type ~= "table" then error({"lua", "table expected, got "..tbl.type}) end
1343 if key.type == "nil" then
1344 for k,v in pairs(tbl.val) do
1345 for k2,v2 in pairs(v) do
1346 return {type=k,val=k2}, v2
1347 end
1348 end
1349 return {type="nil"}
1350 end
1351 if tbl.val[key.type] == nil then error({"lua", "invalid key to next"}) end
1352
1353 -- try next key of the same type
1354 local r = next(tbl.val[key.type], key.val)
1355 if r ~= nil then
1356 return {type=key.type,val=r}, tbl.val[key.type][r]
1357 end
1358
1359 -- try next type
1360 local nt = next(tbl.val, key.type)
1361 if nt == nil then return {type="nil"},{type="nil"} end
1362
1363 -- get first key of next type, or nil if none left
1364 local r = next(tbl.val[nt])
1365 if r == nil then return {type="nil"},{type="nil"} end
1366 return {type=nt, val=r}
1367 end
1368
1369 function env.type(o)
1370 return {type="string", val=(o and o.type or "nil")}
1371 end
1372
1373 -- converts a native object into a virtualized object
1374 -- does not wrap functions
1375 -- eg: wrap({"test", hi=5}) -> {type="table", val={string={hi={type="number",val=5}}, number={1={type="string",val="test"}}}}
1376 local function wrap(o, n)
1377 if type(o) == "table" then
1378 local t = {type="table", val={}}
1379 for k,v in pairs(o) do
1380 interp_rawset(t, wrap(k), wrap(v, n.."."..k))
1381 end
1382 return t
1383 else
1384 return {type=type(o), val=o, name=n}
1385 end
1386 end
1387
1388 local new = wrap(env, "_G")
1389 for k,v in pairs(new) do
1390 DEFAULT_GLOBAL_ENV[k] = v
1391 end
1392 interp_rawset(DEFAULT_GLOBAL_ENV, {type="string", val="_G"}, DEFAULT_GLOBAL_ENV)
1393 end
1394
1395 local function logicalNot(e)
1396 return {type="boolean", val=not isTrue(e)}
1397 end
1398
1399 -- example: makeArithFunction(function(a,b) return a+b end, "__add")
1400 local function makeArithFunction(direct, meta)
1401 return function(t)
1402 local a = expr(t[2])[1]
1403 local b = expr(t[3])[1]
1404 if (a.type == "string" or a.type == "number") and (b.type == "string" or b.type == "number") then
1405 return {type="number", val=direct(a.val,b.val)}
1406 end
1407 local mm = getbinhandler(a, b, meta)
1408 return mm and callfunc(mm, {a, b}) or error({"lua", "attempt to "..meta.." "..a.type.." with "..b.type})
1409 end
1410 end
1411
1412 expr = {
1413 tcon = function(t)
1414 local r = {number={}}
1415 local rv = {type="table", val=r}
1416 local auto = 1
1417 for k,v in ipairs(t[2]) do
1418 if v[1] == "auto" then
1419 for k2,v2 in ipairs(expr(v[2])) do
1420 if v2 ~= nil and v2.type ~= "nil" then
1421 r.number[auto] = v2
1422 auto = auto + 1
1423 end
1424 end
1425 else
1426 -- name=value or [expr]=value
1427 local k2 = expr(v[1])[1]
1428 r[k2.type] = r[k2.type] or {}
1429 r[k2.type][k2.val] = expr(v[2])[1]
1430 end
1431 end
1432 return rv
1433 end,
1434 con = function(t)
1435 --print(tostring(t[2]))
1436 return {type=type(t[2]), val=t[2]}
1437 end,
1438 quote = function(t) -- never generated by the parser
1439 return t[2]
1440 end,
1441 ["[]"] = function(t)
1442 local t2 = expr(t[2])[1]
1443 local i = expr(t[3])[1]
1444 local rv = getInTable(t2, i)
1445 return rv
1446 end,
1447 ["."] = function(t)
1448 local t2 = expr(t[2])[1]
1449 local rv = getInTable(t2, {type="string",val=t[3]})
1450 return rv
1451 end,
1452 var = function(t)
1453 --print("var "..t[2])
1454 return getValue(t[2]) or {type="nil"}
1455 end,
1456 call = function(t)
1457 local fn = expr(t[2])[1]
1458 local args = explist(t[3])
1459 local rv = {callfunc(fn, args)}
1460 return unpack(rv)
1461 end,
1462 ["=="] = function(t)
1463 local a = expr(t[2])[1]
1464 local b = expr(t[3])[1]
1465 local rv = true
1466 if a == b then
1467 rv=true
1468 elseif a.type ~= b.type then
1469 rv=false
1470 elseif a.val == b.val then
1471 rv=true
1472 else
1473 local ch = getcomphandler(a, b, "__eq")
1474 if ch == nil then
1475 rv = false
1476 else
1477 return callfunc(ch, {a, b})
1478 end
1479 end
1480 return {type="boolean",val=rv}
1481 end,
1482 ["<="] = function(t)
1483 local a = expr(t[2])[1]
1484 local b = expr(t[3])[1]
1485 if a.type == "number" and b.type == "number" then
1486 return {type="boolean", val=(a.val <= b.val)}
1487 elseif a.type == "string" and b.type == "string" then
1488 return {type="boolean", val=(a.val <= b.val)}
1489 else
1490 local f = getcomphandler(a, b, "__le")
1491 if f ~= nil then
1492 return callfunc(f, {a, b})
1493 else
1494 f = getcomphandler(a, b, "__lt")
1495 if f ~= nil then
1496 return logicalNot(callfunc(f, {b, a}))
1497 else
1498 error({"lua", "attempt to compare "..a.type.." and "..b.type})
1499 end
1500 end
1501 end
1502 end,
1503 ["<"] = function(t)
1504 return logicalNot(expr({"<=", t[3], t[2]})[1])
1505 end,
1506 [">="] = function(t)
1507 return expr({"<=", t[3], t[2]})[1]
1508 end,
1509 [">"] = function(t)
1510 return expr({"<", t[3], t[2]})[1]
1511 end,
1512 ["~="] = function(t)
1513 return expr({"not", {"==", t[2], t[3]}})[1]
1514 end,
1515 ["not"] = function(t)
1516 local a = expr(t[2])[1]
1517 return {type="boolean", val=not isTrue(a)}
1518 end,
1519 ["and"] = function(t)
1520 local a = expr(t[2])[1]
1521 if not isTrue(a) then
1522 return a
1523 else
1524 local b = expr(t[3])[1]
1525 return b
1526 end
1527 end,
1528 ["or"] = function(t)
1529 local a = expr(t[2])[1]
1530 if isTrue(a) then
1531 return a
1532 else
1533 local b = expr(t[3])[1]
1534 return b
1535 end
1536 end,
1537 [".."] = function(t)
1538 local a = expr(t[2])[1]
1539 local b = expr(t[3])[1]
1540 if (a.type == "string" or a.type == "number") and (b.type == "string" or b.type == "number") then
1541 return {type="string", val=tostring(a.val)..tostring(b.val)}
1542 end
1543 local mm = getbinhandler(a, b, "__concat")
1544 return mm and callfunc(mm, {a, b}) or error({"lua", "attempt to concatenate "..a.type.." with "..b.type})
1545 end,
1546 ["+"] = makeArithFunction(function(a,b) return a+b end, "__add"),
1547 ["-"] = makeArithFunction(function(a,b) return a-b end, "__sub"),
1548 ["*"] = makeArithFunction(function(a,b) return a*b end, "__mul"),
1549 ["^"] = makeArithFunction(function(a,b) return a^b end, "__pow"),
1550 ["/"] = makeArithFunction(function(a,b) return a/b end, "__div"),
1551 ["%"] = makeArithFunction(function(a,b) return a%b end, "__mod"),
1552 negate = function(t)
1553 local a = expr(t[2])[1]
1554 if a.type == "number" then
1555 return {type="number",val=-a.val}
1556 end
1557 local mm = getmetamethod(a, "__unm")
1558 if mm == nil then
1559 error({"lua", "attempt to negate "..a.type})
1560 else
1561 return callfunc(mm, {a})
1562 end
1563 end,
1564 ["#"] = function(t)
1565 local e = expr(t[2])[1]
1566 if e.type == "string" then
1567 return {type="number", val=#e.val}
1568 elseif e.type == "table" then
1569 local default = (e.val.number and #e.val.number or 0)
1570 return callmetamethod(e, "__len", {e}, {type="number",val=default})
1571 else
1572 error({"lua", "attempt to get length of "..e.type})
1573 end
1574 end,
1575 ["()"] = function(t)
1576 return expr(t[2])[1]
1577 end,
1578 func = function(t)
1579 return funcdef(t[2])
1580 end,
1581 ["..."] = function()
1582 if #scope.dotdotdot == 0 then return {type="nil"} end
1583 return unpack(scope.dotdotdot)
1584 end
1585 }
1586 setmetatable(expr, {__call = function(t, a)
1587 scope.context = a.context or scope.context
1588 local et = a[1]
1589 assert(type(et) == "string", "expected string, got "..type(et), 2)
1590 --print(et)
1591 local f = t[et]
1592 if f == nil then error("unimplemented expression type: " .. et) end
1593 --log("begin "..et)
1594 local rv = {f(a)}
1595 --log("end "..et)
1596 yield()
1597 assert(#rv > 0, "Expression type " .. et .. " returned no values")
1598 for k,v in ipairs(rv) do
1599 assert(type(v) == "table", "Expression type " .. et .. " returned " .. type(v) .. " value")
1600 end
1601 return rv
1602 end})
1603 function funcdef(t)
1604 pushScope()
1605 local f = {}
1606 f.argnames = t[1]
1607 f.body = t[2]
1608 f.scope = scope
1609 local fval = {type="function",val=f}
1610 f.scope.func = fval
1611 popScope()
1612 return fval
1613 end
1614
1615 -- holds the return value when throwing a "return" error
1616 local retval
1617
1618 -- calls block(code), catching "break" errors and returning normally
1619 -- return value: true if loop was broken, otherwise false
1620 local function catchBreak(code)
1621 local sh = #scopeStack
1622 local ok, status = pcall(block, code)
1623 if ok then
1624 if #scopeStack ~= sh then error({"interp", "Scope stack imbalance after loop!"}) end
1625 return false
1626 end
1627 while #scopeStack > sh do popScope() end
1628 if status == "break" then return true end
1629 error(status, 0)
1630 end
1631
1632 stmt = {
1633 ["local"] = function(t)
1634 local names = t[2]
1635 if names[1] == "BIOS_CODE" then print(textutils.serialize(t[3])) end
1636 local vals = explist(t[3] or {})
1637 for k=1,#names do
1638 addLocal(names[k], vals[k] or {type="nil"})
1639 end
1640 end,
1641 lfunc = function(t)
1642 --log("LFUNC "..t[2])
1643 addLocal(t[2], funcdef(t[3]))
1644 end,
1645 funcst = function(t)
1646 setValue(t[2], funcdef(t[3]))
1647 end,
1648 call = function(t)
1649 expr(t)
1650 end,
1651 ["return"] = function(t)
1652 retval = explist(t[2])
1653 if #retval == 0 then retval = {{type="nil"}} end
1654 error("return", -1)
1655 end,
1656 ["="] = function(t)
1657 local names = t[2]
1658 local values = explist(t[3])
1659 scope.context = t.context or scope.context
1660 for k=1,#names do
1661 --log("assign "..tostring(names[k]))
1662 setValue(names[k], values[k] or {type="nil"})
1663 end
1664 end,
1665 ["if"] = function(t)
1666 -- "if" {cond block} [block]
1667 --log("tryExit is "..getValue("tryExit").type)
1668 for k = 2, #t-1, 2 do
1669 --print(k)
1670 local test = expr(t[k])[1]
1671 --print(test.type, " ", tostring(test.val))
1672 if isTrue(test) then
1673 block(t[k+1])
1674 return
1675 end
1676 end
1677 if ((#t)%2) == 0 then
1678 block(t[#t])
1679 end
1680 --print(getValue("tryExit").type)
1681
1682 end,
1683
1684 -- generic for
1685 gfor = function(t)
1686 local el = explist(t[3]) -- expression list
1687 local names = t[2] -- name list
1688 local f, s, var = el[1], el[2], el[3]
1689 f = f or {type="nil"}
1690 s = s or {type="nil"}
1691 var = var or {type="nil"}
1692 while true do
1693 local vals = {callfunc(f, {s, var})}
1694 var = vals[1]
1695 if var == nil or var.type == "nil" then break end
1696
1697 -- reset scope and assign local variables
1698 pushScope()
1699 -- assign local variables
1700 for k=1,#names do
1701 addLocal(names[k], vals[k] or {type="nil"})
1702 end
1703
1704 if catchBreak(t[4]) then popScope() break end
1705
1706 popScope()
1707 end
1708 end,
1709
1710 nfor = function(t)
1711 local name = t[2]
1712 local start = expr(t[3])[1]
1713 local stop = expr(t[4])[1]
1714 local step = expr(t[5])[1]
1715 if start.type ~= "number" or stop.type ~= "number" or step.type ~= "number" then
1716 error({"lua","expected number"})
1717 end
1718 for var=start.val,stop.val,step.val do
1719 pushScope()
1720 addLocal(name, {type="number",val=var})
1721
1722 if catchBreak(t[6]) then popScope() break end
1723
1724 popScope()
1725 end
1726 end,
1727
1728 ["while"] = function(t)
1729 local cond = t[2]
1730 local code = t[3]
1731
1732 while isTrue(expr(cond)[1]) do
1733 pushScope()
1734-- print(getValue("tCommandHistory").type.." test")
1735 if catchBreak(code) then popScope() break end
1736 popScope()
1737 end
1738 end,
1739
1740 ["repeat"] = function(t)
1741 local code = t[2]
1742 local cond = t[3]
1743
1744 while true do
1745 pushScope()
1746 if catchBreak(code) then popScope() break end
1747 if not isTrue(expr(cond)[1]) then popScope() break end
1748 popScope()
1749 end
1750 end,
1751
1752 block = function(t)
1753 block(t[2])
1754 end,
1755
1756 ["break"] = function(t)
1757 error("break", 0)
1758 end,
1759 }
1760 -- allows us to call stmt(t) to run the statement in table t
1761 setmetatable(stmt, {__call = function(t, a)
1762 if type(a) ~= "table" then error({"interp", "expected table, got "..type(a)},2) end
1763 local st = a[1]
1764 scope.context = a.context or scope.context
1765 --log("stmt "..st)
1766 yield()
1767 if type(st) == "table" then
1768 log("Statement type is table, WTF???")
1769 for k,v in pairs(st) do log(k," ",v) end
1770 end
1771 if t[st] == nil then error("unimplemented statement type: " .. tostring(st)) end
1772
1773 local sh = #scopeStack
1774 t[st](a)
1775 if sh ~= #scopeStack then error({"interp", "scope stack imbalance after stmt "..st}) end
1776 end})
1777 function block(t)
1778 pushScope()
1779 for k,v in ipairs(t) do
1780 stmt(v)
1781 end
1782 popScope()
1783 end
1784 function callfunc(fn, args)
1785 if fn.type~="function" then error({"lua","attempt to call "..fn.type}) end
1786 local name = fn.name
1787 fn = fn.val
1788 if type(fn)=="function" then
1789 -- "C" function (actually written in Lua; but a native function)
1790 local rv = {pcall(fn, unpack(args))}
1791 if not table.remove(rv, 1) then
1792 if type(rv[1]) == "string" then
1793 error({"lua", "Native function error in "..tostring(name)..": "..tostring(rv[1])}, 2)
1794 else
1795 error(rv[1], 0)
1796 end
1797 end
1798 if #rv == 0 then
1799 error({"interp","Native function "..tostring(name).." returned no values"})
1800 end
1801 for k,v in ipairs(rv) do
1802 if type(v) ~= "table" then
1803 error({"interp","Native function "..tostring(name).." returned "..type(v).." value"})
1804 end
1805 end
1806 return unpack(rv)
1807 end
1808 pushScope(fn.scope)
1809 if fn.scope.env.string and fn.scope.env.string.shell then
1810 print(getInTable(fn.scope.env,"shell").type)
1811 end
1812 pushScope()
1813 scope.allArgs = {}
1814 for k,v in pairs(args) do
1815 scope.allArgs[k]=v
1816 end
1817 assert(type(fn.argnames)=="table", "corrupt function",2)
1818 for k,v in ipairs(fn.argnames) do
1819 if v == "..." then
1820 scope.dotdotdot = {}
1821 for n=k,#args do
1822 scope.dotdotdot[n-k+1] = args[n]
1823 end
1824 else
1825 addLocal(v, args[k] or {type="nil"})
1826 end
1827 end
1828
1829 -- run code, catching returns and breaks
1830 local nScopes = #scopeStack
1831 local ok, status = pcall(block, fn.body)
1832 if not ok then
1833 while #scopeStack > nScopes do
1834 popScope()
1835 end
1836 end
1837 if #scopeStack ~= nScopes then
1838 error({"interp", "Scope stack imbalance after function call!"})
1839 end
1840 popScope()
1841 popScope()
1842 if not ok then
1843 if status == "return" then
1844 return unpack(retval)
1845 elseif status == "break" then
1846 error({"lua", "break outside of loop"})
1847 else
1848 error(status, -1)
1849 end
1850 end
1851
1852 return {type="nil"}
1853 end
1854 for _,ast in ipairs(ast_list) do
1855 local ok, err = pcall(block, ast)
1856 if not ok then
1857 if err == "break" then
1858 err = {"lua", "break outside of loop"}
1859 end
1860 if err == "return" then
1861 elseif type(err) == "string" then
1862 if logfile then logfile.close() end
1863 print(context)
1864 oldError(err, 0)
1865 elseif type(err) == "table" then
1866 if err[1] == "lua" then
1867 print("Lua error: "..err)
1868 else
1869 print(context)
1870 print(err.location.."Internal error: " .. table.concat(err, ": "))
1871 end
1872 break
1873 elseif err == nil then -- terminated
1874 print("Terminating LiL interpreter")
1875 else
1876 print("Caught a "..type(err).." error???")
1877 break
1878 end
1879 end
1880 end
1881 if logfile then logfile.close() end
1882end
1883
1884local function serializeInt(i)
1885 local s = ""
1886 repeat
1887 s = s .. string.char((i % 128) + ((i >= 128) and 128 or 0))
1888 i = math.floor(i / 128)
1889 until i == 0
1890 return s
1891end
1892-- returns int, next position
1893local function deserializeInt(s,pos)
1894 local k = pos
1895 local i = 0
1896 local m = 1
1897 while true do
1898 local b = string.byte(s:sub(k,k))
1899 i = i + m * (b % 128)
1900 m = m * 128
1901 k = k + 1
1902 if b < 128 then break end
1903 end
1904 return i, k
1905end
1906
1907local nextid_key = {}
1908local function serializeInternal(obj, seen)
1909 if obj ~= nil and seen[obj] then
1910 return "\06" .. serializeInt(seen[obj])
1911 end
1912 if type(obj) == "table" then
1913 local id = seen[nextid_key]
1914 seen[nextid_key] = id + 1
1915 seen[obj] = id
1916
1917 local s = "\05"
1918 local ikeys = {}
1919 for k,v in ipairs(obj) do
1920 ikeys[k] = v
1921 s = s .. serializeInternal(v, seen)
1922 end
1923 s = s .. serializeInternal(nil, seen)
1924 for k,v in pairs(obj) do
1925 if ikeys[k] == nil then
1926 s = s .. serializeInternal(k, seen) .. serializeInternal(v, seen)
1927 end
1928 end
1929 s = s .. serializeInternal(nil, seen)
1930 return s
1931 elseif type(obj) == "number" then
1932 local ns = tostring(obj)
1933 return "\04" .. serializeInt(ns:len()) .. ns
1934 elseif type(obj) == "string" then
1935 return "\03" .. serializeInt(obj:len()) .. obj
1936 elseif type(obj) == "boolean" then
1937 if obj then
1938 return "\01"
1939 else
1940 return "\x02"
1941 end
1942 elseif type(obj) == "nil" then
1943 return "\00"
1944 elseif type(obj) == "userdata" then
1945 error("cannot serialize userdata")
1946 elseif type(obj) == "thread" then
1947 error("cannot serialize threads")
1948 elseif type(obj) == "function" then
1949 error("cannot serialize functions")
1950 else
1951 error("unknown type: " .. type(obj))
1952 end
1953end
1954function serialize(obj)
1955 return serializeInternal(obj, {[nextid_key] = 0})
1956end
1957function deserialize(s)
1958 local pos = 1
1959 local seen = {}
1960 local nextid = 0
1961 local function internal()
1962 local tch = s:sub(pos,pos)
1963 local len
1964 pos = pos + 1
1965 if tch == "\00" then
1966 return nil
1967 elseif tch == "\01" then
1968 return true
1969 elseif tch == "\02" then
1970 return false
1971 elseif tch == "\03" then
1972 len, pos = deserializeInt(s, pos)
1973 local rv = s:sub(pos, pos+len-1)
1974 pos = pos + len
1975 return rv
1976 elseif tch == "\04" then
1977 len, pos = deserializeInt(s, pos)
1978 local rv = s:sub(pos, pos+len-1)
1979 pos = pos + len
1980 return tonumber(rv)
1981 elseif tch == "\05" then
1982 local id = nextid
1983 nextid = id + 1
1984 local t = {}
1985 seen[id] = t
1986
1987 local k = 1
1988 while true do
1989 local v = internal()
1990 if v == nil then break end
1991 t[k] = v
1992 k = k + 1
1993 end
1994
1995 while true do
1996 local k = internal()
1997 if k == nil then break end
1998 local v = internal()
1999 if v == nil then break end
2000 t[k] = v
2001 end
2002 return t
2003 elseif tch == "\06" then
2004 local id
2005 id, pos = deserializeInt(s, pos)
2006 return seen[id]
2007 else
2008 return nil
2009 end
2010 end
2011 return internal()
2012end
2013
2014
2015local function parseFile(filename)
2016 local file = fs.open(filename, "r")
2017 local code = file.readAll()
2018 file.close()
2019 return parse(tokenize(code))
2020end
2021
2022local function cachedAST(file, cacheFile)
2023 if fs.exists(cacheFile) then
2024 local f = fs.open(cacheFile, "r")
2025 local s = f.readAll()
2026 f.close()
2027 return deserialize(s)
2028 end
2029 local ast = parseFile(file)
2030 local f = fs.open(cacheFile, "w")
2031 f.write(serialize(ast))
2032 f.close()
2033 return ast
2034end
2035
2036local function dumpAST(code, dumpFile)
2037 local ast = parse(tokenize(code))
2038 local f = fs.open(dumpFile, "w")
2039 f.write(serialize(ast))
2040 f.close()
2041 return ast
2042end
2043
2044local BIOS_CODE = [[
2045------ Standard CC Lua functions ------
2046function loadfile(fn)
2047 local f = fs.open(fn, "r")
2048 if f == nil then return nil, "Failed to open file" end
2049 local code = f.readAll()
2050 f.close()
2051
2052 return loadstring(code, fn)
2053end
2054
2055function __inext(tbl, key)
2056 key = key + 1
2057 if tbl[key] ~= nil then
2058 return key, tbl[key]
2059 else
2060 return nil, nil
2061 end
2062end
2063
2064function ipairs(tbl)
2065 return __inext, tbl, 0
2066end
2067
2068function pairs(tbl)
2069 return next, tbl, nil
2070end
2071
2072function assert(expr, message)
2073 if not expr then
2074 error(message, 2)
2075 else
2076 return expr
2077 end
2078end
2079
2080_VERSION = "LiL ]]..LIL_VERSION..[["
2081
2082function table.foreachi(tbl, fn)
2083 for k,v in ipairs(tbl) do
2084 -- LuaJ truncates this to one result, so emulate that
2085 local rv = fn(k, v)
2086 if rv ~= nil then
2087 return rv
2088 end
2089 end
2090end
2091
2092function table.foreach(tbl, fn)
2093 for k,v in pairs(tbl) do
2094 -- LuaJ truncates this to one result, so emulate that
2095 local rv = fn(k, v)
2096 if rv ~= nil then
2097 return rv
2098 end
2099 end
2100end
2101
2102function table.concat(tbl, sep)
2103 local s = ""
2104 for k,v in ipairs(tbl) do
2105 if k > 1 then s = s .. sep end
2106 s = s .. v
2107 end
2108 return s
2109end
2110
2111function table.maxn(tbl)
2112 local k = 1
2113 while tbl[k] ~= nil do
2114 k = k + 1
2115 end
2116 return k - 1
2117end
2118
2119function table.getn(tbl)
2120 return tbl.n or table.maxn(tbl)
2121end
2122
2123-- I'm sure all these table constructors are horrible for performance.
2124-- Especially inside LiL.
2125-- Oh well. (TODO: Make table.sort a native function)
2126local function mergesort(tbl, start, _end, comp)
2127 if start == _end then return {tbl[start]} end
2128 if start == _end - 1 then
2129 if not comp(tbl[start], tbl[_end]) then
2130 return {tbl[_end], tbl[start]}
2131 else
2132 return {tbl[start], tbl[_end]}
2133 end
2134 end
2135 local midp = math.floor((_end + start)/2)
2136 local left = mergesort(tbl, start, midp, comp)
2137 local right = mergesort(tbl, midp + 1, _end, comp)
2138
2139 -- merge left and right
2140 local result = {}
2141 while #left > 0 and #right > 0 do
2142 if not comp(left[1], right[1]) then
2143 table.insert(result, table.remove(right, 1))
2144 else
2145 table.insert(result, table.remove(left, 1))
2146 end
2147 end
2148 for k,v in ipairs(left) do table.insert(result, v) end
2149 for k,v in ipairs(right) do table.insert(result, v) end
2150 return result
2151end
2152
2153function table.sort(tbl, comp)
2154 comp = comp or function(a, b)
2155 if a == nil then return false end
2156 if b == nil then return true end
2157 return a < b
2158 end
2159 local result = mergesort(tbl, 1, #tbl, comp)
2160 for k,v in ipairs(result) do
2161 tbl[k] = result[k]
2162 end
2163end
2164
2165
2166------ IO library ------
2167io = {}
2168function io.write(str)
2169 write(str)
2170end
2171function io.read(fmt)
2172 if fmt ~= nil and fmt ~= "*l" then error("Unsupported format") end
2173 return read()
2174end
2175function io.open(fn, mode)
2176 local f = fs.open(fn, mode)
2177 if f == nil then return nil end
2178
2179 local rv = {
2180 close = function(self)
2181 if self.bClosed then return end
2182 f.close()
2183 self.bClosed = true
2184 f = nil
2185 end,
2186 bFileHandle = true,
2187 bClosed = false
2188 }
2189
2190 if mode == "r" then
2191 rv.read = function(self, fmt)
2192 if fmt == nil or fmt == "*l" then return f.readLine() end
2193 if fmt == "*a" then return f.readAll() end
2194 error("Unsupported format")
2195 local rv = {}
2196 for _,f in ipairs({...}) do
2197 end
2198 return unpack(rv)
2199 end
2200 rv.lines = function(self)
2201 return function()
2202 local line = self:read()
2203 if line == nil then self:close() end
2204 return line
2205 end
2206 end
2207 elseif mode == "rb" then
2208 rv.read = function(self)
2209 return f.read()
2210 end
2211 elseif mode == "w" or mode == "a" or mode == "wb" or mode == "ab" then
2212 rv.write = function(self, data)
2213 return f.write(data)
2214 end
2215 else
2216 f.close()
2217 error("Unsupported mode")
2218 end
2219
2220 return rv
2221end
2222function io.type(f)
2223 if type(f) == "table" and f.bFileHandle == true then
2224 return f.bClosed and "closed file" or "file"
2225 end
2226 return nil
2227end
2228
2229
2230------ Standard CC functions ------
2231function redstone.getSides()
2232 return {"top","bottom","front","back","left","right"}
2233end
2234
2235function os.computerID()
2236 return ]]..os.computerID()..[[
2237end
2238os.getComputerID = os.computerID
2239os.computerLabel = os.getComputerLabel
2240
2241function os.version()
2242 if turtle then
2243 return "LiL ]]..LIL_VERSION..[[/TurtleOS 1.3"
2244 end
2245 return "LiL ]]..LIL_VERSION..[[/CraftOS 1.3"
2246end
2247
2248function os.pullEvent(filter)
2249 -- bios.lua limits this to 5 arguments, so we do too
2250 local evt, a, b, c, d, e = os.pullEventRaw(filter)
2251 if evt == "terminate" then
2252 print("Terminated")
2253 error()
2254 end
2255 return evt, a, b, c, d, e
2256end
2257
2258function sleep(seconds)
2259 local timer = os.startTimer(seconds)
2260 repeat
2261 local sEvent, param = os.pullEvent( "timer" )
2262 until param == timer
2263end
2264
2265-- From bios.lua
2266function write( sText )
2267 local w,h = term.getSize()
2268 local x,y = term.getCursorPos()
2269
2270 local nLinesPrinted = 0
2271 local function newLine()
2272 if y + 1 <= h then
2273 term.setCursorPos(1, y + 1)
2274 else
2275 term.scroll(1)
2276 term.setCursorPos(1, h)
2277 end
2278 x, y = term.getCursorPos()
2279 nLinesPrinted = nLinesPrinted + 1
2280 end
2281
2282 -- Print the line with proper word wrapping
2283 while string.len(sText) > 0 do
2284 local whitespace = string.match( sText, "^[ \t]+" )
2285 if whitespace then
2286 -- Print whitespace
2287 term.write( whitespace )
2288 x,y = term.getCursorPos()
2289 sText = string.sub( sText, string.len(whitespace) + 1 )
2290 end
2291
2292 local newline = string.match( sText, "^\n" )
2293 if newline then
2294 -- Print newlines
2295 newLine()
2296 sText = string.sub( sText, 2 )
2297 end
2298
2299 local text = string.match( sText, "^[^ \t\n]+" )
2300 if text then
2301 sText = string.sub( sText, string.len(text) + 1 )
2302 if string.len(text) > w then
2303 -- Print a multiline word
2304 while string.len( text ) > 0 do
2305 if x > w then
2306 newLine()
2307 end
2308 term.write( text )
2309 text = string.sub( text, (w-x) + 2 )
2310 x,y = term.getCursorPos()
2311 end
2312 else
2313 -- Print a word normally
2314 if x + string.len(text) > w then
2315 newLine()
2316 end
2317 term.write( text )
2318 x,y = term.getCursorPos()
2319 end
2320 end
2321 end
2322
2323 return nLinesPrinted
2324end
2325
2326os._LUACEPTION_LEVEL = ]]..LUACEPTION_LEVEL..[[
2327
2328function print(...)
2329 local s = ""
2330 for k,v in ipairs({...}) do
2331 s = s .. tostring(v)
2332 end
2333 return write(s .. "\n")
2334end
2335
2336-- From bios.lua
2337function read( _sReplaceChar, _tHistory )
2338 term.setCursorBlink( true )
2339
2340 local sLine = ""
2341 local nHistoryPos = nil
2342 local nPos = 0
2343 if _sReplaceChar then
2344 _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
2345 end
2346
2347 local w, h = term.getSize()
2348 local sx, sy = term.getCursorPos()
2349 local function redraw()
2350 local nScroll = 0
2351 if sx + nPos >= w then
2352 nScroll = (sx + nPos) - w
2353 end
2354
2355 term.setCursorPos( sx, sy )
2356 term.write( string.rep(" ", w - sx + 1) )
2357 term.setCursorPos( sx, sy )
2358 if _sReplaceChar then
2359 term.write( string.rep(_sReplaceChar, string.len(sLine) - nScroll) )
2360 else
2361 term.write( string.sub( sLine, nScroll + 1 ) )
2362 end
2363 term.setCursorPos( sx + nPos - nScroll, sy )
2364 end
2365
2366 while true do
2367 local sEvent, param = os.pullEvent()
2368 if sEvent == "char" then
2369 sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
2370 nPos = nPos + 1
2371 redraw()
2372
2373 elseif sEvent == "key" then
2374 if param == 28 then
2375 -- Enter
2376 break
2377
2378 elseif param == 203 then
2379 -- Left
2380 if nPos > 0 then
2381 nPos = nPos - 1
2382 redraw()
2383 end
2384
2385 elseif param == 205 then
2386 -- Right
2387 if nPos < string.len(sLine) then
2388 nPos = nPos + 1
2389 redraw()
2390 end
2391
2392 elseif param == 200 or param == 208 then
2393 -- Up or down
2394 if _tHistory then
2395 if param == 200 then
2396 -- Up
2397 if nHistoryPos == nil then
2398 if #_tHistory > 0 then
2399 nHistoryPos = #_tHistory
2400 end
2401 elseif nHistoryPos > 1 then
2402 nHistoryPos = nHistoryPos - 1
2403 end
2404 else
2405 -- Down
2406 if nHistoryPos == #_tHistory then
2407 nHistoryPos = nil
2408 elseif nHistoryPos ~= nil then
2409 nHistoryPos = nHistoryPos + 1
2410 end
2411 end
2412
2413 if nHistoryPos then
2414 sLine = _tHistory[nHistoryPos]
2415 nPos = string.len( sLine )
2416 else
2417 sLine = ""
2418 nPos = 0
2419 end
2420 redraw()
2421 end
2422 elseif param == 14 then
2423 -- Backspace
2424 if nPos > 0 then
2425 sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
2426 nPos = nPos - 1
2427 redraw()
2428 end
2429 end
2430 end
2431 end
2432
2433 term.setCursorBlink( false )
2434 term.setCursorPos( w + 1, sy )
2435 print()
2436
2437 return sLine
2438end
2439
2440dofile = function(path)
2441 local func, err = loadfile(path)
2442 if func ~= nil then
2443 setfenv(f, getfenv(2))
2444 func() -- and bios.lua doesn't return the result? we can't either for compatibility
2445 else
2446 error(err)
2447 end
2448end
2449
2450-- Install the rest of the OS api
2451function os.run( _tEnv, _sPath, ... )
2452 local tArgs = { ... }
2453 local fnFile, err = loadfile( _sPath )
2454 if fnFile then
2455 local tEnv = _tEnv
2456 setmetatable( tEnv, { __index = _G } )
2457 setfenv( fnFile, tEnv )
2458 local ok, err = pcall( function()
2459 fnFile( unpack( tArgs ) )
2460 end )
2461 if not ok then
2462 if err and err ~= "" then
2463 print( err )
2464 end
2465 return false
2466 end
2467 return true
2468 end
2469 if err and err ~= "" then
2470 print( err )
2471 end
2472 return false
2473end
2474
2475local bProtected = true
2476local function protect( _t )
2477 local meta = getmetatable( _t )
2478 if meta == "Protected" then
2479 -- already protected
2480 return
2481 end
2482
2483 setmetatable( _t, {
2484 __newindex = function( t, k, v )
2485 if bProtected then
2486 error( "Attempt to write to global" )
2487 else
2488 rawset( t, k, v )
2489 end
2490 end,
2491 __metatable = "Protected",
2492 } )
2493end
2494
2495local tAPIsLoading = {}
2496function os.loadAPI( _sPath )
2497 local sName = fs.getName( _sPath )
2498 if tAPIsLoading[sName] == true then
2499 print( "API "..sName.." is already being loaded" )
2500 return false
2501 end
2502 tAPIsLoading[sName] = true
2503
2504 local tEnv = {}
2505 setmetatable( tEnv, { __index = _G } )
2506 local fnAPI, err = loadfile( _sPath )
2507 if fnAPI then
2508 setfenv( fnAPI, tEnv )
2509 fnAPI()
2510 else
2511 print( err )
2512 return false
2513 end
2514
2515 local tAPI = {}
2516 for k,v in pairs( tEnv ) do
2517 tAPI[k] = v
2518 end
2519 protect( tAPI )
2520
2521 bProtected = false
2522 _G[sName] = tAPI
2523 bProtected = true
2524
2525 tAPIsLoading[sName] = nil
2526 return true
2527end
2528
2529function os.unloadAPI( _sName )
2530 if _sName ~= "_G" and type(_G[_sName] == "table") then
2531 bProtected = false
2532 _G[sName] = nil
2533 bProtected = true
2534 end
2535end
2536
2537os.sleep = sleep
2538
2539-- Install the lua part of the HTTP api (if enabled)
2540if http then
2541 http.get = function( _url )
2542 local requestID = http.request( _url )
2543 while true do
2544 local event, param1, param2 = os.pullEvent()
2545 if event == "http_success" and param1 == _url then
2546 return param2
2547 elseif event == "http_failure" and param1 == _url then
2548 return nil
2549 end
2550 end
2551 end
2552end
2553
2554-- Install the lua part of the peripheral api
2555peripheral.wrap = function( _sSide )
2556 if peripheral.isPresent( _sSide ) then
2557 local tMethods = peripheral.getMethods( _sSide )
2558 local tResult = {}
2559 for n,sMethod in ipairs( tMethods ) do
2560 tResult[sMethod] = function( ... )
2561 return peripheral.call( _sSide, sMethod, ... )
2562 end
2563 end
2564 return tResult
2565 end
2566 return nil
2567end
2568
2569-- Protect the global table against modifications
2570protect( _G )
2571for k,v in pairs( _G ) do
2572 if type(v) == "table" then
2573 protect( v )
2574 end
2575end
2576
2577-- Load APIs
2578local tApis = fs.list( "rom/apis" )
2579for n,sFile in ipairs( tApis ) do
2580 if string.sub( sFile, 1, 1 ) ~= "." then
2581 local sPath = fs.combine( "rom/apis", sFile )
2582 if not fs.isDir( sPath ) and (sFile ~= "parallel" or coroutine) then
2583 print("Loading API "..sFile)
2584 os.loadAPI( sPath )
2585 end
2586 end
2587end
2588
2589-- don't allow programs to shut down or reboot the computer
2590os.shutdown = function() end
2591os.reboot = function() end
2592
2593if turtle then
2594 local tApis = fs.list( "rom/apis/turtle" )
2595 for n,sFile in ipairs( tApis ) do
2596 if string.sub( sFile, 1, 1 ) ~= "." then
2597 local sPath = fs.combine( "rom/apis/turtle", sFile )
2598 if not fs.isDir( sPath ) then
2599 print("Loading API "..sFile)
2600 os.loadAPI( sPath )
2601 end
2602 end
2603 end
2604end
2605]]
2606
2607local MAIN_CODE = [[
2608local ok, err = pcall(os.run, {}, "rom/programs/shell")
2609if not ok then print(err) end
2610]]
2611
2612print(">>> LUACEPTION LEVEL "..(LUACEPTION_LEVEL))
2613
2614local astList = {
2615 dumpAST(BIOS_CODE, "bios.ast.txt"),
2616 --cachedAST("lil", "lil.ast.txt"),
2617 parse(tokenize(MAIN_CODE)),
2618 --cachedAST("/rom/programs/computer/adventure", "ast.txt")
2619}
2620
2621local status, err = pcall(interpret, astList)
2622if not status and type(err) == "table" then
2623 print(err[1], ": ", err[2])
2624else
2625 print(err)
2626end
2627
2628print("<<< LUACEPTION LEVEL "..(LUACEPTION_LEVEL - 1))