From f180fef6fbae45a02331a94f85e23e258caa066f Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 9 Jun 2018 15:04:21 -0400 Subject: [PATCH] Massive changes to this. Now compiles code then runs it! Total engine overhaul --- parseManager/AICM.lua | 37 - parseManager/EBIM.lua | 71 -- parseManager/bytecode.lua | 44 - parseManager/init.lua | 2116 +++++++++++++------------------ parseManager/interpreter.lua | 10 - parseManager/standardDefine.lua | 310 +++++ parsetest2.txt | 12 +- parsetest3.txt | 32 + test.lua | 7 +- 9 files changed, 1267 insertions(+), 1372 deletions(-) delete mode 100644 parseManager/AICM.lua delete mode 100644 parseManager/EBIM.lua delete mode 100644 parseManager/bytecode.lua delete mode 100644 parseManager/interpreter.lua create mode 100644 parseManager/standardDefine.lua create mode 100644 parsetest3.txt diff --git a/parseManager/AICM.lua b/parseManager/AICM.lua deleted file mode 100644 index 23496fa..0000000 --- a/parseManager/AICM.lua +++ /dev/null @@ -1,37 +0,0 @@ -AICM={} -AICM.functions={ - getAICMVersion=function(self) - return "1.0.0" - end, -} -function AICM:InitSyntax(obj,name) - obj:debug("Now using the Artificial Intelligence Communication module!") - obj.OnExtendedBlock(self.blockModule) - obj.OnCustomSyntax(self.syntaxModule) - obj:define(self.functions) -end -AICM.syntaxModule=function(self,line) - pVars,mStr=line:match("p%((.-)%)(.+)") - if pVars then - local vRef,vars=pVars:match("(.-):(.+)") - if vars:find(",") then - vars={unpack(vars:split(","))} - else - vars={vars} - end - tab={self:varExists(vRef):match(mStr)} -- self:varExists allows for all internal structures to just work - for i=1,#tab do - if vars[i] then - self._variables[vars[i]]=tab[i] - end - end - self:p() -- requried to progress the script - return { - text=line, - Type="AICMModule" - } - end -end -AICM.blockModule=function(obj,name,t,chunk,filename) - -- -end diff --git a/parseManager/EBIM.lua b/parseManager/EBIM.lua deleted file mode 100644 index 08ac193..0000000 --- a/parseManager/EBIM.lua +++ /dev/null @@ -1,71 +0,0 @@ -EBIM={} -EBIM.functions={ - getEBIMVersion=function(self) - return "1.0.0" - end, -} -EBIM.registry={} -function EBIM:registerEBlock(name,func) - self.registry[name]=func -end -function EBIM:InitSyntax(obj,name) - obj:debug("Now using the Extended Block Interface module!") - obj.OnExtendedBlock(self.blockModule) - obj.OnCustomSyntax(self.syntaxModule) - obj:define(self.functions) -end -EBIM.syntaxModule=function(self,line) - local cmd,args=line:match("(.-) (.+):") - if cmd then - local goal=nil - local _tab={} - for i=self.pos+1,#self._cblock do - if self._cblock[i]=="end"..cmd then - goal=i - break - else - table.insert(_tab,self._cblock[i]) - end - end - if goal==nil then - self:pushError("'end"..cmd.."' Expected to close '"..cmd.."'") - end - if EBIM.registry[cmd] then - EBIM.registry[cmd](self,args,_tab) - self.pos=goal+1 - else - self:pushError("Unknown command: "..cmd) - end - return { - Type="EBIM-Data", - text=cmd.." Block" - } - else - return - end -end -EBIM.blockModule=function(obj,name,t,chunk,filename) - --print(">: ",obj,name,t,chunk,filename) -end -EBIM:registerEBlock("string",function(self,args,tab) - local str={} - for i=1,#tab do - table.insert(str,tab[i]) - end - self:setVariable(args,table.concat(str,"\n")) -end) -EBIM:registerEBlock("list",function(self,args,tab) - local str={} - for i=1,#tab do - table.insert(str,self:varExists(tab[i])) - end - self:setVariable(args,str) -end) -EBIM:registerEBlock("dict",function(self,args,tab) - local str={} - for i=1,#tab do - local a,b=tab[i]:match("(.-):%s*(.+)") - str[a]=self:varExists(b) - end - self:setVariable(args,str) -end) diff --git a/parseManager/bytecode.lua b/parseManager/bytecode.lua deleted file mode 100644 index 26fb46f..0000000 --- a/parseManager/bytecode.lua +++ /dev/null @@ -1,44 +0,0 @@ --- In an attempt to speed up my library I will use a virtual machine that runs bytecode -compiler={} -compiler.cmds={ -- list of all of the commands - EVAL="\01", -- evaluate - SPLT="\02", -- split - TRIM="\03", -- trim - VEXT="\04", -- variable exists - ILST="\05", -- is a list - LSTR="\06", -- load string - FCAL="\07", -- Function call - SVAR="\08", -- set variable - LOAD="\09", -- load file - LAOD="\10", -- _load file - DEFN="\11", -- define external functions - HCBK="\12", -- Has c Block - CMBT="\13", -- combine truths - SETB="\14", -- set block - STRT="\15", -- start - PERR="\16", -- push error - PROG="\17", -- progress - PHED="\18", -- parse header - SSLT="\19", -- split string - NEXT="\20", -- next - -- Needs refining... One step at a time right! -} -function compiler:compile(filename) -- compiles the code into bytecode - -- First we load the code but don't run it - local engine=parseManager:load(filename) - -- This captures all of the methods and important info. This also ensures that the compiler and interperter stay in sync! - local bytecodeheader=bin.new() -- header will contain the order of blocks and important flags - local bytecode=bin.newDataBuffer() -- lets leave it at unlimited size because we don't know how long it will need to be - local functions={} -- will be populated with the important methods that must be preloaded - local prebytecode={} -- this contains bytecode that has yet to be sterilized - for blockname,blockdata in pairs(engine._chunks) do - -- lets get some variables ready - local code,_type,nextblock,filename=blockdata[1],blockdata[2],blockdata.next,blockdata.file - -- note nextblock may be nil on 2 condidions. The first is when the leaking flag is disabled and the other is when the block in question was the last block defined - local lines=bin._lines(code) - print("\n["..blockname.."]\n") - for i=1,#lines do - print(lines[i]) - end - end -end diff --git a/parseManager/init.lua b/parseManager/init.lua index da7aedb..3038f31 100644 --- a/parseManager/init.lua +++ b/parseManager/init.lua @@ -1,30 +1,127 @@ -function trim(s) - return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)' -end +require("bin") 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 +parseManager.__index=parseManager +parseManager.chunks={} +parseManager.stats={} +parseManager.stack={} +parseManager.cFuncs={} +parseManager.mainENV={} +parseManager.currentENV=parseManager.mainENV +parseManager.entry="START" +parseManager.methods={} +parseManager.lastCall=nil +parseManager.currentHandle=nil +parseManager.currentHandleName=nil +function readonlytable(tab) + return setmetatable({},{ + __index=tab, + __newindex=function() + error("Attempt to modify read-only table!") + end, + __metatable=false + }) end -function parseManager:debug(txt) - if self.stats.debugging then - self._methods:debug(txt) +function debug(...) + if parseManager.stats.debugging then + print("",...) end end +function parseManager:newENV() + local env={} + function env:getParent() + return getmetatable(self).__index + end + setmetatable(env,{__index=self.currentENV}) + return env +end +function parseManager:setENV(env) + self.currentENV=env +end +function parseManager:defualtENV() + self:setENV(self.mainENV) +end +function parseManager:exposeNamespace(name,ref) + self.mainENV[name]=ref +end +function parseManager:load(path,c) + local c = c + if not c then + c = {} + setmetatable(c,parseManager) + end + local file + if type(path)=="table" then + if path.Type=="bin" then + file = path + else + error("Currently unsupported path type!") + end + elseif type(path)=="string" then + if bin.fileExists(path) then + file=bin.load(path) + else + error("File: "..path.." does not exist!") + end + end + -- process the data + file.data=file.data:gsub('%b""',function(a) a=a:gsub("//","\2") return a end) + file.data=file.data:gsub("%b''",function(a) a=a:gsub("//","\2") return a end) + file.data=file.data:gsub("//.-\n","\n") + file.data=file.data:gsub('%b""',function(a) a=a:gsub(";","\1") return a end) + file.data=file.data:gsub("%b''",function(a) a=a:gsub(";","\1") return a end) + file.data=file.data:gsub(";\n","\n") + file.data=file.data:gsub(";\r","\r") + file.data=file.data:gsub(";","\n") + file.data=file.data:gsub("\r\n","\n") + file.data=file.data:gsub("\n\n","\n") + file.data=file.data:gsub("\1",";") + file.data=file.data:gsub("\2","//") + file.data=file.data:gsub("\t","") + file:trim() + for fn in file:gmatch("LOAD (.-)\n") do + debug("L",fn) + self:load(fn,c) + end + for fn in file:gmatch("ENABLE (.-)\n") do + debug("E",fn) + self.stats[string.lower(fn)]=true + end + for fn in file:gmatch("DISABLE (.-)\n") do + debug("D",fn) + self.stats[string.lower(fn)]=false + end + for fn in file:gmatch("ENTRY (.-)\n") do + debug("E",fn) + self.entry=fn + end + for name,data in file:gmatch("%[(.-)[:.-]?%].-{(.-)}") do + local ctype="BLOCK" + if name:find(":") then + ctype=name:match(":(.+)") + name=name:match("(.-):") + end + c.chunks[name]={} + c.chunks[name].type=ctype + c.chunks[name].pos=1 + c.chunks[name].labels={} + c.chunks[name].path=path + c.chunks[name].name=name + parseManager.currentHandleName=name + parseManager.currentHandle=c + c:compile(name,ctype,data) + end + --c.chunks=readonlytable(c.chunks) + return c +end +function parseManager:extractState() + return {name=self.currentChunk.name,pos = self.currentChunk.pos,variables = self.mainENV,cc = self.currentENV} +end +function parseManager:injectState(tbl) + self.chunks[tbl.name].pos=tbl.pos + self.currentChunk=self.chunks[tbl.name] + self.mainENV = tbl.variables + self.currentENV = tbl.cc +end function parseManager.split(s,pat) local pat=pat or "," local res = {} @@ -73,1205 +170,818 @@ function parseManager.split(s,pat) 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,inf) - --print(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 inf then - --print("NO!") - else - return self:evaluate(var) - 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" + end + if type(tab[g])=="string" then + tab[g]=tab[g]:gsub("\1","") + end + tab[g]=tostring(tab[g]) + end + return table.concat(tab,sep) 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*(%b())") - if not functest then - functest,args=line:match("^([%w_]-)%s*(%b())") - end - if not functest then - functest,args=line:match("([%w_]-)%s*(%b())") - local funccheck=line:match("([%+%-%*/"..self:populateSymbolList().."]+).-%(.-%)") - 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 - if args then - args=args:sub(2,-2) - 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]))])(.+)") - 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) +function parseManager:dump() + local bytecode = deepcopy(self.chunks) local str="" - for i=1,#self.symbols do - str=self.symbols[i][1]..str + for i,v in pairs(bytecode) do + str=str.."BLOCK: ["..i.."]\n" + for k=1,#v do + if type(v[k].Func)=="table" and v[k].Func.IsALookup==true then + if v[k].Type=="fwor" then + str=str.."\t"..v[k].Func[2].." "..concat(v[k].args,", ").."\n" + elseif v[k].Type=="fwr" then + str=str.."\t"..concat(v[k].vars,", ").." <- "..v[k].Func[2].." "..concat(v[k].args,", ").."\n" + end + elseif v[k].Type=="fwor" then + str=str.."\t"..v[k].Func.." "..concat(v[k].args,", ").."\n" + elseif v[k].Type=="funcblock" then + str=str.."\tFUNCTION: args("..concat(v[k].args,", ")..")\n" + elseif v[k].Type=="return" then + str=str.."\tRETURN: rets("..concat(v[k].RETArgs,", ")..")\n" + elseif v[k].Type=="label" then + str=str.."\t::"..v[k].label.."::\n" + elseif v[k].Type=="fwr" then + str=str.."\t"..concat(v[k].vars,", ").." <- "..v[k].Func.." "..concat(v[k].args,", ").."\n" + elseif v[k].Type=="choice" then + local opt={} + local met={} + local args={} + for i=1,#v[k] do + opt[#opt+1]=v[k][i][1] + met[#met+1]=v[k][i][2].Func + args[#args+1]=concat(v[k][i][2].args," ") + end + str=str.."\tCHOICE["..v[k].prompt.."]$C<"..concat(opt,", ")..">$F<"..concat(met,", ")..">$A<"..concat(args,", ")..">\n" + elseif v[k].Type=="text" then + str=str.."\tDISP_MSG \""..v[k].text.."\"\n" + elseif v[k].Type=="assign" then + str=str.."\t"..concat(v[k].vars,", ").." <- "..concat(v[k].vals,", ").."\n" + else + str=str.."\tUnknown Code!: "..tostring(v[k].data).."\n" + end + end 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}) +function table.print(tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ': ' + if type(v) == 'table' then + print(formatting) + table.print(v, indent+1) + else + print(formatting .. tostring(v)) 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,true) +function parseManager:pushError(err,sym) + error(err.." "..tostring(sym)) +end +local function pieceList(list,self,name) + if #list==0 then + return {} + end + local list=parseManager.split(list) + local L={} + local mathass=1 + for i=1,#list do + if list[i]:match("[%w_]-%[.-%]") then + local dict,sym=list[i]:match("([%w_]-)%[(.-)%]") + if tonumber(sym) then + L[#L+1]={"\1"..dict,tonumber(sym),IsALookup=true} + elseif sym:sub(1,1)=="\"" and sym:sub(-1,-1)=="\"" then + L[#L+1]={"\1"..dict,sym:sub(2,-2),IsALookup=true} end - end - if type(r)=="string" then - if r:find("%D") then - r=self:varExists(r,true) - 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 + elseif list[i]:sub(1,1)=="[" and list[i]:sub(-1,-1)=="]" then + L[#L+1]=pieceList(list[i]:sub(2,-2),self,name) + elseif tonumber(list[i]) then + L[#L+1]=tonumber(list[i]) + elseif list[i]:sub(1,1)=="\"" and list[i]:sub(-1,-1)=="\"" then + L[#L+1]=list[i]:sub(2,-2) + elseif list[i]:match("[%w_]-%..-") then + local dict,sym=list[i]:match("([%w_]-)%.(.+)") + L[#L+1]={"\1"..dict,sym,IsALookup=true} + elseif list[i]=="true" then + L[#L+1]=true + elseif list[i]=="false" then + L[#L+1]=false + elseif list[i]:match("[%w_]+")==list[i] then + L[#L+1]="\1"..list[i] + elseif list[i]:match("[_%w%+%-/%*%^%(%)%%]+") then + local char="$"..string.char(mathass+64) + self:compileExpr(char,list[i],name) + mathass=mathass+1 + L[#L+1]="\1"..char + else + self:pushError("Invalid Syntax!",list[i]) 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 + return L +end +local function pieceAssign(a,self,name) + local dict,ind=a:match("(.-)%[(.+)%]") + local var + if dict and ind then + if ind:sub(1,1)=="\"" and ind:sub(-1,-1)=="\"" then + var={dict,ind:sub(2,-2)} + elseif tonumber(ind) then + var={dict,tonumber(ind)} + elseif ind:match("[%w_]+")==ind then + var={dict,"\1"..ind} + elseif ind:match("[_%w%+%-/%*%^%(%)%%]+") then + local sym="@A" + self:compileExpr(sym,ind,name) + var={dict,"\1"..sym} + else + self:pushError("Invalid way to index a dictonary/array!",ind) + end + elseif a:match("[%w_]+")==a then + var="\1"..a + elseif a:match("[%w_]-%..-") then + local dict,sym=a:match("([%w_]-)%.(.+)") + var={dict,sym,IsALookup=true} + elseif a:find(",") then + local list = parseManager.split(a) + var={} + for i=1,#list do + table.insert(var,pieceAssign(list[i],self,name)) + end + else + self:pushError("Invalid Syntax, Assignment is invalid!",a) + end + return var +end +function parseManager:compileAssign(assignA,assignB,name) + local listA=parseManager.split(assignA) + local listB=parseManager.split(assignB) + local assign={ + Type="assign", + vars={}, + vals={} + } + for k=1,#listA do + local mathTest=false + debug("VAL: "..listB[k]) + debug("NAME: "..listA[k]) + if listB[k]:sub(1,1)=="[" and listB[k]:sub(-1,-1)=="]" then + if listB[k]:match("%[%]") then + assign.vals[#assign.vals+1]={} + else + assign.vals[#assign.vals+1]=pieceList(listB[k]:sub(2,-2),self,name) + table.print(assign.vals[#assign.vals]) + end + elseif listB[k]:match("[%w_]-%[.-%]") then + local dict,sym=listB[k]:match("([%w_]-)%[(.-)%]") + if tonumber(sym) then + assign.vals[#assign.vals+1]={"\1"..dict,tonumber(sym),IsALookup=true} + elseif sym:sub(1,1)=="\"" and sym:sub(-1,-1)=="\"" then + assign.vals[#assign.vals+1]={"\1"..dict,sym:sub(2,-2),IsALookup=true} + end + elseif listB[k]:match("[%w_]-%..-") then + local dict,sym=listB[k]:match("([%w_]-)%.(.+)") + assign.vals[#assign.vals+1]={"\1"..dict,sym,IsALookup=true} + elseif tonumber(listB[k]) then + assign.vals[#assign.vals+1]=tonumber(listB[k]) + elseif listB[k]:sub(1,1)=="\"" and listB[k]:sub(-1,-1)=="\"" then + assign.vals[#assign.vals+1]=listB[k]:sub(2,-2) + elseif listB[k]=="true" then + assign.vals[#assign.vals+1]=true + elseif listB[k]=="false" then + assign.vals[#assign.vals+1]=false + elseif listB[k]:match("[%w_]+")==listB[k] then + assign.vals[#assign.vals+1]="\1"..listB[k] + elseif listB[k]:match("[_%w%+%-/%*%^%(%)%%]+")==listB[k] then + mathTest=true + self:compileExpr(listA[k],listB[k],name) + else + self:pushError("Invalid Systax:",listB[k]) + end + if not mathTest then + assign.vars[#assign.vars+1]=pieceAssign(listA[k],self,name) + else + print("FUCK!!!!!!!!!!!") + end + end + table.print(assign) + table.insert(self.chunks[name],assign) +end +function parseManager:compileCondition(condition,iff,elsee,name) + self:compileLogic(condition,iff,elsee,name) +end +function parseManager:compileExpr(eql,expr,name) + local cmds={} + expr=expr:gsub("([%W])(%-%d)",function(b,a) + return b.."(0-"..a:match("%d+")..")" + end) + local mathAss=1 + function packFunc(l,o,r) + local l=tonumber(l) or l + local o=tonumber(o) or o + local r=tonumber(r) or r + if type(l)=="string" and l:match("[%w_]") then + l="\1"..l + end + if type(r)=="string" and r:match("[%w_]") then + r="\1"..r + end + if type(o)=="string" and o:match("[%w_]") then + o="\1"..o + end + if l=="@" then + l=r + r="" + end + if type(l)=="string" then + if l:find("\3") then + if type(o)=="number" then + cmds[#cmds+1]={Func=l:match("\3(.+)"),args={o}} + else + if o=="@" then o="" end + if o=="" then o=nil end + cmds[#cmds+1]={Func=l:match("\3(.+)"),o} + end + return + end + end + if l=="@" then -- Fancy movement of math + local n=#cmds + cmds[n]["vars"]={"\1%"..string.char(mathAss+64)} + l="\1%"..string.char(mathAss+64) + mathAss=mathAss+1 + cmds[n+1]["vars"]={"\1%"..string.char(mathAss+64)} + r="\1%"..string.char(mathAss+64) + mathAss=mathAss+1 + end + if r=="@" then -- Fancy movement of math + local n=#cmds + cmds[n]["vars"]={"\1%"..string.char(mathAss+64)} + r="\1%"..string.char(mathAss+64) + mathAss=mathAss+1 + -- cmds[n]["vars"]={"\1%"..string.char(mathAss+64)} + -- l="\1%"..string.char(mathAss+64) + -- mathAss=mathAss+1 + end + if r=="" then + local n=#cmds + cmds[n]["vars"]={"\1%"..string.char(mathAss+64)} + r=l + l="\1%"..string.char(mathAss+64) + mathAss=mathAss+1 + end + if o=="+" then + cmds[#cmds+1]={Func="ADD",args={l,(r or "")}} + elseif o=="-" then + cmds[#cmds+1]={Func="SUB",args={l,(r or "")}} + elseif o=="/" then + cmds[#cmds+1]={Func="DIV",args={l,(r or "")}} + elseif o=="*" then + cmds[#cmds+1]={Func="MUL",args={l,(r or "")}} + elseif o=="^" then + cmds[#cmds+1]={Func="POW",args={l,(r or "")}} + elseif o=="%" then + cmds[#cmds+1]={Func="MOD",args={l,(r or "")}} + else + self:pushError("Something went wrong!",tostring(l)..","..tostring(o)..","..tostring(r)) + end + end + function parseManager:pieceExpr(expr) + local count=0 + for i,v in pairs(self.methods) do + expr=expr:gsub(i.."(%b())",function(a) + a=a:sub(2,-2) + if a:sub(1,1)=="-" then + a="0"..a + end + packFunc("\3"..i,self:pieceExpr(a)) + return "@" + end) + end + for i,v in pairs(self.cFuncs) do + expr=expr:gsub(i.."(%b())",function(a) + a=a:sub(2,-2) + if a:sub(1,1)=="-" then + a="0"..a + end + packFunc("\3"..i,self:pieceExpr(a)) + return "@" + end) + end--self.cFuncs + expr=expr:gsub("%b()",function(a) + return self:pieceExpr(a:sub(2,-2)) + end) + local loop + for l,o,r in expr:gmatch("(.*)([%+%^%-%*/%%])(.*)") do + loop=true + if l:match("[%+%^%-%*/%%]") then + packFunc(self:pieceExpr(l),o,r) + else + packFunc(l,o,r) + end + end + if loop then + return "@" + else + return expr + end + end + if expr:match("[!%$&_%w%+%-/%*%^%(%)%%]+")==expr then + parseManager:pieceExpr(expr) + cmds[#cmds]["vars"]={"\1"..eql} + for i=1,#cmds do + if cmds[i].vars then + cmds[i].Type="fwr" + else + cmds[i].Type="fwor" + end + if not name then + --self:pushError("Unknown Error:",name) + else + table.insert(self.chunks[name],cmds[i]) + end + end + else + --self:pushError("Invalid math Expression!",expr) + end +end +function parseManager:compileFWR(FWR,vars,args,name) + vars=pieceAssign(vars,self,name) + if type(vars)=="string" then + vars={vars} + end + table.insert(self.chunks[name],{ + Type="fwr", + Func=FWR, + vars=vars, + args=pieceList(args,self,name), + }) +end +function parseManager:compileFWOR(FWOR,args,name) + table.insert(self.chunks[name],{ + Type="fwor", + Func=FWOR, + args=pieceList(args,self,name), + }) +end +function parseManager:compileLabel(label,name) + self.chunks[name].labels[label]=#self.chunks[name]+1 -- store this inside the chunk + table.insert(self.chunks[name],{ + Type="label", + pos=#self.chunks[name]+1, + label=label, + }) +end +function parseManager:compileLine(line,name) + table.insert(self.chunks[name],{ + Type="text", + text=line + }) +end +function parseManager:compileLogic(condition,iff,elsee,name) + local cmds={} + local function pieceLogic(conds) + conds=conds.." or 1==0" + conds=conds:gsub(" and ","\4") + conds=conds:gsub(" or ","\5") + conds=conds:gsub("(\".*[\4].*\")",function(a) + return a:gsub("\4"," and ") + end) + conds=conds:gsub("(\".*[\5].*\")",function(a) + return a:gsub("\4"," or ") + end) + local count=0 + local mathass=0 + _conds=conds:gsub("%s*\5".."1==0","") + local cmds={} + for l,eq,r in conds:gmatch("(.-)([=~!><][=]*)(.-)%s*[\4\5]") do + charL=string.char(count+65) + charM=string.char(mathass+65) + count=count+1 + cmds={ + Type="fwr", + Func="COMPARE", + args={[3]=eq}, + vars={"\1!"..charL}, + } + local l,r=(l:gsub("[\4\5\6]*%(","")),(r:gsub("%)","")) + if l=="true" then + cmds.args[1]=true + elseif l=="false" then + cmds.args[1]=false + elseif tonumber(l) then + cmds.args[1]=tonumber(l) + elseif l:match("[%w_]+")==l then + cmds.args[1]="\1"..l + elseif l:match("[_%w%+%-/%*%^%(%)%%]+")==l then + self:compileExpr("&"..charM,l,name) + cmds.args[1]="\1&"..charM + mathass=mathass+1 + elseif l:sub(1,1)=="\"" and l:sub(-1,-1) then + cmds.args[1]=l:sub(2,-2) + else + self:pushError("Invalid Syntax in logical expression!",l) + end + r=r:gsub("%s*\5".."1==0","") + charM=string.char(mathass+65) + if r=="true" then + cmds.args[2]=true + elseif r=="false" then + cmds.args[2]=false + elseif tonumber(r) then + cmds.args[2]=tonumber(r) + elseif r:match("[%w_]+")==r then + cmds.args[2]="\1"..r + elseif r:match("[_%w%+%-/%*%^%(%)%%]+")==r then + self:compileExpr("&"..charM,r,name) + cmds.args[2]="\1&"..charM + mathass=mathass+1 + elseif r:sub(1,1)=="\"" and r:sub(-1,-1) then + cmds.args[2]=r:sub(2,-2) + else + self:pushError("Invalid Syntax in logical expression!",r) + end + l=l:gsub("%%","%%%%");r=r:gsub("%%","%%%%");l=l:gsub("%+","%%%+");r=r:gsub("%+","%%%+");l=l:gsub("%*","%%%*");r=r:gsub("%*","%%%*");l=l:gsub("%-","%%%-");r=r:gsub("%-","%%%-");l=l:gsub("%^","%%%^");r=r:gsub("%^","%%%^");l=l:gsub("%$","%%%$");r=r:gsub("%$","%%%$");l=l:gsub("%.","%%%.");r=r:gsub("%.","%%%.");l=l:gsub("%[","%%%[");r=r:gsub("%[","%%%[");l=l:gsub("%]","%%%]");r=r:gsub("%]","%%%]");l=l:gsub("%?","%%%?");r=r:gsub("%?","%%%?");l=l:gsub("%(","%%%(");r=r:gsub("%(","%%%(");l=l:gsub("%)","%%%)");r=r:gsub("%)","%%%)") + _conds=_conds:gsub(l.."%s*"..eq.."%s*"..r,"!"..charL) + table.insert(self.chunks[name],cmds) + end + _conds=_conds:gsub("\4","*") + _conds=_conds:gsub("\5","+") + if not _conds:find("%*") and not _conds:find("%+") then + cmds.vars[1]="\1L$" + else + self:compileExpr("L$",_conds,name) + end + table.insert(self.chunks[name],{ + Type="fwor", + Func="CSIM", + args={"\1L$"}, + }) + FWORi,argsi=iff:match("^([%w_]+)%s*%((.*)%)") + FWORe,argse=elsee:match("^([%w_]+)%s*%((.*)%)") + self:compileFWOR(FWORi,argsi,name) + self:compileFWOR(FWORe,argse,name) + end + pieceLogic(condition) +end +local function trim1(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end +function parseManager:compile(name,ctype,data) + local isFBlock,FBArgs=ctype:match("(f)unction%((.*)%)") + --Check if we are dealing with a FBlock + if isFBlock=="f" then + self.cFuncs[name]=true + -- if self.methods[name] then + -- self:pushError("You cannot create a method with the same name as a standard method or duplicate method names!",name) + -- end + self.methods[name]=function(...) + self:Invoke(name,{},...) + end + self.mainENV[name]=self.methods[name] + table.insert(self.chunks[name],{ + Type="funcblock", + args=pieceList(FBArgs,self,name) + }) + end + debug("COMPILING Block: "..name) + local data=bin.new(data):lines() + local choiceBlock=false + local choice={} + for i=1,#data do + data[i]=trim1(data[i]) + if data[i]~="" then + if data[i]:match(".-\"<%s*") then + choiceBlock=true + choice={} + j=0 + end + if choiceBlock then + if data[i]:find(">") then + choiceBlock=false + table.insert(self.chunks[name],choice) + else + dat=data[i]:gsub("<","") + if j==0 then + choice.Type="choice" + choice.prompt=dat:sub(2,-2) + j=1 + else + local a,b=dat:match("\"(.-)\"%s*(.+)") + if b then + local f,ag=b:match("^([%w_]+)%s*%((.*)%)") + if ag~="" then + choice[#choice+1]={a,{ + Type="fwor", + Func=f, + args=pieceList(ag,self,name), + }} + else + choice[#choice+1]={a,{ + Type="fwor", + Func=f, + args={}, + }} + end + end + end + end + else + local cmd={} + local Return,RETArgs=data[i]:match("(return)%s*(.*)$") + local line=data[i]:match("^\"(.+)\"") + local assignA,assignB=data[i]:match("^([%w,%[%]\"_%(%)%+%-%*%%%./]+)%s*=%s*(.+)") + local vars,FWR,args=data[i]:match("([\"%[%]%w_,]+)%s*=%s*([%w_]+)%s*%((.*)%)$") + local FWOR + if not args then + FWOR,args=data[i]:match("^([%w_]+)%s*%((.*)%)$") + end + local label=data[i]:match("::(.*)::") + local condition,iff,elsee=data[i]:match("if%s*(.+)%s*then%s*(.-%))%s*|%s*(.+%))") + ------ + local vars2,FWR2,args2=data[i]:match("([%[%]\"%w_,]+)%s*=%s*([%.:%w_]+)%s*%((.*)%)$") + if not args2 then + FWOR2,args2=data[i]:match("^([%.:%w_]+)%s*%((.*)%)$") + end + ------ + if line then + self:compileLine(line,name) + elseif condition then + self:compileCondition(condition,iff,elsee,name) + elseif FWR then + self:compileFWR(FWR,vars,args,name) + elseif FWOR then + self:compileFWOR(FWOR,args,name) + elseif FWR2 then + local dict,dot,sym=FWR2:match("([%w_]-)([%.:])(.+)") + if dot==":" then + args2=dict..","..args2 + end + self:compileFWR({dict,sym,IsALookup=true},vars2,args2,name) + elseif FWOR2 then + local dict,dot,sym=FWOR2:match("([%w_]-)([%.:])(.+)") + if dot==":" then + args2=dict..","..args2 + end + self:compileFWOR({dict,sym,IsALookup=true},args2,name) + elseif assignA then + self:compileAssign(assignA,assignB,name) + elseif label then + self:compileLabel(label,name) + elseif Return and isFBlock then + table.insert(self.chunks[name],{ + Type="return", + RETArgs=pieceList(RETArgs,self,name) + }) + elseif Return and not(isFBlock) then + self:pushError("Attempt to call return in a non function block!",data[i]) + else + table.insert(self.chunks[name],{ + Type="customdata", + data=data[i], + }) + end + end + end + end +end +function parseManager:testDict(dict) + if type(dict[1])=="string" then + if dict[1]:sub(1,1)=="\1" and dict.IsALookup then + return true + end + else + return + end +end +function parseManager:dataToValue(name,envF) -- includes \1\ + envF=envF or self.currentENV + local tab=name + if type(name)=="string" then + if name:sub(1,1)=="\1" then + return self:parseHeader(envF[name:sub(2)]) + else + return self:parseHeader(name) + end + elseif type(name)=="table" then + if name.__index then + return name + end + if self:testDict(name) then + return envF[name[1]:sub(2,-1)][self:dataToValue(name[2])] + else + tab={} + for i=1,#name do + tab[i]=self:dataToValue(name[i],envF) + end + end + end + return tab or {} +end +function parseManager:define(t) + for i,v in pairs(t) do + self.methods[i]=v + end +end +function push(s,n) + table.insert(s,n) +end +function pop(s) + return table.remove(s) +end +function peek(s) + return s[#s] +end +function parseManager:Invoke(func,vars,...) + local name=func + local func=self.chunks[func] + if func then + if func.type:sub(1,1)=="f" then + local returndata={} + self.fArgs={...} + if #self.fArgs==0 then + self.fArgs={self.lastCall} + end + push(self.stack,{chunk=self.currentChunk,pos=self.currentChunk.pos,env=self.currentENV,vars=vars}) + self.methods.JUMP(self,name) + return + else + self:pushError("Attempt to call a non function block!",name) + end + else + self:pushError("Attempt to call a non existing function!",name) + end +end +function parseManager:parseHeader(data) + if type(data)=="string" then + data=data:gsub("%$([%w_:%.%[%]\"']+)%$",function(dat) + debug("PARSE: "..dat) + if dat:find(":") then + -- for later use + elseif dat:find("%[") then + -- for dicts + else + -- regular strings + debug("PARSE DATA: "..tostring(self.currentENV[dat])) + if self.currentENV[dat]~=nil then + if type(self.currentENV[dat])=="table" then + local str="" + for i=1,#self.currentENV[dat] do + str=str..tostring(self.currentENV[dat][i]).."," + end + str=str:sub(1,-2) + return "["..str.."]" + else + return tostring(self.currentENV[dat]) + end + else + return "nil" + end 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) + return data +end +function parseManager:pairAssign(vars,vals,envF) + for i=1,#vars do + debug("ASSIGN NAME: "..tostring(vars[i])) + debug("ASSIGN DATA: "..tostring(self:dataToValue(vals[i],envF))) + if type(vars[i])=="table" then + if type(self.currentENV[vars[i][1]])~="table" then + self:pushError("Attempt to index a non table object:",vars[i][1].."[\""..vars[i][2].."\"]") + end + self.currentENV[vars[i][1]][self:dataToValue(vars[i][2])]=self:dataToValue(vals[i],envF) else - if count==1 then - v=helper(o,r,l) + self.currentENV[vars[i]:sub(2,-1)]=self:dataToValue(vals[i],envF) + end + end +end +function parseManager:next(block,choice) + if self.entry then + block = block or self.entry + self.entry = nil + end + local chunk = self.currentChunk or self.chunks[block] or self.chunks["START"] + self.currentChunk=chunk + if choice then + local c=self.choiceData[choice][2] + local args=self:dataToValue(c.args) + if not self.methods[c.Func] then + self.lastCall=self:Invoke(c.Func,"lastCall",unpack(args)) + else + self.lastCall=self.methods[c.Func](self,unpack(args)) + end + return {Type="method"} + end + local ret + local data=chunk[chunk.pos] + if not data then return end + if data.Type=="label" then + chunk.pos=chunk.pos+1 + data=chunk[chunk.pos] + end + chunk.pos=chunk.pos+1 + debug("TYPE: "..data.Type) + if data.Type=="text" then + self.lastCall=nil + return {Type="text",text=self:parseHeader(data.text)} + elseif data.Type=="funcblock" then + local env=self.currentENV + self.currentENV=self:newENV() + self:pairAssign(data.args,self.fArgs,env) + return {Type="FBLOCK"} + elseif data.Type=="return" then + local obj=pop(self.stack) + local chunk=obj.chunk + local pos=obj.pos + local env=obj.env + local vars=obj.vars + local name=chunk.name + local env=self.currentENV + chunk.pos=pos + self.currentENV=env + self:pairAssign(vars,data.RETArgs,env) + self.currentChunk=chunk + return {Type="method"} + elseif data.Type=="fwr" then + local args=self:dataToValue(data.args) + local rets={} + local Func + local Ext=false + if type(data.Func)=="table" then + Ext=true + Func=self.currentENV[data.Func[1]][data.Func[2]] + else + Func=self.methods[data.Func] + end + if Ext then + rets={Func(unpack(args))} + else + if type(Func)~="function" then + rets={self:Invoke(data.Func,data.vars,unpack(args))} + return {Type="method"} + else + rets={Func(self,unpack(args))} end end - end - if not loop then return self:varExists(cmd,true) end - return v -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) + if #rets~=0 then + self:pairAssign(data.vars,rets) 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 + self.lastCall=nil + return {Type="method"} + elseif data.Type=="fwor" then + local args=self:dataToValue(data.args) + local Func + local Ext=false + if type(data.Func)=="table" then + Ext=true + Func=self.currentENV[data.Func[1]][data.Func[2]] + else + Func=self.methods[data.Func] 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 + if Ext then + self.lastCall=Func(unpack(args)) + else + if type(Func)~="function" then + self.lastCall=self:Invoke(data.Func,"lastCall",unpack(args)) + return {Type="method"} + else + self.lastCall=Func(self,unpack(args)) + end + end + return {Type="method"} + elseif data.Type=="choice" then + self.choiceData=data + local CList={} + for i=1,#data do + CList[#CList+1]=self:parseHeader(data[i][1]) + end + self.lastCall=nil + return {Type="choice",prompt=self:parseHeader(data.prompt),CList} + elseif data.Type=="assign" then + self:pairAssign(data.vars,data.vals) + self.lastCall=nil + return {Type="assign"} + else + self.lastCall=nil + return {Type="Custom Syntax"} + end + return rets end ---registerSymbol(sym,code) -parseManager.OnExtendedBlock(parseManager.funcType) -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 -parseManager.OnExtendedBlock(parseManager.constructType) +require("parseManager.standardDefine") diff --git a/parseManager/interpreter.lua b/parseManager/interpreter.lua deleted file mode 100644 index e4a5b84..0000000 --- a/parseManager/interpreter.lua +++ /dev/null @@ -1,10 +0,0 @@ -engine={} -function engine:init(bytecodeFile) - self.code=bin.load(bytecodeFile).data -end ---[[OP-CODES - -]] -function engine:run(assessors) - -- -end diff --git a/parseManager/standardDefine.lua b/parseManager/standardDefine.lua new file mode 100644 index 0000000..5bd23d0 --- /dev/null +++ b/parseManager/standardDefine.lua @@ -0,0 +1,310 @@ +parseManager:define{ + print=function(self,...) + print(...) + end, + error=function(self,msg) + self:pushError(msg,"\2") + end, + QUIT=function() + os.exit() + end, + JUMP=function(self,block) + if self.chunks[block] then + self.chunks[block].pos=1 + self.currentChunk=self.chunks[block] + else + self:pushError("Attempt to jump to a non existing block:","\2") + end + end, + SKIP=function(self,n) + if type(n)~="number" then self:pushError("Number expected got: "..type(n),"SKIP( ? )") end + self.currentChunk.pos=self.currentChunk.pos+n + end, + GOTO=function(self,label) + if self.currentChunk.labels[label] then + self.currentChunk.pos=self.currentChunk.labels[label] + else + self:pushError("Attempt to goto a non existing label:","\2") + end + end, + GOTOE=function(self,label) + local chunks=self.chunks + for i,v in pairs(chunks) do + if chunks[i].labels[label] then + self.currentChunk=chunks[i] + self.currentChunk.pos=chunks[i].labels[label] + return + end + end + self:pushError("Attempt to goto a non existing label:","\2") + end, + ADD=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return self.lastCall+a + elseif type(a)=="number" and type(b)=="number" then + return a+b + else + self:pushError("Invalid Arguments!","ADD("..tostring(a)..","..tostring(b)..")") + end + end, + SUB=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return self.lastCall-a + elseif type(a)=="number" and type(b)=="number" then + return a-b + else + self:pushError("Invalid Arguments!","SUB("..tostring(a)..","..tostring(b)..")") + end + end, + MUL=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return self.lastCall*a + elseif type(a)=="number" and type(b)=="number" then + return a*b + else + self:pushError("Invalid Arguments!","MUL("..tostring(a)..","..tostring(b)..")") + end + end, + DIV=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return self.lastCall/a + elseif type(a)=="number" and type(b)=="number" then + return a/b + else + self:pushError("Invalid Arguments!","DIV("..tostring(a)..","..tostring(b)..")") + end + end, + POW=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return self.lastCall^a + elseif type(a)=="number" and type(b)=="number" then + return a^b + else + self:pushError("Invalid Arguments!","POW("..tostring(a)..","..tostring(b)..")") + end + end, + sqrt=function(self,a) + if type(self.lastCall)=="number" then + return math.sqrt(self.lastCall) + elseif type(a)=="number" then + return math.sqrt(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + cos=function(self,a) + if type(self.lastCall)=="number" then + return math.cos(self.lastCall) + elseif type(a)=="number" then + return math.cos(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + sin=function(self,a) + if type(self.lastCall)=="number" then + return math.sin(self.lastCall) + elseif type(a)=="number" then + return math.sin(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + tan=function(self,a) + if type(self.lastCall)=="number" then + return math.tan(self.lastCall) + elseif type(a)=="number" then + return math.tan(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + log=function(self,a) + if type(self.lastCall)=="number" then + return math.log(self.lastCall) + elseif type(a)=="number" then + return math.log(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + acos=function(self,a) + if type(self.lastCall)=="number" then + return math.acos(self.lastCall) + elseif type(a)=="number" then + return math.acos(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + tanh=function(self,a) + if type(self.lastCall)=="number" then + return math.tanh(self.lastCall) + elseif type(a)=="number" then + return math.tanh(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + deg=function(self,a) + if type(self.lastCall)=="number" then + return math.deg(self.lastCall) + elseif type(a)=="number" then + return math.deg(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + cosh=function(self,a) + if type(self.lastCall)=="number" then + return math.cosh(self.lastCall) + elseif type(a)=="number" then + return math.cosh(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + sinh=function(self,a) + if type(self.lastCall)=="number" then + return math.sinh(self.lastCall) + elseif type(a)=="number" then + return math.sinh(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + randomseed=function(self,a) + if type(self.lastCall)=="number" then + return math.randomseed(self.lastCall) + elseif type(a)=="number" then + return math.randomseed(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + ceil=function(self,a) + if type(self.lastCall)=="number" then + return math.ceil(self.lastCall) + elseif type(a)=="number" then + return math.ceil(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + floor=function(self,a) + if type(self.lastCall)=="number" then + return math.floor(self.lastCall) + elseif type(a)=="number" then + return math.floor(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + rad=function(self,a) + if type(self.lastCall)=="number" then + return math.rad(self.lastCall) + elseif type(a)=="number" then + return math.rad(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + abs=function(self,a) + if type(self.lastCall)=="number" then + return math.abs(self.lastCall) + elseif type(a)=="number" then + return math.abs(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + asin=function(self,a) + if type(self.lastCall)=="number" then + return math.asin(self.lastCall) + elseif type(a)=="number" then + return math.asin(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + log10=function(self,a) + if type(self.lastCall)=="number" then + return math.log10(self.lastCall) + elseif type(a)=="number" then + return math.log10(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + atan2=function(self,a) + if type(self.lastCall)=="number" then + return math.atan2(self.lastCall) + elseif type(a)=="number" then + return math.atan2(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + exp=function(self,a) + if type(self.lastCall)=="number" then + return math.exp(self.lastCall) + elseif type(a)=="number" then + return math.exp(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + atan=function(self,a) + if type(self.lastCall)=="number" then + return math.atan(self.lastCall) + elseif type(a)=="number" then + return math.atan(a) + else + self:pushError("Invalid Arguments!","\2") + end + end, + max=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return max(self.lastCall,a) + elseif type(a)=="number" and type(b)=="number" then + return max(a,b) + else + self:pushError("Invalid Arguments!","\2") + end + end, + mod=function(self,a,b) + if type(self.lastCall)=="number" and type(a)=="number" then + return mod(self.lastCall,a) + elseif type(a)=="number" and type(b)=="number" then + return mod(a,b) + else + self:pushError("Invalid Arguments!","\2") + end + end, + COMPARE=function(self,v1,v2,sym) + if sym==nil then self:pushError("Unexpected Error has occured!",":(") end + if sym=="==" then + if v1==v2 then return 1 else return 0 end + elseif sym==">=" then + if v1>=v2 then return 1 else return 0 end + elseif sym=="<=" then + if v1<=v2 then return 1 else return 0 end + elseif sym==">" then + if v1>v2 then return 1 else return 0 end + elseif sym=="<" then + if v11 -- bit shift + a=10>1 b=10<1 "test: $a$ $b$" + test["name"]="Ryan" + name=test["name"] + "Hi $test[name]$! $name$" testfunc("hello",(5!),15@1) + test("hello",sqrt(5!),(15@1)/2) } [@:construct]{ -- get % out of 100 ret=l/(r/100) @@ -29,6 +32,9 @@ ENTRY START return(ret) ::end:: } +[test:function(a,b,c)]{ + "$a$ $b$ $c$" +} -- You dont have too many symbols left to use though. For now a symbol is only 1 char long so you are limited [fact:function(n)]{ count=1 @@ -46,7 +52,7 @@ ENTRY START --Bind the fact function to the symbol '!' [!:construct]{ env=fact(l) - ret=ret<-env + ret=env["ret"] return(ret) } [NOVAR]{ diff --git a/parsetest3.txt b/parsetest3.txt new file mode 100644 index 0000000..d8e6627 --- /dev/null +++ b/parsetest3.txt @@ -0,0 +1,32 @@ +ENABLE leaking +[TEST]{ + "Jump was successful!" + --GOTO("HERE") +} +[START]{ + "Test 1:" + t=15 + test=2*100 + "test = $test$" + c=5 + ::HERE:: + a,b="sdf",true + "a,b,c = $a$ $b$ $c$" + food="yum" + test=true + stop=21 + "food = $food$" + "test = $test$" + "stop = $stop$" + "Test 2:" + count=0 + ::loop:: + "Count = $count$" + count=count+1 + if count>10 then GOTO("end")|GOTO("loop") + ::end:: + "Done!" + QUIT() +} + +-- (count==stop and name=="bob") ? GOTO(end) : GOTO(loop) \ No newline at end of file diff --git a/test.lua b/test.lua index d824027..ae5a45f 100644 --- a/test.lua +++ b/test.lua @@ -2,7 +2,6 @@ package.path="?/init.lua;lua/?/init.lua;lua/?.lua;"..package.path require("bin") require("multi.all") require("parseManager") ---~ require("Library") require("bit") parseManager:define({ rshift=function(self,a,b) @@ -12,11 +11,11 @@ parseManager:define({ return bit.lshift(a,b) end, testfunc=function(self,a,b,c) - print("> "..tostring(a).." "..tostring(b).." "..tostring(c)) + print(tostring(a).." "..tostring(b).." "..tostring(c)) end }) -test=parseManager:load("parsetest2.txt") -t=test:start() +test=parseManager:load("parsetest3.txt") +t=test:start("START") while true do if t.Type=="text" then print(t.text)