Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local RAW_ARGS = { ... }
- local TYPE_FUNCTION = 1
- local TYPE_VALUE = 2
- local TYPE_VAR = 3
- local TYPE_USER_FUNCTION = 4
- local TYPE_DEFAULT_FUNCTION = 5
- local TYPE_ARR = 6
- local TYPE_CONSTANT = 9
- local TYPE_DECLARATION = 10
- local TYPE_CMP = 11
- local TYPE_CONTROL_FLOW = 12
- local TYPE_OR = 13
- local TYPE_AND = 15
- local TYPE_POLY_DECLARATION = 16
- local TYPE_UNARY_OP_STATEMENT = 17
- local TYPE_PARENTHESES = 20
- local TYPE_ARITHMETIC = 21
- local TYPE_UNARY_OP = 18
- local TYPE_ARR_INDEX = 22
- local TYPE_ARR_INDEX_DECLARATION = 24
- local TYPE_HOOK_FUNCTION = 25
- local TYPE_ATTRIBUTE = 26
- local TYPE_METHOD = 27
- local TYPE_TABLE = 28
- local TYPE_NON_CRITICAL_SECTION = 29
- local TYPE_CAST = 30
- local TYPE_FUNCTION_HANDLE = 31
- local TYPE_EXCLUSIVE_SECTION = 32
- local TYPE_CF_WHILE = 1
- local TYPE_CF_IF = 2
- local TYPE_CF_FOR = 3
- local coreStorage
- local persistentStorage
- local defaultFunctions = {}
- local fctsSetStorageData = {}
- local fctsSetPersistentStorageData = {}
- local libraryMethodHooks = {}
- local initToContinue
- local test
- local errInfo
- local methods
- local hookedLibraries = {}
- local threads = {}
- local nonCriticalSectionCount = 0
- local fOut, fProfile, fDebug
- local turtiRunning = false
- local tTurtiStart
- local CLASS_PROTOTYPES = {}
- ThreadManager = {
- queuedThreads = {},
- runningThreads = {},
- privilegedThread = nil,
- privilegedThreadCount = 0,
- currentThread = nil
- }
- function ThreadManager.run()
- local eventData = { n = 0 }
- while true do
- for i = 1, #ThreadManager.runningThreads do
- local thread = ThreadManager.runningThreads[i]
- local first = true
- while #thread.eventQueue > 0 or first do
- if ThreadManager.privilegedThread == nil or ThreadManager.privilegedThread == thread then
- local event
- if first then
- first = false
- if #thread.eventQueue > 0 then
- event = table.remove(thread.eventQueue)
- else
- event = { nil }
- end
- else
- event = table.remove(thread.eventQueue)
- end
- if not thread.eventFilter or thread.eventFilter == event[1] or event[1] == "terminate" then
- ThreadManager.currentThread = thread
- local ok, ret = coroutine.resume(thread.coroutine, table.unpack(event, 1, event.n))
- ThreadManager.currentThread = nil
- if not ok then
- error(ret, 0)
- else
- thread.eventFilter = ret
- end
- end
- else
- break
- end
- if coroutine.status(thread.coroutine) == "dead" then
- break
- end
- end
- end
- local i = 1
- while i <= #ThreadManager.runningThreads do
- local thread = ThreadManager.runningThreads[i]
- if coroutine.status(thread.coroutine) == "dead" or thread.terminate then
- table.remove(ThreadManager.runningThreads, i)
- else
- i = i + 1
- end
- end
- local addedThread = false
- while true do
- local thread = table.remove(ThreadManager.queuedThreads)
- if not thread then
- break
- end
- addedThread = true
- thread.coroutine = coroutine.create(thread.fct)
- thread.eventFilter = nil
- table.insert(ThreadManager.runningThreads, thread)
- end
- if addedThread and not test then
- os.queueEvent("startThread")
- end
- local hasNonDaemon
- for _, thread in ipairs(ThreadManager.runningThreads) do
- if not thread.isDaemon then
- hasNonDaemon = true
- break
- end
- end
- if not hasNonDaemon then
- return
- end
- if not test then
- eventData = table.pack(os.pullEventRaw())
- end
- if eventData[1] ~= nil then
- for _, thread in ipairs(ThreadManager.runningThreads) do
- table.insert(thread.eventQueue, eventData)
- end
- end
- if eventData[1] == "terminate" then
- error("Terminated")
- end
- end
- end
- function ThreadManager.terminateRunningThread()
- ThreadManager.currentThread.terminate = true
- yield()
- end
- function ThreadManager.startThread(fct, isDaemon, name, storage)
- table.insert(ThreadManager.queuedThreads, { fct = function()
- local status, err = xpcall(fct, debug.traceback)
- if not status then
- error(err)
- end
- end, isDaemon = isDaemon, name = name, storage = storage, eventQueue = {} })
- end
- function ThreadManager.blockExecutionOfOtherThreads()
- ThreadManager.privilegedThread = ThreadManager.currentThread
- ThreadManager.privilegedThreadCount = ThreadManager.privilegedThreadCount + 1
- if ThreadManager.currentThread then
- printDebug("block all threads except ", ThreadManager.currentThread.name)
- end
- end
- function ThreadManager.unblockExecutionOfOtherThreads()
- ThreadManager.privilegedThreadCount = ThreadManager.privilegedThreadCount - 1
- if ThreadManager.privilegedThreadCount < 1 then
- ThreadManager.privilegedThread = nil
- ThreadManager.privilegedThreadCount = 0
- printDebug("unblock threads")
- end
- end
- local TURTI_THREAD_CLASS_PROTOTYPE = {
- key = "thread",
- getters = {
- name = function(tbl, context)
- return context.name
- end,
- isDaemon = function(tbl, context)
- return context.isDaemon
- end
- },
- init = function(obj, name, isDaemon)
- return { name = name, isDaemon = isDaemon }
- end
- }
- defaultFunctions.getThread = {
- type = TYPE_DEFAULT_FUNCTION,
- pars = { 0 },
- nonCritical = true,
- fct = function()
- local current = ThreadManager.currentThread
- if not current.threadObject then
- current.threadObject = instantiateClass("thread",
- ThreadManager.currentThread.name,
- ThreadManager.currentThread.isDaemon)
- end
- return current.threadObject
- end
- }
- gpsAdapter = {
- locate = function()
- while true do
- local x, y, z = gps.locate()
- if x == nil then
- printPoly("unable to execute gps.locate(), retrying in 1s")
- sleep(1)
- else
- return { x, y, z }
- end
- end
- end
- }
- function isTurtiRunning()
- return turtiRunning
- end
- local function executeClassToString(v)
- local prototype = v._class_prototype
- if not prototype then
- printTable(v)
- error("value is not an object")
- end
- local to_string = prototype.to_string
- if not to_string then
- return "<object of type '" .. prototype.key .. "'>"
- end
- return to_string(v, v._class_context)
- end
- function isObject(v)
- if type(v) ~= "table" then
- return false
- end
- if v._class_prototype then
- return true
- end
- return false
- end
- function getClassContext(v)
- return v._class_context
- end
- function toString(v)
- if isObject(v) then
- return executeClassToString(v)
- elseif v == nil then
- return "null"
- elseif type(v) == "boolean" then
- if v then
- return "true"
- else
- return "false"
- end
- else
- return tostring(v)
- end
- end
- local TURTI_TABLE_CLASS_PROTOTYPE = {
- key = "table",
- to_string = function(tbl, context)
- local result = "{"
- local first = true
- for key, value in pairs(tbl) do
- if key ~= "_class_context" and key ~= "_class_prototype" then
- if first then
- first = false
- else
- result = result .. ", "
- end
- result = result .. toString(key) .. "=" .. toString(value)
- end
- end
- return result .. "}"
- end,
- methods = {
- remove = function(tbl, context, value)
- local v = tbl[value]
- if v then
- table.remove(tbl, value)
- return v
- else
- return nil
- end
- end,
- keys = function(tbl, context)
- local keys = instantiateClass('list', 0)
- for key, _ in pairs(tbl) do
- if key ~= "_class_context" and key ~= "_class_prototype" then
- executeClassMethod(keys, 'add', { key })
- end
- end
- return keys
- end
- },
- item_getter = function(tbl, context, index)
- return tbl[index]
- end,
- item_setter = function(tbl, context, index, value)
- tbl[index] = value
- end,
- cast = function(value)
- local object = instantiateClass("table")
- for k, v in pairs(value) do
- object[k] = v
- end
- return object
- end
- }
- local TURTI_LIST_CLASS_PROTOTYPE = {
- key = "list",
- to_string = function(tbl, context)
- local result = "["
- for i = 1, context.length do
- if i ~= 1 then
- result = result .. ", "
- end
- result = result .. toString(tbl[i])
- end
- return result .. "]"
- end,
- methods = {
- sort = function(tbl, context, key, descending)
- local prototype = tbl._class_prototype
- local c = tbl._class_context
- tbl._class_prototype = nil
- tbl._class_context = nil
- local sortingFct
- if key then
- sortingFct = function(a, b)
- return a[key] < b[key]
- end
- else
- sortingFct = function(a, b)
- return a < b
- end
- end
- if descending then
- table.sort(tbl, function(a, b)
- return sortingFct(b, a)
- end)
- else
- table.sort(tbl, sortingFct)
- end
- tbl._class_prototype = prototype
- tbl._class_context = c
- end,
- add = function(tbl, context, value)
- context.length = context.length + 1
- tbl[context.length] = value
- end,
- remove = function(tbl, context, value)
- for i = 1, context.length do
- if tbl[i] == value then
- tbl._class_prototype.privateMethods.shiftLeft(tbl, i + 1, context.length)
- context.length = context.length - 1
- return true
- end
- end
- return false
- end,
- removeAt = function(tbl, context, index)
- if index < 0 or context.length <= index then
- error("index out of range: " .. index)
- end
- local value = tbl[index + 1]
- tbl._class_prototype.privateMethods.shiftLeft(tbl, index + 2, context.length)
- context.length = context.length - 1
- return value
- end
- },
- privateMethods = {
- shiftLeft = function(tbl, iStart, iEnd)
- for i = iStart, iEnd do
- tbl[i - 1] = tbl[i]
- end
- table.remove(tbl, iEnd)
- end
- },
- getters = {
- length = function(tbl, context)
- return context.length
- end
- },
- item_getter = function(tbl, context, index)
- if type(index) ~= "number" then
- error("index is not a number")
- end
- if index < 0 or context.length <= index then
- error("index out of range: " .. index)
- end
- return tbl[index + 1]
- end,
- item_setter = function(tbl, context, index, value)
- if type(index) ~= "number" then
- error("index is not a number")
- end
- if index < 0 or context.length <= index then
- error("index out of range: " .. index)
- end
- tbl[index + 1] = value
- end,
- init = function(object, length)
- if not length then
- error("attribute 1 (length) not provided")
- end
- return { length = length }
- end,
- cast = function(value)
- local len = #value
- local object = instantiateClass("list", len)
- for i = 1, len do
- object[i] = value[i]
- end
- return object
- end
- }
- local TURTI_STRING_CLASS_PROTOTYPE = {
- key = "string",
- to_string = function(obj, context)
- return context.str
- end,
- methods = {
- find = function(tbl, context, pattern)
- return context.str:find(pattern) - 1
- end,
- substring = function(tbl, context, ind1, ind2)
- if ind2 == nil then
- ind2 = #context.str
- end
- return context.str:sub(ind1 + 1, ind2)
- end
- },
- getters = {
- length = function(tbl, context)
- return #context.str
- end
- },
- init = function(obj, str)
- return { str = str }
- end
- }
- CLASS_PROTOTYPES[TURTI_LIST_CLASS_PROTOTYPE.key] = TURTI_LIST_CLASS_PROTOTYPE
- CLASS_PROTOTYPES[TURTI_TABLE_CLASS_PROTOTYPE.key] = TURTI_TABLE_CLASS_PROTOTYPE
- CLASS_PROTOTYPES[TURTI_STRING_CLASS_PROTOTYPE.key] = TURTI_STRING_CLASS_PROTOTYPE
- CLASS_PROTOTYPES[TURTI_THREAD_CLASS_PROTOTYPE.key] = TURTI_THREAD_CLASS_PROTOTYPE
- local function toObject(v)
- if isObject(v) then
- return v;
- elseif type(v) == "string" then
- return instantiateClass("string", v)
- else
- return v;
- end
- end
- local function executeClassItemGetter(v, index)
- v = toObject(v)
- local prototype = v._class_prototype
- if not prototype then
- printTable(v)
- error("value is not an object")
- end
- if not prototype.item_getter then
- error("type '" .. prototype.key .. "' doesn't implement an item setter ")
- end
- return prototype.item_getter(v, v._class_context, index)
- end
- local function executeClassItemSetter(v, index, value)
- v = toObject(v)
- local prototype = v["_class_prototype"]
- if not prototype then
- printTable(v)
- error("value is not an object")
- end
- local item_setter = prototype["item_setter"]
- if not item_setter then
- error("type '" .. prototype.key .. "' doesn't implement an item setter ")
- end
- return item_setter(v, v._class_context, index, value)
- end
- function executeClassMethod(v, name, pars)
- v = toObject(v)
- if type(v) ~= "table" then
- printPoly(v)
- error("value is not an object")
- end
- local prototype = v["_class_prototype"]
- if not prototype then
- printTable(v)
- error("value is not an object")
- end
- local methods = prototype["methods"]
- if not methods then
- printTable(v)
- error("method '" .. name .. "' is not defined on object of type " .. prototype.key)
- end
- local method = methods[name]
- if not method then
- printTable(v)
- error("method '" .. name .. "' is not defined on object of type " .. prototype.key)
- end
- return method(v, v._class_context, table.unpack(pars))
- end
- local function executeClassGetter(v, name)
- v = toObject(v)
- local prototype = v["_class_prototype"]
- if not prototype then
- printTable(v)
- error("value is not an object")
- end
- local getters = prototype["getters"]
- if not getters then
- printTable(v)
- error("attribute '" .. name .. "' is not defined on object of type " .. prototype.key)
- end
- local getter = getters[name]
- if not getters then
- printTable(v)
- error("attribute '" .. name .. "' is not defined on object of type " .. prototype.key)
- end
- return getter(v, v._class_context)
- end
- local function findClassPrototype(key)
- local prototype = CLASS_PROTOTYPES[key]
- if not prototype then
- error("unknown class key: " .. key)
- end
- return prototype
- end
- local function executeClassCast(v, prototypeKey)
- if v == nil then
- return v
- end
- local prototype = findClassPrototype(prototypeKey)
- local cast = prototype["cast"]
- if not cast then
- printTable(v)
- error("object type " .. prototypeKey .. " doesn't support casting")
- end
- return cast(v)
- end
- function constructClass(prototypeKey, ...)
- local prototype = findClassPrototype(prototypeKey)
- local object = instantiateClass(prototypeKey)
- prototype.construct(object, object._class_context, ...)
- return object
- end
- function instantiateClass(prototypeKey, ...)
- local prototype = findClassPrototype(prototypeKey)
- local object = {
- _class_prototype = prototype
- }
- if prototype.init then
- object._class_context = prototype.init(object, ...)
- else
- object._class_context = {}
- end
- return object
- end
- function toLuaTable(tbl)
- if not isObject(tbl) then
- error("value is not an object")
- end
- if tbl._class_prototype.key ~= TURTI_TABLE_CLASS_PROTOTYPE.key then
- error("value is not a turti table")
- end
- local result = {}
- for key, v in pairs(tbl) do
- if key ~= "_class_context" and key ~= "_class_prototype" then
- result[key] = v
- end
- end
- return result
- end
- function toTurtiTable(tbl)
- local turtiTbl = instantiateClass(TURTI_TABLE_CLASS_PROTOTYPE.key)
- for k, v in pairs(tbl) do
- turtiTbl[k] = v
- end
- return turtiTbl
- end
- function toTurtiArray(tbl)
- local length = #tbl
- local array = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, length)
- for i = 1, #tbl do
- array[i] = tbl[i]
- end
- return array
- end
- function readAll(file)
- local f = assert(io.open(file, "rb"))
- local content = f:read("*all")
- f:close()
- return content
- end
- SPLIT_TEXT_CONSIDER_STRUCTURE = 1 -- 0b001
- SPLIT_TEXT_CONSIDER_ESCAPES = 2 -- 0b010
- SPLIT_TEXT_INCLUDE_EMPTY = 4 -- 0b100
- function splitTextStructural(source, letters)
- return splitText(source, letters, true)
- end
- function splitText(source, letters, considerStructure, considerEscapes, includeEmpty)
- local parts = {}
- local akt = "";
- local qM1 = 0;
- local qM2 = 0;
- local b1 = 0;
- local b2 = 0;
- local b3 = 0;
- local escaped = false
- for i = 1, #source do
- local z = source:sub(i, i)
- if considerEscapes and escaped then
- escaped = false
- akt = akt .. z
- else
- if considerStructure then
- if z == "'" and qM2 == 0 then
- qM1 = 1 - qM1;
- elseif z == "\"" and qM1 == 0 then
- qM2 = 1 - qM2;
- elseif qM1 == 0 and qM2 == 0 then
- if z == "(" then
- b1 = b1 + 1
- elseif z == ")" then
- b1 = b1 - 1
- elseif z == "[" then
- b2 = b2 + 1
- elseif z == "]" then
- b2 = b2 - 1
- elseif z == "{" then
- b3 = b3 + 1
- elseif z == "}" then
- b3 = b3 - 1
- end
- end
- end
- if considerEscapes and z == "\\" then
- escaped = true;
- end
- local split = false
- if b1 == 0 and b2 == 0 and b3 == 0 and qM1 == 0 and qM2 == 0 then
- for j = 1, #letters do
- if z == letters:sub(j, j) then
- split = true
- break
- end
- end
- end
- if split then
- if includeEmpty or akt ~= "" then
- table.insert(parts, akt)
- akt = ""
- end
- else
- akt = akt .. z
- end
- end
- end
- if includeEmpty or akt ~= "" then
- table.insert(parts, akt)
- end
- return parts
- end
- function splitTextPattern(source, letters, ignoreStructure)
- local parts = {}
- local akt = "";
- local qM1 = 0;
- local qM2 = 0;
- local b1 = 0;
- local b2 = 0;
- local b3 = 0;
- local i = 1
- while (i <= #source) do
- local z = source:sub(i, i)
- if not ignoreStructure then
- if z == "'" and qM2 == 0 then
- qM1 = 1 - qM1;
- elseif z == "\"" and qM1 == 0 then
- qM2 = 1 - qM2;
- elseif qM1 == 0 and qM2 == 0 then
- if z == "(" then
- b1 = b1 + 1
- elseif z == ")" then
- b1 = b1 - 1
- elseif z == "[" then
- b2 = b2 + 1
- elseif z == "]" then
- b2 = b2 - 1
- elseif z == "{" then
- b3 = b3 + 1
- elseif z == "}" then
- b3 = b3 - 1
- end
- end
- end
- local split = false
- if b1 == 0 and b2 == 0 and b3 == 0 and qM1 == 0 and qM2 == 0 then
- split = true
- for j = 1, #letters do
- if source:sub(i + j - 1, i + j - 1) ~= letters:sub(j, j) then
- split = false
- break
- end
- end
- end
- if split then
- if akt ~= "" then
- table.insert(parts, akt)
- akt = ""
- i = i + #letters - 1
- end
- else
- akt = akt .. z
- end
- i = i + 1
- end
- if akt ~= "" then
- table.insert(parts, akt)
- end
- return parts
- end
- function removeLetters(source, letters)
- local target = ""
- for i = 1, #source do
- local valid = true
- for j = 1, #letters do
- if source:sub(i, i) == letters:sub(j, j) then
- valid = false
- end
- end
- if valid then
- target = target .. source:sub(i, i)
- end
- end
- return target
- end
- function removeLettersIgnoreStrings(source, letters)
- local target = ""
- local b1 = false
- local b2 = false
- for i = 1, #source do
- local letter = source:sub(i, i)
- if letter == "'" and not b2 then
- b1 = not b1
- elseif letter == "\"" and not b1 then
- b2 = not b2
- end
- local valid = true
- if not b1 and not b2 then
- for j = 1, #letters do
- if letter == letters:sub(j, j) then
- valid = false
- end
- end
- end
- if valid then
- target = target .. letter
- end
- end
- return target
- end
- function removeValue(tbl, value)
- for i, v in pairs(tbl) do
- if v == value then
- table.remove(tbl, i)
- return
- end
- end
- end
- function readFileText(file)
- if test then
- return readAll(file)
- else
- local f = fs.open(file, "r")
- local text = f.readAll()
- f.close()
- return text
- end
- end
- function analyseCommands(source)
- local cmdsSource = splitTextStructural(source, ";")
- local cmds = {}
- for i, v in ipairs(cmdsSource) do
- if v ~= "" then
- if v:sub(1, 2) ~= "//" then
- table.insert(cmds, loadCommand(v))
- end
- end
- end
- return cmds
- end
- function nextOpenIndex(source, chClose, chOpen)
- local a = 0;
- local qM1 = 0;
- local qM2 = 0
- local len = #source
- for i = 1, len do
- local i1 = len - i + 1;
- local z = source:sub(i1, i1)
- if z == "\"" and qM2 == 0 then
- qM1 = 1 - qM1
- elseif z == "'" and qM1 == 0 then
- qM2 = 1 - qM2
- elseif z == chClose and qM1 == 0 and qM2 == 0 then
- a = a + 1
- elseif z == chOpen and qM1 == 0 and qM2 == 0 then
- a = a - 1
- if a == 0 then
- return i1
- end
- end
- end
- return -1
- end
- function nextCloseIndex(source, chOpen, chClose)
- local a = 0;
- local qM1 = 0;
- local qM2 = 0
- for i = 1, #source do
- local z = source:sub(i, i)
- if z == "\"" and qM2 == 0 then
- qM1 = 1 - qM1
- elseif z == "'" and qM1 == 0 then
- qM2 = 1 - qM2
- elseif z == chOpen and qM1 == 0 and qM2 == 0 then
- a = a + 1
- elseif z == chClose and qM1 == 0 and qM2 == 0 then
- a = a - 1
- if a == 0 then
- return i
- end
- end
- end
- return -1
- end
- function loadCommand(source)
- if source:sub(1, 1) == "(" and nextCloseIndex(source, "(", ")") == #source then
- return { type = TYPE_PARENTHESES, cmd = loadCommand(source:sub(2, -2)) }
- end
- if source:sub(1, 2) == "#{" then
- local body = analyseCommands(source:sub(3, #source - 1))
- return { type = TYPE_NON_CRITICAL_SECTION, body = body }
- end
- if source:sub(1, 10) == "exclusive{" then
- local body = analyseCommands(source:sub(11, #source - 1))
- return { type = TYPE_EXCLUSIVE_SECTION, body = body }
- end
- if source:find("%(") ~= nil and source:find("){") ~= nil then
- local ind = source:find("%(")
- local name = source:sub(1, ind - 1)
- if name == "while" then
- local ind1 = source:find("){")
- local body = analyseCommands(source:sub(ind1 + 2, #source - 1))
- local cmdCondition = loadCommand(source:sub(ind + 1, ind1 - 1))
- return { type = TYPE_CONTROL_FLOW, cond = cmdCondition, body = body, id = newCFId(), typeCF = TYPE_CF_WHILE }
- elseif name == "for" then
- local ind1 = source:find("){")
- local body = analyseCommands(source:sub(ind1 + 2, #source - 1))
- local headPts = splitTextStructural(source:sub(ind + 1, ind1 - 1), ";")
- if #headPts ~= 3 then
- error("invalid for-head: " .. source:sub(ind + 1, ind1 - 1))
- end
- local firstCommand = loadCommand(headPts[1])
- local secondCondition = loadCommand(headPts[2])
- local thirdCommand = loadCommand(headPts[3])
- return { type = TYPE_CONTROL_FLOW, first = firstCommand, second = secondCondition, third = thirdCommand, body = body, id = newCFId(), typeCF = TYPE_CF_FOR }
- elseif name == "if" then
- local pts = splitTextStructural(source, "else")
- local data = { type = TYPE_CONTROL_FLOW, typeCF = TYPE_CF_IF, id = newCFId(), ifCmdBlocks = {} }
- for i = 1, #pts do
- local pt = pts[i]
- local ind = pt:find("%(")
- local ind1 = pt:find("{")
- local body = analyseCommands(pt:sub(ind1 + 1, #pt - 1))
- if ind1 == 1 then
- if i ~= #pts then
- error("'else' statement needs to be at end of 'if'-block (source: " .. source .. ")")
- end
- table.insert(data.ifCmdBlocks, { body = body })
- else
- local name = pt:sub(1, ind - 1)
- if name ~= "if" then
- error("invalid keyword: [else]" .. name)
- end
- local cond = loadCommand(pt:sub(ind + 1, ind1 - 2))
- table.insert(data.ifCmdBlocks, { body = body, cond = cond })
- end
- end
- return data
- else
- error("unknown keyword '" .. name .. "'")
- end
- end
- local ptsSources = splitTextPattern(source, "||")
- if #ptsSources > 1 then
- local values = {};
- for i = 1, #ptsSources do
- values[i] = loadCommand(ptsSources[i])
- end
- return { type = TYPE_OR, values = values }
- end
- local ptsSources = splitTextPattern(source, "&&")
- if #ptsSources > 1 then
- local values = {};
- for i = 1, #ptsSources do
- values[i] = loadCommand(ptsSources[i])
- end
- return { type = TYPE_AND, values = values }
- end
- for i, ch in ipairs({ ">=", "<=", "<", ">", "==", "!=" }) do
- local ptsSources = splitTextPattern(source, ch)
- if #ptsSources > 1 then
- if #ptsSources > 2 then
- error("only 2 arguments allowed for '" .. ch .. "'")
- end
- local pts = {}
- for i, v in ipairs(ptsSources) do
- table.insert(pts, loadCommand(v))
- end
- return { type = TYPE_CMP, ch = ch, pts = pts }
- end
- end
- local ptsSources = splitTextStructural(source, "=")
- if #ptsSources > 1 then
- local name = ptsSources[1]
- if #ptsSources > 2 then
- error("there is only one '=' allowed in a declaration (" .. source:sub(1, 10))
- end
- if name:match("%$[A-Za-z_][A-Za-z0-9_]*%$") == name or
- name:match("[A-Za-z_][A-Za-z0-9_]*") == name or
- name:match("%$[A-Za-z_][A-Za-z0-9_]*") == name then
- return { type = TYPE_DECLARATION, name = name, value = loadCommand(ptsSources[2]) }
- else
- if name:sub(#name, #name) == "]" then
- local i = nextOpenIndex(name, "]", "[")
- if i > 1 then
- return { type = TYPE_ARR_INDEX_DECLARATION, arr = loadCommand(name:sub(1, i - 1)),
- index = loadCommand(name:sub(i + 1, -2)),
- value = loadCommand(ptsSources[2]) }
- end
- end
- local nameSources = splitTextStructural(name, ",")
- for i, v in ipairs(nameSources) do
- if v:match("%$[A-Za-z_][A-Za-z0-9_]*%$") ~= v and
- v:match("[A-Za-z_][A-Za-z0-9_]*") ~= v and
- v:match("%$[A-Za-z_][A-Za-z0-9_]*") ~= v then
- error("invalid declaration variable: " .. v)
- end
- end
- return { type = TYPE_POLY_DECLARATION, names = nameSources, value = loadCommand(ptsSources[2]) }
- end
- end
- for _, symbol in ipairs({ "+", "-", "*", "/", "%" }) do
- local ptsSources = splitTextStructural(source, symbol)
- if #ptsSources > 1 then
- local pts = {}
- for i, v in ipairs(ptsSources) do
- table.insert(pts, loadCommand(v))
- end
- return { type = TYPE_ARITHMETIC, pts = pts, symbol = symbol }
- end
- end
- local ptsSources = splitTextStructural(source, "@")
- if #ptsSources > 1 then
- local key = ptsSources[2]
- if key:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= key then
- error("invalid cast key: " .. key)
- end
- return { type = TYPE_CAST, key = key, value = loadCommand(ptsSources[1]) }
- end
- local ptsSources = splitTextStructural(source, ".")
- if #ptsSources > 1 then
- if not tonumber(ptsSources[1]) then
- local cmd = loadCommand(ptsSources[1])
- for iPt = 2, #ptsSources do
- local pt = ptsSources[iPt]
- local i = pt:find("%(")
- if i == nil then
- if pt:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= pt then
- error("invalid attribute name: " .. pt)
- end
- cmd = { type = TYPE_ATTRIBUTE, cmd = cmd, name = pt }
- else
- local funcName = pt:sub(1, i - 1)
- if funcName:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= funcName then
- error("invalid function name: " .. funcName)
- end
- local pars = {}
- local parSources = splitTextStructural(pt:sub(i + 1, -2), ",")
- for _, par in ipairs(parSources) do
- table.insert(pars, loadCommand(par))
- end
- cmd = { type = TYPE_METHOD, cmd = cmd, name = funcName, pars = pars }
- end
- end
- return cmd
- end
- end
- local i = source:find("%(")
- if i ~= nil and nextCloseIndex(source, "(", ")") == #source then
- local funcName = source:sub(1, i - 1)
- if funcName:match("[a-zA-Z_][a-zA-Z_0-9]*") ~= funcName then
- error("invalid function name: " .. funcName)
- end
- local pars = {}
- local parSources = splitTextStructural(source:sub(i + 1, -2), ",")
- for i, par in ipairs(parSources) do
- table.insert(pars, loadCommand(par))
- end
- return { type = TYPE_FUNCTION, name = funcName, pars = pars }
- end
- if source:sub(#source, #source) == "]" and nextOpenIndex(source, "]", "[") then
- local i = nextOpenIndex(source, "]", "[")
- if i > 1 then
- local body = loadCommand(source:sub(1, i - 1))
- local index = loadCommand(source:sub(i + 1, #source - 1))
- return { type = TYPE_ARR_INDEX, body = body, index = index }
- end
- end
- if tonumber(source) ~= nil then
- return { type = TYPE_VALUE, value = tonumber(source) }
- elseif source:match("%$[A-Za-z_][A-Za-z0-9_]*%$") == source then
- local key = source:sub(2, #source - 1)
- return { type = TYPE_CONSTANT, key = key }
- elseif source:match("%$[A-Za-z_][A-Za-z0-9_]*") == source then
- local key = source:sub(2, #source)
- return { type = TYPE_CONSTANT, key = key }
- elseif source == "true" or source == "false" then
- return { type = TYPE_VALUE, value = source == "true" }
- elseif source == "null" then
- return { type = TYPE_VALUE, value = nil }
- elseif source:match("[A-Za-z_][A-Za-z0-9_]*") == source then
- return { type = TYPE_VAR, name = source }
- elseif source:sub(1, 1) == "[" and nextCloseIndex(source, "[", "]") == #source then
- local parts = splitTextStructural(source:sub(2, #source - 1), ",")
- local vars = {}
- for i, v in ipairs(parts) do
- table.insert(vars, loadCommand(v))
- end
- return { type = TYPE_ARR, vars = vars, varCount = #vars }
- elseif source:sub(1, 1) == "{" and nextCloseIndex(source, "{", "}") == #source then
- local parts = splitTextStructural(source:sub(2, #source - 1), ",")
- local vars = {}
- local keyOrder = {}
- for _, v in ipairs(parts) do
- local pts = splitTextStructural(v, "=")
- if #pts ~= 2 then
- error("unknown value: " .. source)
- end
- local key = loadCommand(pts[1])
- vars[key] = loadCommand(pts[2])
- table.insert(keyOrder, key)
- end
- return { type = TYPE_TABLE, vars = vars, keyOrder = keyOrder }
- elseif (source:sub(1, 1) == "\"" and source:sub(#source) == "\"") or
- source:sub(1, 1) == "'" and source:sub(#source) == "'" then
- return { type = TYPE_VALUE, value = replaceSpecialStringCharacters(source:sub(2, #source - 1)) }
- elseif source:sub(-2, -1) == "++" or source:sub(-2, -1) == "--" then
- local name = source:sub(1, -3)
- if name:match("%$[A-Za-z_][A-Za-z0-9_]*%$?") == name or
- name:match("[A-Za-z_][A-Za-z0-9_]*") == name then
- return { type = TYPE_UNARY_OP_STATEMENT, op = source:sub(-2, -1), name = name }
- else
- error("invalid declaration variable: " .. name)
- end
- elseif source:sub(1, 2) == "++" or source:sub(1, 2) == "--" then
- local name = source:sub(3, -1)
- if name:match("%$[A-Za-z_][A-Za-z0-9_]*%$?") == name or
- name:match("[A-Za-z_][A-Za-z0-9_]*") == name then
- return { type = TYPE_UNARY_OP_STATEMENT, op = "_" .. source:sub(1, 2), name = name }
- else
- error("invalid declaration variable: " .. name)
- end
- elseif source:sub(1, 1) == "-" then
- return { type = TYPE_UNARY_OP, op = "-", value = loadCommand(source:sub(2, -1)) }
- elseif source:sub(1, 1) == "@" then
- local name = source:sub(2)
- if source:match("[A-Za-z_][A-Za-z0-9_]*") ~= name then
- error("invalid function name: @" .. name)
- end
- return { type = TYPE_FUNCTION_HANDLE, name = name }
- else
- error("unknown value: " .. source)
- end
- end
- function replaceSpecialStringCharacters(str)
- local escaped = false
- local result = ""
- for i = 1, #str do
- local c = str:sub(i, i);
- if escaped then
- escaped = false
- if c == "n" then
- result = result .. "\n"
- elseif c == "r" then
- result = result .. "\r"
- elseif c == "t" then
- result = result .. "\t"
- elseif c == "\\" then
- result = result .. "\\"
- else
- result = result .. "\\" .. c
- end
- elseif c == "\\" then
- escaped = true
- else
- result = result .. c
- end
- end
- return result
- end
- function loadValue(source)
- return loadValueFromCommand(loadCommand(source))
- end
- function loadValueFromCommand(command)
- if command.type == TYPE_ARR then
- local result = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, command.varCount)
- for i, v in ipairs(command.vars) do
- result[i] = loadValueFromCommand(v)
- end
- return result
- elseif command.type == TYPE_VALUE then
- return command.value
- else
- printTable(command)
- error("unknown command type: " .. command.type)
- end
- end
- local currentCFId = 0
- function newCFId()
- local id = currentCFId
- currentCFId = currentCFId + 1
- return id
- end
- function analyseStack(source)
- local values = {}
- for i, v in ipairs(splitTextStructural(source, ",")) do
- if v ~= "" then
- table.insert(values, tonumber(v))
- end
- end
- return values
- end
- function combineArr(table1, table2)
- local table = {}
- for i = 1, #table1 do
- table[i] = table1[i]
- end
- for i = 1, #table2 do
- table[i + #table1] = table2[i]
- end
- return table
- end
- function getTableFromSaveText(source)
- local tableSources = splitText(source, ";", true, true)
- local tables = {}
- for i = 1, #tableSources do
- table.insert(tables, {})
- end
- for i, v in ipairs(tableSources) do
- local tbl = tables[i]
- getTableFromSaveTextInd(tbl, v, tables)
- end
- return tables[1]
- end
- function getTableFromSaveTextInd(tbl, tblSource, tables)
- if tblSource:sub(1, 1) ~= "{" or tblSource:sub(#tblSource) ~= "}" then
- error("invalid source (braces around table): " .. tblSource)
- end
- local pts = splitText(tblSource:sub(2, #tblSource - 1), ",", true, true)
- for i, pt in ipairs(pts) do
- local i = pt:find("=")
- local vKey = getValueFromSaveText(pt:sub(1, i - 1), tables)
- local vValue = getValueFromSaveText(pt:sub(i + 1), tables)
- if vKey == "_class_prototype" then
- vValue = findClassPrototype(vValue)
- end
- tbl[vKey] = vValue
- end
- end
- function getValueFromSaveText(source, tables)
- if source:sub(1, 1) == "\"" and source:sub(#source) == "\"" then
- return unescapeString(source:sub(2, #source - 1))
- elseif tonumber(source) ~= nil then
- return tonumber(source)
- elseif source:sub(1, 4) == "tbl:" then
- return tables[tonumber(source:sub(5))]
- elseif source == "false" then
- return false
- elseif source == "true" then
- return true
- elseif source == "nan" then
- return 0 / 0
- else
- error("invalid source (getValueAnalysis): " .. source)
- end
- end
- function getTableSaveText(sourceTable)
- local alreadyUsed = {}
- local sources = {}
- getTableSaveTextInd(sourceTable, alreadyUsed, sources)
- local txt = ""
- for _, v in ipairs(sources) do
- txt = txt .. v .. ";"
- end
- return txt
- end
- function getTableSaveTextInd(sourceTable, alreadyUsed, sources)
- local index
- for i, v in ipairs(alreadyUsed) do
- if v == sourceTable then
- index = i
- break
- end
- end
- if index ~= nil then
- return "tbl:" .. index
- else
- table.insert(alreadyUsed, sourceTable)
- local sourceString = ""
- local ind = #alreadyUsed
- for k, v in pairs(sourceTable) do
- if k == "_class_prototype" then
- v = v.key
- end
- sourceString = sourceString .. getSaveValueString(k, alreadyUsed, sources) .. "=" .. getSaveValueString(v, alreadyUsed, sources, k) .. ","
- end
- sources[ind] = "{" .. sourceString .. "}"
- return "tbl:" .. ind
- end
- end
- function escapeString(src, delimiter)
- local result = ""
- for i = 1, #src do
- local ch = src:sub(i, i)
- if ch == delimiter then
- result = result .. "\\"
- end
- result = result .. ch
- end
- return result
- end
- function unescapeString(src)
- local result = ""
- local escaped = false
- for i = 1, #src do
- local ch = src:sub(i, i)
- if escaped then
- result = result .. ch
- escaped = false
- elseif ch == "\\" then
- escaped = true
- else
- result = result .. ch
- end
- end
- return result
- end
- function getSaveValueString(obj, alreadyUsed, sources, key)
- local t = type(obj)
- if t == "string" then
- return "\"" .. escapeString(obj, "\"") .. "\""
- elseif t == "number" then
- return obj
- elseif t == "table" then
- return getTableSaveTextInd(obj, alreadyUsed, sources)
- elseif t == "boolean" then
- if obj then
- return "true"
- else
- return "false"
- end
- else
- if key == nil then
- key = ""
- end
- error("unknown type: " .. t .. "(" .. key .. ")")
- end
- end
- function finished()
- deleteFile(coreStorage.stackFileName)
- deleteFile(coreStorage.dataFileName)
- end
- function deleteFile(fileName)
- if test then
- os.remove(fileName)
- else
- shell.run("delete", fileName)
- end
- end
- function savePersistentStorage(key, fileName)
- if nonCriticalSectionCount > 0 then
- return
- end
- writeToFile(fileName, getTableSaveText(persistentStorage[key]))
- end
- local sleptLast = 0
- local tProfileSaveStorageSerialize = 0
- local tProfileSaveStorageWrite = 0
- local tProfileWrite = 0
- local tLastProfiling
- defaultFunctions.setProfilingLabel = {
- type = TYPE_DEFAULT_FUNCTION,
- pars = { 1 },
- nonCritical = true,
- fct = function(label)
- tProfileSaveStorageSerialize = 0
- tProfileSaveStorageWrite = 0
- tProfileWrite = 0
- tLastProfiling = os.clock()
- if fProfile then
- writeToOpenFile(fProfile, "label: " .. toString(label) .. "\n")
- writeProfileData(true)
- end
- end
- }
- defaultFunctions.forceStorageSave = {
- type = TYPE_DEFAULT_FUNCTION,
- pars = { 0, 1 },
- nonCritical = true,
- fct = function(label)
- if label then
- tProfileSaveStorageSerialize = 0
- tProfileSaveStorageWrite = 0
- tProfileWrite = 0
- tLastProfiling = os.clock()
- if fProfile then
- writeToOpenFile(fProfile, "label: " .. toString(label) .. "\n")
- end
- end
- saveStorage(true, label)
- end
- }
- function saveStorage(force, indentProfileLog)
- -- yield every second
- local now = os.clock()
- if now - sleptLast > 1 then
- yield()
- sleptLast = now
- end
- if nonCriticalSectionCount > 0 then
- return
- end
- if coreStorage.saveRequested or force then
- local t = os.clock()
- local txt = getTableSaveText(coreStorage)
- local t1 = os.clock()
- writeToFile(coreStorage.stackFileName .. "1", txt)
- writeToFile(coreStorage.stackFileName, txt)
- coreStorage.saveRequested = false
- tProfileSaveStorageSerialize = tProfileSaveStorageSerialize + t1 - t
- tProfileSaveStorageWrite = tProfileSaveStorageWrite + os.clock() - t1
- t = os.clock()
- if fProfile then
- writeProfileData(indentProfileLog)
- end
- tProfileWrite = tProfileWrite + os.clock() - t
- end
- end
- function writeProfileData(indent)
- if not tLastProfiling then
- tLastProfiling = tTurtiStart
- end
- if not tLastProfiling then
- return
- end
- local prefix = ""
- if indent then
- prefix = " "
- end
- local elapsed = os.clock() - tLastProfiling
- writeToOpenFile(
- fProfile,
- prefix .. "tRun: " .. tostring(elapsed) .. "\n" ..
- prefix .. "tSerializeStorage: " .. tostring(tProfileSaveStorageSerialize) .. "(" .. tostring(tProfileSaveStorageSerialize / elapsed * 100) .. "%)" .. "\n" ..
- prefix .. "tWriteStorage: " .. tostring(tProfileSaveStorageWrite) .. "(" .. tostring(tProfileSaveStorageWrite / elapsed * 100) .. "%)" .. "\n" ..
- prefix .. "tWriteProfile: " .. tostring(tProfileWrite) .. "(" .. tostring(tProfileWrite / elapsed * 100) .. "%)" .. "\n"
- )
- end
- function writeToFile(fileName, data)
- if test then
- local f = io.open(fileName, "w")
- local oldOut = io.output()
- io.output(f)
- io.write(data)
- io.close(f)
- io.output(oldOut)
- else
- local f = fs.open(fileName, "w")
- f.write(data)
- f.close()
- end
- end
- function fileExists(fileName)
- if test then
- local f = io.open(fileName)
- if f ~= nil then
- io.close(f)
- return true
- end
- return false
- else
- return fs.exists(fileName)
- end
- end
- function tryLoadStorage(fileName, stackFileName, dataFileName, persistentStorageDirName, currentFileData)
- if fileExists(stackFileName) then
- if isSameFileData(currentFileData, readFileText(dataFileName)) then
- print("loading storage")
- local data
- local state, msg = pcall(function()
- data = getTableFromSaveText(readFileText(stackFileName))
- end)
- if state then
- return true, data
- else
- return true, getTableFromSaveText(readFileText(stackFileName .. "1"))
- end
- else
- print("source file has changed, restarting program")
- deleteFile(stackFileName)
- deleteFile(dataFileName)
- end
- end
- return false, {
- stack = nil,
- fileName = fileName,
- stackFileName = stackFileName,
- dataFileName = dataFileName,
- dfData = { save = true },
- diedLast = 0,
- constants = {}
- }
- end
- function isSameFileData(data1, data2)
- local pts1 = getLengthFormatedParts(data1)
- local pts2 = getLengthFormatedParts(data2)
- while #pts1 > 0 do
- local data = pts1[1]
- local success = false
- for j, data2 in ipairs(pts2) do
- if data2 == data then
- table.remove(pts1, 1)
- table.remove(pts2, j)
- success = true
- break
- end
- end
- if not success then
- return false
- end
- end
- return #pts2 == 0
- end
- function getLengthFormatedParts(data)
- local pts = {}
- while #data > 0 do
- local i = data:find("|")
- local len = tonumber(data:sub(1, i - 1))
- data = data:sub(i + 1)
- table.insert(pts, data:sub(1, len))
- data = data:sub(len + 1)
- end
- return pts
- end
- function parseMethod(source)
- local m = source:find(":{")
- if not m then
- error("invalid method source: " .. source)
- end
- local name = source:sub(1, m - 1)
- errInfo = { type = "plain", value = "method '" .. name .. "'", parent = errInfo }
- local fct = { }
- fct.pars = {}
- if name:find("%(") ~= nil and name:sub(#name, #name) == ")" then
- local i = name:find("%(")
- local parSource = name:sub(i + 1, -2)
- name = name:sub(1, i - 1)
- local parNames = splitTextStructural(parSource, ",")
- for i, n in ipairs(parNames) do
- if n:match("[A-Za-z_][A-Za-z0-9_]*") ~= n then
- error("invalid parameter-name: " .. n)
- end
- table.insert(fct.pars, n)
- end
- end
- if name:match("[A-Za-z_][A-Za-z0-9_]*") ~= name then
- error("invalid function-name: " .. name)
- end
- fct.name = name
- local srcCommands = source:sub(m + 2, -2)
- fct.cmds = analyseCommands(srcCommands)
- errInfo = errInfo.parent
- return fct
- end
- function trimSourceCode(code)
- code = code:gsub("/%*[^%*]*%*/", "")
- code = code:gsub("//[^\n]*", "")
- return removeLettersIgnoreStrings(code, "\n\r\t ")
- end
- function loadProgram(file, stackFile, dataFile, persistentStorageDirName, cFCount)
- local source = readFileText(file)
- source = trimSourceCode(source)
- local libraryId = 1
- while source:sub(1, 1) == "#" do
- local m = source:find(";")
- local directivePts = splitTextStructural(source:sub(2, m - 1), ":")
- if directivePts[1] == "import" then
- local pastebinId = directivePts[2]
- if not test then
- importLibrary(pastebinId)
- end
- else
- error("unknown directive: '" .. directivePts[1] .. "'")
- end
- source = source:sub(m + 1, -1)
- libraryId = libraryId + 1
- end
- methods = {}
- for i, v in ipairs(splitTextStructural(source, ";")) do
- local method = parseMethod(v)
- methods[method.name] = method
- end
- for _, hook in pairs(libraryMethodHooks) do
- local method = parseMethod(trimSourceCode(hook))
- method.isHook = true
- methods[method.name] = method
- end
- local validMethods = {}
- for name, fct in pairs(methods) do
- validMethods[name] = { type = TYPE_USER_FUNCTION, pars = { #fct.pars }, name = name, fct = fct, cmds = fct.cmds }
- end
- local allMethods = {}
- merge(defaultFunctions, allMethods)
- merge(validMethods, allMethods)
- checkMethodValidity(allMethods)
- if methods["main"] == nil then
- error("function 'main' needs to be defined in commands-source")
- end
- if #methods["main"]["pars"] ~= 0 then
- error("function 'main' doesn't take any arguments (" .. #methods["main"]["pars"] .. " given)")
- end
- local currentFileData = getFileData(methods)
- -- persistent storage
- if not test then
- shell.run("mkdir", persistentStorageDirName)
- end
- persistentStorage = {}
- for key, fct in pairs(fctsSetPersistentStorageData) do
- local fileName = persistentStorageDirName .. "/" .. key
- local data = {}
- if fileExists(fileName) then
- data = getTableFromSaveText(readFileText(fileName))
- end
- if data == nil then
- data = {}
- end
- persistentStorage[key] = data
- fct(persistentStorage[key], function()
- savePersistentStorage(key, fileName)
- end)
- end
- -- storage
- local success
- success, coreStorage = tryLoadStorage(file, stackFile, dataFile, persistentStorageDirName, currentFileData)
- coreStorage.dfData.save = true
- for key, fct in pairs(fctsSetStorageData) do
- if not coreStorage.dfData[key] then
- coreStorage.dfData[key] = {}
- end
- fct(coreStorage.dfData[key], function()
- coreStorage.dfData.save = true
- saveStorage()
- end)
- end
- --[[if success then
- initToContinue()
- end]]
- writeToFile(dataFile, currentFileData)
- -- init stack on program start
- if coreStorage.stacks == nil then
- local mainStack = {}
- table.insert(mainStack, { name = "main", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
- if methods["init"] ~= nil then
- if #methods["init"]["pars"] ~= 0 then
- error("function 'init' doesn't take any arguments (" .. #methods["init"]["pars"] .. " given)")
- end
- table.insert(mainStack, { name = "init", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
- end
- coreStorage.stacks = { { name = "main", frames = mainStack, isDaemon = false, storage = {} } }
- else
- end
- --[[if coreStorage.stack == nil then
- local stack = {}
- table.insert(stack, { name = "main", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
- if methods["init"] ~= nil then
- if #methods["init"]["pars"] ~= 0 then
- error("function 'init' doesn't take any arguments (" .. #methods["init"]["pars"] .. " given)")
- end
- table.insert(stack, { name = "init", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
- end
- coreStorage.stack = stack
- else
- if methods["onRestart"] ~= nil then
- if #methods["onRestart"]["pars"] ~= 0 then
- error("function 'onRestart' doesn't take any arguments (" .. #methods["onRestart"]["pars"] .. " given)")
- end
- table.insert(coreStorage.stack, { name = "onRestart", offset = 1, executed = false, localVars = { { v = {}, e = {} } }, cFStack = {} })
- end
- end]]
- saveStorage()
- -- TODO support library threads
- for _, stack in ipairs(coreStorage.stacks) do
- ThreadManager.startThread(
- function()
- stack.threadName = ThreadManager.currentThread.name
- executeStack(stack, true, cFCount)
- removeValue(coreStorage.stacks, stack)
- end,
- stack.isDaemon,
- stack.name,
- stack.storage
- )
- end
- print(">> setup successful <<")
- turtiRunning = true
- tTurtiStart = os.clock()
- ThreadManager.run()
- turtiRunning = false
- --[[turtiRunning = true
- if #threads > 0 then
- print("registered " .. tostring(#threads) .. " background threads")
- print(">> setup successful <<")
- parallel.waitForAll(function()
- execute(methods, true, cFCount)
- turtiRunning = false
- end, table.unpack(threads))
- else
- print(">> setup successful <<")
- execute(methods, true, cFCount)
- turtiRunning = false
- end]]
- finished()
- end
- function invokeDaemonHookNoWait(methodName, ...)
- return invokeHookInternal(false, true, methodName, ...)
- end
- function invokeDaemonHook(methodName, ...)
- return invokeHookInternal(true, true, methodName, ...)
- end
- function invokeHookNoWait(methodName, ...)
- return invokeHookInternal(false, false, methodName, ...)
- end
- function invokeHook(methodName, ...)
- return invokeHookInternal(true, false, methodName, ...)
- end
- function invokeHookInternal(wait, daemon, methodName, ...)
- local args = { ... }
- if methods[methodName] then
- -- turti function
- local stack = { name = "hook@" .. methodName, frames = { }, isDaemon = daemon, storage = {} }
- table.insert(coreStorage.stacks, stack)
- local ret = {}
- ThreadManager.startThread(
- function()
- local result = executeTurtiFunction(stack, methodName, args, 0)
- removeValue(coreStorage.stacks, stack)
- ret[1] = true
- ret[2] = result
- end,
- stack.isDaemon,
- stack.name,
- stack.storage
- )
- if not wait then
- return
- end
- while not ret[1] do
- yield()
- end
- return ret[2]
- elseif defaultFunctions[methodName] then
- local ret = {}
- ThreadManager.startThread(
- function()
- local result = executeDefaultFunction(methodName, args)
- ret[1] = true
- ret[2] = result
- end,
- false,
- "hook@" .. methodName,
- { }
- )
- else
- error("unknown method: " .. methodName)
- end
- end
- local yieldedLast = 0
- function yield()
- local now = os.clock()
- if test then
- coroutine.yield()
- elseif now - yieldedLast > 0.5 then
- sleep(0)
- yieldedLast = os.clock()
- end
- end
- function getFileData(methods)
- local data = ""
- for methodName, v in pairs(methods) do
- if not v.isHook then
- local pars = v.pars
- local cmds = v.cmds
- local methodString = methodName .. "("
- for i, par in ipairs(pars) do
- if i ~= 1 then
- methodString = methodString .. ","
- end
- methodString = methodString .. par
- end
- methodString = methodString .. ")|" .. getFileDataCmds(cmds)
- data = data .. #methodString .. "|" .. methodString
- end
- end
- return data
- end
- function getFileDataCmds(cmds)
- local txt = ""
- for i, cmd in ipairs(cmds) do
- if i ~= 1 then
- txt = txt .. ";"
- end
- txt = txt .. getFileDataCmd(cmd)
- end
- return txt
- end
- function getFileDataCmd(cmd)
- if cmd.type == TYPE_VALUE then
- if cmd.value == nil then
- return "null"
- elseif type(cmd.value) == "string" then
- return "\"" .. cmd.value .. "\""
- elseif type(cmd.value) == "boolean" then
- return "\"" .. ((cmd.value and "true") or "false") .. "\""
- else
- return "" .. cmd.value
- end
- --elseif cmd.type == TYPE_DECLARATION then
- elseif cmd.type == TYPE_FUNCTION then
- local txtPars = ""
- for i, par in ipairs(cmd.pars) do
- if i ~= 1 then
- txtPars = txtPars .. ","
- end
- txtPars = txtPars .. getFileDataCmd(par)
- end
- return cmd.name .. "(" .. txtPars .. ")"
- elseif cmd.type == TYPE_CONSTANT then
- return "$" .. cmd.key .. "$"
- elseif cmd.type == TYPE_ARR then
- local txt = ""
- for i, v in ipairs(cmd.vars) do
- if i ~= 1 then
- txt = txt .. ","
- end
- txt = txt .. getFileDataCmd(v)
- end
- return "[" .. txt .. "]"
- elseif cmd.type == TYPE_TABLE then
- local txt = ""
- for i, key in ipairs(cmd.keyOrder) do
- if i ~= 1 then
- txt = txt .. ","
- end
- txt = txt .. getFileDataCmd(key) .. "=" .. getFileDataCmd(cmd.vars[key])
- end
- return "{" .. txt .. "}"
- elseif cmd.type == TYPE_VAR then
- return cmd.name
- elseif cmd.type == TYPE_ARITHMETIC then
- local txt = ""
- for i, v in ipairs(cmd.pts) do
- if i ~= 1 then
- txt = txt .. cmd.symbol
- end
- txt = txt .. getFileDataCmd(v)
- end
- return txt
- elseif cmd.type == TYPE_DECLARATION then
- return cmd.name .. "=" .. getFileDataCmd(cmd.value)
- elseif cmd.type == TYPE_ARR_INDEX_DECLARATION then
- return getFileDataCmd(cmd.arr) .. "[" .. getFileDataCmd(cmd.index) .. "]" .. "=" .. getFileDataCmd(cmd.value)
- elseif cmd.type == TYPE_POLY_DECLARATION then
- local txt = ""
- local first = false
- for i, name in ipairs(cmd.names) do
- if first then
- first = false
- else
- txt = txt .. ","
- end
- txt = txt .. name
- end
- return txt .. "=" .. getFileDataCmd(cmd.value)
- elseif cmd.type == TYPE_CONTROL_FLOW then
- if cmd.typeCF == TYPE_CF_WHILE then
- return "while(" .. getFileDataCmd(cmd.cond) .. "){" .. getFileDataCmds(cmd.body) .. "}"
- elseif cmd.typeCF == TYPE_CF_FOR then
- return "for(" .. getFileDataCmd(cmd.first) .. ";" .. getFileDataCmd(cmd.second) .. ";" .. getFileDataCmd(cmd.third) .. "){" .. getFileDataCmds(cmd.body) .. "}"
- elseif cmd.typeCF == TYPE_CF_IF then
- local txt = "";
- for i, block in ipairs(cmd.ifCmdBlocks) do
- if i ~= 1 then
- txt = txt .. "else"
- end
- if block.cond ~= nil then
- txt = txt .. "if(" .. getFileDataCmd(block.cond) .. ")"
- end
- txt = txt .. "{" .. getFileDataCmds(block.body) .. "}"
- end
- return txt
- else
- error("fail: " .. cmd.typeCF)
- end
- elseif cmd.type == TYPE_NON_CRITICAL_SECTION then
- return "#{" .. getFileDataCmds(cmd.body) .. "}"
- elseif cmd.type == TYPE_EXCLUSIVE_SECTION then
- return "exclusive{" .. getFileDataCmds(cmd.body) .. "}"
- elseif cmd.type == TYPE_CMP then
- return getFileDataCmd(cmd.pts[1]) .. cmd.ch .. getFileDataCmd(cmd.pts[2])
- elseif cmd.type == TYPE_UNARY_OP_STATEMENT then
- if cmd.op == "++" or cmd.op == "--" then
- return cmd.name .. cmd.op
- elseif cmd.op == "_++" or cmd.op == "_--" then
- return cmd.op:sub(2, 3) .. cmd.name
- else
- error("invalid op: " .. cmd.op)
- end
- elseif cmd.type == TYPE_UNARY_OP then
- if cmd.op == "-" then
- return "-" .. getFileDataCmd(cmd.value)
- else
- error("invalid op: " .. cmd.op)
- end
- elseif cmd.type == TYPE_OR then
- local txt = ""
- for i = 1, #cmd.values do
- if txt ~= "" then
- txt = txt .. "||"
- end
- txt = txt .. getFileDataCmd(cmd.values[i])
- end
- return txt
- elseif cmd.type == TYPE_AND then
- local txt = ""
- for i = 1, #cmd.values do
- if txt ~= "" then
- txt = txt .. "&&"
- end
- txt = txt .. getFileDataCmd(cmd.values[i])
- end
- return txt
- elseif cmd.type == TYPE_PARENTHESES then
- return "(" .. getFileDataCmd(cmd.cmd) .. ")"
- elseif cmd.type == TYPE_ARR_INDEX then
- return getFileDataCmd(cmd.body) .. "[" .. getFileDataCmd(cmd.index) .. "]"
- elseif cmd.type == TYPE_ATTRIBUTE then
- return getFileDataCmd(cmd.cmd) .. "." .. cmd.name
- elseif cmd.type == TYPE_METHOD then
- local txtPars = ""
- for i, par in ipairs(cmd.pars) do
- if i ~= 1 then
- txtPars = txtPars .. ","
- end
- txtPars = txtPars .. getFileDataCmd(par)
- end
- return getFileDataCmd(cmd.cmd) .. "." .. cmd.name .. "(" .. txtPars .. ")"
- elseif cmd.type == TYPE_CAST then
- return getFileDataCmd(cmd.value) .. "@" .. cmd.key
- elseif cmd.type == TYPE_FUNCTION_HANDLE then
- return "@" .. cmd.name
- else
- printTable(cmd)
- if cmd.type ~= nil then
- error("unknown type: " .. cmd.type)
- end
- error("unknown command: ")
- end
- end
- function printKeys(t)
- local keys = ""
- local c = 0
- for i, v in ipairs(t) do
- keys = keys .. ((c ~= 0 and ",") or "") .. i
- c = 1
- end
- print(keys)
- end
- function countKeys(t)
- local c = 0
- for i, v in pairs(t) do
- c = c + 1
- end
- return c
- end
- local runningStacks = {}
- function executeStack(stack, isStartThread, cFCount)
- local frame = stack.frames[#stack.frames]
- if not frame then
- return
- end
- local methodName = frame.name
- local methodOffset = frame.offset
- local methodExecuted = frame.executed --TODO check pos or anything else when executed=false
- local currentMethod = methods[methodName]
- local cmds = currentMethod.cmds
- local localVars = frame.localVars
- if methodExecuted then
- frame.offset = frame.offset + 1
- end
- runningStacks[stack] = true
- executeCommands(stack, frame, cmds, localVars, cFCount)
- runningStacks[stack] = nil
- if isStartThread and #stack.frames > 1 then
- table.remove(stack.frames)
- saveStorage()
- executeStack(stack, true, cFCount)
- end
- if frame.executeReturn then
- return
- end
- end
- function executeDefaultFunction(name, args)
- --default function
- local defaultFunction = defaultFunctions[name]
- if not defaultFunction.nonCritical then
- coreStorage.saveRequested = true
- end
- return defaultFunction.fct(table.unpack(args))
- end
- function executeTurtiFunction(stack, fctName, args, cFCount)
- local method = methods[fctName]
- local newLocalVars = { { v = {}, e = {} } }
- for i, p in ipairs(args) do
- local par = method.pars[i]
- newLocalVars[1].e[par] = true
- newLocalVars[1].v[par] = p
- end
- table.insert(stack.frames, { name = method.name, offset = 1, executed = false, localVars = newLocalVars, cFStack = {} })
- saveStorage()
- executeStack(stack, false, 1)
- local frame1 = table.remove(stack.frames)
- saveStorage(true)
- if frame1.returnValues == nil then
- return nil
- end
- if #frame1.returnValues == 1 then
- return frame1.returnValues[1]
- end
- if isObject(frame1.returnValues) and frame1.returnValues._class_prototype.key == "list" then
- if executeClassGetter(frame1.returnValues, "length") == 1 then
- return frame1.returnValues[1]
- end
- end
- return frame1.returnValues
- end
- function mimicLuaConstructorForTable(tbl, n)
- local str = "return function(p) return {"
- for i = 1, n do
- if i ~= 1 then
- str = str .. ","
- end
- str = str .. "p[" .. i .. "]"
- end
- str = str .. "} end"
- return load(str)()(tbl)
- end
- function executeCommand(stack, cmd, localVars, cFCount)
- yield()
- errInfo = { type = "cmd", value = cmd }
- if cmd.type == TYPE_METHOD then
- local pars = {}
- local n
- local mimic = false
- for i, p in ipairs(cmd.pars) do
- local value = executeGetValue(stack, p, localVars, methods, cFCount)
- if value == nil then
- mimic = true
- end
- pars[i] = value
- n = i
- end
- if mimic then
- pars = mimicLuaConstructorForTable(pars, n)
- end
- local v = executeGetValue(stack, cmd.cmd, localVars, methods, cFCount)
- return executeClassMethod(v, cmd.name, pars)
- elseif cmd.type == TYPE_FUNCTION or cmd.type == TYPE_HOOK_FUNCTION then
- local frame = stack.frames[#stack.frames]
- if cmd.name == "return" then
- frame.executeReturn = true
- if #cmd.pars > 0 then
- local returnValues = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, #cmd.pars)
- for i, par in ipairs(cmd.pars) do
- returnValues[i] = executeGetValue(stack, par, localVars, methods, cFCount)
- end
- frame.returnValues = returnValues
- end
- return
- end
- local pars = {}
- if cmd.type == TYPE_FUNCTION then
- for i, p in ipairs(cmd.pars) do
- local value = executeGetValue(stack, p, localVars, methods, cFCount)
- pars[i] = value
- end
- else
- pars = cmd.args
- end
- if cmd.name == "startThread" then
- local method = pars[1].name
- table.remove(pars, 1)
- invokeHookNoWait(method, table.unpack(pars))
- elseif cmd.name == "startDaemonThread" then
- local method = pars[1].name
- table.remove(pars, 1)
- invokeDaemonHookNoWait(method, table.unpack(pars))
- elseif methods[cmd.name] then
- --turti function
- return executeTurtiFunction(stack, cmd.name, pars, cFCount)
- elseif defaultFunctions[cmd.name] then
- return executeDefaultFunction(cmd.name, pars)
- elseif cmd.type == TYPE_HOOK_FUNCTION then
- error("dynamically invoked method '" .. cmd.name .. "' not found")
- else
- local prototype = CLASS_PROTOTYPES[cmd.name]
- if prototype and prototype.construct then
- return constructClass(prototype.key, table.unpack(pars))
- end
- error("method '" .. cmd.name .. "' not found")
- end
- elseif cmd.type == TYPE_DECLARATION then
- local name = cmd.name
- local v = executeGetValue(stack, cmd.value, localVars, methods, cFCount)
- setVariable(name, v, cFCount, localVars)
- elseif cmd.type == TYPE_POLY_DECLARATION then
- local names = cmd.names
- local values = executeGetValue(stack, cmd.value, localVars, methods, cFCount)
- for i, name in ipairs(names) do
- setVariable(name, values[i], cFCount, localVars)
- end
- elseif cmd.type == TYPE_ARR_INDEX_DECLARATION then
- local arr = executeGetValue(stack, cmd.arr, localVars, methods, cFCount)
- local index = executeGetValue(stack, cmd.index, localVars, methods, cFCount)
- local value = executeGetValue(stack, cmd.value, localVars, methods, cFCount)
- if isObject(arr) then
- executeClassItemSetter(arr, index, value)
- return
- end
- error(toString(arr) .. " is not an object, cast using e.g. var@table or var@list")
- elseif cmd.type == TYPE_CONTROL_FLOW then
- if cmd.typeCF == TYPE_CF_IF then
- local cFCountIf = cFCount + 1
- local id = cmd.id
- local data
- local frame = stack.frames[#stack.frames]
- if frame.cFStack[cFCountIf] == nil or frame.cFStack[cFCountIf].cmd.id ~= cmd.id then
- data = { cmd = cmd, condExecuted = false, offset = 1, ifOffset = 1, executed = false, condResult = false }
- localVars[cFCountIf] = { v = {}, e = {} }
- frame.cFStack[cFCountIf] = data
- saveStorage()
- else
- data = frame.cFStack[cFCountIf]
- end
- if data.executeBreak then
- return
- end
- --print(cFCount, #frame.cFStack)
- for i = data.ifOffset, #cmd.ifCmdBlocks do
- local cond = cmd.ifCmdBlocks[i].cond
- local body = cmd.ifCmdBlocks[i].body
- if not data.condExecuted then
- local v
- if cond ~= nil then
- v = executeGetValue(stack, cond, localVars, methods, cFCountIf)
- if v == nil or type(v) ~= "boolean" then
- error("type of condition needs to be 'boolean' (type '" .. type(v) .. "' given)")
- end
- else
- v = true
- end
- data.condExecuted = true
- data.condResult = v
- saveStorage()
- end
- if data.condResult then
- if data.executed then
- data.offset = data.offset + 1
- end
- saveStorage()
- executeCommands(stack, data, body, localVars, cFCountIf)
- if data.executeReturn then
- frame.executeReturn = data.executeReturn
- frame.returnValues = data.returnValues
- end
- end
- if data.executeBreak then
- frame.cFStack[cFCount].executeBreak = data.executeBreak
- saveStorage()
- break
- end
- data.ifOffset = i + 1
- data.condExecuted = false
- data.executed = false
- data.offset = 1
- saveStorage()
- if data.condResult then
- break
- end
- end
- frame.cFStack[cFCountIf] = nil
- localVars[cFCountIf] = nil
- elseif cmd.typeCF == TYPE_CF_FOR then
- local cfCountFor = cFCount + 1
- local id = cmd.id
- local data
- local frame = stack.frames[#stack.frames]
- if frame.cFStack[cfCountFor] == nil or frame.cFStack[cfCountFor].cmd.id ~= cmd.id then
- data = { cmd = cmd, firstExecuted = false, offset = 1, condExecuted = false, lastExecuted = false, condResult = false }
- localVars[cfCountFor] = { v = {}, e = {} }
- frame.cFStack[cfCountFor] = data
- saveStorage()
- else
- data = frame.cFStack[cfCountFor]
- end
- if not data.firstExecuted then
- executeCommand(stack, data.cmd.first, localVars, cfCountFor)
- data.firstExecuted = true
- if frame.executeReturn then
- return
- end
- if frame.executeBreak then
- error("break outside loop")
- end
- saveStorage()
- end
- while true do
- yield()
- if not data.condExecuted then
- local v = executeGetValue(stack, data.cmd.second, localVars, methods, cfCountFor)
- if v == nil or type(v) ~= "boolean" then
- error("type of condition needs to be 'boolean' (type '" .. type(v) .. "' given)")
- end
- data.condExecuted = true
- data.condResult = v
- saveStorage()
- if not v then
- break
- end
- end
- if data.executed then
- data.offset = data.offset + 1
- saveStorage()
- end
- executeCommands(stack, data, data.cmd.body, localVars, cfCountFor)
- if data.executeReturn then
- frame.executeReturn = data.executeReturn
- frame.returnValues = data.returnValues
- break
- end
- if frame.executeReturn then
- break
- end
- if not data.lastExecuted and not data.executeBreak ~= "break" then
- executeCommand(stack, data.cmd.third, localVars, cfCountFor)
- data.lastExecuted = true
- saveStorage()
- end
- data.lastExecuted = false
- data.condExecuted = false
- data.executed = false
- data.offset = 1
- saveStorage()
- if data.executeBreak then
- if data.executeBreak == "break" then
- break
- elseif data.executeBreak == "continue" then
- data.executeBreak = nil
- else
- error("invalid break type: " .. tostring(data.executeBreak))
- end
- end
- end
- frame.cFStack[cfCountFor] = nil
- localVars[cfCountFor] = nil
- elseif cmd.typeCF == TYPE_CF_WHILE then
- local cfCountWhile = cFCount + 1
- local data
- local frame = stack.frames[#stack.frames]
- if frame.cFStack[cfCountWhile] == nil or frame.cFStack[cfCountWhile].cmd.id ~= cmd.id then
- data = { cmd = cmd, condExecuted = false, offset = 1, executed = false, condResult = false }
- localVars[cfCountWhile] = { v = {}, e = {} }
- frame.cFStack[cfCountWhile] = data
- saveStorage()
- else
- data = frame.cFStack[cfCountWhile]
- end
- while true do
- yield()
- if not data.condExecuted then
- local v = executeGetValue(stack, data.cmd.cond, localVars, methods, cfCountWhile)
- if v == nil or type(v) ~= "boolean" then
- error("type of condition needs to be 'boolean' (type '" .. type(v) .. "' given)")
- end
- data.condExecuted = true
- data.condResult = v
- saveStorage()
- if not v then
- break
- end
- end
- if data.executed then
- data.offset = data.offset + 1
- end
- saveStorage()
- executeCommands(stack, data, data.cmd.body, localVars, cfCountWhile)
- if data.executeReturn then
- frame.executeReturn = data.executeReturn
- frame.returnValues = data.returnValues
- break
- end
- data.condExecuted = false
- data.executed = false
- data.offset = 1
- saveStorage()
- if data.executeBreak then
- if data.executeBreak == "break" then
- break
- elseif data.executeBreak == "continue" then
- data.executeBreak = nil
- else
- error("invalid break type: " .. tostring(data.executeBreak))
- end
- end
- end
- frame.cFStack[cfCountWhile] = nil
- localVars[cfCountWhile] = nil
- else
- error("unknown typeCF: " .. cmd.typeCF .. "")
- end
- elseif cmd.type == TYPE_UNARY_OP_STATEMENT then
- executeGetValue(stack, cmd, localVars, methods, cFCount)
- elseif cmd.type == TYPE_VAR then
- local frame = stack.frames[#stack.frames]
- if cmd.name == "break" then
- if cFCount < 1 then
- error("break outside loop")
- end
- frame.cFStack[cFCount].executeBreak = "break"
- return
- elseif cmd.name == "continue" then
- if cFCount < 1 then
- error("continue outside loop")
- end
- frame.cFStack[cFCount].executeBreak = "continue"
- return
- else
- error("unexpected value: " .. cmd.name)
- end
- elseif cmd.type == TYPE_EXCLUSIVE_SECTION then
- local cFCountExclusive = cFCount + 1
- local frame = stack.frames[#stack.frames]
- local data
- if frame.cFStack[cFCountExclusive] == nil or frame.cFStack[cFCountExclusive].cmd.id ~= cmd.id then
- data = { cmd = cmd, offset = 1 }
- localVars[cFCountExclusive] = { v = {}, e = {} }
- frame.cFStack[cFCountExclusive] = data
- else
- data = frame.cFStack[cFCountExclusive]
- end
- ThreadManager.blockExecutionOfOtherThreads()
- executeCommands(stack, data, cmd.body, localVars, cFCountExclusive)
- ThreadManager.unblockExecutionOfOtherThreads()
- frame.cFStack[cFCountExclusive] = nil
- local varsNew = localVars[cFCountExclusive]
- table.remove(localVars)
- localVars[cFCountExclusive] = nil
- local varsOld = localVars[cFCount]
- for k, v in pairs(varsNew.e) do
- varsOld.e[k] = v
- end
- for k, v in pairs(varsNew.v) do
- varsOld.v[k] = v
- end
- elseif cmd.type == TYPE_NON_CRITICAL_SECTION then
- local cFCountNonCritical = cFCount + 1
- local frame = stack.frames[#stack.frames]
- local data
- if frame.cFStack[cFCountNonCritical] == nil or frame.cFStack[cFCountNonCritical].cmd.id ~= cmd.id then
- data = { cmd = cmd, offset = 1 }
- localVars[cFCountNonCritical] = { v = {}, e = {} }
- frame.cFStack[cFCountNonCritical] = data
- else
- data = frame.cFStack[cFCountNonCritical]
- end
- nonCriticalSectionCount = nonCriticalSectionCount + 1
- executeCommands(stack, data, cmd.body, localVars, cFCountNonCritical)
- nonCriticalSectionCount = nonCriticalSectionCount - 1
- frame.cFStack[cFCountNonCritical] = nil
- local varsNew = localVars[cFCountNonCritical]
- table.remove(localVars)
- localVars[cFCountNonCritical] = nil
- local varsOld = localVars[cFCount]
- for k, v in pairs(varsNew.e) do
- varsOld.e[k] = v
- end
- for k, v in pairs(varsNew.v) do
- varsOld.v[k] = v
- end
- else
- error("unknown type: " .. cmd.type)
- end
- end
- function setVariable(name, v, cFCount, localVars)
- if name:sub(1, 1) == "$" then
- local len = #name
- if name:sub(len, len) == "$" then
- name = name:sub(2, len - 1)
- else
- name = name:sub(2, len)
- end
- coreStorage.constants[name] = v
- else
- for i = cFCount - 1, 1, -1 do
- if localVars[i] == nil then
- localVars[i] = { v = {}, e = {} }
- else
- for n, _ in pairs(localVars[i].e) do
- if n == name then
- localVars[i].v[name] = v
- return
- end
- end
- end
- end
- if not localVars[cFCount] then
- localVars[cFCount] = { v = {}, e = {} }
- end
- localVars[cFCount].v[name] = v
- localVars[cFCount].e[name] = true
- end
- end
- function executeCommands(stack, frame, cmds, localVars, cFCount)
- local topFrame = stack.frames[#stack.frames]
- for i = frame.offset, #cmds do
- local cmd = cmds[i]
- frame.offset = i
- frame.executed = false
- if cmd.type == TYPE_FUNCTION then
- local isTurtiFunction = false
- for name, _ in pairs(methods) do
- if name == cmd.name then
- isTurtiFunction = true
- end
- end
- frame.executed = isTurtiFunction
- end
- saveStorage()
- executeCommand(stack, cmd, localVars, cFCount)
- if topFrame.executeReturn then
- frame.executeReturn = topFrame.executeReturn
- frame.returnValues = topFrame.returnValues
- return
- end
- if frame.executeBreak then
- break
- end
- frame.offset = i + 1
- frame.executed = false
- saveStorage()
- end
- end
- function getVariableValue(name, localVars)
- for i = #localVars, 1, -1 do
- --for i = 1, count + 1 do
- local vars = localVars[i]
- if vars ~= nil then
- for n, v in pairs(vars.v) do
- if name == n then
- return v
- end
- end
- end
- end
- return nil
- --error("var '" .. name .. "' isn't defined yet")
- end
- function executeGetValue(stack, value, localVars, methods, cFCount)
- if value.type == TYPE_VALUE then
- return value.value
- elseif value.type == TYPE_ARR then
- local vars = instantiateClass(TURTI_LIST_CLASS_PROTOTYPE.key, #value.vars)
- for i = 1, #value.vars do
- vars[i] = executeGetValue(stack, value.vars[i], localVars, methods, cFCount);
- end
- return vars
- elseif value.type == TYPE_TABLE then
- local tbl = instantiateClass(TURTI_TABLE_CLASS_PROTOTYPE.key)
- for key, v in pairs(value.vars) do
- tbl[executeGetValue(stack, key, localVars, methods, cFCount)] = executeGetValue(stack, v, localVars, methods, cFCount)
- end
- return tbl
- elseif value.type == TYPE_VAR then
- return getVariableValue(value.name, localVars)
- elseif value.type == TYPE_FUNCTION then
- return executeCommand(stack, value, localVars, cFCount)
- elseif value.type == TYPE_ARITHMETIC then
- local v = executeGetValue(stack, value.pts[1], localVars, methods, cFCount)
- if value.symbol == "+" then
- for i = 2, #value.pts do
- local v1 = executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
- if type(v) == "string" or type(v1) == "string" then
- v = toString(v) .. toString(v1)
- else
- v = v + v1
- end
- end
- return v
- elseif value.symbol == "-" then
- for i = 2, #value.pts do
- v = v - executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
- end
- return v
- elseif value.symbol == "*" then
- for i = 2, #value.pts do
- v = v * executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
- end
- return v
- elseif value.symbol == "/" then
- for i = 2, #value.pts do
- v = v / executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
- end
- return v
- elseif value.symbol == "%" then
- for i = 2, #value.pts do
- v = v % executeGetValue(stack, value.pts[i], localVars, methods, cFCount)
- end
- return v
- end
- elseif value.type == TYPE_CONSTANT then
- for k, val in pairs(coreStorage.constants) do
- if (k == value.key) then
- return val
- end
- end
- return nil
- elseif value.type == TYPE_CMP then
- local v = executeGetValue(stack, value.pts[1], localVars, methods, cFCount)
- local v1 = executeGetValue(stack, value.pts[2], localVars, methods, cFCount)
- if value.ch == ">" then
- return v > v1
- elseif value.ch == "<" then
- return v < v1
- elseif value.ch == ">=" then
- return v >= v1
- elseif value.ch == "<=" then
- return v <= v1
- elseif value.ch == "==" then
- return v == v1
- elseif value.ch == "!=" then
- return v ~= v1
- else
- error("unknown char: '" .. value.ch .. "'")
- end
- elseif value.type == TYPE_UNARY_OP_STATEMENT then
- local v = getVariableValue(value.name, localVars)
- local newValue
- if value.op == "++" then
- newValue = v + 1
- elseif value.op == "--" then
- newValue = v - 1
- elseif value.op == "_++" then
- newValue = v + 1
- v = v + 1
- elseif value.op == "_--" then
- newValue = v - 1
- v = v - 1
- end
- setVariable(value.name, newValue, cFCount, localVars)
- return v
- elseif value.type == TYPE_UNARY_OP then
- return -executeGetValue(stack, value.value, localVars, methods, cFCount)
- elseif value.type == TYPE_OR then
- for i = 1, #value.values do
- if executeGetValue(stack, value.values[i], localVars, methods, cFCount) then
- return true
- end
- end
- return false
- elseif value.type == TYPE_AND then
- for i = 1, #value.values do
- if not executeGetValue(stack, value.values[i], localVars, methods, cFCount) then
- return false
- end
- end
- return true
- elseif value.type == TYPE_PARENTHESES then
- return executeGetValue(stack, value.cmd, localVars, methods, cFCount)
- elseif value.type == TYPE_ARR_INDEX then
- local body = executeGetValue(stack, value.body, localVars, methods, cFCount)
- local index = executeGetValue(stack, value.index, localVars, methods, cFCount)
- if not isObject(body) then
- error(toString(body) .. " is not an object, cast using e.g. var@table or var@list")
- end
- return executeClassItemGetter(body, index)
- elseif value.type == TYPE_ATTRIBUTE then
- local v = toObject(executeGetValue(stack, value.cmd, localVars, methods, cFCount))
- if not isObject(v) then
- error(toString(v) .. " is not an object, cast using e.g. var@table or var@list")
- end
- return executeClassGetter(v, value.name)
- elseif value.type == TYPE_METHOD then
- return executeCommand(stack, value, localVars, cFCount)
- elseif value.type == TYPE_CAST then
- local v = executeGetValue(stack, value.value, localVars, methods, cFCount)
- return executeClassCast(v, value.key)
- elseif value.type == TYPE_FUNCTION_HANDLE then
- local method = methods[value.name]
- if method == nil then
- error("method " .. value.name .. " not found")
- end
- return {
- type = "function handle",
- name = value.name
- }
- else
- printTable(value)
- error("unknown type: " .. value.type)
- end
- end
- function checkMethodValidity(methods)
- local definedNames = {}
- for name, v in pairs(methods) do
- definedNames[name] = v.pars
- end
- for i, v in ipairs(methods) do
- if v.type == TYPE_USER_FUNCTION then
- checkCmdsValidity(v.cmds, definedNames)
- end
- end
- end
- function checkCmdsValidity(cmds, definedNames)
- for i, cmd in ipairs(cmds) do
- if cmd.type == TYPE_FUNCTION then
- if definedNames[cmd.name] == nil then
- error("function '" .. cmd.name .. "' isn't defined")
- end
- local args = definedNames[cmd.name]
- local cStart = args[1]
- local cEnd = cStart
- if #args > 1 then
- cEnd = args[2]
- end
- local count = #cmd.pars
- if count < cStart or (count > cEnd and cEnd ~= -1) then
- error("function '" .. cmd.name .. "' takes between " .. cStart .. " and " .. cEnd .. " argument(s) (" .. count .. " given)")
- end
- checkCmdsValidity(cmd.pars, definedNames)
- elseif cmd.type == TYPE_CONTROL_FLOW then
- if cmd.typeCF == TYPE_CF_WHILE then
- checkCmdsValidity(cmd.body, definedNames)
- elseif cmd.typeCF == TYPE_CF_FOR then
- checkCmdsValidity({ cmd.first, cmd.second, cmd.third }, definedNames)
- checkCmdsValidity(cmd.body, definedNames)
- elseif cmd.typeCF == TYPE_CF_IF then
- for i, block in ipairs(cmd.ifCmdBlocks) do
- if cmd.cond ~= nil then
- checkCmdsValidity({ block.cond }, definedNames)
- end
- checkCmdsValidity(block.body, definedNames)
- end
- else
- error("unknown typeCF: " .. cmd.typeCF)
- end
- elseif cmd.type == TYPE_ARR then
- checkCmdsValidity(cmd.vars, definedNames)
- elseif cmd.type == TYPE_ARITHMETIC then
- checkCmdsValidity(cmd.pts, definedNames)
- elseif cmd.type == TYPE_OR or cmd.type == TYPE_AND then
- checkCmdsValidity(cmd.values, definedNames)
- elseif cmd.type == TYPE_DECLARATION then
- checkCmdsValidity({ cmd.value }, definedNames)
- elseif cmd.type == TYPE_POLY_DECLARATION then
- checkCmdsValidity({ cmd.value }, definedNames)
- elseif cmd.type == TYPE_ARR_INDEX_DECLARATION then
- checkCmdsValidity({ cmd.index, cmd.arr, cmd.value }, definedNames)
- elseif cmd.type == TYPE_PARENTHESES then
- checkCmdsValidity({ cmd.cmd }, definedNames)
- elseif cmd.type == TYPE_UNARY_OP then
- checkCmdsValidity({ cmd.value }, definedNames)
- elseif cmd.type == TYPE_ARR_INDEX then
- checkCmdsValidity({ cmd.body, cmd.index }, definedNames)
- elseif cmd.type == TYPE_ATTRIBUTE then
- checkCmdsValidity({ cmd.cmd }, definedNames)
- elseif cmd.type == TYPE_METHOD then
- local cmds1 = {}
- table.insert(cmds1, cmd.cmd)
- for _, c in ipairs(cmd.pars) do
- table.insert(cmds1, c)
- end
- checkCmdsValidity(cmds1, definedNames)
- elseif cmd.type ~= TYPE_CONSTANT and cmd.type ~= TYPE_VAR and cmd.type ~= TYPE_VALUE and cmd.type ~= TYPE_CMP
- and cmd.type ~= TYPE_UNARY_OP_STATEMENT then
- printTable(cmd)
- error("unknown type: " .. cmd.type)
- end
- end
- end
- function write(data)
- io.write(data)
- io.flush()
- if fOut then
- writeToOpenFile(fOut, data)
- end
- end
- function writeToDebug(data)
- if fDebug then
- writeToOpenFile(fDebug, data)
- end
- end
- function writeToOpenFile(file, data)
- if not test then
- file.write(data)
- file.flush()
- else
- local oldOut = io.output()
- io.output(file)
- io.write(data)
- io.flush()
- io.output(oldOut)
- end
- end
- function printPolyTo(write, ...)
- local args = table.pack(...)
- if #args == 0 then
- write("null\n")
- return
- end
- local first = true
- for i = 1, args.n do
- local v = args[i]
- if first then
- first = false
- else
- write(" ")
- end
- if v == nil then
- write("null")
- elseif type(v) == "table" then
- printTable(v, true)
- else
- write(tostring(v))
- end
- end
- write("\n")
- end
- function printPoly(...)
- printPolyTo(write, ...)
- end
- function printDebug(...)
- printPolyTo(writeToDebug, ...)
- end
- function printTable(tbl, suppressLinebreak)
- printTableIndex(tbl, {}, true)
- if not suppressLinebreak then
- write("\n")
- end
- end
- function printTableIndex(tbl, alreadyUsed, topLevel)
- if tbl == nil then
- write("null")
- elseif type(tbl) == "table" then
- if tableContainsValue(alreadyUsed, tbl) then
- write("already used")
- elseif isObject(tbl) then
- write(executeClassToString(tbl))
- elseif tbl._turti_array_length then
- table.insert(alreadyUsed, tbl)
- io.write("[")
- local first = true
- for i = 1, tbl._turti_array_length do
- if first then
- first = false
- else
- io.write(", ")
- end
- printTableIndex(tbl[i], alreadyUsed)
- end
- for key, value in pairs(tbl) do
- if key == "_turti_array_length" then
- elseif type(key) ~= "number" or key < 1 or key > tbl._turti_array_length then
- if first then
- first = false
- else
- io.write(", ")
- end
- io.write(key .. ": ");
- printTableIndex(value, alreadyUsed)
- end
- end
- io.write("]")
- else
- table.insert(alreadyUsed, tbl)
- write("[")
- local first = true
- for i, v in pairs(tbl) do
- if first then
- first = false
- else
- write(", ")
- end
- printTableIndex(i, alreadyUsed)
- write(": ");
- printTableIndex(v, alreadyUsed)
- end
- write("]")
- end
- elseif type(tbl) == "string" then
- if not topLevel then
- write('"')
- write(tbl)
- write('"')
- else
- write(tbl)
- end
- else
- write(tostring(tbl))
- end
- end
- function tableContainsValue(table, value)
- if not table then
- return false
- end
- for i, v in pairs(table) do
- if v == value then
- return true
- end
- end
- return false
- end
- function getPastebin(pastebinId, fileName)
- if fs.exists(fileName) then
- shell.run("delete", impFileName)
- end
- while true do
- local result, err = shell.run("pastebin", "get", pastebinId, fileName)
- if fs.exists(fileName) then
- return
- end
- print("downloading " .. pastebinId .. ">" .. fileName .. " failed, retrying in 1s")
- sleep(1)
- end
- end
- function importLibrary(pastebinId)
- errInfo = { type = "plain", "import library " .. pastebinId, parent = errInfo }
- local impFileName = "libraryIn_" .. pastebinId
- getPastebin(pastebinId, impFileName)
- local data = require(impFileName)
- if data.name then
- if fs.exists("lib_" .. data.name) then
- shell.run("delete", "lib_" .. data.name)
- end
- shell.run("mv", impFileName, "lib_" .. data.name)
- loadLibrary("lib_" .. data.name)
- else
- loadLibrary(impFileName)
- end
- errInfo = errInfo.parent
- end
- function loadLibrary(name)
- local data
- if test then
- data = require("libraries/" .. name)
- else
- data = require(name)
- end
- if data.dependencies then
- for _, dependency in ipairs(data.dependencies) do
- importLibrary(dependency)
- end
- end
- if data.name then
- print("importing library '" .. data.name .. "'")
- else
- print("importing unnamed library")
- end
- local libraryDefinition = {}
- if data.api then
- for fName, definition in pairs(data.api) do
- if type(definition) == "function" then
- local info = debug.getinfo(definition)
- if info.isvararg then
- libraryDefinition[fName] = {
- type = TYPE_DEFAULT_FUNCTION,
- pars = { info.nparams, -1 },
- fct = definition
- }
- else
- libraryDefinition[fName] = {
- type = TYPE_DEFAULT_FUNCTION,
- pars = { info.nparams },
- fct = definition
- }
- end
- else
- if not definition.fct then
- error("field 'fct' isn't defined in api>" .. fName .. " of library '" .. name .. "'")
- end
- libraryDefinition[fName] = {
- type = TYPE_DEFAULT_FUNCTION,
- pars = definition.pars or { debug.getinfo(definition.fct).nparams },
- fct = definition.fct,
- nonCritical = definition.nonCritical
- }
- end
- end
- end
- if data.classes then
- for _, prototype in ipairs(data.classes) do
- CLASS_PROTOTYPES[prototype.key] = prototype
- end
- end
- if data.threads then
- for _, v in ipairs(data.threads) do
- table.insert(threads, v)
- end
- end
- if data.onInitStorage then
- fctsSetStorageData[name] = data.onInitStorage
- end
- if data.onInitPersistentStorage then
- fctsSetPersistentStorageData[name] = data.onInitPersistentStorage
- end
- if data.hooks then
- for _, hook in ipairs(data.hooks) do
- table.insert(libraryMethodHooks, hook)
- end
- end
- if data.onSetup then
- data.onSetup()
- end
- merge(libraryDefinition, defaultFunctions)
- end
- function merge(source, target)
- for k, v in pairs(source) do
- target[k] = v
- end
- end
- function installPreExecutionHook(library, key, hookId, hook, conditional)
- if not library._turtiCoreHooks then
- library._turtiCoreHooks = {}
- end
- if not hookedLibraries[library] then
- -- clear hooks not assigned by this program instance
- hookedLibraries[library] = true
- for _, hookInfo in pairs(library._turtiCoreHooks) do
- hookInfo.hooks = {}
- end
- end
- local hooksInfos = library._turtiCoreHooks
- if not hooksInfos[key] then
- local hooksInfo = { ogFct = library[key], hooks = {} }
- hooksInfos[key] = hooksInfo
- library[key] = function(...)
- local valid = true
- for _, h in pairs(hooksInfos[key].hooks) do
- local result = h.hook()
- if h.conditional and not result then
- valid = false
- end
- end
- if not valid then
- return
- end
- return hooksInfo.ogFct(...)
- end
- end
- local hooksInfo = hooksInfos[key]
- hooksInfo.hooks[hookId] = { hook = hook, conditional = conditional }
- return function(...)
- local valid = true
- for n, h in pairs(hooksInfo.hooks) do
- if n ~= hookId then
- local result = h.hook()
- if h.conditional and not result then
- valid = false
- end
- end
- end
- if not valid then
- return
- end
- return hooksInfo.ogFct(...)
- end
- end
- local args = {}
- local kwargs = {}
- for i, v in ipairs(RAW_ARGS) do
- local pts = splitTextStructural(v, "=")
- if #pts == 1 then
- table.insert(args, pts[1])
- else
- kwargs[pts[1]] = pts[2]
- end
- end
- test = false
- if kwargs["test"] == "true" then
- test = true
- os.pullEvent = function(ev)
- print("Warning: event " .. ev .. " can't be pulled in test mode")
- ThreadManager.terminateRunningThread()
- end
- end
- local restartOnError = false
- if kwargs["restartOnError"] and kwargs["restartOnError"] == "true" then
- print("activated restart on error")
- restartOnError = true
- end
- --[[if test then
- local data = require("std")
- data[1](true, {})
- fctsSetStorageData = { df = data[2] }
- defaultFunctions = data[3]
- initToContinue = data[4]
- else
- local data = require("std")
- data[1](false, { shell = shell, turtle = turtle })
- fctsSetStorageData = { df = data[2] }
- defaultFunctions = data[3]
- initToContinue = data[4]
- end]]
- loadLibrary("std")
- if kwargs["libraries"] ~= nil then
- for _, libraryName in ipairs(loadValue(kwargs["libraries"])) do
- loadLibrary(libraryName)
- end
- end
- local fileName = args[1]
- local stackFileName = fileName .. ".stack"
- local dataFileName = fileName .. ".id"
- local debugFileName = fileName .. ".debug"
- if fileExists(debugFileName) then
- deleteFile(debugFileName)
- end
- if false then
- if not test then
- fOut = fs.open("out", "w")
- fProfile = fs.open(fileName .. ".profile", "w")
- fDebug = fs.open(debugFileName, "w")
- else
- fOut = io.open("out", "w")
- fProfile = io.open(fileName .. ".profile", "w")
- fDebug = io.open(debugFileName, "w")
- end
- end
- local persistentStorageDirName = fileName .. "_persistent"
- if args[3] ~= nil then
- if args[3] == "restart" then
- print("restart")
- if test then
- os.remove(stackFileName)
- else
- fs.delete(stackFileName)
- end
- else
- error("unknown second argument: '" .. args[3] .. "'")
- end
- end
- local function getStackErrorInfos()
- local info = "turti stack trace:\n"
- for stack, _ in pairs(runningStacks) do
- local threadName = stack.threadName
- if not threadName then
- threadName = "unknown"
- end
- info = info .. "thread '" .. threadName .. "':\n"
- local frameInfo = ""
- for _, frame in ipairs(stack.frames) do
- local cfInfo = ""
- for _, cfFrame in ipairs(frame.cFStack) do
- local cFName = "?"
- if cfFrame.cmd.typeCF == TYPE_CF_FOR then
- cFName = "for"
- elseif cfFrame.cmd.typeCF == TYPE_CF_WHILE then
- cFName = "while"
- elseif cfFrame.cmd.typeCF == TYPE_CF_IF then
- cFName = "if"
- end
- cfInfo = cfInfo .. " statement " .. tostring(cfFrame.offset) .. " in " .. cFName .. "\n"
- end
- frameInfo = cfInfo .. frameInfo
- frameInfo = " statement " .. tostring(frame.offset) .. " in function '" .. frame.name .. "'\n" .. frameInfo
- end
- info = info .. frameInfo
- end
- return info;
- end
- function logError(err)
- print("error: ", splitText(err, "\n\r")[1])
- local fileTxt = tostring(err)
- if errInfo then
- local info
- if errInfo.type == "cmd" then
- info = "command: " .. getFileDataCmd(errInfo.value) .. ""
- elseif errInfo.type == "plain" then
- info = errInfo.value
- elseif errInfo.type then
- print("unknown error info type: " .. errInfo.type)
- end
- if info then
- info = "(" .. info .. ")"
- print(info)
- fileTxt = fileTxt .. "\n" .. info
- end
- end
- fileTxt = fileTxt .. "\n\n" .. getStackErrorInfos()
- print("(check file 'err' for stacktrace)")
- writeToFile("err", fileTxt)
- end
- local error = false
- local started = os.clock()
- while true do
- local status, err = xpcall(loadProgram, debug.traceback, fileName, stackFileName, dataFileName, persistentStorageDirName, 1)
- if not status then
- error = true
- logError(err)
- break
- else
- break
- end
- end
- if not test then
- for _,v in pairs({fOut,fProfile,fDebug}) do
- if v then
- v.close()
- end
- end
- else
- for _,v in pairs({fOut,fProfile,fDebug}) do
- if v then
- io.close(v)
- end
- end
- end
- if error and restartOnError then
- if os.clock() - started < 20 then
- deleteFile(stackFileName)
- end
- shell.run("mkdir", "errLogs1")
- for i = 0, 9 do
- local fileNameErr = "errLogs/err_" .. tostring(i)
- local fileNameOut = "errLogs/out_" .. tostring(i)
- local nextFileNameErr = "errLogs/err_" .. tostring((i + 1) % 10)
- local nextFileNameOut = "errLogs/out_" .. tostring((i + 1) % 10)
- if not fileExists(fileNameErr) then
- shell.run("mv", "err", fileNameErr)
- shell.run("mv", "out", fileNameOut)
- if fileExists(nextFileNameErr) then
- deleteFile(nextFileNameErr)
- end
- if fileExists(nextFileNameOut) then
- deleteFile(nextFileNameOut)
- end
- break
- end
- end
- print("configured to restart on error")
- print("restarting in 5s")
- sleep(5)
- os.reboot()
- end
Add Comment
Please, Sign In to add comment