Ryan cf22651949 Added intergration loveManager
Adds multi.intergrations.loveManager,lua
Created an example file for you to look at
2017-06-24 22:46:44 -04:00

1278 lines
34 KiB
Lua

function trim(s)
return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)'
end
parseManager={}
parseManager._VERSION={1,0,0}
dialogueManager=parseManager -- for backwards purposes
parseManager.OnExtendedBlock=multi:newConnection(true) -- true protects the module from crashes
parseManager.OnCustomSyntax=multi:newConnection(true) -- true protects the module from crashes
function string:split( inSplitPattern, outResults )
if not outResults then
outResults = {}
end
local theStart = 1
local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
while theSplitStart do
table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
theStart = theSplitEnd + 1
theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
end
table.insert( outResults, string.sub( self, theStart ) )
return outResults
end
function parseManager:debug(txt)
if self.stats.debugging then
self._methods:debug(txt)
end
end
function parseManager.split(s,pat)
local pat=pat or ","
local res = {}
local start = 1
local state = 0
local c = '.'
local elem = ''
for i = 1, #s do
c = s:sub(i, i)
if state == 0 or state == 3 then -- start state or space after comma
if state == 3 and c == ' ' then
state = 0 -- skipped the space after the comma
else
state = 0
if c == '"' or c=="'" then
state = 1
elem = elem .. '"'
elseif c=="[" then
state = 1
elem = elem .. '['
elseif c == pat then
res[#res + 1] = elem
elem = ''
state = 3 -- skip over the next space if present
else
elem = elem .. c
end
end
elseif state == 1 then -- inside quotes
if c == '"' or c=="'" then --quote detection could be done here
state = 0
elem = elem .. '"'
elseif c=="]" then
state = 0
elem = elem .. ']'
elseif c == '\\' then
state = 2
else
elem = elem .. c
end
elseif state == 2 then -- after \ in string
elem = elem .. c
state = 1
end
end
res[#res + 1] = elem
return res
end
parseManager._chunks={}
parseManager._cblock={}
parseManager._cblockname=""
parseManager._pos=1
parseManager._labels={
-- {chunkname,pos}
}
parseManager.stats={
leaking=false,
debugging=false,
topdown=true,
forseelabels=true,
}
parseManager._types={}
parseManager.__index=parseManager
parseManager._variables={__TYPE="ENV"}
parseManager.defualtENV=parseManager._variables
function parseManager:varExists(var)
if var==nil or var=="nil" then return end
if type(var)=="userdata" then return var end
if tonumber(var) then
return tonumber(var)
end
local aa,bb=var:match("(.-)%[\"(.-)\"%]")
if aa and bb then
return self.defualtENV[aa][bb]
end
if var:find('"') then
return self:parseHeader(var:sub(2,-2),self.defualtENV)
end
if var:find("%[%]") then
return {}
end
if var:sub(1,1)=="[" and var:sub(-1,-1)=="]" then
local list=var:match("[(.+)]")
if not list then
self:pushError("Invalid List assignment!")
end
local t=list:split(",")
local nlist={}
local a=":)"
for i=1,#t do
a=self:varExists(t[i])
if a then
table.insert(nlist,a)
end
end
return nlist
end
if var=="true" then
return true
elseif var=="false" then
return false
end
local a,b=var:match("(.-)%[(.-)%]")
if a and b then
if type(self.defualtENV[a])=="table" then
if b=="-1" then
return self.defualtENV[a][#self.defualtENV[a]]
elseif b=="#" then
return self.defualtENV[a][math.random(1,#self.defualtENV[a])]
else
return self.defualtENV[a][tonumber(b) or self:varExists(b)]
end
end
if type(self.defualtENV[var])=="table" then
return self.defualtENV[var]
end
end
return self.defualtENV[var] or var -- if all tests fail, just pass on the data for the function to manage
end
function parseManager:isList(var)
local a,b=var:match("(.-)%[(.-)%]")
if not a or b then return end
if type(self.defualtENV[a])=="table" then
if b=="-1" then
return self.defualtENV[a][#self.defualtENV[a]]
else
return self.defualtENV[a][tonumber(b)]
end
end
return
end
function parseManager:loadString(data)
self:_load(bin.new(data),self)
end
parseManager.loadeddata={}
parseManager.envs={}
parseManager._methods={
getLength=function(self,list)
return #(self:varExists(list) or {})
end,
emptyList=function(self)
return {}
end,
DEBUG=function(self,text)
print(text)
end,
DIS=function(self,var)
print(var)
end,
SEED=function(self,n)
math.randomseed(tonumber(self:varExists(n) or n) or os.time())
end,
delElem=function(self,l,i)
table.remove(l,i)
end,
addElem=function(self,l,d,i)
table.insert(l,(i or -1),d)
return l
end,
RANDOM=function(self,v1,v2)
if v1 then
return math.random(1,v1)
elseif v1 or v2 then
return math.random(tonumber(v1),tonumber(v2))
else
return math.random()
end
end,
CALC=function(self,eq)
return self:evaluate(eq)
end,
GOTOV=function(self,label)
print(self:varExists(label))
self._methods.GOTO(self,self:varExists(label))
end,
GOTO=function(self,label)
label=label:gsub("-","")
if label=="__LASTGOTO" then
self:setBlock(self._labels.__LASTGOTO[1])
self.pos=self._labels[label][2]
return true
end
--search current block for a label
if self.pos==nil then
error("Attempt to load a non existing block from the host script!")
end
for i=self.pos,#self._cblock do
local line=self._cblock[i]
local labeltest=line:match("::(.-)::")
if labeltest==label then
self._labels["__LASTGOTO"]={self._cblockname,self.pos}
self.pos=i
return true
end
end
--search for saved labels
if self._labels[label] then
self._labels["__LASTGOTO"]={self._cblockname,self.pos}
self:setBlock(self._labels[label][1])
self.pos=self._labels[label][2]
return true
end
--search other blocks if enabled for labels
if self.stats.forseelabels then
for i,v in pairs(self._chunks) do
local chunks=bin._lines(v[1])
for p=1,#chunks do
local line=chunks[p]
local labeltest=line:match("::(.-)::")
if labeltest==label then
self._labels["__LASTGOTO"]={self._cblockname,self.pos}
self:setBlock(i)
self.pos=p-1
return true
end
end
end
end
if self.stats.forseelabels then
if self._methods.GOTOV(self,label) then return end
end
self:pushError("Attempt to goto a non existing label! You can only access labels in the current scope! Or labels that the code has seen thus far! "..label.." does not exist as a label!")
end,
QUIT=function()
os.exit()
end,
EXIT=function(self)
self.pos=math.huge
end,
TYPE=function(self,val)
return type(val)
end,
SAVE=function(self,filename)
if trim(filename)=="" then filename="saveData.sav" end
local t=bin.new()
t:addBlock(self.defualtENV)
t:addBlock(self._cblockname)
t:addBlock(self.pos)
t:addBlock(self._labels)
t:tofile(filename)
end,
UNSAVE=function(self,filename)
if trim(filename)=="" then filename="saveData.sav" end
self.defualtENV={}
os.remove(filename)
end,
RESTORE=function(self)
if not(self.loadeddata.load) then self:pushError("A call to RESTORE without calling LOAD") end
self.defualtENV=self.loadeddata:getBlock("t")
self:setBlock(self.loadeddata:getBlock("s"))
self.pos=self.loadeddata:getBlock("n")
self._labels=self.loadeddata:getBlock("t")
end,
LOAD=function(self,filename)
print(filename)
if not filename then filename="saveData.sav" end
if io.fileExists(filename) then
self.loadeddata=bin.load(filename)
return 1
end
return 0
end,
JUMP=function(self,to)
self:setBlock(to)
end,
SKIP=function(self,n)
self.pos=self.pos+tonumber(n)
end,
PRINT=function(self,text) print(text) end,
TRIGGER=function(self,to)
self:setBlock(to)
end,
COMPARE=function(self,t,v1,v2,trueto,falseto) -- if a blockname is __STAY then it will continue on
if t=="=" or t=="==" then
if v1==v2 then
self:setBlock(trueto)
else
self:setBlock(falseto)
end
elseif t==">=" then
if v1>=v2 then
self:setBlock(trueto)
else
self:setBlock(falseto)
end
elseif t=="<=" then
if v1<=v2 then
self:setBlock(trueto)
else
self:setBlock(falseto)
end
elseif t==">" then
if v1>v2 then
self:setBlock(trueto)
else
self:setBlock(falseto)
end
elseif t=="<" then
if v1<v2 then
self:setBlock(trueto)
else
self:setBlock(falseto)
end
end
end,
getInput=function(self,msg)
io.write((msg or ""))
return io.read()
end,
debug=function(self,txt)
-- if self.stats.debugging then
print("DEBUG: "..txt)
-- end
end,
ADD=function(self,val,n)
return val+n
end,
SUB=function(self,val,n)
return val-n
end,
MUL=function(self,val,n)
return val*n
end,
DIV=function(self,val,n)
return val/n
end,
MOD=function(self,val,n)
return val%n
end,
["return"]=function(self,n)
if type(n)~="number" then self:pushError("For now only numbers can be returned!") end
self.defualtENV["ret-urn"]=n
end,
SYSTEMTIME=os.clock,
STRFORMAT=string.format,
error=function(self,msg)
self:pushError(msg)
end,
setENV=function(self,env)
self.defualtENV=env
end,
getENV=function(self,name)
if name then
return self._variables
end
return self.defualtENV
end,
createENV=function(self)
local _env={__TYPE="ENV"}
setmetatable(_env,{__index=self._variables})
return _env
end,
setGlobal=function(self)
self.defualtENV=self._variables
end,
setLocal=function(self)
self.defualtENV=self._methods.createENV(self)
return self.defualtENV
end,
stringSUB=function(self,str,a,b)
return str:sub(a,b)
end,
stringFIND=function(self,str,pat)
return str:find(pat)
end,
stringLEN=function(self,str)
return #str
end,
stringLOWER=function(self,str)
return str:lower()
end,
stringUPPER=function(self,str)
return str:upper()
end,
stringREVERSE=function(self,str)
return str:reverse()
end,
stringREP=function(self,str,n)
return str:rep(n)
end,
stringReplace=function(self,str,rep)
return str:gsub(rep)
end,
setVar=function(self,name,value)
self.defualtENV[name]=value
end,
}
function parseManager:setVariable(name,value)
self.defualtENV[name]=value
end
function parseManager:setGlobalVariable(name,value)
self._variables[name]=value
end
function parseManager:_load(filename,c)
local file={}
if love then
file=bin.new((love.filesystem.read(filename)))
elseif type(filename)=="table" then
file=filename
else
file=bin.load(filename)
end
file:gsub("%-%-%[%[.-%]%]","\n")
file:gsub('%b""',function(a) a=a:gsub("%-%-","\2") return a end)
file:gsub("%b''",function(a) a=a:gsub("%-%-","\2") return a end)
file:gsub("(%-%-.-)\n","\n")
file:gsub("%-:.-:%-","\n")
file:gsub('%b""',function(a) a=a:gsub(";","\1") return a end)
file:gsub("%b''",function(a) a=a:gsub(";","\1") return a end)
file:gsub(";\n","\n")
file:gsub(";\r","\r")
file:gsub(";","\n")
file:gsub("\r\n","\n")
file:gsub("\n\n","")
file:gsub("\1",";")
file:gsub("\2","--")
for fn in file:gmatch("LOAD (.-)\n") do
self:_load(fn,c)
end
for fn in file:gmatch("ENABLE (.-)\n") do
self.stats[string.lower(fn)]=true
end
for fn in file:gmatch("USING (.-)\n") do
require("parseManager."..fn)
_G[fn]:InitSyntax(c,fn)
end
for fn in file:gmatch("DISABLE (.-)\n") do
self.stats[string.lower(fn)]=false
end
file:fullTrim(true)
for blockname,chunk in file:gmatch("%[(.-)[:.-]?%].-{(.-)}") do
if not c.firstblcok then c.firstblcok=blockname end
if blockname:find(":") then
local name,t=blockname:match("(.-):(.+)")
c._chunks[name]={chunk,t,file=filename}
if c.stats.leaking then
if c.lastblock then
c._chunks[c.lastblock]={c._chunks[c.lastblock][1],c._chunks[c.lastblock][2],next=name,file=filename}
end
end
local rets=parseManager.OnExtendedBlock:Fire(c,name,t,chunk,filename)
for i=1,#rets do
if rets[i][1]==true then
c._chunks[name]=nil
end
end
if c._chunks[name] then
c.lastblock=name
end
else
c._chunks[blockname]={chunk,"ALL",file=filename}
if c.stats.leaking then
if c.lastblock then
c._chunks[c.lastblock]={c._chunks[c.lastblock][1],c._chunks[c.lastblock][2],next=blockname,file=filename}
end
end
c.lastblock=blockname
end
end
return c
end
function parseManager:load(filename)
local c={}
setmetatable(c,parseManager)
local file={}
if love then
file=bin.new((love.filesystem.read(filename)))
elseif type(filename)=="table" then
file=filename
else
file=bin.load(filename)
end
file:gsub("%-%-%[%[.-%]%]","\n")
file:gsub('%b""',function(a) a=a:gsub("%-%-","\2") return a end)
file:gsub("%b''",function(a) a=a:gsub("%-%-","\2") return a end)
file:gsub("(%-%-.-)\n","\n")
file:gsub("%-:.-:%-","\n")
file:gsub('%b""',function(a) a=a:gsub(";","\1") return a end)
file:gsub("%b''",function(a) a=a:gsub(";","\1") return a end)
file:gsub(";\n","\n")
file:gsub(";\r","\r")
file:gsub(";","\n")
file:gsub("\r\n","\n")
file:gsub("\n\n","")
file:gsub("\1",";")
file:gsub("\2","--")
file:fullTrim(true)
for fn in file:gmatch("LOAD (.-)\n") do
self:_load(fn,c)
end
local test=file:match("ENTRY (.-)\n")
if test then
c.entrypoint=test
end
for fn in file:gmatch("ENABLE (.-)\n") do
self.stats[string.lower(fn)]=true
end
for fn in file:gmatch("USING (.-)\n") do
require("parseManager."..fn)
_G[fn]:InitSyntax(c,fn)
end
for fn in file:gmatch("DISABLE (.-)\n") do
self.stats[string.lower(fn)]=false
end
for blockname,chunk in file:gmatch("%[(.-)[:.-]?%].-{(.-)}") do
if blockname:find(":") then
local name,t=blockname:match("(.-):(.+)")
if t=="struct" then
local lines=bin._lines(chunk)
self.defualtENV[name]={}
else
c._chunks[name]={chunk,t,file=filename}
if c.stats.leaking then
if c.lastblock then
c._chunks[c.lastblock]={c._chunks[c.lastblock][1],c._chunks[c.lastblock][2],next=name,file=filename}
end
end
local rets=parseManager.OnExtendedBlock:Fire(c,name,t,chunk,filename)
for i=1,#rets do
if rets[i][1]==true then
c._chunks[name]=nil
end
end
if c._chunks[name] then
c.lastblock=name
end
end
else
c._chunks[blockname]={chunk,"ALL",file=filename}
if c.stats.leaking then
if c.lastblock then
c._chunks[c.lastblock]={c._chunks[c.lastblock][1],c._chunks[c.lastblock][2],next=blockname,file=filename}
end
end
c.lastblock=blockname
end
if not self.firstloadedblock then
self.firstloadedblock=blockname
fir=true
end
end
setmetatable(c._variables,{__index=_G})
parseManager.loaded=true
return c
end
function parseManager:define(t)
table.merge(self._methods,t)
end
function parseManager:hasCBlock()
return #self._cblock~=0
end
function parseManager:combineTruths(t)
--t={1,"a",0,"o",0}
if #t==1 then
return t[1]
end
local v=false
for i=#t,1,-2 do
if t[i-1] then
if t[i-1]=="o" then
v=(t[i] or t[i-2])
elseif t[i-1]=="a" then
v=(t[i] and t[i-2])
else
self:pushError("INVALID TRUTH TABLE!!!")
end
t[i-2]=v -- set the next index to the value
end
end
return v
end
function parseManager:setBlock(chunk)
if chunk:find("%-") then
local label=chunk:match("%-(.-)%-")
self._methods.GOTO(self,label)
return
end
if chunk=="__STAY" then return end
if chunk:sub(1,6)=="__SKIP" then local n=tonumber(chunk:sub(7,-1)) if n==-1 then self:pushError("-1 will put the skip command back on its self, creating an infinte pause! use -2 or less to go back 1 or more") return end self.pos=self.pos+n return end
if not(self._chunks[chunk]) then self._methods.GOTO(self,chunk) return end
local test=bin.new(self._chunks[chunk][1])
test:fullTrim(true)
if self._cblockname~="" then
self.pos=0
elseif self._cblockname==chunk then
self.pos=-1
else
self.pos=1
end
self._cblockname=chunk
self._cblock=bin._lines(test.data)
end
function parseManager:start(chunk,env)
local chunk = self.entrypoint or chunk or self.firstblcok
return self:next(chunk,nil,env)
end
--~ function parseManager:pushError(err)
--~ local file=self._chunks[self._cblockname].file
--~ local d={}
--~ if love then
--~ d=bin.new((love.filesystem.read(file)))
--~ else
--~ d=bin.load(file)
--~ end
--~ local t=d:lines()
--~ local pos=0
--~ local switch=false
--~ for i=1,#t do
--~ if t[i]:find("["..self._cblockname,1,true) then
--~ switch=true
--~ end
--~ if switch==true and bin._trim(t[i])==self._cblock[self.pos] then
--~ pos=i
--~ break
--~ end
--~ end
--~ print("In Block '"..self._cblockname.."' LIQ: '"..self._cblock[self.pos].."' Filename: "..file.." On line: "..pos..": "..err)
--~ io.read()
--~ os.exit()
--~ end
--~ function parseManager:pushError(err)
--~ print(err) -- print to the console
--~ local file=self._chunks[self._cblockname].file
--~ local d={}
--~ if love then
--~ print(file)
--~ d=bin.new((love.filesystem.read(file)))
--~ else
--~ d=bin.load(file)
--~ end
--~ local _d={}
--~ local t=d:lines()
--~ for i=1,#t do
--~ _d[i]=trim(t[i])
--~ end
--~ d=table.concat(_d,"\n")
--~ local pos=0
--~ local cc=d:match("%["..self._cblockname..".*%].*({.-})")
--~ cc=cc:gsub("{.-\n","")
--~ cc=cc:gsub((self._cblock[self.pos]:gsub("%%","%%%%"):gsub("%(","%%%("):gsub("%)","%%%)"):gsub("%[","%%%["):gsub("%]","%%%]"):gsub("%+","%%%+"):gsub("%-","%%%-"):gsub("%*","%%%*"):gsub("%.","%%%."):gsub("%$","%%%$"):gsub("%^","%%%^")).."(.+)","NOPE LOL")
--~ _,b=cc:gsub("^(%-%-.-)\n","\n")
--~ _,c=cc:gsub("%-:.-:%-","\n")
--~ e=b+c
--~ for i=1,#t do
--~ if t[i]:find("["..self._cblockname,1,true) then
--~ pos=i+self.pos
--~ break
--~ end
--~ end
--~ error("In Block '"..self._cblockname.."' LIQ: '"..self._cblock[self.pos].."' Filename: "..file.." On line: "..pos+e..": "..err)
--~ end
function dialogueManager:pushError(err)
print(err) -- print to the console
local file=self._chunks[self._cblockname].file
local d={}
if love then
print(file)
d=bin.new((love.filesystem.read(file)))
elseif type(file)=="table" then
d=file
else
d=bin.load(file)
end
local t=d:lines()
local pos=0
--Sigh... comments are still a pain to deal with...
local cc=d:match("%["..self._cblockname.."[:.-]?%].-({.-})")
cc=cc:gsub("{.-\n","")
cc=cc:gsub((self._cblock[self.pos]:gsub("%%","%%%%"):gsub("%(","%%%("):gsub("%)","%%%)"):gsub("%[","%%%["):gsub("%]","%%%]"):gsub("%+","%%%+"):gsub("%-","%%%-"):gsub("%*","%%%*"):gsub("%.","%%%."):gsub("%$","%%%$"):gsub("%^","%%%^")).."(.+)","NOPE LOL")
--mlc,a=cc:gsub("%-%-%[%[.-%]%]","\n")
--print(mlc)
--d=#bin._lines(mlc or "")
_,b=cc:gsub("(%-%-.-)\n","\n")
_,c=cc:gsub("%-:.-:%-","\n")
e=b+c
print(a,b,c)
for i=1,#t do
if t[i]:find("["..self._cblockname,1,true) then
pos=i+self.pos
break
end
end
if type(file)=="table" then
filename="runCode"
else
filename=file
end
error("In Block '"..self._cblockname.."' LIQ: '"..self._cblock[self.pos].."' Filename: "..filename.." On line: "..pos+e..": "..err)
end
function parseManager:p()
self.pos=self.pos+1
end
function parseManager:parseHeader(header,env)
header=header:gsub("(%$%S-%$)",function(a)
local t1,t2=a:match("%$(.-)%[(.-)%]%$")
if t1 and t2 then
if type(env[t1])=="table" then
if t2=="-1" then
return env[t1][#env[t1]]
end
if env[t1][t2] then
return tostring(env[t1][t2])
else
return tostring(env[t1][tonumber(self:varExists(t2) or t2) or self:varExists(t2)])
end
end
end
a=a:gsub("%$","")
if type(env[a])=="table" then
if #env[a]==0 then
self:pushError("Invalid Syntax!")
end
return env[a][math.random(1,#env[a])]
end
return tostring(env[a])
end)
return header
end
function string:split(sep)
local sep, fields = sep or ":", {}
local pattern = string.format("([^%s]+)", sep)
self:gsub(pattern, function(c) fields[#fields+1] = c end)
return fields
end
function parseManager:next(chunk,a,env,dd)
if not env then
env=self.defualtENV
end
if self.stats.topdown and chunk==nil then
chunk=self.firstloadedblock
else
chunk=chunk or "START"
end
if not self:hasCBlock() then
self:setBlock(chunk)
end
local line=self._cblock[self.pos]
if type(a)=="number" then
for i=1,#dd.methods+1 do
self:p()
end
line=dd.methods[a] -- sneaky, but also prevents wrong choices
end
if line==nil then
if self.stats.leaking then
if self._chunks[self._cblockname].next then
self:setBlock(self._chunks[self._cblockname].next)
self:p()
return {
Type="end",
text="leaking",
blocktype=self._chunks[self._cblockname][2]
}
else
self.endData={Type="end",text="Reached end of block!",lastblock=self._cblockname,lastline=self._cblock[self.pos-1],blocktype=self._chunks[self._cblockname][2]}
return self.endData
end
end
return {Type="end",text="Reached end of block!",lastblock=self._cblockname,lastline=self._cblock[self.pos-1],blocktype=self._chunks[self._cblockname][2]}
end
local holder,functest,args=line:match("([%w_]-):([%w_]-)%s*%((.-)%)$")
if not functest then
functest,args=line:match("([%w_]-)%s*%((.-)%)$")
end
if functest then
local funccheck=line:match("([%+%-%*/]+).-%(.-%)")
if funccheck then
functest=nil
end
for i,v in pairs(math) do
if functest==i and type(v)=="function" then
functest=nil
break
end
end
end
line=line:gsub("(.-)%[\"(.-)\"%]=(.+)",function(a,b,c)
return b.."="..c.."->"..a
end)
local choicetest=line:find("<$") or line:find("^<")
local lasttest=line:match("^\"(.+)\"$")
local labeltest=line:match("::(.-)::")
local var,list=line:match("([%w_]-)=%[(.+)%]")
local assignA,assignB=line:match("(.-)=(.+)")
local cond,f1,f2=line:match("^if%s*(.-)%s*then%s*([%w-%(%)]-)%s*|%s*([%w-%(%)]*)")
if choicetest then
local c=self._chunks[self._cblockname][1]
local test=bin.new(c:match("\"<(.-)>"))
test:fullTrim(true)
local header=line:match("\"(.-)\"<")
local stuff=test:lines()
local cho,met={},{}
for i=1,#stuff do
local a1,a2=stuff[i]:match("\"(.-)\" (.+)")
a1=tostring(self:parseHeader(a1,env))
table.insert(cho,a1)
table.insert(met,a2)
end
return {
Type="choice",
text=tostring(self:parseHeader(header,env)),
choices=cho,
methods=met,
blocktype=self._chunks[self._cblockname][2]
}
elseif cond and f1 and f2 then
conds={["andors"]={}}
mtc=""
for a,b in cond:gmatch("(.-)([and ]+[or ]+)") do
b=b:gsub(" ","")
mtc=mtc..".-"..b
v1,c,v2=a:match("(.-)%s*([<>!~=]+)%s*(.+)")
table.insert(conds,{v1,c,v2})
table.insert(conds.andors,b)
end
a=cond:match(mtc.."%s*(.+)")
v1,c,v2=a:match("(.-)%s*([<>!~=]+)%s*(.+)")
table.insert(conds,{v1,c,v2})
truths={}
for i=1,#conds do
conds[i][1]=conds[i][1]:gsub("\"","")
conds[i][3]=conds[i][3]:gsub("\"","")
if conds[i][2]=="==" then
table.insert(truths,tostring((self:varExists(conds[i][1]) or conds[i][1]))==tostring((self:varExists(conds[i][3]) or conds[i][3])))
elseif conds[i][2]=="!=" or conds[i][2]=="~=" then
table.insert(truths,tostring((self:varExists(conds[i][1]) or conds[i][1]))~=tostring((self:varExists(conds[i][3]) or conds[i][3])))
elseif conds[i][2]==">=" then
table.insert(truths,tonumber((self:varExists(conds[i][1]) or conds[i][1]))>=tonumber((self:varExists(conds[i][3]) or conds[i][3])))
elseif conds[i][2]=="<=" then
table.insert(truths,tonumber((self:varExists(conds[i][1]) or conds[i][1]))<=tonumber((self:varExists(conds[i][3]) or conds[i][3])))
elseif conds[i][2]==">" then
table.insert(truths,tonumber((self:varExists(conds[i][1]) or conds[i][1]))>tonumber((self:varExists(conds[i][3]) or conds[i][3])))
elseif conds[i][2]=="<" then
table.insert(truths,tonumber((self:varExists(conds[i][1]) or conds[i][1]))<tonumber((self:varExists(conds[i][3]) or conds[i][3])))
else
self:pushError("Invalid condition! "..conds[i][2].." is not a valid condition!")
end
if conds.andors[i] then
table.insert(truths,conds.andors[i]:sub(1,1))
end
end
local val=self:combineTruths(truths)
if val then
_args={}
functest,args=f1:match("([%w_]-)%s*%((.-)%)$")
for k, v in ipairs(parseManager.split(args)) do
table.insert(_args,v)
end
self._methods[functest](self,unpack(_args))
else
_args={}
functest,args=f2:match("([%w_]-)%s*%((.-)%)$")
for k, v in ipairs(parseManager.split(args)) do
table.insert(_args,self:varExists(v))
end
self._methods[functest](self,unpack(_args))
end
self:p()
return{
Type="condition",
text="Result of the test was: "..tostring(val)
}
elseif "\""..(lasttest or "").."\""==line then
self:p()
local lasttest=tostring(self:parseHeader(lasttest,env))
return {
Type="text",
text=lasttest,
blocktype=self._chunks[self._cblockname][2]
}
elseif var1 and cond and var2 and f1 and f2 then
if cond~="==" or cond~="~=" or cond~="!=" then
var1=self:varExists(var1) or tonumber(var1)
var2=self:varExists(var2) or tonumber(var2)
else
var1=self:varExists(var1) or var1
var2=self:varExists(var2) or var2
end
elseif labeltest then
if labeltest:find("|") then
labeltest,t1=labeltest:match("(.-)|(.+)")
self._methods["JUMP"](self,t1)
end
self._labels[labeltest]={self._cblockname,self.pos}
self:p()
return {
Type="label",
text="Label was assigned {"..self._cblockname..","..self.pos.."}"
}
elseif var and list then
local t=list:split(",")
local nlist={}
local slist="["
local a=":)"
for i=1,#t do
a=self:varExists(t[i])
if a then
table.insert(nlist,a)
slist=slist..a..","
end
end
slist=slist:sub(1,-2).."]"
env[var]=nlist
self:p()
return {
Type="assignment",
text=var.."="..slist
}
elseif functest then
local vars={}
if line:find("^(.-)=.+") then
local t=line:match("^(.-)=.+")
if t:find(",") then
for k, v in ipairs(parseManager.split(t)) do
table.insert(vars,v)
end
else
vars={t}
end
end
_args={}
for k, v in ipairs(parseManager.split(args)) do
if type(v)=="userdata" then
table.insert(_args,v)
else
table.insert(_args,(self:varExists(v)))
end
end
if not holder then
if not(self._methods[functest]) then return self:pushError("Attempt to call a non existing method: "..functest) end
end
local stuff={}
if holder then
if not type(holder)=="table" then self:pushError(holder.." Is not an object!") end
if not self.defualtENV[holder][functest] then self:pushError(holder.." does not contain a method: "..functest) end
stuff={self.defualtENV[holder][functest](self.defualtENV[holder],unpack(_args))}
else
stuff={self._methods[functest](self,unpack(_args))}
end
for i=1,#vars do
env[vars[i]]=stuff[i] or "NONE"
end
self:p()
return {
Type="method",
returns=stuff,
blocktype=self._chunks[self._cblockname][2]
}
elseif assignA and assignB then
--if pp
local vv,d,_env=assignB:match("(.-)([<%-][%->])(.+)")
if d then
if d=="<-" then
self:setVariable(assignA,self.defualtENV[_env][vv])
self:p()
return {
Type="assignment",
var=assignA,
value=assignB,
env=true,
text=assignA.."="..assignB
}
elseif d=="->" then
self.defualtENV[_env][assignA]=self:varExists(vv)
self:p()
return {
Type="assignment",
var=assignA,
value=assignB,
env=true,
text=assignA.."="..assignB
}
end
end
local a1,a2=parseManager.split(assignA),parseManager.split(assignB)
for i=1,#a1 do
local a=self._methods.CALC(self,a2[i])
if a then
a2[i]=a
end
local t=tonumber(a2[i])
if not t then
t=a2[i]
end
env[a1[i]]=t
end
self:p()
return {
Type="assignment",
var=assignA,
value=assignB,
text=assignA.."="..assignB
}
else
local rets=self.OnCustomSyntax:Fire(self,line)
for i=1,#rets do
if type(rets[i][1])=="table" then
return rets[i][1]
else
return {
Type="unknown",
text=line
}
end
end
self:p()
return {
Type="unknown",
text=line
}
end
end
function parseManager:RunCode(code,entry,sel,env) -- returns an env or selectVarName
local file = bin.new("ENTRY "..(entry or "START").."\n"..code)
local run=parseManager:load(file)
run._methods = self._methods
run.defualtENV=self.defualtENV
run.defualtENV=self.defualtENV
for i,v in pairs(env or {}) do
run.defualtENV[i]=v
end
local t=run:start()
while true do
if t.Type=="text" then
print(t.text)
t=run:next()
elseif t.Type=="condition" then
t=run:next()
elseif t.Type=="assignment" then
t=run:next()
elseif t.Type=="label" then
t=run:next()
elseif t.Type=="method" then
t=run:next()
elseif t.Type=="choice" then
t=run:next(nil,math.random(1,#t.choices),nil,t)
elseif t.Type=="end" then
if t.text=="leaking" then -- go directly to the block right under the current block if it exists
t=run:next()
else
return (run.defualtENV[sel] or run.defualtENV)
end
elseif t.Type=="error" then
error(t.text)
else
t=run:next()
end
end
end
parseManager.symbols={} -- {sym,code}
function parseManager:registerSymbol(sym,code)
self.symbols[#self.symbols+1]={sym,code}
end
function parseManager:populateSymbolList(o)
local str=""
for i=1,#self.symbols do
str=self.symbols[i][1]..str
end
return str
end
function parseManager:isRegisteredSymbol(o,r,v)
for i=1,#self.symbols do
if self.symbols[i][1]==o then
return parseManager:RunCode(self.symbols[i][2],"CODE","ret-urn",{["l"]=r,["r"]=v,["mainenv"]=self.defualtENV})
end
end
return false --self:pushError("Invalid Symbol "..o.."!")
end
function parseManager:evaluate(cmd,v)
v=v or 0
local loop
local count=0
local function helper(o,v,r)
if type(v)=="string" then
if v:find("%D") then
v=self:varExists(v)
end
end
if type(r)=="string" then
if r:find("%D") then
r=self:varExists(r)
end
end
local r=tonumber(r) or 0
local gg=self:isRegisteredSymbol(o,r,v)
if gg then
return gg
elseif o=="+" then
return r+v
elseif o=="-" then
return r-v
elseif o=="/" then
return r/v
elseif o=="*" then
return r*v
elseif o=="^" then
return r^v
end
end
for i,v in pairs(math) do
cmd=cmd:gsub(i.."(%b())",function(a)
a=a:sub(2,-2)
if a:sub(1,1)=="-" then
a="0"..a
end
return v(self:evaluate(a))
end)
end
cmd=cmd:gsub("%b()",function(a)
return self:evaluate(a:sub(2,-2))
end)
for l,o,r in cmd:gmatch("(.*)([%+%^%-%*/"..self:populateSymbolList().."])(.*)") do
loop=true
count=count+1
if l:find("[%+%^%-%*/]") then
v=self:evaluate(l,v)
v=helper(o,r,v)
else
if count==1 then
v=helper(o,r,l)
end
end
end
if not loop then return self:varExists(cmd) end
return v
end
parseManager.constructType=function(self,name,t,data,filename)
if t~="construct" then return end
--print(name,t,"[CODE]{"..data.."}")
self:registerSymbol(name,"[CODE]{"..data.."}")
end
-- Let's add function
Stack = {}
function Stack:Create()
local t = {}
t._et = {}
function t:push(...)
if ... then
local targs = {...}
for _,v in ipairs(targs) do
table.insert(self._et, v)
end
end
end
function t:pop(num)
local num = num or 1
local entries = {}
for i = 1, num do
if #self._et ~= 0 then
table.insert(entries, self._et[#self._et])
table.remove(self._et)
else
break
end
end
return unpack(entries)
end
function t:getn()
return #self._et
end
function t:list()
for i,v in pairs(self._et) do
print(i, v)
end
end
return t
end
parseManager.funcstack=Stack:Create()
parseManager:define{
__TRACEBACK=function(self) -- internal function to handle function calls
local t=self.funcstack:pop()
self:setBlock(t[1])
self.pos=t[2]
-- We finished the function great. Lets restore the old env
self._methods.setENV(self,t[3])
end
}
parseManager.funcType=function(link,name,t,data,filename)
local test,args=t:match("(function)%(*([%w,]*)%)*")
if not test then return false end
local vars={}
if args~="" then
for k, v in ipairs(parseManager.split(args)) do
table.insert(vars,v)
end
-- Time to collect local vars to populate we will use these below
end
link._chunks[name][1]=link._chunks[name][1].."\n__TRACEBACK()"
local func=function(self,...)
-- Here we will use the vars. First lets capture the args from the other side
local args={...}
-- Here we will play a matching game assigning vars to values. This cannot be done yet...
-- Now we have to change the enviroment so function vars are local to the function.
-- Also we need functions to be able to access the globals too
-- Now we invoke the createnv method
local env=self._methods.createENV(self)
-- A little messy compared to how its done within the interpreted language
-- Now we need a copy of the previous Env
-- We then invoke getEnv method
local lastEnv=self._methods.getENV(self)
-- Great now we have a new enviroment to play with and the current one
-- Next we need to store the current one somewhere
self.funcstack:push({self._cblockname,self.pos,lastEnv})
-- We use a stack to keep track of function calls. Before I tried something else and it was a horrible mess
-- Stacks make it real nice and easy to use. We store a bit of data into the stack to use later
if self.funcstack:getn()>1024 then self:pushError("Stack Overflow!") end
-- Throw an error if the stack reaches 1024 elements. We don't want it to go forever and odds are neither does the user
-- Lets set that new env and prepare for the jump. To do this we invoke setEnv
self._methods.setENV(self,env)
-- Now lets play match making
for i=1,#vars do
self:setVariable(vars[i],args[i]) -- this method defualts to the current env
end
-- We are ready to make the jump with our stored data
self._methods.JUMP(self,name)
-- we need to be able to catch returns... This is where things get tricky.
-- We need a way to run the other code while also waiting here so we can return data
-- What we can do is return a reference to the enviroment and from there you can take what you want from the function
-- This is a really strange way to do things, but whats wrong with different
return env
end
link._methods[name]=func
end
parseManager.OnExtendedBlock(parseManager.funcType)
parseManager.constructType=function(link,name,t,data,filename)
local test,args=t:match("(construct)%(*([%w,]*)%)*")
if not test then return false end
local vars={}
if args~="" then
for k, v in ipairs(parseManager.split(args)) do
table.insert(vars,v)
end
end
link._chunks[name][1]=link._chunks[name][1].."\n__TRACEBACK()"
local func=function(self,...)
local args={...}
local env=self._methods.createENV(self)
local lastEnv=self._methods.getENV(self)
self.funcstack:push({self._cblockname,self.pos,lastEnv})
if self.funcstack:getn()>1024 then self:pushError("Stack Overflow!") end
self._methods.setENV(self,env)
for i=1,#vars do
self:setVariable(vars[i],args[i])
end
self._methods.JUMP(self,name)
return env
end
link._methods[name]=func
end
parseManager.OnExtendedBlock(parseManager.constructType)