melzneni

Turti Core

Aug 14th, 2023 (edited)
2,177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 106.68 KB | None | 0 0
  1. local RAW_ARGS = { ... }
  2.  
  3. local TYPE_FUNCTION = 1
  4. local TYPE_VALUE = 2
  5. local TYPE_VAR = 3
  6. local TYPE_USER_FUNCTION = 4
  7. local TYPE_DEFAULT_FUNCTION = 5
  8. local TYPE_ARR = 6
  9. local TYPE_CONSTANT = 9
  10. local TYPE_DECLARATION = 10
  11. local TYPE_CMP = 11
  12. local TYPE_CONTROL_FLOW = 12
  13. local TYPE_OR = 13
  14. local TYPE_AND = 15
  15. local TYPE_POLY_DECLARATION = 16
  16. local TYPE_UNARY_OP_STATEMENT = 17
  17. local TYPE_PARENTHESES = 20
  18. local TYPE_ARITHMETIC = 21
  19. local TYPE_UNARY_OP = 18
  20. local TYPE_ARR_INDEX = 22
  21. local TYPE_ARR_INDEX_DECLARATION = 24
  22. local TYPE_HOOK_FUNCTION = 25
  23. local TYPE_ATTRIBUTE = 26
  24. local TYPE_METHOD = 27
  25. local TYPE_TABLE = 28
  26. local TYPE_NON_CRITICAL_SECTION = 29
  27. local TYPE_CAST = 30
  28. local TYPE_FUNCTION_HANDLE = 31
  29. local TYPE_EXCLUSIVE_SECTION = 32
  30.  
  31. local TYPE_CF_WHILE = 1
  32. local TYPE_CF_IF = 2
  33. local TYPE_CF_FOR = 3
  34.  
  35. local coreStorage
  36. local persistentStorage
  37.  
  38. local defaultFunctions = {}
  39. local fctsSetStorageData = {}
  40. local fctsSetPersistentStorageData = {}
  41. local libraryMethodHooks = {}
  42. local initToContinue
  43. local test
  44. local errInfo
  45. local methods
  46. local hookedLibraries = {}
  47. local threads = {}
  48. local nonCriticalSectionCount = 0
  49. local fOut, fProfile, fDebug
  50. local turtiRunning = false
  51. local tTurtiStart
  52. local CLASS_PROTOTYPES = {}
  53.  
  54. ThreadManager = {
  55.     queuedThreads = {},
  56.     runningThreads = {},
  57.     privilegedThread = nil,
  58.     privilegedThreadCount = 0,
  59.     currentThread = nil
  60. }
  61.  
  62. function ThreadManager.run()
  63.     local eventData = { n = 0 }
  64.     while true do
  65.         for i = 1, #ThreadManager.runningThreads do
  66.             local thread = ThreadManager.runningThreads[i]
  67.             local first = true
  68.             while #thread.eventQueue > 0 or first do
  69.                 if ThreadManager.privilegedThread == nil or ThreadManager.privilegedThread == thread then
  70.                     local event
  71.                     if first then
  72.                         first = false
  73.                         if #thread.eventQueue > 0 then
  74.                             event = table.remove(thread.eventQueue)
  75.                         else
  76.                             event = { nil }
  77.                         end
  78.                     else
  79.                         event = table.remove(thread.eventQueue)
  80.                     end
  81.                     if not thread.eventFilter or thread.eventFilter == event[1] or event[1] == "terminate" then
  82.                         ThreadManager.currentThread = thread
  83.                         local ok, ret = coroutine.resume(thread.coroutine, table.unpack(event, 1, event.n))
  84.                         ThreadManager.currentThread = nil
  85.                         if not ok then
  86.                             error(ret, 0)
  87.                         else
  88.                             thread.eventFilter = ret
  89.                         end
  90.                     end
  91.                 else
  92.                     break
  93.                 end
  94.                 if coroutine.status(thread.coroutine) == "dead" then
  95.                     break
  96.                 end
  97.             end
  98.         end
  99.         local i = 1
  100.         while i <= #ThreadManager.runningThreads do
  101.             local thread = ThreadManager.runningThreads[i]
  102.             if coroutine.status(thread.coroutine) == "dead" or thread.terminate then
  103.                 table.remove(ThreadManager.runningThreads, i)
  104.             else
  105.                 i = i + 1
  106.             end
  107.         end
  108.         local addedThread = false
  109.         while true do
  110.             local thread = table.remove(ThreadManager.queuedThreads)
  111.             if not thread then
  112.                 break
  113.             end
  114.             addedThread = true
  115.             thread.coroutine = coroutine.create(thread.fct)
  116.             thread.eventFilter = nil
  117.             table.insert(ThreadManager.runningThreads, thread)
  118.         end
  119.         if addedThread and not test then
  120.             os.queueEvent("startThread")
  121.         end
  122.         local hasNonDaemon
  123.         for _, thread in ipairs(ThreadManager.runningThreads) do
  124.             if not thread.isDaemon then
  125.                 hasNonDaemon = true
  126.                 break
  127.             end
  128.         end
  129.         if not hasNonDaemon then
  130.             return
  131.         end
  132.  
  133.         if not test then
  134.             eventData = table.pack(os.pullEventRaw())
  135.         end
  136.         if eventData[1] ~= nil then
  137.             for _, thread in ipairs(ThreadManager.runningThreads) do
  138.                 table.insert(thread.eventQueue, eventData)
  139.             end
  140.         end
  141.         if eventData[1] == "terminate" then
  142.             error("Terminated")
  143.         end
  144.     end
  145. end
  146.  
  147. function ThreadManager.terminateRunningThread()
  148.     ThreadManager.currentThread.terminate = true
  149.     yield()
  150. end
  151.  
  152. function ThreadManager.startThread(fct, isDaemon, name, storage)
  153.     table.insert(ThreadManager.queuedThreads, { fct = function()
  154.         local status, err = xpcall(fct, debug.traceback)
  155.         if not status then
  156.             error(err)
  157.         end
  158.     end, isDaemon = isDaemon, name = name, storage = storage, eventQueue = {} })
  159. end
  160.  
  161. function ThreadManager.blockExecutionOfOtherThreads()
  162.     ThreadManager.privilegedThread = ThreadManager.currentThread
  163.     ThreadManager.privilegedThreadCount = ThreadManager.privilegedThreadCount + 1
  164.     if ThreadManager.currentThread then
  165.         printDebug("block all threads except ", ThreadManager.currentThread.name)
  166.     end
  167. end
  168.  
  169. function ThreadManager.unblockExecutionOfOtherThreads()
  170.     ThreadManager.privilegedThreadCount = ThreadManager.privilegedThreadCount - 1
  171.     if ThreadManager.privilegedThreadCount < 1 then
  172.         ThreadManager.privilegedThread = nil
  173.         ThreadManager.privilegedThreadCount = 0
  174.         printDebug("unblock threads")
  175.     end
  176. end
  177.  
  178. local TURTI_THREAD_CLASS_PROTOTYPE = {
  179.     key = "thread",
  180.     getters = {
  181.         name = function(tbl, context)
  182.             return context.name
  183.         end,
  184.         isDaemon = function(tbl, context)
  185.             return context.isDaemon
  186.         end
  187.     },
  188.     init = function(obj, name, isDaemon)
  189.         return { name = name, isDaemon = isDaemon }
  190.     end
  191. }
  192.  
  193. defaultFunctions.getThread = {
  194.     type = TYPE_DEFAULT_FUNCTION,
  195.     pars = { 0 },
  196.     nonCritical = true,
  197.     fct = function()
  198.         local current = ThreadManager.currentThread
  199.         if not current.threadObject then
  200.             current.threadObject = instantiateClass("thread",
  201.                     ThreadManager.currentThread.name,
  202.                     ThreadManager.currentThread.isDaemon)
  203.         end
  204.         return current.threadObject
  205.     end
  206. }
  207.  
  208. gpsAdapter = {
  209.     locate = function()
  210.         while true do
  211.             local x, y, z = gps.locate()
  212.             if x == nil then
  213.                 printPoly("unable to execute gps.locate(), retrying in 1s")
  214.                 sleep(1)
  215.             else
  216.                 return { x, y, z }
  217.             end
  218.         end
  219.     end
  220. }
  221.  
  222. function isTurtiRunning()
  223.     return turtiRunning
  224. end
  225.  
  226. local function executeClassToString(v)
  227.     local prototype = v._class_prototype
  228.     if not prototype then
  229.         printTable(v)
  230.         error("value is not an object")
  231.     end
  232.     local to_string = prototype.to_string
  233.     if not to_string then
  234.         return "<object of type '" .. prototype.key .. "'>"
  235.     end
  236.     return to_string(v, v._class_context)
  237. end
  238.  
  239. function isObject(v)
  240.     if type(v) ~= "table" then
  241.         return false
  242.     end
  243.     if v._class_prototype then
  244.         return true
  245.     end
  246.     return false
  247. end
  248.  
  249. function getClassContext(v)
  250.     return v._class_context
  251. end
  252.  
  253. function toString(v)
  254.     if isObject(v) then
  255.         return executeClassToString(v)
  256.     elseif v == nil then
  257.         return "null"
  258.     elseif type(v) == "boolean" then
  259.         if v then
  260.             return "true"
  261.         else
  262.             return "false"
  263.         end
  264.     else
  265.         return tostring(v)
  266.     end
  267. end
  268.  
  269. local TURTI_TABLE_CLASS_PROTOTYPE = {
  270.     key = "table",
  271.     to_string = function(tbl, context)
  272.         local result = "{"
  273.         local first = true
  274.         for key, value in pairs(tbl) do
  275.             if key ~= "_class_context" and key ~= "_class_prototype" then
  276.                 if first then
  277.                     first = false
  278.                 else
  279.                     result = result .. ", "
  280.                 end
  281.                 result = result .. toString(key) .. "=" .. toString(value)
  282.             end
  283.         end
  284.         return result .. "}"
  285.     end,
  286.     methods = {
  287.         remove = function(tbl, context, value)
  288.             local v = tbl[value]
  289.             if v then
  290.                 table.remove(tbl, value)
  291.                 return v
  292.             else
  293.                 return nil
  294.             end
  295.         end,
  296.         keys = function(tbl, context)
  297.             local keys = instantiateClass('list', 0)
  298.             for key, _ in pairs(tbl) do
  299.                 if key ~= "_class_context" and key ~= "_class_prototype" then
  300.                     executeClassMethod(keys, 'add', { key })
  301.                 end
  302.             end
  303.             return keys
  304.         end
  305.     },
  306.     item_getter = function(tbl, context, index)
  307.         return tbl[index]
  308.     end,
  309.     item_setter = function(tbl, context, index, value)
  310.         tbl[index] = value
  311.     end,
  312.     cast = function(value)
  313.         local object = instantiateClass("table")
  314.         for k, v in pairs(value) do
  315.             object[k] = v
  316.         end
  317.         return object
  318.     end
  319. }
  320.  
  321. local TURTI_LIST_CLASS_PROTOTYPE = {
  322.     key = "list",
  323.     to_string = function(tbl, context)
  324.         local result = "["
  325.         for i = 1, context.length do
  326.             if i ~= 1 then
  327.                 result = result .. ", "
  328.             end
  329.             result = result .. toString(tbl[i])
  330.         end
  331.         return result .. "]"
  332.     end,
  333.     methods = {
  334.         sort = function(tbl, context, key, descending)
  335.             local prototype = tbl._class_prototype
  336.             local c = tbl._class_context
  337.             tbl._class_prototype = nil
  338.             tbl._class_context = nil
  339.             local sortingFct
  340.             if key then
  341.                 sortingFct = function(a, b)
  342.                     return a[key] < b[key]
  343.                 end
  344.             else
  345.                 sortingFct = function(a, b)
  346.                     return a < b
  347.                 end
  348.             end
  349.             if descending then
  350.                 table.sort(tbl, function(a, b)
  351.                     return sortingFct(b, a)
  352.                 end)
  353.             else
  354.                 table.sort(tbl, sortingFct)
  355.             end
  356.  
  357.             tbl._class_prototype = prototype
  358.             tbl._class_context = c
  359.         end,
  360.         add = function(tbl, context, value)
  361.             context.length = context.length + 1
  362.             tbl[context.length] = value
  363.         end,
  364.         remove = function(tbl, context, value)
  365.             for i = 1, context.length do
  366.                 if tbl[i] == value then
  367.                     tbl._class_prototype.privateMethods.shiftLeft(tbl, i + 1, context.length)
  368.                     context.length = context.length - 1
  369.                     return true
  370.                 end
  371.             end
  372.             return false
  373.         end,
  374.         removeAt = function(tbl, context, index)
  375.             if index < 0 or context.length <= index then
  376.                 error("index out of range: " .. index)
  377.             end
  378.             local value = tbl[index + 1]
  379.             tbl._class_prototype.privateMethods.shiftLeft(tbl, index + 2, context.length)
  380.             context.length = context.length - 1
  381.             return value
  382.         end
  383.     },
  384.     privateMethods = {
  385.         shiftLeft = function(tbl, iStart, iEnd)
  386.             for i = iStart, iEnd do
  387.                 tbl[i - 1] = tbl[i]
  388.             end
  389.             table.remove(tbl, iEnd)
  390.         end
  391.     },
  392.     getters = {
  393.         length = function(tbl, context)
  394.             return context.length
  395.         end
  396.     },
  397.     item_getter = function(tbl, context, index)
  398.         if type(index) ~= "number" then
  399.             error("index is not a number")
  400.         end
  401.         if index < 0 or context.length <= index then
  402.             error("index out of range: " .. index)
  403.         end
  404.         return tbl[index + 1]
  405.     end,
  406.     item_setter = function(tbl, context, index, value)
  407.         if type(index) ~= "number" then
  408.             error("index is not a number")
  409.         end
  410.         if index < 0 or context.length <= index then
  411.             error("index out of range: " .. index)
  412.         end
  413.         tbl[index + 1] = value
  414.     end,
  415.     init = function(object, length)
  416.         if not length then
  417.             error("attribute 1 (length) not provided")
  418.         end
  419.         return { length = length }
  420.     end,
  421.     cast = function(value)
  422.         local len = #value
  423.         local object = instantiateClass("list", len)
  424.         for i = 1, len do
  425.             object[i] = value[i]
  426.         end
  427.         return object
  428.     end
  429. }
  430.  
  431. local TURTI_STRING_CLASS_PROTOTYPE = {
  432.     key = "string",
  433.     to_string = function(obj, context)
  434.         return context.str
  435.     end,
  436.     methods = {
  437.         find = function(tbl, context, pattern)
  438.             return context.str:find(pattern) - 1
  439.         end,
  440.         substring = function(tbl, context, ind1, ind2)
  441.             if ind2 == nil then
  442.                 ind2 = #context.str
  443.             end
  444.             return context.str:sub(ind1 + 1, ind2)
  445.         end
  446.     },
  447.     getters = {
  448.         length = function(tbl, context)
  449.             return #context.str
  450.         end
  451.     },
  452.     init = function(obj, str)
  453.         return { str = str }
  454.     end
  455. }
  456.  
  457. CLASS_PROTOTYPES[TURTI_LIST_CLASS_PROTOTYPE.key] = TURTI_LIST_CLASS_PROTOTYPE
  458. CLASS_PROTOTYPES[TURTI_TABLE_CLASS_PROTOTYPE.key] = TURTI_TABLE_CLASS_PROTOTYPE
  459. CLASS_PROTOTYPES[TURTI_STRING_CLASS_PROTOTYPE.key] = TURTI_STRING_CLASS_PROTOTYPE
  460. CLASS_PROTOTYPES[TURTI_THREAD_CLASS_PROTOTYPE.key] = TURTI_THREAD_CLASS_PROTOTYPE
  461.  
  462. local function toObject(v)
  463.     if isObject(v) then
  464.         return v;
  465.     elseif type(v) == "string" then
  466.         return instantiateClass("string", v)
  467.     else
  468.         return v;
  469.     end
  470. end
  471.  
  472. local function executeClassItemGetter(v, index)
  473.     v = toObject(v)
  474.     local prototype = v._class_prototype
  475.     if not prototype then
  476.         printTable(v)
  477.         error("value is not an object")
  478.     end
  479.     if not prototype.item_getter then
  480.         error("type '" .. prototype.key .. "' doesn't implement an item setter ")
  481.     end
  482.     return prototype.item_getter(v, v._class_context, index)
  483. end
  484.  
  485. local function executeClassItemSetter(v, index, value)
  486.     v = toObject(v)
  487.     local prototype = v["_class_prototype"]
  488.     if not prototype then
  489.         printTable(v)
  490.         error("value is not an object")
  491.     end
  492.     local item_setter = prototype["item_setter"]
  493.     if not item_setter then
  494.         error("type '" .. prototype.key .. "' doesn't implement an item setter ")
  495.     end
  496.     return item_setter(v, v._class_context, index, value)
  497. end
  498.  
  499. function executeClassMethod(v, name, pars)
  500.     v = toObject(v)
  501.     if type(v) ~= "table" then
  502.         printPoly(v)
  503.         error("value is not an object")
  504.     end
  505.     local prototype = v["_class_prototype"]
  506.     if not prototype then
  507.         printTable(v)
  508.         error("value is not an object")
  509.     end
  510.     local methods = prototype["methods"]
  511.     if not methods then
  512.         printTable(v)
  513.         error("method '" .. name .. "' is not defined on object of type " .. prototype.key)
  514.     end
  515.     local method = methods[name]
  516.     if not method then
  517.         printTable(v)
  518.         error("method '" .. name .. "' is not defined on object of type " .. prototype.key)
  519.     end
  520.     return method(v, v._class_context, table.unpack(pars))
  521. end
  522.  
  523. local function executeClassGetter(v, name)
  524.     v = toObject(v)
  525.     local prototype = v["_class_prototype"]
  526.     if not prototype then
  527.         printTable(v)
  528.         error("value is not an object")
  529.     end
  530.     local getters = prototype["getters"]
  531.     if not getters then
  532.         printTable(v)
  533.         error("attribute '" .. name .. "' is not defined on object of type " .. prototype.key)
  534.     end
  535.     local getter = getters[name]
  536.     if not getters then
  537.         printTable(v)
  538.         error("attribute '" .. name .. "' is not defined on object of type " .. prototype.key)
  539.     end
  540.     return getter(v, v._class_context)
  541. end
  542.  
  543. local function findClassPrototype(key)
  544.     local prototype = CLASS_PROTOTYPES[key]
  545.     if not prototype then
  546.         error("unknown class key: " .. key)
  547.     end
  548.     return prototype
  549. end
  550.  
  551. local function executeClassCast(v, prototypeKey)
  552.     if v == nil then
  553.         return v
  554.     end
  555.     local prototype = findClassPrototype(prototypeKey)
  556.     local cast = prototype["cast"]
  557.     if not cast then
  558.         printTable(v)
  559.         error("object type " .. prototypeKey .. " doesn't support casting")
  560.     end
  561.     return cast(v)
  562. end
  563.  
  564. function constructClass(prototypeKey, ...)
  565.     local prototype = findClassPrototype(prototypeKey)
  566.     local object = instantiateClass(prototypeKey)
  567.     prototype.construct(object, object._class_context, ...)
  568.     return object
  569. end
  570.  
  571. function instantiateClass(prototypeKey, ...)
  572.     local prototype = findClassPrototype(prototypeKey)
  573.     local object = {
  574.         _class_prototype = prototype
  575.     }
  576.     if prototype.init then
  577.         object._class_context = prototype.init(object, ...)
  578.     else
  579.         object._class_context = {}
  580.     end
  581.  
  582.     return object
  583. end
  584.  
  585. function toLuaTable(tbl)
  586.     if not isObject(tbl) then
  587.         error("value is not an object")
  588.     end
  589.     if tbl._class_prototype.key ~= TURTI_TABLE_CLASS_PROTOTYPE.key then
  590.         error("value is not a turti table")
  591.     end
  592.     local result = {}
  593.     for key, v in pairs(tbl) do
  594.         if key ~= "_class_context" and key ~= "_class_prototype" then
  595.             result[key] = v
  596.         end
  597.     end
  598.     return result
  599. end
  600.  
  601. function toTurtiTable(tbl)
  602.     local turtiTbl = instantiateClass(TURTI_TABLE_CLASS_PROTOTYPE.key)
  603.     for k, v in pairs(tbl) do
  604.         turtiTbl[k] = v
  605.     end
  606.     return turtiTbl
  607. end
  608.  
  609. function toTurtiArray(tbl)
  610.     local length = #tbl
  611.     local array = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, length)
  612.     for i = 1, #tbl do
  613.         array[i] = tbl[i]
  614.     end
  615.     return array
  616. end
  617.  
  618. function readAll(file)
  619.     local f = assert(io.open(file, "rb"))
  620.     local content = f:read("*all")
  621.     f:close()
  622.     return content
  623. end
  624.  
  625. SPLIT_TEXT_CONSIDER_STRUCTURE = 1 -- 0b001
  626. SPLIT_TEXT_CONSIDER_ESCAPES = 2 -- 0b010
  627. SPLIT_TEXT_INCLUDE_EMPTY = 4    -- 0b100
  628.  
  629. function splitTextStructural(source, letters)
  630.     return splitText(source, letters, true)
  631. end
  632.  
  633. function splitText(source, letters, considerStructure, considerEscapes, includeEmpty)
  634.     local parts = {}
  635.     local akt = "";
  636.     local qM1 = 0;
  637.     local qM2 = 0;
  638.     local b1 = 0;
  639.     local b2 = 0;
  640.     local b3 = 0;
  641.     local escaped = false
  642.     for i = 1, #source do
  643.         local z = source:sub(i, i)
  644.         if considerEscapes and escaped then
  645.             escaped = false
  646.             akt = akt .. z
  647.         else
  648.             if considerStructure then
  649.                 if z == "'" and qM2 == 0 then
  650.                     qM1 = 1 - qM1;
  651.                 elseif z == "\"" and qM1 == 0 then
  652.                     qM2 = 1 - qM2;
  653.                 elseif qM1 == 0 and qM2 == 0 then
  654.                     if z == "(" then
  655.                         b1 = b1 + 1
  656.                     elseif z == ")" then
  657.                         b1 = b1 - 1
  658.                     elseif z == "[" then
  659.                         b2 = b2 + 1
  660.                     elseif z == "]" then
  661.                         b2 = b2 - 1
  662.                     elseif z == "{" then
  663.                         b3 = b3 + 1
  664.                     elseif z == "}" then
  665.                         b3 = b3 - 1
  666.                     end
  667.                 end
  668.             end
  669.             if considerEscapes and z == "\\" then
  670.                 escaped = true;
  671.             end
  672.             local split = false
  673.             if b1 == 0 and b2 == 0 and b3 == 0 and qM1 == 0 and qM2 == 0 then
  674.                 for j = 1, #letters do
  675.                     if z == letters:sub(j, j) then
  676.                         split = true
  677.                         break
  678.                     end
  679.                 end
  680.             end
  681.             if split then
  682.                 if includeEmpty or akt ~= "" then
  683.                     table.insert(parts, akt)
  684.                     akt = ""
  685.                 end
  686.             else
  687.                 akt = akt .. z
  688.             end
  689.         end
  690.     end
  691.     if includeEmpty or akt ~= "" then
  692.         table.insert(parts, akt)
  693.     end
  694.     return parts
  695. end
  696.  
  697. function splitTextPattern(source, letters, ignoreStructure)
  698.     local parts = {}
  699.     local akt = "";
  700.     local qM1 = 0;
  701.     local qM2 = 0;
  702.     local b1 = 0;
  703.     local b2 = 0;
  704.     local b3 = 0;
  705.     local i = 1
  706.     while (i <= #source) do
  707.         local z = source:sub(i, i)
  708.         if not ignoreStructure then
  709.             if z == "'" and qM2 == 0 then
  710.                 qM1 = 1 - qM1;
  711.             elseif z == "\"" and qM1 == 0 then
  712.                 qM2 = 1 - qM2;
  713.             elseif qM1 == 0 and qM2 == 0 then
  714.                 if z == "(" then
  715.                     b1 = b1 + 1
  716.                 elseif z == ")" then
  717.                     b1 = b1 - 1
  718.                 elseif z == "[" then
  719.                     b2 = b2 + 1
  720.                 elseif z == "]" then
  721.                     b2 = b2 - 1
  722.                 elseif z == "{" then
  723.                     b3 = b3 + 1
  724.                 elseif z == "}" then
  725.                     b3 = b3 - 1
  726.                 end
  727.             end
  728.         end
  729.         local split = false
  730.         if b1 == 0 and b2 == 0 and b3 == 0 and qM1 == 0 and qM2 == 0 then
  731.             split = true
  732.             for j = 1, #letters do
  733.                 if source:sub(i + j - 1, i + j - 1) ~= letters:sub(j, j) then
  734.                     split = false
  735.                     break
  736.                 end
  737.             end
  738.         end
  739.         if split then
  740.             if akt ~= "" then
  741.                 table.insert(parts, akt)
  742.                 akt = ""
  743.                 i = i + #letters - 1
  744.             end
  745.         else
  746.             akt = akt .. z
  747.         end
  748.         i = i + 1
  749.     end
  750.     if akt ~= "" then
  751.         table.insert(parts, akt)
  752.     end
  753.     return parts
  754. end
  755.  
  756. function removeLetters(source, letters)
  757.     local target = ""
  758.     for i = 1, #source do
  759.         local valid = true
  760.         for j = 1, #letters do
  761.             if source:sub(i, i) == letters:sub(j, j) then
  762.                 valid = false
  763.             end
  764.         end
  765.         if valid then
  766.             target = target .. source:sub(i, i)
  767.         end
  768.     end
  769.     return target
  770. end
  771.  
  772. function removeLettersIgnoreStrings(source, letters)
  773.     local target = ""
  774.     local b1 = false
  775.     local b2 = false
  776.     for i = 1, #source do
  777.         local letter = source:sub(i, i)
  778.  
  779.         if letter == "'" and not b2 then
  780.             b1 = not b1
  781.         elseif letter == "\"" and not b1 then
  782.             b2 = not b2
  783.         end
  784.  
  785.         local valid = true
  786.         if not b1 and not b2 then
  787.             for j = 1, #letters do
  788.                 if letter == letters:sub(j, j) then
  789.                     valid = false
  790.                 end
  791.             end
  792.         end
  793.         if valid then
  794.             target = target .. letter
  795.         end
  796.     end
  797.     return target
  798. end
  799.  
  800. function removeValue(tbl, value)
  801.     for i, v in pairs(tbl) do
  802.         if v == value then
  803.             table.remove(tbl, i)
  804.             return
  805.         end
  806.     end
  807. end
  808.  
  809. function readFileText(file)
  810.     if test then
  811.         return readAll(file)
  812.     else
  813.         local f = fs.open(file, "r")
  814.         local text = f.readAll()
  815.         f.close()
  816.         return text
  817.     end
  818. end
  819.  
  820. function analyseCommands(source)
  821.     local cmdsSource = splitTextStructural(source, ";")
  822.     local cmds = {}
  823.     for i, v in ipairs(cmdsSource) do
  824.         if v ~= "" then
  825.             if v:sub(1, 2) ~= "//" then
  826.                 table.insert(cmds, loadCommand(v))
  827.             end
  828.         end
  829.     end
  830.     return cmds
  831. end
  832.  
  833. function nextOpenIndex(source, chClose, chOpen)
  834.     local a = 0;
  835.     local qM1 = 0;
  836.     local qM2 = 0
  837.     local len = #source
  838.     for i = 1, len do
  839.         local i1 = len - i + 1;
  840.         local z = source:sub(i1, i1)
  841.         if z == "\"" and qM2 == 0 then
  842.             qM1 = 1 - qM1
  843.         elseif z == "'" and qM1 == 0 then
  844.             qM2 = 1 - qM2
  845.         elseif z == chClose and qM1 == 0 and qM2 == 0 then
  846.             a = a + 1
  847.         elseif z == chOpen and qM1 == 0 and qM2 == 0 then
  848.             a = a - 1
  849.             if a == 0 then
  850.                 return i1
  851.             end
  852.         end
  853.     end
  854.     return -1
  855. end
  856.  
  857. function nextCloseIndex(source, chOpen, chClose)
  858.     local a = 0;
  859.     local qM1 = 0;
  860.     local qM2 = 0
  861.     for i = 1, #source do
  862.         local z = source:sub(i, i)
  863.         if z == "\"" and qM2 == 0 then
  864.             qM1 = 1 - qM1
  865.         elseif z == "'" and qM1 == 0 then
  866.             qM2 = 1 - qM2
  867.         elseif z == chOpen and qM1 == 0 and qM2 == 0 then
  868.             a = a + 1
  869.         elseif z == chClose and qM1 == 0 and qM2 == 0 then
  870.             a = a - 1
  871.             if a == 0 then
  872.                 return i
  873.             end
  874.         end
  875.     end
  876.     return -1
  877. end
  878.  
  879. function loadCommand(source)
  880.  
  881.     if source:sub(1, 1) == "(" and nextCloseIndex(source, "(", ")") == #source then
  882.         return { type = TYPE_PARENTHESES, cmd = loadCommand(source:sub(2, -2)) }
  883.     end
  884.  
  885.     if source:sub(1, 2) == "#{" then
  886.         local body = analyseCommands(source:sub(3, #source - 1))
  887.         return { type = TYPE_NON_CRITICAL_SECTION, body = body }
  888.     end
  889.     if source:sub(1, 10) == "exclusive{" then
  890.         local body = analyseCommands(source:sub(11, #source - 1))
  891.         return { type = TYPE_EXCLUSIVE_SECTION, body = body }
  892.     end
  893.  
  894.     if source:find("%(") ~= nil and source:find("){") ~= nil then
  895.         local ind = source:find("%(")
  896.         local name = source:sub(1, ind - 1)
  897.         if name == "while" then
  898.             local ind1 = source:find("){")
  899.             local body = analyseCommands(source:sub(ind1 + 2, #source - 1))
  900.             local cmdCondition = loadCommand(source:sub(ind + 1, ind1 - 1))
  901.             return { type = TYPE_CONTROL_FLOW, cond = cmdCondition, body = body, id = newCFId(), typeCF = TYPE_CF_WHILE }
  902.         elseif name == "for" then
  903.             local ind1 = source:find("){")
  904.             local body = analyseCommands(source:sub(ind1 + 2, #source - 1))
  905.             local headPts = splitTextStructural(source:sub(ind + 1, ind1 - 1), ";")
  906.             if #headPts ~= 3 then
  907.                 error("invalid for-head: " .. source:sub(ind + 1, ind1 - 1))
  908.             end
  909.             local firstCommand = loadCommand(headPts[1])
  910.             local secondCondition = loadCommand(headPts[2])
  911.             local thirdCommand = loadCommand(headPts[3])
  912.             return { type = TYPE_CONTROL_FLOW, first = firstCommand, second = secondCondition, third = thirdCommand, body = body, id = newCFId(), typeCF = TYPE_CF_FOR }
  913.         elseif name == "if" then
  914.             local pts = splitTextStructural(source, "else")
  915.             local data = { type = TYPE_CONTROL_FLOW, typeCF = TYPE_CF_IF, id = newCFId(), ifCmdBlocks = {} }
  916.  
  917.             for i = 1, #pts do
  918.                 local pt = pts[i]
  919.                 local ind = pt:find("%(")
  920.                 local ind1 = pt:find("{")
  921.                 local body = analyseCommands(pt:sub(ind1 + 1, #pt - 1))
  922.                 if ind1 == 1 then
  923.                     if i ~= #pts then
  924.                         error("'else' statement needs to be at end of 'if'-block (source: " .. source .. ")")
  925.                     end
  926.                     table.insert(data.ifCmdBlocks, { body = body })
  927.                 else
  928.                     local name = pt:sub(1, ind - 1)
  929.                     if name ~= "if" then
  930.                         error("invalid keyword: [else]" .. name)
  931.                     end
  932.                     local cond = loadCommand(pt:sub(ind + 1, ind1 - 2))
  933.                     table.insert(data.ifCmdBlocks, { body = body, cond = cond })
  934.                 end
  935.             end
  936.             return data
  937.         else
  938.             error("unknown keyword '" .. name .. "'")
  939.         end
  940.     end
  941.  
  942.     local ptsSources = splitTextPattern(source, "||")
  943.     if #ptsSources > 1 then
  944.         local values = {};
  945.         for i = 1, #ptsSources do
  946.             values[i] = loadCommand(ptsSources[i])
  947.         end
  948.         return { type = TYPE_OR, values = values }
  949.     end
  950.  
  951.     local ptsSources = splitTextPattern(source, "&&")
  952.     if #ptsSources > 1 then
  953.         local values = {};
  954.         for i = 1, #ptsSources do
  955.             values[i] = loadCommand(ptsSources[i])
  956.         end
  957.         return { type = TYPE_AND, values = values }
  958.     end
  959.  
  960.     for i, ch in ipairs({ ">=", "<=", "<", ">", "==", "!=" }) do
  961.         local ptsSources = splitTextPattern(source, ch)
  962.         if #ptsSources > 1 then
  963.             if #ptsSources > 2 then
  964.                 error("only 2 arguments allowed for '" .. ch .. "'")
  965.             end
  966.             local pts = {}
  967.             for i, v in ipairs(ptsSources) do
  968.                 table.insert(pts, loadCommand(v))
  969.             end
  970.             return { type = TYPE_CMP, ch = ch, pts = pts }
  971.         end
  972.     end
  973.  
  974.     local ptsSources = splitTextStructural(source, "=")
  975.     if #ptsSources > 1 then
  976.         local name = ptsSources[1]
  977.         if #ptsSources > 2 then
  978.             error("there is only one '=' allowed in a declaration (" .. source:sub(1, 10))
  979.         end
  980.         if name:match("%$[A-Za-z_][A-Za-z0-9_]*%$") == name or
  981.                 name:match("[A-Za-z_][A-Za-z0-9_]*") == name or
  982.                 name:match("%$[A-Za-z_][A-Za-z0-9_]*") == name then
  983.             return { type = TYPE_DECLARATION, name = name, value = loadCommand(ptsSources[2]) }
  984.         else
  985.             if name:sub(#name, #name) == "]" then
  986.                 local i = nextOpenIndex(name, "]", "[")
  987.                 if i > 1 then
  988.                     return { type = TYPE_ARR_INDEX_DECLARATION, arr = loadCommand(name:sub(1, i - 1)),
  989.                              index = loadCommand(name:sub(i + 1, -2)),
  990.                              value = loadCommand(ptsSources[2]) }
  991.                 end
  992.             end
  993.  
  994.             local nameSources = splitTextStructural(name, ",")
  995.             for i, v in ipairs(nameSources) do
  996.                 if v:match("%$[A-Za-z_][A-Za-z0-9_]*%$") ~= v and
  997.                         v:match("[A-Za-z_][A-Za-z0-9_]*") ~= v and
  998.                         v:match("%$[A-Za-z_][A-Za-z0-9_]*") ~= v then
  999.                     error("invalid declaration variable: " .. v)
  1000.                 end
  1001.             end
  1002.             return { type = TYPE_POLY_DECLARATION, names = nameSources, value = loadCommand(ptsSources[2]) }
  1003.         end
  1004.     end
  1005.  
  1006.     for _, symbol in ipairs({ "+", "-", "*", "/", "%" }) do
  1007.         local ptsSources = splitTextStructural(source, symbol)
  1008.         if #ptsSources > 1 then
  1009.             local pts = {}
  1010.             for i, v in ipairs(ptsSources) do
  1011.                 table.insert(pts, loadCommand(v))
  1012.             end
  1013.             return { type = TYPE_ARITHMETIC, pts = pts, symbol = symbol }
  1014.         end
  1015.     end
  1016.  
  1017.     local ptsSources = splitTextStructural(source, "@")
  1018.     if #ptsSources > 1 then
  1019.         local key = ptsSources[2]
  1020.         if key:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= key then
  1021.             error("invalid cast key: " .. key)
  1022.         end
  1023.         return { type = TYPE_CAST, key = key, value = loadCommand(ptsSources[1]) }
  1024.     end
  1025.  
  1026.     local ptsSources = splitTextStructural(source, ".")
  1027.     if #ptsSources > 1 then
  1028.         if not tonumber(ptsSources[1]) then
  1029.             local cmd = loadCommand(ptsSources[1])
  1030.  
  1031.             for iPt = 2, #ptsSources do
  1032.                 local pt = ptsSources[iPt]
  1033.                 local i = pt:find("%(")
  1034.                 if i == nil then
  1035.                     if pt:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= pt then
  1036.                         error("invalid attribute name: " .. pt)
  1037.                     end
  1038.                     cmd = { type = TYPE_ATTRIBUTE, cmd = cmd, name = pt }
  1039.                 else
  1040.                     local funcName = pt:sub(1, i - 1)
  1041.                     if funcName:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= funcName then
  1042.                         error("invalid function name: " .. funcName)
  1043.                     end
  1044.                     local pars = {}
  1045.                     local parSources = splitTextStructural(pt:sub(i + 1, -2), ",")
  1046.                     for _, par in ipairs(parSources) do
  1047.                         table.insert(pars, loadCommand(par))
  1048.                     end
  1049.                     cmd = { type = TYPE_METHOD, cmd = cmd, name = funcName, pars = pars }
  1050.                 end
  1051.             end
  1052.             return cmd
  1053.         end
  1054.     end
  1055.  
  1056.     local i = source:find("%(")
  1057.     if i ~= nil and nextCloseIndex(source, "(", ")") == #source then
  1058.         local funcName = source:sub(1, i - 1)
  1059.         if funcName:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= funcName then
  1060.             error("invalid function name: " .. funcName)
  1061.         end
  1062.         local pars = {}
  1063.         local parSources = splitTextStructural(source:sub(i + 1, -2), ",")
  1064.         for i, par in ipairs(parSources) do
  1065.             table.insert(pars, loadCommand(par))
  1066.         end
  1067.         return { type = TYPE_FUNCTION, name = funcName, pars = pars }
  1068.     end
  1069.     if source:sub(#source, #source) == "]" and nextOpenIndex(source, "]", "[") then
  1070.         local i = nextOpenIndex(source, "]", "[")
  1071.         if i > 1 then
  1072.             local body = loadCommand(source:sub(1, i - 1))
  1073.             local index = loadCommand(source:sub(i + 1, #source - 1))
  1074.             return { type = TYPE_ARR_INDEX, body = body, index = index }
  1075.         end
  1076.     end
  1077.     if tonumber(source) ~= nil then
  1078.         return { type = TYPE_VALUE, value = tonumber(source) }
  1079.     elseif source:match("%$[A-Za-z_][A-Za-z0-9_]*%$") == source then
  1080.         local key = source:sub(2, #source - 1)
  1081.         return { type = TYPE_CONSTANT, key = key }
  1082.     elseif source:match("%$[A-Za-z_][A-Za-z0-9_]*") == source then
  1083.         local key = source:sub(2, #source)
  1084.         return { type = TYPE_CONSTANT, key = key }
  1085.     elseif source == "true" or source == "false" then
  1086.         return { type = TYPE_VALUE, value = source == "true" }
  1087.     elseif source == "null" then
  1088.         return { type = TYPE_VALUE, value = nil }
  1089.     elseif source:match("[A-Za-z_][A-Za-z0-9_]*") == source then
  1090.         return { type = TYPE_VAR, name = source }
  1091.     elseif source:sub(1, 1) == "[" and nextCloseIndex(source, "[", "]") == #source then
  1092.         local parts = splitTextStructural(source:sub(2, #source - 1), ",")
  1093.         local vars = {}
  1094.         for i, v in ipairs(parts) do
  1095.             table.insert(vars, loadCommand(v))
  1096.         end
  1097.         return { type = TYPE_ARR, vars = vars, varCount = #vars }
  1098.     elseif source:sub(1, 1) == "{" and nextCloseIndex(source, "{", "}") == #source then
  1099.         local parts = splitTextStructural(source:sub(2, #source - 1), ",")
  1100.         local vars = {}
  1101.         local keyOrder = {}
  1102.         for _, v in ipairs(parts) do
  1103.             local pts = splitTextStructural(v, "=")
  1104.             if #pts ~= 2 then
  1105.                 error("unknown value: " .. source)
  1106.             end
  1107.             local key = loadCommand(pts[1])
  1108.             vars[key] = loadCommand(pts[2])
  1109.             table.insert(keyOrder, key)
  1110.         end
  1111.         return { type = TYPE_TABLE, vars = vars, keyOrder = keyOrder }
  1112.     elseif (source:sub(1, 1) == "\"" and source:sub(#source) == "\"") or
  1113.             source:sub(1, 1) == "'" and source:sub(#source) == "'" then
  1114.         return { type = TYPE_VALUE, value = replaceSpecialStringCharacters(source:sub(2, #source - 1)) }
  1115.     elseif source:sub(-2, -1) == "++" or source:sub(-2, -1) == "--" then
  1116.         local name = source:sub(1, -3)
  1117.         if name:match("%$[A-Za-z_][A-Za-z0-9_]*%$?") == name or
  1118.                 name:match("[A-Za-z_][A-Za-z0-9_]*") == name then
  1119.             return { type = TYPE_UNARY_OP_STATEMENT, op = source:sub(-2, -1), name = name }
  1120.         else
  1121.             error("invalid declaration variable: " .. name)
  1122.         end
  1123.     elseif source:sub(1, 2) == "++" or source:sub(1, 2) == "--" then
  1124.         local name = source:sub(3, -1)
  1125.         if name:match("%$[A-Za-z_][A-Za-z0-9_]*%$?") == name or
  1126.                 name:match("[A-Za-z_][A-Za-z0-9_]*") == name then
  1127.             return { type = TYPE_UNARY_OP_STATEMENT, op = "_" .. source:sub(1, 2), name = name }
  1128.         else
  1129.             error("invalid declaration variable: " .. name)
  1130.         end
  1131.     elseif source:sub(1, 1) == "-" then
  1132.         return { type = TYPE_UNARY_OP, op = "-", value = loadCommand(source:sub(2, -1)) }
  1133.     elseif source:sub(1, 1) == "@" then
  1134.         local name = source:sub(2)
  1135.         if source:match("[A-Za-z_][A-Za-z0-9_]*") ~= name then
  1136.             error("invalid function name: @" .. name)
  1137.         end
  1138.         return { type = TYPE_FUNCTION_HANDLE, name = name }
  1139.     else
  1140.         error("unknown value: " .. source)
  1141.     end
  1142. end
  1143.  
  1144. function replaceSpecialStringCharacters(str)
  1145.     local escaped = false
  1146.     local result = ""
  1147.     for i = 1, #str do
  1148.         local c = str:sub(i, i);
  1149.  
  1150.         if escaped then
  1151.             escaped = false
  1152.             if c == "n" then
  1153.                 result = result .. "\n"
  1154.             elseif c == "r" then
  1155.                 result = result .. "\r"
  1156.             elseif c == "t" then
  1157.                 result = result .. "\t"
  1158.             elseif c == "\\" then
  1159.                 result = result .. "\\"
  1160.             else
  1161.                 result = result .. "\\" .. c
  1162.             end
  1163.         elseif c == "\\" then
  1164.             escaped = true
  1165.         else
  1166.             result = result .. c
  1167.         end
  1168.     end
  1169.     return result
  1170. end
  1171.  
  1172. function loadValue(source)
  1173.     return loadValueFromCommand(loadCommand(source))
  1174. end
  1175.  
  1176. function loadValueFromCommand(command)
  1177.     if command.type == TYPE_ARR then
  1178.         local result = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, command.varCount)
  1179.         for i, v in ipairs(command.vars) do
  1180.             result[i] = loadValueFromCommand(v)
  1181.         end
  1182.         return result
  1183.     elseif command.type == TYPE_VALUE then
  1184.         return command.value
  1185.     else
  1186.         printTable(command)
  1187.         error("unknown command type: " .. command.type)
  1188.     end
  1189. end
  1190.  
  1191. local currentCFId = 0
  1192. function newCFId()
  1193.     local id = currentCFId
  1194.     currentCFId = currentCFId + 1
  1195.     return id
  1196. end
  1197.  
  1198. function analyseStack(source)
  1199.     local values = {}
  1200.     for i, v in ipairs(splitTextStructural(source, ",")) do
  1201.         if v ~= "" then
  1202.             table.insert(values, tonumber(v))
  1203.         end
  1204.     end
  1205.     return values
  1206. end
  1207.  
  1208. function combineArr(table1, table2)
  1209.     local table = {}
  1210.     for i = 1, #table1 do
  1211.         table[i] = table1[i]
  1212.     end
  1213.     for i = 1, #table2 do
  1214.         table[i + #table1] = table2[i]
  1215.     end
  1216.     return table
  1217. end
  1218.  
  1219. function getTableFromSaveText(source)
  1220.     local tableSources = splitText(source, ";", true, true)
  1221.     local tables = {}
  1222.     for i = 1, #tableSources do
  1223.         table.insert(tables, {})
  1224.     end
  1225.     for i, v in ipairs(tableSources) do
  1226.         local tbl = tables[i]
  1227.         getTableFromSaveTextInd(tbl, v, tables)
  1228.     end
  1229.     return tables[1]
  1230. end
  1231.  
  1232. function getTableFromSaveTextInd(tbl, tblSource, tables)
  1233.     if tblSource:sub(1, 1) ~= "{" or tblSource:sub(#tblSource) ~= "}" then
  1234.         error("invalid source (braces around table): " .. tblSource)
  1235.     end
  1236.     local pts = splitText(tblSource:sub(2, #tblSource - 1), ",", true, true)
  1237.     for i, pt in ipairs(pts) do
  1238.         local i = pt:find("=")
  1239.         local vKey = getValueFromSaveText(pt:sub(1, i - 1), tables)
  1240.         local vValue = getValueFromSaveText(pt:sub(i + 1), tables)
  1241.         if vKey == "_class_prototype" then
  1242.             vValue = findClassPrototype(vValue)
  1243.         end
  1244.         tbl[vKey] = vValue
  1245.     end
  1246. end
  1247.  
  1248. function getValueFromSaveText(source, tables)
  1249.     if source:sub(1, 1) == "\"" and source:sub(#source) == "\"" then
  1250.         return unescapeString(source:sub(2, #source - 1))
  1251.     elseif tonumber(source) ~= nil then
  1252.         return tonumber(source)
  1253.     elseif source:sub(1, 4) == "tbl:" then
  1254.         return tables[tonumber(source:sub(5))]
  1255.     elseif source == "false" then
  1256.         return false
  1257.     elseif source == "true" then
  1258.         return true
  1259.     elseif source == "nan" then
  1260.         return 0 / 0
  1261.     else
  1262.         error("invalid source (getValueAnalysis): " .. source)
  1263.     end
  1264. end
  1265.  
  1266. function getTableSaveText(sourceTable)
  1267.     local alreadyUsed = {}
  1268.     local sources = {}
  1269.     getTableSaveTextInd(sourceTable, alreadyUsed, sources)
  1270.     local txt = ""
  1271.     for _, v in ipairs(sources) do
  1272.         txt = txt .. v .. ";"
  1273.     end
  1274.     return txt
  1275. end
  1276.  
  1277. function getTableSaveTextInd(sourceTable, alreadyUsed, sources)
  1278.     local index
  1279.     for i, v in ipairs(alreadyUsed) do
  1280.         if v == sourceTable then
  1281.             index = i
  1282.             break
  1283.         end
  1284.     end
  1285.  
  1286.     if index ~= nil then
  1287.         return "tbl:" .. index
  1288.     else
  1289.         table.insert(alreadyUsed, sourceTable)
  1290.         local sourceString = ""
  1291.         local ind = #alreadyUsed
  1292.         for k, v in pairs(sourceTable) do
  1293.             if k == "_class_prototype" then
  1294.                 v = v.key
  1295.             end
  1296.             sourceString = sourceString .. getSaveValueString(k, alreadyUsed, sources) .. "=" .. getSaveValueString(v, alreadyUsed, sources, k) .. ","
  1297.         end
  1298.         sources[ind] = "{" .. sourceString .. "}"
  1299.         return "tbl:" .. ind
  1300.     end
  1301. end
  1302.  
  1303. function escapeString(src, delimiter)
  1304.     local result = ""
  1305.     for i = 1, #src do
  1306.         local ch = src:sub(i, i)
  1307.         if ch == delimiter then
  1308.             result = result .. "\\"
  1309.         end
  1310.         result = result .. ch
  1311.     end
  1312.     return result
  1313. end
  1314.  
  1315. function unescapeString(src)
  1316.     local result = ""
  1317.     local escaped = false
  1318.     for i = 1, #src do
  1319.         local ch = src:sub(i, i)
  1320.         if escaped then
  1321.             result = result .. ch
  1322.             escaped = false
  1323.         elseif ch == "\\" then
  1324.             escaped = true
  1325.         else
  1326.             result = result .. ch
  1327.         end
  1328.     end
  1329.     return result
  1330. end
  1331.  
  1332. function getSaveValueString(obj, alreadyUsed, sources, key)
  1333.     local t = type(obj)
  1334.     if t == "string" then
  1335.         return "\"" .. escapeString(obj, "\"") .. "\""
  1336.     elseif t == "number" then
  1337.         return obj
  1338.     elseif t == "table" then
  1339.         return getTableSaveTextInd(obj, alreadyUsed, sources)
  1340.     elseif t == "boolean" then
  1341.         if obj then
  1342.             return "true"
  1343.         else
  1344.             return "false"
  1345.         end
  1346.     else
  1347.         if key == nil then
  1348.             key = ""
  1349.         end
  1350.         error("unknown type: " .. t .. "(" .. key .. ")")
  1351.     end
  1352. end
  1353.  
  1354. function finished()
  1355.     deleteFile(coreStorage.stackFileName)
  1356.     deleteFile(coreStorage.dataFileName)
  1357. end
  1358.  
  1359. function deleteFile(fileName)
  1360.     if test then
  1361.         os.remove(fileName)
  1362.     else
  1363.         shell.run("delete", fileName)
  1364.     end
  1365. end
  1366.  
  1367. function savePersistentStorage(key, fileName)
  1368.     if nonCriticalSectionCount > 0 then
  1369.         return
  1370.     end
  1371.     writeToFile(fileName, getTableSaveText(persistentStorage[key]))
  1372. end
  1373.  
  1374. local sleptLast = 0
  1375.  
  1376. local tProfileSaveStorageSerialize = 0
  1377. local tProfileSaveStorageWrite = 0
  1378. local tProfileWrite = 0
  1379. local tLastProfiling
  1380.  
  1381. defaultFunctions.setProfilingLabel = {
  1382.     type = TYPE_DEFAULT_FUNCTION,
  1383.     pars = { 1 },
  1384.     nonCritical = true,
  1385.     fct = function(label)
  1386.         tProfileSaveStorageSerialize = 0
  1387.         tProfileSaveStorageWrite = 0
  1388.         tProfileWrite = 0
  1389.         tLastProfiling = os.clock()
  1390.         if fProfile then
  1391.             writeToOpenFile(fProfile, "label: " .. toString(label) .. "\n")
  1392.             writeProfileData(true)
  1393.         end
  1394.     end
  1395. }
  1396.  
  1397. defaultFunctions.forceStorageSave = {
  1398.     type = TYPE_DEFAULT_FUNCTION,
  1399.     pars = { 0, 1 },
  1400.     nonCritical = true,
  1401.     fct = function(label)
  1402.         if label then
  1403.             tProfileSaveStorageSerialize = 0
  1404.             tProfileSaveStorageWrite = 0
  1405.             tProfileWrite = 0
  1406.             tLastProfiling = os.clock()
  1407.             if fProfile then
  1408.                 writeToOpenFile(fProfile, "label: " .. toString(label) .. "\n")
  1409.             end
  1410.         end
  1411.         saveStorage(true, label)
  1412.     end
  1413. }
  1414.  
  1415. function saveStorage(force, indentProfileLog)
  1416.     -- yield every second
  1417.     local now = os.clock()
  1418.     if now - sleptLast > 1 then
  1419.         yield()
  1420.         sleptLast = now
  1421.     end
  1422.  
  1423.     if nonCriticalSectionCount > 0 then
  1424.         return
  1425.     end
  1426.     if coreStorage.saveRequested or force then
  1427.         local t = os.clock()
  1428.         local txt = getTableSaveText(coreStorage)
  1429.         local t1 = os.clock()
  1430.         writeToFile(coreStorage.stackFileName .. "1", txt)
  1431.         writeToFile(coreStorage.stackFileName, txt)
  1432.         coreStorage.saveRequested = false
  1433.         tProfileSaveStorageSerialize = tProfileSaveStorageSerialize + t1 - t
  1434.         tProfileSaveStorageWrite = tProfileSaveStorageWrite + os.clock() - t1
  1435.         t = os.clock()
  1436.         if fProfile then
  1437.             writeProfileData(indentProfileLog)
  1438.         end
  1439.         tProfileWrite = tProfileWrite + os.clock() - t
  1440.     end
  1441. end
  1442.  
  1443. function writeProfileData(indent)
  1444.     if not tLastProfiling then
  1445.         tLastProfiling = tTurtiStart
  1446.     end
  1447.     if not tLastProfiling then
  1448.         return
  1449.     end
  1450.     local prefix = ""
  1451.     if indent then
  1452.         prefix = "    "
  1453.     end
  1454.  
  1455.     local elapsed = os.clock() - tLastProfiling
  1456.  
  1457.     writeToOpenFile(
  1458.             fProfile,
  1459.             prefix .. "tRun: " .. tostring(elapsed) .. "\n" ..
  1460.                     prefix .. "tSerializeStorage: " .. tostring(tProfileSaveStorageSerialize) .. "(" .. tostring(tProfileSaveStorageSerialize / elapsed * 100) .. "%)" .. "\n" ..
  1461.                     prefix .. "tWriteStorage: " .. tostring(tProfileSaveStorageWrite) .. "(" .. tostring(tProfileSaveStorageWrite / elapsed * 100) .. "%)" .. "\n" ..
  1462.                     prefix .. "tWriteProfile: " .. tostring(tProfileWrite) .. "(" .. tostring(tProfileWrite / elapsed * 100) .. "%)" .. "\n"
  1463.     )
  1464. end
  1465.  
  1466. function writeToFile(fileName, data)
  1467.     if test then
  1468.         local f = io.open(fileName, "w")
  1469.         local oldOut = io.output()
  1470.         io.output(f)
  1471.         io.write(data)
  1472.         io.close(f)
  1473.         io.output(oldOut)
  1474.     else
  1475.         local f = fs.open(fileName, "w")
  1476.         f.write(data)
  1477.         f.close()
  1478.     end
  1479. end
  1480.  
  1481. function fileExists(fileName)
  1482.     if test then
  1483.         local f = io.open(fileName)
  1484.         if f ~= nil then
  1485.             io.close(f)
  1486.             return true
  1487.         end
  1488.         return false
  1489.     else
  1490.         return fs.exists(fileName)
  1491.     end
  1492. end
  1493.  
  1494. function tryLoadStorage(fileName, stackFileName, dataFileName, persistentStorageDirName, currentFileData)
  1495.     if fileExists(stackFileName) then
  1496.         if isSameFileData(currentFileData, readFileText(dataFileName)) then
  1497.             print("loading storage")
  1498.             local data
  1499.             local state, msg = pcall(function()
  1500.                 data = getTableFromSaveText(readFileText(stackFileName))
  1501.             end)
  1502.             if state then
  1503.                 return true, data
  1504.             else
  1505.                 return true, getTableFromSaveText(readFileText(stackFileName .. "1"))
  1506.             end
  1507.         else
  1508.             print("source file has changed, restarting program")
  1509.             deleteFile(stackFileName)
  1510.             deleteFile(dataFileName)
  1511.         end
  1512.     end
  1513.  
  1514.     return false, {
  1515.         stack = nil,
  1516.         fileName = fileName,
  1517.         stackFileName = stackFileName,
  1518.         dataFileName = dataFileName,
  1519.         dfData = { save = true },
  1520.         diedLast = 0,
  1521.         constants = {}
  1522.     }
  1523. end
  1524.  
  1525. function isSameFileData(data1, data2)
  1526.  
  1527.     local pts1 = getLengthFormatedParts(data1)
  1528.     local pts2 = getLengthFormatedParts(data2)
  1529.  
  1530.     while #pts1 > 0 do
  1531.         local data = pts1[1]
  1532.         local success = false
  1533.         for j, data2 in ipairs(pts2) do
  1534.             if data2 == data then
  1535.                 table.remove(pts1, 1)
  1536.                 table.remove(pts2, j)
  1537.                 success = true
  1538.                 break
  1539.             end
  1540.         end
  1541.         if not success then
  1542.             return false
  1543.         end
  1544.     end
  1545.     return #pts2 == 0
  1546. end
  1547.  
  1548. function getLengthFormatedParts(data)
  1549.     local pts = {}
  1550.     while #data > 0 do
  1551.         local i = data:find("|")
  1552.         local len = tonumber(data:sub(1, i - 1))
  1553.         data = data:sub(i + 1)
  1554.         table.insert(pts, data:sub(1, len))
  1555.         data = data:sub(len + 1)
  1556.     end
  1557.     return pts
  1558. end
  1559.  
  1560. function parseMethod(source)
  1561.     local m = source:find(":{")
  1562.     if not m then
  1563.         error("invalid method source: " .. source)
  1564.     end
  1565.     local name = source:sub(1, m - 1)
  1566.     errInfo = { type = "plain", value = "method '" .. name .. "'", parent = errInfo }
  1567.     local fct = { }
  1568.     fct.pars = {}
  1569.     if name:find("%(") ~= nil and name:sub(#name, #name) == ")" then
  1570.         local i = name:find("%(")
  1571.         local parSource = name:sub(i + 1, -2)
  1572.         name = name:sub(1, i - 1)
  1573.         local parNames = splitTextStructural(parSource, ",")
  1574.         for i, n in ipairs(parNames) do
  1575.             if n:match("[A-Za-z_][A-Za-z0-9_]*") ~= n then
  1576.                 error("invalid parameter-name: " .. n)
  1577.             end
  1578.             table.insert(fct.pars, n)
  1579.         end
  1580.     end
  1581.     if name:match("[A-Za-z_][A-Za-z0-9_]*") ~= name then
  1582.         error("invalid function-name: " .. name)
  1583.     end
  1584.     fct.name = name
  1585.     local srcCommands = source:sub(m + 2, -2)
  1586.     fct.cmds = analyseCommands(srcCommands)
  1587.     errInfo = errInfo.parent
  1588.     return fct
  1589. end
  1590.  
  1591. function trimSourceCode(code)
  1592.     code = code:gsub("/%*[^%*]*%*/", "")
  1593.     code = code:gsub("//[^\n]*", "")
  1594.     return removeLettersIgnoreStrings(code, "\n\r\t ")
  1595. end
  1596.  
  1597. function loadProgram(file, stackFile, dataFile, persistentStorageDirName, cFCount)
  1598.     local source = readFileText(file)
  1599.     source = trimSourceCode(source)
  1600.  
  1601.     local libraryId = 1
  1602.     while source:sub(1, 1) == "#" do
  1603.         local m = source:find(";")
  1604.  
  1605.         local directivePts = splitTextStructural(source:sub(2, m - 1), ":")
  1606.         if directivePts[1] == "import" then
  1607.             local pastebinId = directivePts[2]
  1608.             if not test then
  1609.                 importLibrary(pastebinId)
  1610.             end
  1611.         else
  1612.             error("unknown directive: '" .. directivePts[1] .. "'")
  1613.         end
  1614.         source = source:sub(m + 1, -1)
  1615.         libraryId = libraryId + 1
  1616.     end
  1617.  
  1618.     methods = {}
  1619.     for i, v in ipairs(splitTextStructural(source, ";")) do
  1620.         local method = parseMethod(v)
  1621.         methods[method.name] = method
  1622.     end
  1623.  
  1624.     for _, hook in pairs(libraryMethodHooks) do
  1625.         local method = parseMethod(trimSourceCode(hook))
  1626.         method.isHook = true
  1627.         methods[method.name] = method
  1628.     end
  1629.  
  1630.     local validMethods = {}
  1631.     for name, fct in pairs(methods) do
  1632.         validMethods[name] = { type = TYPE_USER_FUNCTION, pars = { #fct.pars }, name = name, fct = fct, cmds = fct.cmds }
  1633.     end
  1634.     local allMethods = {}
  1635.     merge(defaultFunctions, allMethods)
  1636.     merge(validMethods, allMethods)
  1637.     checkMethodValidity(allMethods)
  1638.  
  1639.     if methods["main"] == nil then
  1640.         error("function 'main' needs to be defined in commands-source")
  1641.     end
  1642.     if #methods["main"]["pars"] ~= 0 then
  1643.         error("function 'main' doesn't take any arguments (" .. #methods["main"]["pars"] .. " given)")
  1644.     end
  1645.  
  1646.     local currentFileData = getFileData(methods)
  1647.  
  1648.     -- persistent storage
  1649.     if not test then
  1650.         shell.run("mkdir", persistentStorageDirName)
  1651.     end
  1652.  
  1653.     persistentStorage = {}
  1654.     for key, fct in pairs(fctsSetPersistentStorageData) do
  1655.         local fileName = persistentStorageDirName .. "/" .. key
  1656.         local data = {}
  1657.         if fileExists(fileName) then
  1658.             data = getTableFromSaveText(readFileText(fileName))
  1659.         end
  1660.         if data == nil then
  1661.             data = {}
  1662.         end
  1663.         persistentStorage[key] = data
  1664.         fct(persistentStorage[key], function()
  1665.             savePersistentStorage(key, fileName)
  1666.         end)
  1667.     end
  1668.  
  1669.     -- storage
  1670.     local success
  1671.     success, coreStorage = tryLoadStorage(file, stackFile, dataFile, persistentStorageDirName, currentFileData)
  1672.     coreStorage.dfData.save = true
  1673.  
  1674.     for key, fct in pairs(fctsSetStorageData) do
  1675.         if not coreStorage.dfData[key] then
  1676.             coreStorage.dfData[key] = {}
  1677.         end
  1678.         fct(coreStorage.dfData[key], function()
  1679.             coreStorage.dfData.save = true
  1680.             saveStorage()
  1681.         end)
  1682.     end
  1683.  
  1684.     --[[if success then
  1685.         initToContinue()
  1686.     end]]
  1687.  
  1688.     writeToFile(dataFile, currentFileData)
  1689.     -- init stack on program start
  1690.     if coreStorage.stacks == nil then
  1691.         local mainStack = {}
  1692.         table.insert(mainStack, { name = "main", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
  1693.         if methods["init"] ~= nil then
  1694.             if #methods["init"]["pars"] ~= 0 then
  1695.                 error("function 'init' doesn't take any arguments (" .. #methods["init"]["pars"] .. " given)")
  1696.             end
  1697.             table.insert(mainStack, { name = "init", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
  1698.         end
  1699.         coreStorage.stacks = { { name = "main", frames = mainStack, isDaemon = false, storage = {} } }
  1700.     else
  1701.  
  1702.     end
  1703.  
  1704.     --[[if coreStorage.stack == nil then
  1705.         local stack = {}
  1706.         table.insert(stack, { name = "main", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
  1707.         if methods["init"] ~= nil then
  1708.             if #methods["init"]["pars"] ~= 0 then
  1709.                 error("function 'init' doesn't take any arguments (" .. #methods["init"]["pars"] .. " given)")
  1710.             end
  1711.             table.insert(stack, { name = "init", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
  1712.         end
  1713.         coreStorage.stack = stack
  1714.     else
  1715.         if methods["onRestart"] ~= nil then
  1716.             if #methods["onRestart"]["pars"] ~= 0 then
  1717.                 error("function 'onRestart' doesn't take any arguments (" .. #methods["onRestart"]["pars"] .. " given)")
  1718.             end
  1719.             table.insert(coreStorage.stack, { name = "onRestart", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
  1720.         end
  1721.     end]]
  1722.  
  1723.     saveStorage()
  1724.     -- TODO support library threads
  1725.     for _, stack in ipairs(coreStorage.stacks) do
  1726.         ThreadManager.startThread(
  1727.                 function()
  1728.                     stack.threadName = ThreadManager.currentThread.name
  1729.                     executeStack(stack, true, cFCount)
  1730.                     removeValue(coreStorage.stacks, stack)
  1731.                 end,
  1732.                 stack.isDaemon,
  1733.                 stack.name,
  1734.                 stack.storage
  1735.         )
  1736.     end
  1737.  
  1738.     print(">> setup successful <<")
  1739.     turtiRunning = true
  1740.     tTurtiStart = os.clock()
  1741.     ThreadManager.run()
  1742.     turtiRunning = false
  1743.     --[[turtiRunning = true
  1744.     if #threads > 0 then
  1745.         print("registered " .. tostring(#threads) .. " background threads")
  1746.         print(">> setup successful <<")
  1747.  
  1748.         parallel.waitForAll(function()
  1749.             execute(methods, true, cFCount)
  1750.             turtiRunning = false
  1751.         end, table.unpack(threads))
  1752.     else
  1753.         print(">> setup successful <<")
  1754.         execute(methods, true, cFCount)
  1755.         turtiRunning = false
  1756.     end]]
  1757.  
  1758.     finished()
  1759. end
  1760.  
  1761. function invokeDaemonHookNoWait(methodName, ...)
  1762.     return invokeHookInternal(false, true, methodName, ...)
  1763. end
  1764.  
  1765. function invokeDaemonHook(methodName, ...)
  1766.     return invokeHookInternal(true, true, methodName, ...)
  1767. end
  1768.  
  1769. function invokeHookNoWait(methodName, ...)
  1770.     return invokeHookInternal(false, false, methodName, ...)
  1771. end
  1772.  
  1773. function invokeHook(methodName, ...)
  1774.     return invokeHookInternal(true, false, methodName, ...)
  1775. end
  1776.  
  1777. function invokeHookInternal(wait, daemon, methodName, ...)
  1778.     local args = { ... }
  1779.     if methods[methodName] then
  1780.         -- turti function
  1781.         local stack = { name = "hook@" .. methodName, frames = {  }, isDaemon = daemon, storage = {} }
  1782.         table.insert(coreStorage.stacks, stack)
  1783.         local ret = {}
  1784.         ThreadManager.startThread(
  1785.                 function()
  1786.                     local result = executeTurtiFunction(stack, methodName, args, 0)
  1787.                     removeValue(coreStorage.stacks, stack)
  1788.                     ret[1] = true
  1789.                     ret[2] = result
  1790.                 end,
  1791.                 stack.isDaemon,
  1792.                 stack.name,
  1793.                 stack.storage
  1794.         )
  1795.         if not wait then
  1796.             return
  1797.         end
  1798.         while not ret[1] do
  1799.             yield()
  1800.         end
  1801.         return ret[2]
  1802.     elseif defaultFunctions[methodName] then
  1803.         local ret = {}
  1804.         ThreadManager.startThread(
  1805.                 function()
  1806.                     local result = executeDefaultFunction(methodName, args)
  1807.                     ret[1] = true
  1808.                     ret[2] = result
  1809.                 end,
  1810.                 false,
  1811.                 "hook@" .. methodName,
  1812.                 { }
  1813.         )
  1814.     else
  1815.         error("unknown method: " .. methodName)
  1816.     end
  1817. end
  1818.  
  1819. local yieldedLast = 0
  1820.  
  1821. function yield()
  1822.     local now = os.clock()
  1823.     if test then
  1824.         coroutine.yield()
  1825.     elseif now - yieldedLast > 0.5 then
  1826.         sleep(0)
  1827.         yieldedLast = os.clock()
  1828.     end
  1829. end
  1830.  
  1831. function getFileData(methods)
  1832.     local data = ""
  1833.     for methodName, v in pairs(methods) do
  1834.         if not v.isHook then
  1835.             local pars = v.pars
  1836.             local cmds = v.cmds
  1837.             local methodString = methodName .. "("
  1838.             for i, par in ipairs(pars) do
  1839.                 if i ~= 1 then
  1840.                     methodString = methodString .. ","
  1841.                 end
  1842.                 methodString = methodString .. par
  1843.             end
  1844.             methodString = methodString .. ")|" .. getFileDataCmds(cmds)
  1845.             data = data .. #methodString .. "|" .. methodString
  1846.         end
  1847.     end
  1848.     return data
  1849. end
  1850.  
  1851. function getFileDataCmds(cmds)
  1852.     local txt = ""
  1853.     for i, cmd in ipairs(cmds) do
  1854.         if i ~= 1 then
  1855.             txt = txt .. ";"
  1856.         end
  1857.         txt = txt .. getFileDataCmd(cmd)
  1858.     end
  1859.     return txt
  1860. end
  1861.  
  1862. function getFileDataCmd(cmd)
  1863.     if cmd.type == TYPE_VALUE then
  1864.         if cmd.value == nil then
  1865.             return "null"
  1866.         elseif type(cmd.value) == "string" then
  1867.             return "\"" .. cmd.value .. "\""
  1868.         elseif type(cmd.value) == "boolean" then
  1869.             return "\"" .. ((cmd.value and "true") or "false") .. "\""
  1870.         else
  1871.             return "" .. cmd.value
  1872.         end
  1873.         --elseif cmd.type == TYPE_DECLARATION then
  1874.     elseif cmd.type == TYPE_FUNCTION then
  1875.         local txtPars = ""
  1876.         for i, par in ipairs(cmd.pars) do
  1877.             if i ~= 1 then
  1878.                 txtPars = txtPars .. ","
  1879.             end
  1880.             txtPars = txtPars .. getFileDataCmd(par)
  1881.         end
  1882.         return cmd.name .. "(" .. txtPars .. ")"
  1883.     elseif cmd.type == TYPE_CONSTANT then
  1884.         return "$" .. cmd.key .. "$"
  1885.     elseif cmd.type == TYPE_ARR then
  1886.         local txt = ""
  1887.         for i, v in ipairs(cmd.vars) do
  1888.             if i ~= 1 then
  1889.                 txt = txt .. ","
  1890.             end
  1891.             txt = txt .. getFileDataCmd(v)
  1892.         end
  1893.         return "[" .. txt .. "]"
  1894.     elseif cmd.type == TYPE_TABLE then
  1895.         local txt = ""
  1896.         for i, key in ipairs(cmd.keyOrder) do
  1897.             if i ~= 1 then
  1898.                 txt = txt .. ","
  1899.             end
  1900.             txt = txt .. getFileDataCmd(key) .. "=" .. getFileDataCmd(cmd.vars[key])
  1901.         end
  1902.         return "{" .. txt .. "}"
  1903.     elseif cmd.type == TYPE_VAR then
  1904.         return cmd.name
  1905.     elseif cmd.type == TYPE_ARITHMETIC then
  1906.         local txt = ""
  1907.         for i, v in ipairs(cmd.pts) do
  1908.             if i ~= 1 then
  1909.                 txt = txt .. cmd.symbol
  1910.             end
  1911.             txt = txt .. getFileDataCmd(v)
  1912.         end
  1913.         return txt
  1914.     elseif cmd.type == TYPE_DECLARATION then
  1915.         return cmd.name .. "=" .. getFileDataCmd(cmd.value)
  1916.     elseif cmd.type == TYPE_ARR_INDEX_DECLARATION then
  1917.         return getFileDataCmd(cmd.arr) .. "[" .. getFileDataCmd(cmd.index) .. "]" .. "=" .. getFileDataCmd(cmd.value)
  1918.     elseif cmd.type == TYPE_POLY_DECLARATION then
  1919.         local txt = ""
  1920.         local first = false
  1921.         for i, name in ipairs(cmd.names) do
  1922.             if first then
  1923.                 first = false
  1924.             else
  1925.                 txt = txt .. ","
  1926.             end
  1927.             txt = txt .. name
  1928.         end
  1929.         return txt .. "=" .. getFileDataCmd(cmd.value)
  1930.     elseif cmd.type == TYPE_CONTROL_FLOW then
  1931.         if cmd.typeCF == TYPE_CF_WHILE then
  1932.             return "while(" .. getFileDataCmd(cmd.cond) .. "){" .. getFileDataCmds(cmd.body) .. "}"
  1933.         elseif cmd.typeCF == TYPE_CF_FOR then
  1934.             return "for(" .. getFileDataCmd(cmd.first) .. ";" .. getFileDataCmd(cmd.second) .. ";" .. getFileDataCmd(cmd.third) .. "){" .. getFileDataCmds(cmd.body) .. "}"
  1935.         elseif cmd.typeCF == TYPE_CF_IF then
  1936.             local txt = "";
  1937.             for i, block in ipairs(cmd.ifCmdBlocks) do
  1938.                 if i ~= 1 then
  1939.                     txt = txt .. "else"
  1940.                 end
  1941.                 if block.cond ~= nil then
  1942.                     txt = txt .. "if(" .. getFileDataCmd(block.cond) .. ")"
  1943.                 end
  1944.                 txt = txt .. "{" .. getFileDataCmds(block.body) .. "}"
  1945.             end
  1946.             return txt
  1947.         else
  1948.             error("fail: " .. cmd.typeCF)
  1949.         end
  1950.     elseif cmd.type == TYPE_NON_CRITICAL_SECTION then
  1951.         return "#{" .. getFileDataCmds(cmd.body) .. "}"
  1952.     elseif cmd.type == TYPE_EXCLUSIVE_SECTION then
  1953.         return "exclusive{" .. getFileDataCmds(cmd.body) .. "}"
  1954.     elseif cmd.type == TYPE_CMP then
  1955.         return getFileDataCmd(cmd.pts[1]) .. cmd.ch .. getFileDataCmd(cmd.pts[2])
  1956.     elseif cmd.type == TYPE_UNARY_OP_STATEMENT then
  1957.         if cmd.op == "++" or cmd.op == "--" then
  1958.             return cmd.name .. cmd.op
  1959.         elseif cmd.op == "_++" or cmd.op == "_--" then
  1960.             return cmd.op:sub(2, 3) .. cmd.name
  1961.         else
  1962.             error("invalid op: " .. cmd.op)
  1963.         end
  1964.     elseif cmd.type == TYPE_UNARY_OP then
  1965.         if cmd.op == "-" then
  1966.             return "-" .. getFileDataCmd(cmd.value)
  1967.         else
  1968.             error("invalid op: " .. cmd.op)
  1969.         end
  1970.     elseif cmd.type == TYPE_OR then
  1971.         local txt = ""
  1972.         for i = 1, #cmd.values do
  1973.             if txt ~= "" then
  1974.                 txt = txt .. "||"
  1975.             end
  1976.             txt = txt .. getFileDataCmd(cmd.values[i])
  1977.         end
  1978.         return txt
  1979.     elseif cmd.type == TYPE_AND then
  1980.         local txt = ""
  1981.         for i = 1, #cmd.values do
  1982.             if txt ~= "" then
  1983.                 txt = txt .. "&&"
  1984.             end
  1985.             txt = txt .. getFileDataCmd(cmd.values[i])
  1986.         end
  1987.         return txt
  1988.     elseif cmd.type == TYPE_PARENTHESES then
  1989.         return "(" .. getFileDataCmd(cmd.cmd) .. ")"
  1990.     elseif cmd.type == TYPE_ARR_INDEX then
  1991.         return getFileDataCmd(cmd.body) .. "[" .. getFileDataCmd(cmd.index) .. "]"
  1992.     elseif cmd.type == TYPE_ATTRIBUTE then
  1993.         return getFileDataCmd(cmd.cmd) .. "." .. cmd.name
  1994.     elseif cmd.type == TYPE_METHOD then
  1995.         local txtPars = ""
  1996.         for i, par in ipairs(cmd.pars) do
  1997.             if i ~= 1 then
  1998.                 txtPars = txtPars .. ","
  1999.             end
  2000.             txtPars = txtPars .. getFileDataCmd(par)
  2001.         end
  2002.         return getFileDataCmd(cmd.cmd) .. "." .. cmd.name .. "(" .. txtPars .. ")"
  2003.     elseif cmd.type == TYPE_CAST then
  2004.         return getFileDataCmd(cmd.value) .. "@" .. cmd.key
  2005.     elseif cmd.type == TYPE_FUNCTION_HANDLE then
  2006.         return "@" .. cmd.name
  2007.     else
  2008.         printTable(cmd)
  2009.         if cmd.type ~= nil then
  2010.             error("unknown type: " .. cmd.type)
  2011.         end
  2012.         error("unknown command: ")
  2013.     end
  2014. end
  2015.  
  2016. function printKeys(t)
  2017.     local keys = ""
  2018.     local c = 0
  2019.     for i, v in ipairs(t) do
  2020.         keys = keys .. ((c ~= 0 and ",") or "") .. i
  2021.         c = 1
  2022.     end
  2023.     print(keys)
  2024. end
  2025.  
  2026. function countKeys(t)
  2027.     local c = 0
  2028.     for i, v in pairs(t) do
  2029.         c = c + 1
  2030.     end
  2031.     return c
  2032. end
  2033.  
  2034. local runningStacks = {}
  2035. function executeStack(stack, isStartThread, cFCount)
  2036.     local frame = stack.frames[#stack.frames]
  2037.     if not frame then
  2038.         return
  2039.     end
  2040.     local methodName = frame.name
  2041.     local methodOffset = frame.offset
  2042.     local methodExecuted = frame.executed --TODO check pos or anything else when executed=false
  2043.     local currentMethod = methods[methodName]
  2044.     local cmds = currentMethod.cmds
  2045.     local localVars = frame.localVars
  2046.     if methodExecuted then
  2047.         frame.offset = frame.offset + 1
  2048.     end
  2049.     runningStacks[stack] = true
  2050.     executeCommands(stack, frame, cmds, localVars, cFCount)
  2051.     runningStacks[stack] = nil
  2052.  
  2053.     if isStartThread and #stack.frames > 1 then
  2054.         table.remove(stack.frames)
  2055.         saveStorage()
  2056.         executeStack(stack, true, cFCount)
  2057.     end
  2058.  
  2059.     if frame.executeReturn then
  2060.         return
  2061.     end
  2062. end
  2063.  
  2064. function executeDefaultFunction(name, args)
  2065.     --default function
  2066.     local defaultFunction = defaultFunctions[name]
  2067.  
  2068.     if not defaultFunction.nonCritical then
  2069.         coreStorage.saveRequested = true
  2070.     end
  2071.  
  2072.     return defaultFunction.fct(table.unpack(args))
  2073. end
  2074.  
  2075. function executeTurtiFunction(stack, fctName, args, cFCount)
  2076.     local method = methods[fctName]
  2077.     local newLocalVars = { { v = {}, e = {} } }
  2078.     for i, p in ipairs(args) do
  2079.         local par = method.pars[i]
  2080.         newLocalVars[1].e[par] = true
  2081.         newLocalVars[1].v[par] = p
  2082.     end
  2083.     table.insert(stack.frames, { name = method.name, offset = 1, executed = false, localVars = newLocalVars, cFStack = {} })
  2084.     saveStorage()
  2085.     executeStack(stack, false, 1)
  2086.     local frame1 = table.remove(stack.frames)
  2087.     saveStorage(true)
  2088.     if frame1.returnValues == nil then
  2089.         return nil
  2090.     end
  2091.  
  2092.     if #frame1.returnValues == 1 then
  2093.         return frame1.returnValues[1]
  2094.     end
  2095.     if isObject(frame1.returnValues) and frame1.returnValues._class_prototype.key == "list" then
  2096.         if executeClassGetter(frame1.returnValues, "length") == 1 then
  2097.             return frame1.returnValues[1]
  2098.         end
  2099.     end
  2100.     return frame1.returnValues
  2101. end
  2102.  
  2103. function mimicLuaConstructorForTable(tbl, n)
  2104.     local str = "return function(p) return {"
  2105.     for i = 1, n do
  2106.         if i ~= 1 then
  2107.             str = str .. ","
  2108.         end
  2109.         str = str .. "p[" .. i .. "]"
  2110.     end
  2111.     str = str .. "} end"
  2112.     return load(str)()(tbl)
  2113. end
  2114.  
  2115. function executeCommand(stack, cmd, localVars, cFCount)
  2116.     yield()
  2117.  
  2118.     errInfo = { type = "cmd", value = cmd }
  2119.     if cmd.type == TYPE_METHOD then
  2120.         local pars = {}
  2121.         local n
  2122.         local mimic = false
  2123.         for i, p in ipairs(cmd.pars) do
  2124.             local value = executeGetValue(stack, p, localVars, methods, cFCount)
  2125.             if value == nil then
  2126.                 mimic = true
  2127.             end
  2128.             pars[i] = value
  2129.             n = i
  2130.         end
  2131.         if mimic then
  2132.             pars = mimicLuaConstructorForTable(pars, n)
  2133.         end
  2134.         local v = executeGetValue(stack, cmd.cmd, localVars, methods, cFCount)
  2135.         return executeClassMethod(v, cmd.name, pars)
  2136.     elseif cmd.type == TYPE_FUNCTION or cmd.type == TYPE_HOOK_FUNCTION then
  2137.         local frame = stack.frames[#stack.frames]
  2138.         if cmd.name == "return" then
  2139.             frame.executeReturn = true
  2140.             if #cmd.pars > 0 then
  2141.                 local returnValues = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, #cmd.pars)
  2142.                 for i, par in ipairs(cmd.pars) do
  2143.                     returnValues[i] = executeGetValue(stack, par, localVars, methods, cFCount)
  2144.                 end
  2145.                 frame.returnValues = returnValues
  2146.             end
  2147.             return
  2148.         end
  2149.  
  2150.         local pars = {}
  2151.         if cmd.type == TYPE_FUNCTION then
  2152.             for i, p in ipairs(cmd.pars) do
  2153.                 local value = executeGetValue(stack, p, localVars, methods, cFCount)
  2154.                 pars[i] = value
  2155.             end
  2156.         else
  2157.             pars = cmd.args
  2158.         end
  2159.  
  2160.         if cmd.name == "startThread" then
  2161.             local method = pars[1].name
  2162.             table.remove(pars, 1)
  2163.             invokeHookNoWait(method, table.unpack(pars))
  2164.         elseif cmd.name == "startDaemonThread" then
  2165.             local method = pars[1].name
  2166.             table.remove(pars, 1)
  2167.             invokeDaemonHookNoWait(method, table.unpack(pars))
  2168.         elseif methods[cmd.name] then
  2169.             --turti function
  2170.             return executeTurtiFunction(stack, cmd.name, pars, cFCount)
  2171.         elseif defaultFunctions[cmd.name] then
  2172.             return executeDefaultFunction(cmd.name, pars)
  2173.         elseif cmd.type == TYPE_HOOK_FUNCTION then
  2174.             error("dynamically invoked method '" .. cmd.name .. "' not found")
  2175.         else
  2176.             local prototype = CLASS_PROTOTYPES[cmd.name]
  2177.             if prototype and prototype.construct then
  2178.                 return constructClass(prototype.key, table.unpack(pars))
  2179.             end
  2180.             error("method '" .. cmd.name .. "' not found")
  2181.         end
  2182.     elseif cmd.type == TYPE_DECLARATION then
  2183.         local name = cmd.name
  2184.         local v = executeGetValue(stack, cmd.value, localVars, methods, cFCount)
  2185.         setVariable(name, v, cFCount, localVars)
  2186.     elseif cmd.type == TYPE_POLY_DECLARATION then
  2187.         local names = cmd.names
  2188.         local values = executeGetValue(stack, cmd.value, localVars, methods, cFCount)
  2189.         for i, name in ipairs(names) do
  2190.             setVariable(name, values[i], cFCount, localVars)
  2191.         end
  2192.     elseif cmd.type == TYPE_ARR_INDEX_DECLARATION then
  2193.         local arr = executeGetValue(stack, cmd.arr, localVars, methods, cFCount)
  2194.         local index = executeGetValue(stack, cmd.index, localVars, methods, cFCount)
  2195.         local value = executeGetValue(stack, cmd.value, localVars, methods, cFCount)
  2196.         if isObject(arr) then
  2197.             executeClassItemSetter(arr, index, value)
  2198.             return
  2199.         end
  2200.         error(toString(arr) .. " is not an object, cast using e.g. var@table or var@list")
  2201.     elseif cmd.type == TYPE_CONTROL_FLOW then
  2202.         if cmd.typeCF == TYPE_CF_IF then
  2203.             local cFCountIf = cFCount + 1
  2204.             local id = cmd.id
  2205.             local data
  2206.             local frame = stack.frames[#stack.frames]
  2207.             if frame.cFStack[cFCountIf] == nil or frame.cFStack[cFCountIf].cmd.id ~= cmd.id then
  2208.                 data = { cmd = cmd, condExecuted = false, offset = 1, ifOffset = 1, executed = false, condResult = false }
  2209.                 localVars[cFCountIf] = { v = {}, e = {} }
  2210.                 frame.cFStack[cFCountIf] = data
  2211.                 saveStorage()
  2212.             else
  2213.                 data = frame.cFStack[cFCountIf]
  2214.             end
  2215.             if data.executeBreak then
  2216.                 return
  2217.             end
  2218.             --print(cFCount, #frame.cFStack)
  2219.             for i = data.ifOffset, #cmd.ifCmdBlocks do
  2220.                 local cond = cmd.ifCmdBlocks[i].cond
  2221.                 local body = cmd.ifCmdBlocks[i].body
  2222.  
  2223.                 if not data.condExecuted then
  2224.                     local v
  2225.                     if cond ~= nil then
  2226.                         v = executeGetValue(stack, cond, localVars, methods, cFCountIf)
  2227.                         if v == nil or type(v) ~= "boolean" then
  2228.                             error("type of condition needs to be 'boolean' (type '" .. type(v) .. "' given)")
  2229.                         end
  2230.                     else
  2231.                         v = true
  2232.                     end
  2233.                     data.condExecuted = true
  2234.                     data.condResult = v
  2235.                     saveStorage()
  2236.                 end
  2237.  
  2238.                 if data.condResult then
  2239.                     if data.executed then
  2240.                         data.offset = data.offset + 1
  2241.                     end
  2242.                     saveStorage()
  2243.                     executeCommands(stack, data, body, localVars, cFCountIf)
  2244.                     if data.executeReturn then
  2245.                         frame.executeReturn = data.executeReturn
  2246.                         frame.returnValues = data.returnValues
  2247.                     end
  2248.                 end
  2249.  
  2250.                 if data.executeBreak then
  2251.                     frame.cFStack[cFCount].executeBreak = data.executeBreak
  2252.                     saveStorage()
  2253.                     break
  2254.                 end
  2255.  
  2256.                 data.ifOffset = i + 1
  2257.                 data.condExecuted = false
  2258.                 data.executed = false
  2259.                 data.offset = 1
  2260.                 saveStorage()
  2261.  
  2262.                 if data.condResult then
  2263.                     break
  2264.                 end
  2265.             end
  2266.             frame.cFStack[cFCountIf] = nil
  2267.             localVars[cFCountIf] = nil
  2268.         elseif cmd.typeCF == TYPE_CF_FOR then
  2269.             local cfCountFor = cFCount + 1
  2270.             local id = cmd.id
  2271.             local data
  2272.             local frame = stack.frames[#stack.frames]
  2273.             if frame.cFStack[cfCountFor] == nil or frame.cFStack[cfCountFor].cmd.id ~= cmd.id then
  2274.                 data = { cmd = cmd, firstExecuted = false, offset = 1, condExecuted = false, lastExecuted = false, condResult = false }
  2275.                 localVars[cfCountFor] = { v = {}, e = {} }
  2276.                 frame.cFStack[cfCountFor] = data
  2277.                 saveStorage()
  2278.             else
  2279.                 data = frame.cFStack[cfCountFor]
  2280.             end
  2281.             if not data.firstExecuted then
  2282.                 executeCommand(stack, data.cmd.first, localVars, cfCountFor)
  2283.                 data.firstExecuted = true
  2284.                 if frame.executeReturn then
  2285.                     return
  2286.                 end
  2287.                 if frame.executeBreak then
  2288.                     error("break outside loop")
  2289.                 end
  2290.                 saveStorage()
  2291.             end
  2292.             while true do
  2293.                 yield()
  2294.                 if not data.condExecuted then
  2295.                     local v = executeGetValue(stack, data.cmd.second, localVars, methods, cfCountFor)
  2296.                     if v == nil or type(v) ~= "boolean" then
  2297.                         error("type of condition needs to be 'boolean' (type '" .. type(v) .. "' given)")
  2298.                     end
  2299.                     data.condExecuted = true
  2300.                     data.condResult = v
  2301.                     saveStorage()
  2302.                     if not v then
  2303.                         break
  2304.                     end
  2305.                 end
  2306.                 if data.executed then
  2307.                     data.offset = data.offset + 1
  2308.                     saveStorage()
  2309.                 end
  2310.  
  2311.                 executeCommands(stack, data, data.cmd.body, localVars, cfCountFor)
  2312.                 if data.executeReturn then
  2313.                     frame.executeReturn = data.executeReturn
  2314.                     frame.returnValues = data.returnValues
  2315.                     break
  2316.                 end
  2317.                 if frame.executeReturn then
  2318.                     break
  2319.                 end
  2320.  
  2321.                 if not data.lastExecuted and not data.executeBreak ~= "break" then
  2322.                     executeCommand(stack, data.cmd.third, localVars, cfCountFor)
  2323.                     data.lastExecuted = true
  2324.                     saveStorage()
  2325.                 end
  2326.  
  2327.                 data.lastExecuted = false
  2328.                 data.condExecuted = false
  2329.                 data.executed = false
  2330.                 data.offset = 1
  2331.                 saveStorage()
  2332.  
  2333.                 if data.executeBreak then
  2334.                     if data.executeBreak == "break" then
  2335.                         break
  2336.                     elseif data.executeBreak == "continue" then
  2337.                         data.executeBreak = nil
  2338.                     else
  2339.                         error("invalid break type: " .. tostring(data.executeBreak))
  2340.                     end
  2341.                 end
  2342.             end
  2343.             frame.cFStack[cfCountFor] = nil
  2344.             localVars[cfCountFor] = nil
  2345.         elseif cmd.typeCF == TYPE_CF_WHILE then
  2346.             local cfCountWhile = cFCount + 1
  2347.             local data
  2348.             local frame = stack.frames[#stack.frames]
  2349.             if frame.cFStack[cfCountWhile] == nil or frame.cFStack[cfCountWhile].cmd.id ~= cmd.id then
  2350.                 data = { cmd = cmd, condExecuted = false, offset = 1, executed = false, condResult = false }
  2351.                 localVars[cfCountWhile] = { v = {}, e = {} }
  2352.                 frame.cFStack[cfCountWhile] = data
  2353.                 saveStorage()
  2354.             else
  2355.                 data = frame.cFStack[cfCountWhile]
  2356.             end
  2357.  
  2358.             while true do
  2359.                 yield()
  2360.                 if not data.condExecuted then
  2361.                     local v = executeGetValue(stack, data.cmd.cond, localVars, methods, cfCountWhile)
  2362.                     if v == nil or type(v) ~= "boolean" then
  2363.                         error("type of condition needs to be 'boolean' (type '" .. type(v) .. "' given)")
  2364.                     end
  2365.                     data.condExecuted = true
  2366.                     data.condResult = v
  2367.                     saveStorage()
  2368.                     if not v then
  2369.                         break
  2370.                     end
  2371.                 end
  2372.                 if data.executed then
  2373.                     data.offset = data.offset + 1
  2374.                 end
  2375.                 saveStorage()
  2376.                 executeCommands(stack, data, data.cmd.body, localVars, cfCountWhile)
  2377.                 if data.executeReturn then
  2378.                     frame.executeReturn = data.executeReturn
  2379.                     frame.returnValues = data.returnValues
  2380.                     break
  2381.                 end
  2382.  
  2383.                 data.condExecuted = false
  2384.                 data.executed = false
  2385.                 data.offset = 1
  2386.                 saveStorage()
  2387.                 if data.executeBreak then
  2388.                     if data.executeBreak == "break" then
  2389.                         break
  2390.                     elseif data.executeBreak == "continue" then
  2391.                         data.executeBreak = nil
  2392.                     else
  2393.                         error("invalid break type: " .. tostring(data.executeBreak))
  2394.                     end
  2395.                 end
  2396.             end
  2397.             frame.cFStack[cfCountWhile] = nil
  2398.             localVars[cfCountWhile] = nil
  2399.         else
  2400.             error("unknown typeCF: " .. cmd.typeCF .. "")
  2401.         end
  2402.     elseif cmd.type == TYPE_UNARY_OP_STATEMENT then
  2403.         executeGetValue(stack, cmd, localVars, methods, cFCount)
  2404.     elseif cmd.type == TYPE_VAR then
  2405.         local frame = stack.frames[#stack.frames]
  2406.         if cmd.name == "break" then
  2407.             if cFCount < 1 then
  2408.                 error("break outside loop")
  2409.             end
  2410.             frame.cFStack[cFCount].executeBreak = "break"
  2411.             return
  2412.         elseif cmd.name == "continue" then
  2413.             if cFCount < 1 then
  2414.                 error("continue outside loop")
  2415.             end
  2416.             frame.cFStack[cFCount].executeBreak = "continue"
  2417.             return
  2418.         else
  2419.             error("unexpected value: " .. cmd.name)
  2420.         end
  2421.     elseif cmd.type == TYPE_EXCLUSIVE_SECTION then
  2422.         local cFCountExclusive = cFCount + 1
  2423.         local frame = stack.frames[#stack.frames]
  2424.         local data
  2425.         if frame.cFStack[cFCountExclusive] == nil or frame.cFStack[cFCountExclusive].cmd.id ~= cmd.id then
  2426.             data = { cmd = cmd, offset = 1 }
  2427.             localVars[cFCountExclusive] = { v = {}, e = {} }
  2428.             frame.cFStack[cFCountExclusive] = data
  2429.         else
  2430.             data = frame.cFStack[cFCountExclusive]
  2431.         end
  2432.  
  2433.         ThreadManager.blockExecutionOfOtherThreads()
  2434.         executeCommands(stack, data, cmd.body, localVars, cFCountExclusive)
  2435.         ThreadManager.unblockExecutionOfOtherThreads()
  2436.  
  2437.         frame.cFStack[cFCountExclusive] = nil
  2438.         local varsNew = localVars[cFCountExclusive]
  2439.         table.remove(localVars)
  2440.         localVars[cFCountExclusive] = nil
  2441.         local varsOld = localVars[cFCount]
  2442.  
  2443.         for k, v in pairs(varsNew.e) do
  2444.             varsOld.e[k] = v
  2445.         end
  2446.  
  2447.         for k, v in pairs(varsNew.v) do
  2448.             varsOld.v[k] = v
  2449.         end
  2450.     elseif cmd.type == TYPE_NON_CRITICAL_SECTION then
  2451.         local cFCountNonCritical = cFCount + 1
  2452.         local frame = stack.frames[#stack.frames]
  2453.         local data
  2454.         if frame.cFStack[cFCountNonCritical] == nil or frame.cFStack[cFCountNonCritical].cmd.id ~= cmd.id then
  2455.             data = { cmd = cmd, offset = 1 }
  2456.             localVars[cFCountNonCritical] = { v = {}, e = {} }
  2457.             frame.cFStack[cFCountNonCritical] = data
  2458.         else
  2459.             data = frame.cFStack[cFCountNonCritical]
  2460.         end
  2461.  
  2462.         nonCriticalSectionCount = nonCriticalSectionCount + 1
  2463.         executeCommands(stack, data, cmd.body, localVars, cFCountNonCritical)
  2464.         nonCriticalSectionCount = nonCriticalSectionCount - 1
  2465.  
  2466.         frame.cFStack[cFCountNonCritical] = nil
  2467.         local varsNew = localVars[cFCountNonCritical]
  2468.         table.remove(localVars)
  2469.         localVars[cFCountNonCritical] = nil
  2470.         local varsOld = localVars[cFCount]
  2471.  
  2472.         for k, v in pairs(varsNew.e) do
  2473.             varsOld.e[k] = v
  2474.         end
  2475.  
  2476.         for k, v in pairs(varsNew.v) do
  2477.             varsOld.v[k] = v
  2478.         end
  2479.  
  2480.     else
  2481.         error("unknown type: " .. cmd.type)
  2482.     end
  2483. end
  2484.  
  2485. function setVariable(name, v, cFCount, localVars)
  2486.     if name:sub(1, 1) == "$" then
  2487.         local len = #name
  2488.         if name:sub(len, len) == "$" then
  2489.             name = name:sub(2, len - 1)
  2490.         else
  2491.             name = name:sub(2, len)
  2492.         end
  2493.  
  2494.         coreStorage.constants[name] = v
  2495.     else
  2496.         for i = cFCount - 1, 1, -1 do
  2497.             if localVars[i] == nil then
  2498.                 localVars[i] = { v = {}, e = {} }
  2499.             else
  2500.                 for n, _ in pairs(localVars[i].e) do
  2501.                     if n == name then
  2502.                         localVars[i].v[name] = v
  2503.                         return
  2504.                     end
  2505.                 end
  2506.             end
  2507.         end
  2508.         if not localVars[cFCount] then
  2509.             localVars[cFCount] = { v = {}, e = {} }
  2510.         end
  2511.         localVars[cFCount].v[name] = v
  2512.         localVars[cFCount].e[name] = true
  2513.     end
  2514. end
  2515.  
  2516. function executeCommands(stack, frame, cmds, localVars, cFCount)
  2517.     local topFrame = stack.frames[#stack.frames]
  2518.     for i = frame.offset, #cmds do
  2519.         local cmd = cmds[i]
  2520.         frame.offset = i
  2521.         frame.executed = false
  2522.  
  2523.         if cmd.type == TYPE_FUNCTION then
  2524.             local isTurtiFunction = false
  2525.             for name, _ in pairs(methods) do
  2526.                 if name == cmd.name then
  2527.                     isTurtiFunction = true
  2528.                 end
  2529.             end
  2530.             frame.executed = isTurtiFunction
  2531.         end
  2532.  
  2533.         saveStorage()
  2534.         executeCommand(stack, cmd, localVars, cFCount)
  2535.         if topFrame.executeReturn then
  2536.             frame.executeReturn = topFrame.executeReturn
  2537.             frame.returnValues = topFrame.returnValues
  2538.             return
  2539.         end
  2540.         if frame.executeBreak then
  2541.             break
  2542.         end
  2543.         frame.offset = i + 1
  2544.         frame.executed = false
  2545.         saveStorage()
  2546.     end
  2547. end
  2548.  
  2549. function getVariableValue(name, localVars)
  2550.     for i = #localVars, 1, -1 do
  2551.         --for i = 1, count + 1 do
  2552.         local vars = localVars[i]
  2553.         if vars ~= nil then
  2554.             for n, v in pairs(vars.v) do
  2555.                 if name == n then
  2556.                     return v
  2557.                 end
  2558.             end
  2559.         end
  2560.     end
  2561.     return nil
  2562.     --error("var '" .. name .. "' isn't defined yet")
  2563. end
  2564.  
  2565. function executeGetValue(stack, value, localVars, methods, cFCount)
  2566.     if value.type == TYPE_VALUE then
  2567.         return value.value
  2568.     elseif value.type == TYPE_ARR then
  2569.         local vars = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, #value.vars)
  2570.         for i = 1, #value.vars do
  2571.             vars[i] = executeGetValue(stack, value.vars[i], localVars, methods, cFCount);
  2572.         end
  2573.         return vars
  2574.     elseif value.type == TYPE_TABLE then
  2575.         local tbl = instantiateClass(TURTI_TABLE_CLASS_PROTOTYPE.key)
  2576.         for key, v in pairs(value.vars) do
  2577.             tbl[executeGetValue(stack, key, localVars, methods, cFCount)] = executeGetValue(stack, v, localVars, methods, cFCount)
  2578.         end
  2579.         return tbl
  2580.     elseif value.type == TYPE_VAR then
  2581.         return getVariableValue(value.name, localVars)
  2582.     elseif value.type == TYPE_FUNCTION then
  2583.         return executeCommand(stack, value, localVars, cFCount)
  2584.     elseif value.type == TYPE_ARITHMETIC then
  2585.         local v = executeGetValue(stack, value.pts[1], localVars, methods, cFCount)
  2586.         if value.symbol == "+" then
  2587.             for i = 2, #value.pts do
  2588.                 local v1 = executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
  2589.                 if type(v) == "string" or type(v1) == "string" then
  2590.                     v = toString(v) .. toString(v1)
  2591.                 else
  2592.                     v = v + v1
  2593.                 end
  2594.             end
  2595.             return v
  2596.         elseif value.symbol == "-" then
  2597.             for i = 2, #value.pts do
  2598.                 v = v - executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
  2599.             end
  2600.             return v
  2601.         elseif value.symbol == "*" then
  2602.             for i = 2, #value.pts do
  2603.                 v = v * executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
  2604.             end
  2605.             return v
  2606.         elseif value.symbol == "/" then
  2607.             for i = 2, #value.pts do
  2608.                 v = v / executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
  2609.             end
  2610.             return v
  2611.         elseif value.symbol == "%" then
  2612.             for i = 2, #value.pts do
  2613.                 v = v % executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
  2614.             end
  2615.             return v
  2616.         end
  2617.     elseif value.type == TYPE_CONSTANT then
  2618.         for k, val in pairs(coreStorage.constants) do
  2619.             if (k == value.key) then
  2620.                 return val
  2621.             end
  2622.         end
  2623.         return nil
  2624.     elseif value.type == TYPE_CMP then
  2625.         local v = executeGetValue(stack, value.pts[1], localVars, methods, cFCount)
  2626.         local v1 = executeGetValue(stack, value.pts[2], localVars, methods, cFCount)
  2627.  
  2628.         if value.ch == ">" then
  2629.             return v > v1
  2630.         elseif value.ch == "<" then
  2631.             return v < v1
  2632.         elseif value.ch == ">=" then
  2633.             return v >= v1
  2634.         elseif value.ch == "<=" then
  2635.             return v <= v1
  2636.         elseif value.ch == "==" then
  2637.             return v == v1
  2638.         elseif value.ch == "!=" then
  2639.             return v ~= v1
  2640.         else
  2641.             error("unknown char: '" .. value.ch .. "'")
  2642.         end
  2643.     elseif value.type == TYPE_UNARY_OP_STATEMENT then
  2644.         local v = getVariableValue(value.name, localVars)
  2645.         local newValue
  2646.         if value.op == "++" then
  2647.             newValue = v + 1
  2648.         elseif value.op == "--" then
  2649.             newValue = v - 1
  2650.         elseif value.op == "_++" then
  2651.             newValue = v + 1
  2652.             v = v + 1
  2653.         elseif value.op == "_--" then
  2654.             newValue = v - 1
  2655.             v = v - 1
  2656.         end
  2657.         setVariable(value.name, newValue, cFCount, localVars)
  2658.         return v
  2659.     elseif value.type == TYPE_UNARY_OP then
  2660.         return -executeGetValue(stack, value.value, localVars, methods, cFCount)
  2661.     elseif value.type == TYPE_OR then
  2662.         for i = 1, #value.values do
  2663.             if executeGetValue(stack, value.values[i], localVars, methods, cFCount) then
  2664.                 return true
  2665.             end
  2666.         end
  2667.         return false
  2668.     elseif value.type == TYPE_AND then
  2669.         for i = 1, #value.values do
  2670.             if not executeGetValue(stack, value.values[i], localVars, methods, cFCount) then
  2671.                 return false
  2672.             end
  2673.         end
  2674.         return true
  2675.     elseif value.type == TYPE_PARENTHESES then
  2676.         return executeGetValue(stack, value.cmd, localVars, methods, cFCount)
  2677.     elseif value.type == TYPE_ARR_INDEX then
  2678.         local body = executeGetValue(stack, value.body, localVars, methods, cFCount)
  2679.         local index = executeGetValue(stack, value.index, localVars, methods, cFCount)
  2680.         if not isObject(body) then
  2681.             error(toString(body) .. " is not an object, cast using e.g. var@table or var@list")
  2682.         end
  2683.         return executeClassItemGetter(body, index)
  2684.     elseif value.type == TYPE_ATTRIBUTE then
  2685.         local v = toObject(executeGetValue(stack, value.cmd, localVars, methods, cFCount))
  2686.         if not isObject(v) then
  2687.             error(toString(v) .. " is not an object, cast using e.g. var@table or var@list")
  2688.         end
  2689.         return executeClassGetter(v, value.name)
  2690.     elseif value.type == TYPE_METHOD then
  2691.         return executeCommand(stack, value, localVars, cFCount)
  2692.     elseif value.type == TYPE_CAST then
  2693.         local v = executeGetValue(stack, value.value, localVars, methods, cFCount)
  2694.         return executeClassCast(v, value.key)
  2695.     elseif value.type == TYPE_FUNCTION_HANDLE then
  2696.         local method = methods[value.name]
  2697.         if method == nil then
  2698.             error("method " .. value.name .. " not found")
  2699.         end
  2700.         return {
  2701.             type = "function handle",
  2702.             name = value.name
  2703.         }
  2704.     else
  2705.         printTable(value)
  2706.         error("unknown type: " .. value.type)
  2707.     end
  2708. end
  2709.  
  2710. function checkMethodValidity(methods)
  2711.     local definedNames = {}
  2712.     for name, v in pairs(methods) do
  2713.         definedNames[name] = v.pars
  2714.     end
  2715.     for i, v in ipairs(methods) do
  2716.         if v.type == TYPE_USER_FUNCTION then
  2717.             checkCmdsValidity(v.cmds, definedNames)
  2718.         end
  2719.     end
  2720. end
  2721.  
  2722. function checkCmdsValidity(cmds, definedNames)
  2723.     for i, cmd in ipairs(cmds) do
  2724.         if cmd.type == TYPE_FUNCTION then
  2725.             if definedNames[cmd.name] == nil then
  2726.                 error("function '" .. cmd.name .. "' isn't defined")
  2727.             end
  2728.             local args = definedNames[cmd.name]
  2729.             local cStart = args[1]
  2730.             local cEnd = cStart
  2731.             if #args > 1 then
  2732.                 cEnd = args[2]
  2733.             end
  2734.             local count = #cmd.pars
  2735.             if count < cStart or (count > cEnd and cEnd ~= -1) then
  2736.                 error("function '" .. cmd.name .. "' takes between " .. cStart .. " and " .. cEnd .. " argument(s) (" .. count .. " given)")
  2737.             end
  2738.             checkCmdsValidity(cmd.pars, definedNames)
  2739.         elseif cmd.type == TYPE_CONTROL_FLOW then
  2740.             if cmd.typeCF == TYPE_CF_WHILE then
  2741.                 checkCmdsValidity(cmd.body, definedNames)
  2742.             elseif cmd.typeCF == TYPE_CF_FOR then
  2743.                 checkCmdsValidity({ cmd.first, cmd.second, cmd.third }, definedNames)
  2744.                 checkCmdsValidity(cmd.body, definedNames)
  2745.             elseif cmd.typeCF == TYPE_CF_IF then
  2746.                 for i, block in ipairs(cmd.ifCmdBlocks) do
  2747.                     if cmd.cond ~= nil then
  2748.                         checkCmdsValidity({ block.cond }, definedNames)
  2749.                     end
  2750.                     checkCmdsValidity(block.body, definedNames)
  2751.                 end
  2752.             else
  2753.                 error("unknown typeCF: " .. cmd.typeCF)
  2754.             end
  2755.         elseif cmd.type == TYPE_ARR then
  2756.             checkCmdsValidity(cmd.vars, definedNames)
  2757.         elseif cmd.type == TYPE_ARITHMETIC then
  2758.             checkCmdsValidity(cmd.pts, definedNames)
  2759.         elseif cmd.type == TYPE_OR or cmd.type == TYPE_AND then
  2760.             checkCmdsValidity(cmd.values, definedNames)
  2761.         elseif cmd.type == TYPE_DECLARATION then
  2762.             checkCmdsValidity({ cmd.value }, definedNames)
  2763.         elseif cmd.type == TYPE_POLY_DECLARATION then
  2764.             checkCmdsValidity({ cmd.value }, definedNames)
  2765.         elseif cmd.type == TYPE_ARR_INDEX_DECLARATION then
  2766.             checkCmdsValidity({ cmd.index, cmd.arr, cmd.value }, definedNames)
  2767.         elseif cmd.type == TYPE_PARENTHESES then
  2768.             checkCmdsValidity({ cmd.cmd }, definedNames)
  2769.         elseif cmd.type == TYPE_UNARY_OP then
  2770.             checkCmdsValidity({ cmd.value }, definedNames)
  2771.         elseif cmd.type == TYPE_ARR_INDEX then
  2772.             checkCmdsValidity({ cmd.body, cmd.index }, definedNames)
  2773.         elseif cmd.type == TYPE_ATTRIBUTE then
  2774.             checkCmdsValidity({ cmd.cmd }, definedNames)
  2775.         elseif cmd.type == TYPE_METHOD then
  2776.             local cmds1 = {}
  2777.             table.insert(cmds1, cmd.cmd)
  2778.             for _, c in ipairs(cmd.pars) do
  2779.                 table.insert(cmds1, c)
  2780.             end
  2781.             checkCmdsValidity(cmds1, definedNames)
  2782.         elseif cmd.type ~= TYPE_CONSTANT and cmd.type ~= TYPE_VAR and cmd.type ~= TYPE_VALUE and cmd.type ~= TYPE_CMP
  2783.                 and cmd.type ~= TYPE_UNARY_OP_STATEMENT then
  2784.             printTable(cmd)
  2785.             error("unknown type: " .. cmd.type)
  2786.         end
  2787.     end
  2788. end
  2789.  
  2790. function write(data)
  2791.     io.write(data)
  2792.     io.flush()
  2793.     if fOut then
  2794.         writeToOpenFile(fOut, data)
  2795.     end
  2796. end
  2797.  
  2798. function writeToDebug(data)
  2799.     if fDebug then
  2800.         writeToOpenFile(fDebug, data)
  2801.     end
  2802. end
  2803.  
  2804. function writeToOpenFile(file, data)
  2805.     if not test then
  2806.         file.write(data)
  2807.         file.flush()
  2808.     else
  2809.         local oldOut = io.output()
  2810.         io.output(file)
  2811.         io.write(data)
  2812.         io.flush()
  2813.         io.output(oldOut)
  2814.     end
  2815. end
  2816.  
  2817. function printPolyTo(write, ...)
  2818.     local args = table.pack(...)
  2819.     if #args == 0 then
  2820.         write("null\n")
  2821.         return
  2822.     end
  2823.     local first = true
  2824.     for i = 1, args.n do
  2825.         local v = args[i]
  2826.         if first then
  2827.             first = false
  2828.         else
  2829.             write(" ")
  2830.         end
  2831.         if v == nil then
  2832.             write("null")
  2833.         elseif type(v) == "table" then
  2834.             printTable(v, true)
  2835.         else
  2836.             write(tostring(v))
  2837.         end
  2838.     end
  2839.     write("\n")
  2840. end
  2841.  
  2842. function printPoly(...)
  2843.     printPolyTo(write, ...)
  2844. end
  2845.  
  2846. function printDebug(...)
  2847.     printPolyTo(writeToDebug, ...)
  2848. end
  2849.  
  2850. function printTable(tbl, suppressLinebreak)
  2851.     printTableIndex(tbl, {}, true)
  2852.     if not suppressLinebreak then
  2853.         write("\n")
  2854.     end
  2855. end
  2856.  
  2857. function printTableIndex(tbl, alreadyUsed, topLevel)
  2858.     if tbl == nil then
  2859.         write("null")
  2860.     elseif type(tbl) == "table" then
  2861.         if tableContainsValue(alreadyUsed, tbl) then
  2862.             write("already used")
  2863.         elseif isObject(tbl) then
  2864.             write(executeClassToString(tbl))
  2865.         elseif tbl._turti_array_length then
  2866.             table.insert(alreadyUsed, tbl)
  2867.             io.write("[")
  2868.             local first = true
  2869.             for i = 1, tbl._turti_array_length do
  2870.                 if first then
  2871.                     first = false
  2872.                 else
  2873.                     io.write(", ")
  2874.                 end
  2875.                 printTableIndex(tbl[i], alreadyUsed)
  2876.             end
  2877.             for key, value in pairs(tbl) do
  2878.                 if key == "_turti_array_length" then
  2879.                 elseif type(key) ~= "number" or key < 1 or key > tbl._turti_array_length then
  2880.                     if first then
  2881.                         first = false
  2882.                     else
  2883.                         io.write(", ")
  2884.                     end
  2885.                     io.write(key .. ": ");
  2886.                     printTableIndex(value, alreadyUsed)
  2887.                 end
  2888.             end
  2889.             io.write("]")
  2890.         else
  2891.             table.insert(alreadyUsed, tbl)
  2892.             write("[")
  2893.             local first = true
  2894.             for i, v in pairs(tbl) do
  2895.                 if first then
  2896.                     first = false
  2897.                 else
  2898.                     write(", ")
  2899.                 end
  2900.                 printTableIndex(i, alreadyUsed)
  2901.                 write(": ");
  2902.                 printTableIndex(v, alreadyUsed)
  2903.             end
  2904.             write("]")
  2905.         end
  2906.     elseif type(tbl) == "string" then
  2907.         if not topLevel then
  2908.             write('"')
  2909.             write(tbl)
  2910.             write('"')
  2911.         else
  2912.             write(tbl)
  2913.         end
  2914.     else
  2915.         write(tostring(tbl))
  2916.     end
  2917. end
  2918.  
  2919. function tableContainsValue(table, value)
  2920.     if not table then
  2921.         return false
  2922.     end
  2923.     for i, v in pairs(table) do
  2924.         if v == value then
  2925.             return true
  2926.         end
  2927.     end
  2928.     return false
  2929. end
  2930.  
  2931. function getPastebin(pastebinId, fileName)
  2932.     if fs.exists(fileName) then
  2933.         shell.run("delete", impFileName)
  2934.     end
  2935.     while true do
  2936.         local result, err = shell.run("pastebin", "get", pastebinId, fileName)
  2937.         if fs.exists(fileName) then
  2938.             return
  2939.         end
  2940.         print("downloading " .. pastebinId .. ">" .. fileName .. " failed, retrying in 1s")
  2941.         sleep(1)
  2942.     end
  2943. end
  2944.  
  2945. function importLibrary(pastebinId)
  2946.     errInfo = { type = "plain", "import library " .. pastebinId, parent = errInfo }
  2947.     local impFileName = "libraryIn_" .. pastebinId
  2948.     getPastebin(pastebinId, impFileName)
  2949.     local data = require(impFileName)
  2950.     if data.name then
  2951.         if fs.exists("lib_" .. data.name) then
  2952.             shell.run("delete", "lib_" .. data.name)
  2953.         end
  2954.         shell.run("mv", impFileName, "lib_" .. data.name)
  2955.         loadLibrary("lib_" .. data.name)
  2956.     else
  2957.         loadLibrary(impFileName)
  2958.     end
  2959.     errInfo = errInfo.parent
  2960. end
  2961.  
  2962. function loadLibrary(name)
  2963.     local data
  2964.     if test then
  2965.         data = require("libraries/" .. name)
  2966.     else
  2967.         data = require(name)
  2968.     end
  2969.  
  2970.     if data.dependencies then
  2971.         for _, dependency in ipairs(data.dependencies) do
  2972.             importLibrary(dependency)
  2973.         end
  2974.     end
  2975.  
  2976.     if data.name then
  2977.         print("importing library '" .. data.name .. "'")
  2978.     else
  2979.         print("importing unnamed library")
  2980.     end
  2981.     local libraryDefinition = {}
  2982.     if data.api then
  2983.         for fName, definition in pairs(data.api) do
  2984.             if type(definition) == "function" then
  2985.                 local info = debug.getinfo(definition)
  2986.  
  2987.                 if info.isvararg then
  2988.                     libraryDefinition[fName] = {
  2989.                         type = TYPE_DEFAULT_FUNCTION,
  2990.                         pars = { info.nparams, -1 },
  2991.                         fct = definition
  2992.                     }
  2993.                 else
  2994.                     libraryDefinition[fName] = {
  2995.                         type = TYPE_DEFAULT_FUNCTION,
  2996.                         pars = { info.nparams },
  2997.                         fct = definition
  2998.                     }
  2999.                 end
  3000.             else
  3001.                 if not definition.fct then
  3002.                     error("field 'fct' isn't defined in api>" .. fName .. " of library '" .. name .. "'")
  3003.                 end
  3004.                 libraryDefinition[fName] = {
  3005.                     type = TYPE_DEFAULT_FUNCTION,
  3006.                     pars = definition.pars or { debug.getinfo(definition.fct).nparams },
  3007.                     fct = definition.fct,
  3008.                     nonCritical = definition.nonCritical
  3009.                 }
  3010.             end
  3011.         end
  3012.     end
  3013.  
  3014.     if data.classes then
  3015.         for _, prototype in ipairs(data.classes) do
  3016.             CLASS_PROTOTYPES[prototype.key] = prototype
  3017.         end
  3018.     end
  3019.     if data.threads then
  3020.         for _, v in ipairs(data.threads) do
  3021.             table.insert(threads, v)
  3022.         end
  3023.     end
  3024.  
  3025.     if data.onInitStorage then
  3026.         fctsSetStorageData[name] = data.onInitStorage
  3027.     end
  3028.  
  3029.     if data.onInitPersistentStorage then
  3030.         fctsSetPersistentStorageData[name] = data.onInitPersistentStorage
  3031.     end
  3032.  
  3033.     if data.hooks then
  3034.         for _, hook in ipairs(data.hooks) do
  3035.             table.insert(libraryMethodHooks, hook)
  3036.         end
  3037.     end
  3038.  
  3039.     if data.onSetup then
  3040.         data.onSetup()
  3041.     end
  3042.  
  3043.     merge(libraryDefinition, defaultFunctions)
  3044. end
  3045.  
  3046. function merge(source, target)
  3047.     for k, v in pairs(source) do
  3048.         target[k] = v
  3049.     end
  3050. end
  3051.  
  3052. function installPreExecutionHook(library, key, hookId, hook, conditional)
  3053.     if not library._turtiCoreHooks then
  3054.         library._turtiCoreHooks = {}
  3055.     end
  3056.     if not hookedLibraries[library] then
  3057.         -- clear hooks not assigned by this program instance
  3058.         hookedLibraries[library] = true
  3059.         for _, hookInfo in pairs(library._turtiCoreHooks) do
  3060.             hookInfo.hooks = {}
  3061.         end
  3062.     end
  3063.     local hooksInfos = library._turtiCoreHooks
  3064.     if not hooksInfos[key] then
  3065.         local hooksInfo = { ogFct = library[key], hooks = {} }
  3066.         hooksInfos[key] = hooksInfo
  3067.         library[key] = function(...)
  3068.             local valid = true
  3069.             for _, h in pairs(hooksInfos[key].hooks) do
  3070.                 local result = h.hook()
  3071.                 if h.conditional and not result then
  3072.                     valid = false
  3073.                 end
  3074.             end
  3075.             if not valid then
  3076.                 return
  3077.             end
  3078.             return hooksInfo.ogFct(...)
  3079.         end
  3080.     end
  3081.     local hooksInfo = hooksInfos[key]
  3082.     hooksInfo.hooks[hookId] = { hook = hook, conditional = conditional }
  3083.     return function(...)
  3084.         local valid = true
  3085.         for n, h in pairs(hooksInfo.hooks) do
  3086.             if n ~= hookId then
  3087.                 local result = h.hook()
  3088.                 if h.conditional and not result then
  3089.                     valid = false
  3090.                 end
  3091.             end
  3092.         end
  3093.         if not valid then
  3094.             return
  3095.         end
  3096.         return hooksInfo.ogFct(...)
  3097.     end
  3098. end
  3099.  
  3100. local args = {}
  3101. local kwargs = {}
  3102.  
  3103. for i, v in ipairs(RAW_ARGS) do
  3104.     local pts = splitTextStructural(v, "=")
  3105.     if #pts == 1 then
  3106.         table.insert(args, pts[1])
  3107.     else
  3108.         kwargs[pts[1]] = pts[2]
  3109.     end
  3110. end
  3111.  
  3112. test = false
  3113. if kwargs["test"] == "true" then
  3114.     test = true
  3115.     os.pullEvent = function(ev)
  3116.         print("Warning: event " .. ev .. " can't be pulled in test mode")
  3117.         ThreadManager.terminateRunningThread()
  3118.     end
  3119. end
  3120. local restartOnError = false
  3121. if kwargs["restartOnError"] and kwargs["restartOnError"] == "true" then
  3122.     print("activated restart on error")
  3123.     restartOnError = true
  3124. end
  3125.  
  3126.  
  3127. --[[if test then
  3128.     local data = require("std")
  3129.     data[1](true, {})
  3130.     fctsSetStorageData = { df = data[2] }
  3131.     defaultFunctions = data[3]
  3132.     initToContinue = data[4]
  3133. else
  3134.     local data = require("std")
  3135.     data[1](false, { shell = shell, turtle = turtle })
  3136.     fctsSetStorageData = { df = data[2] }
  3137.     defaultFunctions = data[3]
  3138.     initToContinue = data[4]
  3139. end]]
  3140.  
  3141. loadLibrary("std")
  3142.  
  3143. if kwargs["libraries"] ~= nil then
  3144.     for _, libraryName in ipairs(loadValue(kwargs["libraries"])) do
  3145.         loadLibrary(libraryName)
  3146.     end
  3147. end
  3148.  
  3149. local fileName = args[1]
  3150. local stackFileName = fileName .. ".stack"
  3151. local dataFileName = fileName .. ".id"
  3152. local debugFileName = fileName .. ".debug"
  3153. if fileExists(debugFileName) then
  3154.     deleteFile(debugFileName)
  3155. end
  3156. if false then
  3157.     if not test then
  3158.         fOut = fs.open("out", "w")
  3159.         fProfile = fs.open(fileName .. ".profile", "w")
  3160.         fDebug = fs.open(debugFileName, "w")
  3161.     else
  3162.         fOut = io.open("out", "w")
  3163.         fProfile = io.open(fileName .. ".profile", "w")
  3164.         fDebug = io.open(debugFileName, "w")
  3165.     end
  3166. end
  3167. local persistentStorageDirName = fileName .. "_persistent"
  3168. if args[3] ~= nil then
  3169.     if args[3] == "restart" then
  3170.         print("restart")
  3171.         if test then
  3172.             os.remove(stackFileName)
  3173.         else
  3174.             fs.delete(stackFileName)
  3175.         end
  3176.     else
  3177.         error("unknown second argument: '" .. args[3] .. "'")
  3178.     end
  3179. end
  3180.  
  3181. local function getStackErrorInfos()
  3182.     local info = "turti stack trace:\n"
  3183.     for stack, _ in pairs(runningStacks) do
  3184.         local threadName = stack.threadName
  3185.         if not threadName then
  3186.             threadName = "unknown"
  3187.         end
  3188.         info = info .. "thread '" .. threadName .. "':\n"
  3189.         local frameInfo = ""
  3190.         for _, frame in ipairs(stack.frames) do
  3191.             local cfInfo = ""
  3192.             for _, cfFrame in ipairs(frame.cFStack) do
  3193.                 local cFName = "?"
  3194.                 if cfFrame.cmd.typeCF == TYPE_CF_FOR then
  3195.                     cFName = "for"
  3196.                 elseif cfFrame.cmd.typeCF == TYPE_CF_WHILE then
  3197.                     cFName = "while"
  3198.                 elseif cfFrame.cmd.typeCF == TYPE_CF_IF then
  3199.                     cFName = "if"
  3200.                 end
  3201.                 cfInfo = cfInfo .. "        statement " .. tostring(cfFrame.offset) .. " in " .. cFName .. "\n"
  3202.             end
  3203.             frameInfo = cfInfo .. frameInfo
  3204.             frameInfo = "    statement " .. tostring(frame.offset) .. " in function '" .. frame.name .. "'\n" .. frameInfo
  3205.         end
  3206.         info = info .. frameInfo
  3207.     end
  3208.  
  3209.     return info;
  3210. end
  3211.  
  3212. function logError(err)
  3213.     print("error: ", splitText(err, "\n\r")[1])
  3214.     local fileTxt = tostring(err)
  3215.     if errInfo then
  3216.         local info
  3217.         if errInfo.type == "cmd" then
  3218.             info = "command: " .. getFileDataCmd(errInfo.value) .. ""
  3219.         elseif errInfo.type == "plain" then
  3220.             info = errInfo.value
  3221.         elseif errInfo.type then
  3222.             print("unknown error info type: " .. errInfo.type)
  3223.         end
  3224.         if info then
  3225.             info = "(" .. info .. ")"
  3226.             print(info)
  3227.             fileTxt = fileTxt .. "\n" .. info
  3228.         end
  3229.     end
  3230.     fileTxt = fileTxt .. "\n\n" .. getStackErrorInfos()
  3231.     print("(check file 'err' for stacktrace)")
  3232.  
  3233.     writeToFile("err", fileTxt)
  3234. end
  3235.  
  3236. local error = false
  3237. local started = os.clock()
  3238. while true do
  3239.     local status, err = xpcall(loadProgram, debug.traceback, fileName, stackFileName, dataFileName, persistentStorageDirName, 1)
  3240.     if not status then
  3241.         error = true
  3242.         logError(err)
  3243.         break
  3244.     else
  3245.         break
  3246.     end
  3247. end
  3248. if not test then
  3249.     for _,v in pairs({fOut,fProfile,fDebug}) do
  3250.         if v then
  3251.             v.close()
  3252.         end
  3253.     end
  3254. else
  3255.     for _,v in pairs({fOut,fProfile,fDebug}) do
  3256.         if v then
  3257.             io.close(v)
  3258.         end
  3259.     end
  3260. end
  3261.  
  3262. if error and restartOnError then
  3263.     if os.clock() - started < 20 then
  3264.         deleteFile(stackFileName)
  3265.     end
  3266.  
  3267.     shell.run("mkdir", "errLogs1")
  3268.  
  3269.     for i = 0, 9 do
  3270.         local fileNameErr = "errLogs/err_" .. tostring(i)
  3271.         local fileNameOut = "errLogs/out_" .. tostring(i)
  3272.         local nextFileNameErr = "errLogs/err_" .. tostring((i + 1) % 10)
  3273.         local nextFileNameOut = "errLogs/out_" .. tostring((i + 1) % 10)
  3274.         if not fileExists(fileNameErr) then
  3275.             shell.run("mv", "err", fileNameErr)
  3276.             shell.run("mv", "out", fileNameOut)
  3277.             if fileExists(nextFileNameErr) then
  3278.                 deleteFile(nextFileNameErr)
  3279.             end
  3280.             if fileExists(nextFileNameOut) then
  3281.                 deleteFile(nextFileNameOut)
  3282.             end
  3283.             break
  3284.         end
  3285.     end
  3286.  
  3287.     print("configured to restart on error")
  3288.     print("restarting in 5s")
  3289.     sleep(5)
  3290.     os.reboot()
  3291. end
  3292.  
  3293.  
Add Comment
Please, Sign In to add comment