diff --git a/DMS.xml b/DMS.xml new file mode 100644 index 0000000..7e88b21 --- /dev/null +++ b/DMS.xml @@ -0,0 +1,64 @@ + + + + + + + + 00// 01 02 03/* 04*/ + + + + + + + + + - * / = ( ) [ ] , > < ~ ! | ; : + + { + + } + + + + /* + + */ + ENABLE DISABLE LOADFILE ENTRY USING VERSION as AS enable disable loadfile entry using version + if then return and or true false for while choice end else elseif goto + leaking debugging warnings statesave hostmsg + ceil tan CSIM log10 sinh GOTOE lshift deg MUL QUIT cosh exp rad GOTO SUB log ADD JUMP error POW randomseed floor tanh max atan SKIP acos DIV abs rshif COMPARE print atan2 asin cos sin mod sqrt function getInput sleep getVar setVar newThread setGlobalVar getGlobalVar SAVE LOAD WATCH env + _VERSION + filesystem extendedDefine + + + 00:: 01 02:: 03$ 04 05$ 06" 07 08" 09 10 11 12' 13 14' 15 16 17 18 19 20 21 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler2.lua b/compiler2.lua index f12def3..4ccf752 100644 --- a/compiler2.lua +++ b/compiler2.lua @@ -1,137 +1,4 @@ -file = io.open("test.dms","rb") -content = file:read("*a") -line_num = 0 -CMD = {} -CMD.__index = CMD -function CMD:new(ln,cmd,args) - local c = {} - setmetatable(c,self) - c.line_num = ln - c.command = cmd - c.args = args -end -function CMD:process() - -end -Stack = {} -Stack.__index = Stack -function Stack.__tostring(self) - return table.concat(self.stack, ", ") -end -function Stack:new(n) - local c = {} - setmetatable(c,self) - c.max = n or math.huge - c.stack = {} - return c -end -function Stack:push(n) - table.insert(self.stack,n) -end -function Stack:pop() - return table.remove(self.stack,#self.stack) -end -function Stack:peek(n) - return self.stack[#self.stack - (n or 0)] -end -function Stack:count() - return #self.stack -end - -function string:trim() - return (self:gsub("^%s*(.-)%s*$", "%1")) -end -function string.tabs(str) - local c = 0 - for i in str:gmatch(".") do - if i=="\t" then - c=c+1 - else - break - end - end - return c -end -local choice -local group = 0 -local lastgroup = 0 -local groupStack = Stack:new({}) -local lastpop -groupStack:push({}) -function groupStack:append(t) - local c = self:peek() - val = pcall(function() - table.insert(c,t) - end) - if not val then - error("Inconsistant indentation!") - end -end -local noblock = true -for line in content:gmatch("(.-)\n") do - line_num = line_num + 1 - --line = line:trim() - --line = line:gsub("") - lastgroup = group - group = line:tabs() - if lastgroup>group then - for i=1,lastgroup-group do - local c = groupStack:pop() - lastpop = c - for i,v in pairs(c) do - print(table.concat(v,", ")) - end - end - elseif lastgroup",line} - noblock = false - -- We gonna define header stuff - elseif noblock and line:lower():match("enable%s(.+)") then - groupStack:append{group,line_num,"",line} - elseif noblock and line:lower():match("disable%s(.+)") then - groupStack:append{group,line_num,"",line} - elseif noblock and line:lower():match("loadfile%s(.+)") then - groupStack:append{group,line_num,"",line} - elseif noblock and line:lower():match("entry%s(.+)") then - groupStack:append{group,line_num,"",line} - elseif noblock and line:lower():match("using%s(.+)") then - groupStack:append{group,line_num,"",line} - elseif noblock and line:lower():match("version%s(.+)") then - groupStack:append{group,line_num,"",line} - elseif line:match("choice%s+\".+\"") then - groupStack:append{group,line_num,"",line} - choice = true - elseif line:match("::([_:,%w%(%)]+)::") then - groupStack:append{group,line_num,"",line} - elseif line:match("for%s*[_%w]-%s") then - groupStack:append{group,line_num,"",line} - elseif line:match("%s*while%s*.+") then - groupStack:append{group,line_num,"",line} - elseif choice then - choice = false - if line:match("\".*\"%s*[_:,%w%(%)]+%(.*%)") then - groupStack:append{group,line_num,"",line} - choice = true - else - goto back - end - elseif line:match("[%s,_%w]*=.-%(.+%)") then - groupStack:append{group,line_num,"",line} - elseif line:match(".-%(.+%)") then - groupStack:append{group,line_num,"",line} - elseif line:match("[%s,_%w]*=(.+)") then - groupStack:append{group,line_num,"",line} - elseif line:match("\"(.+)\"") then - groupStack:append{group,line_num,"",line} - else - groupStack:append{group,line_num,"",line} - end - ::continue:: -end \ No newline at end of file +package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path +local parser = require("dms.parser") +local p = parser:new("test.dms") +p:parse() diff --git a/dms/chunk.lua b/dms/chunk.lua new file mode 100644 index 0000000..89655b9 --- /dev/null +++ b/dms/chunk.lua @@ -0,0 +1,33 @@ +local cmd = require("dms.cmd") +local Chunk = {} +Chunk.__index = Chunk +function Chunk:__tostring() + local str = self.chunkname..":"..self.chunktype.."\n" + local s = "" + for i,v in pairs(self.cmds) do + str = str .. tostring(v).."\n" + end + return str +end +function Chunk:new(cname,ctype,filename) + local c = {} + setmetatable(c,self) + c.chunkname = cname + c.chunktype = ctype + c.filename = filename + c.pos = 0 + c.cmds = {} + c.labels = {} -- ["label"] = pos + return c +end +function Chunk:addLabel(label) + self.labels[label] = self:count() +end +function Chunk:addCmd(cmd) + cmd.chunk = self + table.insert(self.cmds,cmd) +end +function Chunk:count() + return #self.cmds +end +return Chunk \ No newline at end of file diff --git a/dms/cmd.lua b/dms/cmd.lua new file mode 100644 index 0000000..f9ab6df --- /dev/null +++ b/dms/cmd.lua @@ -0,0 +1,15 @@ +local CMD = {} +CMD.__index = CMD +CMD.tostring = function(self) return self.command end +function CMD:__tostring() + return self.command .. " " .. self:tostring() +end +function CMD:new(line,cmd,args) + local c = {} + setmetatable(c,self) + c.line = line + c.command = cmd + c.args = args + return c +end +return CMD \ No newline at end of file diff --git a/dms/init.lua b/dms/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/dms/parser.lua b/dms/parser.lua new file mode 100644 index 0000000..75a2eba --- /dev/null +++ b/dms/parser.lua @@ -0,0 +1,335 @@ +require("dms.utils") +local Stack = require("dms.stack") +local Chunk = require("dms.chunk") +local Cmd = require("dms.cmd") +local ENTR,ENAB,DISA,LOAD,VERN,USIN,STAT,DISP,ASGN,LABL,CHOI,OPTN,FORE,UNWN,WHLE,FNWR,FNNR,IFFF,ELIF,ELSE,DEFN = "ENTR","ENAB","DISA","LOAD","VERN","USIN","STAT","DISP","ASGN","LABL","CHOI","OPTN","FORE","????","WHLE","FNWR","FNNR","IFFF","ELIF","ELSE","DEFN" +local controls = {STAT,CHOI,FORE,WHLE,IFFF,ELIF,ELSE} +local flags = {ENTR,ENAB,DISA,LOAD,VERN,USIN,DEFN} +local recognizedFlags = { + "debugging", + "noprint", + "warnings" +} +local parser = {} +parser.__index = parser +function parser:new(path) + local c = {} + setmetatable(c,self) + c.filename = path or error("Must provied a path!") + c.content = io.open(c.filename,"rb"):read("*a") + c.flags = {} + c.chunks = {} + c.ver = {1,0,0} + c.pos = 0 + c.lines = {} + return c +end +function parser:setVersion(ver) + self.ver = ver +end +-- Output Stuffs +function parser:debug(...) + if not self.flags.debugging then return end + self:print(...) +end +function parser:print(...) + if self.flags.noprint then return end + print(...) +end +function parser:error(err,line) + if not line then print(err) os.exit() end + print("Error: <"..line[4]..":"..line[1].."> \""..line[5].."\" "..err) + os.exit() +end +function parser:warn(msg,line) + if not self.flags.warnings then return end + if not line then print(msg) return end + print("Warning: <"..line[4]..":"..line[1].."> \""..line[5].."\" "..msg) +end + +function parser:manageFlag(line) + if self:isFlag(line) then + local flag = line[3] + local dat = line[5]:match("%s+(.+)$") + if flag == ENTR then + if self.entry then + self:error("Entry was already set to: "..self.entry,line) + else + self.entry = dat + end + elseif flag == ENAB then + if not self:isRecognizedFlag(dat) then self:warn("Flag \""..dat.."\" is not recognized!",line) end + self.flags[dat] = true + elseif flag == DISA then + if not self:isRecognizedFlag(dat) then self:warn("Flag \""..dat.."\" is not recognized!",line) end + self.flags[dat] = false + elseif flag == LOAD then + -- TODO + elseif flag == VERN then + local v + local a,b,c = dat:match("(%d*)%.?(%d*)%.?(%d*)") + a,b,c = tonumber(a),tonumber(b) or 0,tonumber(c) or 0 + if a>self.ver[1] or a>self.ver[2] then + self:warn("This script was created for a different version of the DMS! Code may behave unexpectedly or not work at all!",line) + end + elseif flag == USIN then + -- TODO + end + else + self:error("Flag Expected! Got: "..line[3],line) + end +end +function parser:isControl(line) + for i,v in pairs(controls) do + if line[3] == v then + return true + end + end + return false +end +function parser:isFlag(line) + for i,v in pairs(flags) do + if line[3] == v then + return true + end + end + return false +end +function parser:isRecognizedFlag(flag) + for i,v in pairs(recognizedFlags) do + if v == flag:lower() then + return true + end + end + return false +end +function parser:parse() + local link = self + local line_num = 0 + local choice + local group = 0 + local lastgroup = 0 + local groupStack = Stack:new({}) + local lastpop + local noblock = true + local arr = {} + local multilined = false + function groupStack:append(t) + local c = self:peek() + val = pcall(function() + table.insert(c,1,t) + end) + if not val then + link:error("Inconsistant indentation!",t) + end + end + groupStack:push({}) + for line in self.content:gmatch("(.-)\n") do + line_num = line_num + 1 + line = line:gsub("//(.+)$","") -- Filter out comments + if not multilined and line:match("/%*(.+)$") then + multilined = true + line = line:gsub("/%*(.*)$","") + end + if multilined then + if line:match("%*/") then + multilined = false + line = line:gsub("(.+)%*/","") + if #line:trim() == 0 then + goto continue + end + else + goto continue + end + end + if line:trim()=="" then goto continue end -- Skip all whitespace completely + lastgroup = group + group = line:tabs() + if lastgroup>group then + for i=1,lastgroup-group do + local c = groupStack:pop() + lastpop = c + for i,v in pairs(c) do + table.insert(arr,v) + end + end + elseif lastgroup ".. table.concat(vars,", ") + end + self.current_chunk:addCmd(cmd) +end +function parser:parseASGN(line) + local vars,assigns = line[5]:match("(.-)%s*=%s*(.+)") + vars = vars:split() + assigns = assigns:split() + local cmd = Cmd:new(line,ASGN,{vars = vars, assigns = assigns}) + function cmd:tostring() + return "DATA -> "..table.concat(vars,", ") + end + self.current_chunk:addCmd(cmd) + -- TODO: make dict lookups builtin +end +function parser:parseChoice(line) + local text = line[5]:match("\"(.+)\"") + local copt = self:peek() + local choice = {text = text} + local cmd = Cmd:new(line,CHOI,choice) + if copt[3]~=OPTN then + self:error("Choices must have at least one option to choose!") + return + end + while copt do + copt = self:next() + local a,b = copt[5]:match("(.+) %s*(.+)") + print(a,"|",b) + copt = self:peek() + if copt[3]~=OPTN then + break + end + end + -- We need to get functions working first +end +function parser:parseDialogue(line) + local targ,text = line[5]:match("(%w*)%s*(.*)") + if #targ == 0 then targ = nil end + local cmd = Cmd:new(line,DISP,{text=text,target=targ}) + function cmd:tostring() + return table.concat({targ or "@",text},", ") + end + self.current_chunk:addCmd(cmd) +end +function parser:peek() + return self.lines[self.pos + 1] +end +function parser:next() + self.pos = self.pos + 1 + return self.lines[self.pos] +end +return parser \ No newline at end of file diff --git a/dms/stack.lua b/dms/stack.lua new file mode 100644 index 0000000..d7d972e --- /dev/null +++ b/dms/stack.lua @@ -0,0 +1,25 @@ +local Stack = {} +Stack.__index = Stack +function Stack.__tostring(self) + return table.concat(self.stack, ", ") +end +function Stack:new(n) + local c = {} + setmetatable(c,self) + c.max = n or math.huge + c.stack = {} + return c +end +function Stack:push(n) + table.insert(self.stack,n) +end +function Stack:pop() + return table.remove(self.stack,#self.stack) +end +function Stack:peek(n) + return self.stack[#self.stack - (n or 0)] +end +function Stack:count() + return #self.stack +end +return Stack \ No newline at end of file diff --git a/dms/utils.lua b/dms/utils.lua new file mode 100644 index 0000000..e50688f --- /dev/null +++ b/dms/utils.lua @@ -0,0 +1,100 @@ +-- Utils modify the global enviroment +function string.tabs(str) + local c = 0 + for i in str:gmatch(".") do + if i=="\t" then + c=c+1 + else + break + end + end + return c +end +function string.trim(self) + return (self:gsub("^%s*(.-)%s*$", "%1")) +end +function tprint (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) + tprint(v, indent+1) + elseif type(v) == 'boolean' then + print(formatting .. tostring(v)) + else + print(formatting .. v) + end + end +end +function string.split(s,pat) + local pat=pat or "," + local res = {} + local start = 1 + local state = 0 + local c = '.' + local elem = '' + local function helper() + if tonumber(elem) then + elem = tonumber(elem) + elseif elem:sub(1,1) == "\"" and elem:sub(-1,-1) == "\"" then + elem = elem:sub(2,-2) + elseif elem == "true" then + elem = true + elseif elem == "false" then + elem = false + elseif elem:sub(1,1) == "{" and elem:sub(-1,-1)=="}" then + elem = elem:sub(2,-2):split() + else + elem = "\1"..elem + end + end + 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 + helper() + res[#res + 1] = elem + elem = '' + state = 3 -- skip over the next space if present + elseif c == "(" then + state = 1 + elem = elem .. '(' + 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 = 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 + helper() + res[#res + 1] = elem + return res +end \ No newline at end of file diff --git a/test.dms b/test.dms index 9ebd695..0dca866 100644 --- a/test.dms +++ b/test.dms @@ -1,42 +1,48 @@ -ENTRY main -ENABLE test -DISABLE test -LOADFILE test.dat -VERSION 1.4.5 -USING extendedDefine - +entry main +enable warnings +//enable debugging +define +loadfile test.dat +define +version 1.2 +using extendedDefine as [main] "This works!" "What's up" - Ryan "Hello how are you doing?" + Ryan "Hello how are you doing?" // this is a comment Bob "I'm good you?" - list = {1,2,3,true,false, {1,2,3}} + list = {1,2,3},true,"This is a string!",false, {3,2,1} a = list[1] + /* + heheheh + sdfsdf + ghgfh + kjuty + */ list[1] = "Hello" - + var1,var2 = func(1,"string", 2+5) + func(1,"string", 2+5) ::label:: - + //Hello im testing stuff choice "Pick one:" "first" func() "second" func() "third" func() for x = 1,10 - group1A - group1B for y = 1,10 - group2A - group2B for z = 1,10 - group3A - group3B + "test" + "$x$ $y$ $z$" + test = true + test2 = false + while cond ... - if cond ... elseif cond @@ -44,7 +50,13 @@ USING extendedDefine else ... - var1,var2 = func(1,"string", 2+5) - func(1,"string", 2+5) +[Ryan:char] + age = 24 + money = 100 + + +[test:env] [newblock:function()] + "Test #2" + "Does it parse this part properly?"