write to toml & gitsafe
This commit is contained in:
		
							
								
								
									
										155
									
								
								lua/timer.lua
									
									
									
									
									
								
							
							
						
						
									
										155
									
								
								lua/timer.lua
									
									
									
									
									
								
							| @@ -1,30 +1,161 @@ | |||||||
| local augroup = vim.api.nvim_create_augroup("ScratchBuffer", { clear = true }) | local TOML = require("toml") | ||||||
|  |  | ||||||
|  | local augroup = vim.api.nvim_create_augroup("Timer", { clear = true }) | ||||||
|  | local focus_events = {} | ||||||
|  | local clean_to = 1 | ||||||
|  |  | ||||||
|  | function trim(s) | ||||||
|  |   return (s:gsub("^%s*(.-)%s*$", "%1")) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function clean_from(from, events) | ||||||
|  |   local to = 1 | ||||||
|  |   local clean_events = {} | ||||||
|  |  | ||||||
|  |   -- coppy until from | ||||||
|  |   for i = 1, from - 1, 1 do | ||||||
|  |     table.insert(clean_events, events[i]) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local last = {} | ||||||
|  |   for i = from, #events, 1 | ||||||
|  |   do | ||||||
|  |     local event = events[i] | ||||||
|  |     local next_event = events[i + 1] | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     if next_event == nil then | ||||||
|  |       --do nothing | ||||||
|  |     elseif next_event.event == "gain" and event.event == "lose" then | ||||||
|  |       if os.difftime(next_event.time, event.time) > 5 * 60 then | ||||||
|  |         table.insert(clean_events, event) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |     if last == nil then | ||||||
|  |       -- do nothing | ||||||
|  |     elseif last.event == "lose" and event.event == "gain" then | ||||||
|  |       if os.difftime(event.time, last.time) > 5 * 60 then | ||||||
|  |         table.insert(clean_events, event) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     last = event | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   return clean_events | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function time() | ||||||
|  |   focus_events = clean_from(clean_to + 1, focus_events) | ||||||
|  |   clean_to = #focus_events | ||||||
|  |  | ||||||
|  |   local t = 0 | ||||||
|  |  | ||||||
|  |   local start = os.time() | ||||||
|  |   for _, fe in pairs(focus_events) do | ||||||
|  |     if fe.event == "gain" then | ||||||
|  |       start = fe.time | ||||||
|  |     else | ||||||
|  |       t = t + os.difftime(fe.time, start) | ||||||
|  |     end | ||||||
|  |     if focus_events[#focus_events].event == "gain" then | ||||||
|  |       t = t + os.difftime(os.time(), focus_events[#focus_events].time) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   return t | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function log_time() | ||||||
|  |   print(time()) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function log_time_table() | ||||||
|  |   for i, e in pairs(focus_events) do | ||||||
|  |     print(i, e.event, os.date("%H:%M:%S", e.time)) | ||||||
|  |   end | ||||||
|  | end | ||||||
|  |  | ||||||
| local function focus_gained() | local function focus_gained() | ||||||
|   print("Huiii Fokus") |   print("Huiii Fokus") | ||||||
|  |  | ||||||
|  |   table.insert(focus_events, { | ||||||
|  |     time = os.time(), | ||||||
|  |     event = "gain", | ||||||
|  |   }) | ||||||
| end | end | ||||||
|  |  | ||||||
| local function focus_lost() | local function focus_lost() | ||||||
|   print("Hokus pokus fort ist der fokus") |   print("Hokus pokus fort ist der fokus") | ||||||
|  |  | ||||||
|  |   table.insert(focus_events, { | ||||||
|  |     time = os.time(), | ||||||
|  |     event = "lose", | ||||||
|  |   }) | ||||||
| end | end | ||||||
|  |  | ||||||
| local function main() | local function persist() | ||||||
|   print("Hello from our plugin") |   local filename = ".timer.toml" | ||||||
|   print(os.time()) |   local username = trim(os.getenv("USER")) .. "_at_" .. trim(io.popen("hostname -s"):read("*a")) | ||||||
|  |   local today = os.date("%y-%m-%d", os.time()) | ||||||
|  |   local table = {} | ||||||
|  |  | ||||||
|  |   local file = io.open(filename, "r") | ||||||
|  |  | ||||||
|  |   -- if file exists read | ||||||
|  |   if file ~= nil then | ||||||
|  |     table = TOML.parse(file:read("*a")) | ||||||
|  |     file:close() | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- write time to table | ||||||
|  |   if table[today] ~= nil and table[today][username] ~= nil then | ||||||
|  |     table[today][username] = table[today][username] + time() | ||||||
|  |   else | ||||||
|  |     if table[today] == nil then | ||||||
|  |       table[today] = {} | ||||||
|  |     end | ||||||
|  |     table[today][username] = time() | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- write table to file | ||||||
|  |   file = io.open(filename, "w") | ||||||
|  |   if file == nil then | ||||||
|  |     error("can't write to .timer file") | ||||||
|  |     return | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   file:write("## dates and their corresponding seconds been here :)\n", TOML.encode(table), "\n") | ||||||
|  |   file.close() | ||||||
|  |  | ||||||
|  |   --reset timer | ||||||
|  |   clean_to = 1 | ||||||
|  |   focus_events = {} | ||||||
|  |   focus_gained() | ||||||
| end | end | ||||||
|  |  | ||||||
| local function setup() | local function setup() | ||||||
|   vim.api.nvim_create_autocmd("VimEnter", |   vim.api.nvim_create_autocmd("VimEnter", | ||||||
|     { group = augroup, desc = "Set a fennel scratch buffer on load", once = true, callback = main }) |     { group = augroup, desc = "Start Timer", once = true, callback = focus_gained }) | ||||||
|  |   vim.api.nvim_create_autocmd("ExitPre", | ||||||
|  |     { group = augroup, desc = "Persist timer", once = true, callback = persist }) | ||||||
|  |   vim.api.nvim_create_autocmd("FocusGained", { group = augroup, callback = focus_gained }) | ||||||
|  |   vim.api.nvim_create_autocmd("FocusLost", { group = augroup, callback = focus_lost }) | ||||||
|  |  | ||||||
|   vim.api.nvim_create_user_command( |   vim.api.nvim_create_user_command( | ||||||
|     'Timer',                  -- The name of the command (accessible via :TimerStart) |     'Timer',      -- The name of the command (accessible via :TimerStart) | ||||||
|     function() |     log_time, | ||||||
|       print("Timer started!") -- The function executed when the command is called |     { nargs = 0 } -- No arguments for this command (you can configure this later) | ||||||
|     end, |   ) | ||||||
|     { nargs = 0 }             -- No arguments for this command (you can configure this later) |   vim.api.nvim_create_user_command( | ||||||
|  |     'TimerLog',   -- The name of the command (accessible via :TimerStart) | ||||||
|  |     log_time_table, | ||||||
|  |     { nargs = 0 } -- No arguments for this command (you can configure this later) | ||||||
|  |   ) | ||||||
|  |   vim.api.nvim_create_user_command( | ||||||
|  |     'TimerSave',  -- The name of the command (accessible via :TimerStart) | ||||||
|  |     persist, | ||||||
|  |     { nargs = 0 } -- No arguments for this command (you can configure this later) | ||||||
|   ) |   ) | ||||||
|   vim.api.nvim_create_autocmd("FocusGained", { callback = focus_gained }) |  | ||||||
|   vim.api.nvim_create_autocmd("FocusLost", { callback = focus_lost }) |  | ||||||
| end | end | ||||||
|  |  | ||||||
| return { setup = setup } | return { setup = setup } | ||||||
|   | |||||||
							
								
								
									
										644
									
								
								lua/toml.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										644
									
								
								lua/toml.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,644 @@ | |||||||
|  | -- source: https://github.com/jonstoler/lua-toml/blob/master/toml.lua | ||||||
|  | local TOML = { | ||||||
|  |   -- denotes the current supported TOML version | ||||||
|  |   version = 0.40, | ||||||
|  |  | ||||||
|  |   -- sets whether the parser should follow the TOML spec strictly | ||||||
|  |   -- currently, no errors are thrown for the following rules if strictness is turned off: | ||||||
|  |   --   tables having mixed keys | ||||||
|  |   --   redefining a table | ||||||
|  |   --   redefining a key within a table | ||||||
|  |   strict = true, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | -- converts TOML data into a lua table | ||||||
|  | TOML.parse = function(toml, options) | ||||||
|  |   options = options or {} | ||||||
|  |   local strict = (options.strict ~= nil and options.strict or TOML.strict) | ||||||
|  |  | ||||||
|  |   -- the official TOML definition of whitespace | ||||||
|  |   local ws = "[\009\032]" | ||||||
|  |  | ||||||
|  |   -- the official TOML definition of newline | ||||||
|  |   local nl = "[\10" | ||||||
|  |   do | ||||||
|  |     local crlf = "\13\10" | ||||||
|  |     nl = nl .. crlf | ||||||
|  |   end | ||||||
|  |   nl = nl .. "]" | ||||||
|  |  | ||||||
|  |   -- stores text data | ||||||
|  |   local buffer = "" | ||||||
|  |  | ||||||
|  |   -- the current location within the string to parse | ||||||
|  |   local cursor = 1 | ||||||
|  |  | ||||||
|  |   -- the output table | ||||||
|  |   local out = {} | ||||||
|  |  | ||||||
|  |   -- the current table to write to | ||||||
|  |   local obj = out | ||||||
|  |  | ||||||
|  |   -- returns the next n characters from the current position | ||||||
|  |   local function char(n) | ||||||
|  |     n = n or 0 | ||||||
|  |     return toml:sub(cursor + n, cursor + n) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- moves the current position forward n (default: 1) characters | ||||||
|  |   local function step(n) | ||||||
|  |     n = n or 1 | ||||||
|  |     cursor = cursor + n | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- move forward until the next non-whitespace character | ||||||
|  |   local function skipWhitespace() | ||||||
|  |     while (char():match(ws)) do | ||||||
|  |       step() | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- remove the (Lua) whitespace at the beginning and end of a string | ||||||
|  |   local function trim(str) | ||||||
|  |     return str:gsub("^%s*(.-)%s*$", "%1") | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- divide a string into a table around a delimiter | ||||||
|  |   local function split(str, delim) | ||||||
|  |     if str == "" then return {} end | ||||||
|  |     local result = {} | ||||||
|  |     local append = delim | ||||||
|  |     if delim:match("%%") then | ||||||
|  |       append = delim:gsub("%%", "") | ||||||
|  |     end | ||||||
|  |     for match in (str .. append):gmatch("(.-)" .. delim) do | ||||||
|  |       table.insert(result, match) | ||||||
|  |     end | ||||||
|  |     return result | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- produce a parsing error message | ||||||
|  |   -- the error contains the line number of the current position | ||||||
|  |   local function err(message, strictOnly) | ||||||
|  |     if not strictOnly or (strictOnly and strict) then | ||||||
|  |       local line = 1 | ||||||
|  |       local c = 0 | ||||||
|  |       for l in toml:gmatch("(.-)" .. nl) do | ||||||
|  |         c = c + l:len() | ||||||
|  |         if c >= cursor then | ||||||
|  |           break | ||||||
|  |         end | ||||||
|  |         line = line + 1 | ||||||
|  |       end | ||||||
|  |       error("TOML: " .. message .. " on line " .. line .. ".", 4) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- prevent infinite loops by checking whether the cursor is | ||||||
|  |   -- at the end of the document or not | ||||||
|  |   local function bounds() | ||||||
|  |     return cursor <= toml:len() | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local function parseString() | ||||||
|  |     local quoteType = char() -- should be single or double quote | ||||||
|  |  | ||||||
|  |     -- this is a multiline string if the next 2 characters match | ||||||
|  |     local multiline = (char(1) == char(2) and char(1) == char()) | ||||||
|  |  | ||||||
|  |     -- buffer to hold the string | ||||||
|  |     local str = "" | ||||||
|  |  | ||||||
|  |     -- skip the quotes | ||||||
|  |     step(multiline and 3 or 1) | ||||||
|  |  | ||||||
|  |     while (bounds()) do | ||||||
|  |       if multiline and char():match(nl) and str == "" then | ||||||
|  |         -- skip line break line at the beginning of multiline string | ||||||
|  |         step() | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       -- keep going until we encounter the quote character again | ||||||
|  |       if char() == quoteType then | ||||||
|  |         if multiline then | ||||||
|  |           if char(1) == char(2) and char(1) == quoteType then | ||||||
|  |             step(3) | ||||||
|  |             break | ||||||
|  |           end | ||||||
|  |         else | ||||||
|  |           step() | ||||||
|  |           break | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       if char():match(nl) and not multiline then | ||||||
|  |         err("Single-line string cannot contain line break") | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       -- if we're in a double-quoted string, watch for escape characters! | ||||||
|  |       if quoteType == '"' and char() == "\\" then | ||||||
|  |         if multiline and char(1):match(nl) then | ||||||
|  |           -- skip until first non-whitespace character | ||||||
|  |           step(1) -- go past the line break | ||||||
|  |           while (bounds()) do | ||||||
|  |             if not char():match(ws) and not char():match(nl) then | ||||||
|  |               break | ||||||
|  |             end | ||||||
|  |             step() | ||||||
|  |           end | ||||||
|  |         else | ||||||
|  |           -- all available escape characters | ||||||
|  |           local escape = { | ||||||
|  |             b = "\b", | ||||||
|  |             t = "\t", | ||||||
|  |             n = "\n", | ||||||
|  |             f = "\f", | ||||||
|  |             r = "\r", | ||||||
|  |             ['"'] = '"', | ||||||
|  |             ["\\"] = "\\", | ||||||
|  |           } | ||||||
|  |           -- utf function from http://stackoverflow.com/a/26071044 | ||||||
|  |           -- converts \uXXX into actual unicode | ||||||
|  |           local function utf(char) | ||||||
|  |             local bytemarkers = { { 0x7ff, 192 }, { 0xffff, 224 }, { 0x1fffff, 240 } } | ||||||
|  |             if char < 128 then return string.char(char) end | ||||||
|  |             local charbytes = {} | ||||||
|  |             for bytes, vals in pairs(bytemarkers) do | ||||||
|  |               if char <= vals[1] then | ||||||
|  |                 for b = bytes + 1, 2, -1 do | ||||||
|  |                   local mod = char % 64 | ||||||
|  |                   char = (char - mod) / 64 | ||||||
|  |                   charbytes[b] = string.char(128 + mod) | ||||||
|  |                 end | ||||||
|  |                 charbytes[1] = string.char(vals[2] + char) | ||||||
|  |                 break | ||||||
|  |               end | ||||||
|  |             end | ||||||
|  |             return table.concat(charbytes) | ||||||
|  |           end | ||||||
|  |  | ||||||
|  |           if escape[char(1)] then | ||||||
|  |             -- normal escape | ||||||
|  |             str = str .. escape[char(1)] | ||||||
|  |             step(2) -- go past backslash and the character | ||||||
|  |           elseif char(1) == "u" then | ||||||
|  |             -- utf-16 | ||||||
|  |             step() | ||||||
|  |             local uni = char(1) .. char(2) .. char(3) .. char(4) | ||||||
|  |             step(5) | ||||||
|  |             uni = tonumber(uni, 16) | ||||||
|  |             if (uni >= 0 and uni <= 0xd7ff) and not (uni >= 0xe000 and uni <= 0x10ffff) then | ||||||
|  |               str = str .. utf(uni) | ||||||
|  |             else | ||||||
|  |               err("Unicode escape is not a Unicode scalar") | ||||||
|  |             end | ||||||
|  |           elseif char(1) == "U" then | ||||||
|  |             -- utf-32 | ||||||
|  |             step() | ||||||
|  |             local uni = char(1) .. char(2) .. char(3) .. char(4) .. char(5) .. char(6) .. char(7) .. char(8) | ||||||
|  |             step(9) | ||||||
|  |             uni = tonumber(uni, 16) | ||||||
|  |             if (uni >= 0 and uni <= 0xd7ff) and not (uni >= 0xe000 and uni <= 0x10ffff) then | ||||||
|  |               str = str .. utf(uni) | ||||||
|  |             else | ||||||
|  |               err("Unicode escape is not a Unicode scalar") | ||||||
|  |             end | ||||||
|  |           else | ||||||
|  |             err("Invalid escape") | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |       else | ||||||
|  |         -- if we're not in a double-quoted string, just append it to our buffer raw and keep going | ||||||
|  |         str = str .. char() | ||||||
|  |         step() | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     return { value = str, type = "string" } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local function parseNumber() | ||||||
|  |     local num = "" | ||||||
|  |     local exp | ||||||
|  |     local date = false | ||||||
|  |     while (bounds()) do | ||||||
|  |       if char():match("[%+%-%.eE_0-9]") then | ||||||
|  |         if not exp then | ||||||
|  |           if char():lower() == "e" then | ||||||
|  |             -- as soon as we reach e or E, start appending to exponent buffer instead of | ||||||
|  |             -- number buffer | ||||||
|  |             exp = "" | ||||||
|  |           elseif char() ~= "_" then | ||||||
|  |             num = num .. char() | ||||||
|  |           end | ||||||
|  |         elseif char():match("[%+%-0-9]") then | ||||||
|  |           exp = exp .. char() | ||||||
|  |         else | ||||||
|  |           err("Invalid exponent") | ||||||
|  |         end | ||||||
|  |       elseif char():match(ws) or char() == "#" or char():match(nl) or char() == "," or char() == "]" or char() == "}" then | ||||||
|  |         break | ||||||
|  |       elseif char() == "T" or char() == "Z" then | ||||||
|  |         -- parse the date (as a string, since lua has no date object) | ||||||
|  |         date = true | ||||||
|  |         while (bounds()) do | ||||||
|  |           if char() == "," or char() == "]" or char() == "#" or char():match(nl) or char():match(ws) then | ||||||
|  |             break | ||||||
|  |           end | ||||||
|  |           num = num .. char() | ||||||
|  |           step() | ||||||
|  |         end | ||||||
|  |       else | ||||||
|  |         err("Invalid number") | ||||||
|  |       end | ||||||
|  |       step() | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     if date then | ||||||
|  |       return { value = num, type = "date" } | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     local float = false | ||||||
|  |     if num:match("%.") then float = true end | ||||||
|  |  | ||||||
|  |     exp = exp and tonumber(exp) or 0 | ||||||
|  |     num = tonumber(num) | ||||||
|  |  | ||||||
|  |     if not float then | ||||||
|  |       return { | ||||||
|  |         -- lua will automatically convert the result | ||||||
|  |         -- of a power operation to a float, so we have | ||||||
|  |         -- to convert it back to an int with math.floor | ||||||
|  |         value = math.floor(num * 10 ^ exp), | ||||||
|  |         type = "int", | ||||||
|  |       } | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     return { value = num * 10 ^ exp, type = "float" } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local parseArray, getValue | ||||||
|  |  | ||||||
|  |   function parseArray() | ||||||
|  |     step() -- skip [ | ||||||
|  |     skipWhitespace() | ||||||
|  |  | ||||||
|  |     local arrayType | ||||||
|  |     local array = {} | ||||||
|  |  | ||||||
|  |     while (bounds()) do | ||||||
|  |       if char() == "]" then | ||||||
|  |         break | ||||||
|  |       elseif char():match(nl) then | ||||||
|  |         -- skip | ||||||
|  |         step() | ||||||
|  |         skipWhitespace() | ||||||
|  |       elseif char() == "#" then | ||||||
|  |         while (bounds() and not char():match(nl)) do | ||||||
|  |           step() | ||||||
|  |         end | ||||||
|  |       else | ||||||
|  |         -- get the next object in the array | ||||||
|  |         local v = getValue() | ||||||
|  |         if not v then break end | ||||||
|  |  | ||||||
|  |         -- set the type if it hasn't been set before | ||||||
|  |         if arrayType == nil then | ||||||
|  |           arrayType = v.type | ||||||
|  |         elseif arrayType ~= v.type then | ||||||
|  |           err("Mixed types in array", true) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         array = array or {} | ||||||
|  |         table.insert(array, v.value) | ||||||
|  |  | ||||||
|  |         if char() == "," then | ||||||
|  |           step() | ||||||
|  |         end | ||||||
|  |         skipWhitespace() | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |     step() | ||||||
|  |  | ||||||
|  |     return { value = array, type = "array" } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local function parseInlineTable() | ||||||
|  |     step() -- skip opening brace | ||||||
|  |  | ||||||
|  |     local buffer = "" | ||||||
|  |     local quoted = false | ||||||
|  |     local tbl = {} | ||||||
|  |  | ||||||
|  |     while bounds() do | ||||||
|  |       if char() == "}" then | ||||||
|  |         break | ||||||
|  |       elseif char() == "'" or char() == '"' then | ||||||
|  |         buffer = parseString().value | ||||||
|  |         quoted = true | ||||||
|  |       elseif char() == "=" then | ||||||
|  |         if not quoted then | ||||||
|  |           buffer = trim(buffer) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         step() -- skip = | ||||||
|  |         skipWhitespace() | ||||||
|  |  | ||||||
|  |         if char():match(nl) then | ||||||
|  |           err("Newline in inline table") | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         local v = getValue().value | ||||||
|  |         tbl[buffer] = v | ||||||
|  |  | ||||||
|  |         skipWhitespace() | ||||||
|  |  | ||||||
|  |         if char() == "," then | ||||||
|  |           step() | ||||||
|  |         elseif char():match(nl) then | ||||||
|  |           err("Newline in inline table") | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         quoted = false | ||||||
|  |         buffer = "" | ||||||
|  |       else | ||||||
|  |         buffer = buffer .. char() | ||||||
|  |         step() | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |     step() -- skip closing brace | ||||||
|  |  | ||||||
|  |     return { value = tbl, type = "array" } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   local function parseBoolean() | ||||||
|  |     local v | ||||||
|  |     if toml:sub(cursor, cursor + 3) == "true" then | ||||||
|  |       step(4) | ||||||
|  |       v = { value = true, type = "boolean" } | ||||||
|  |     elseif toml:sub(cursor, cursor + 4) == "false" then | ||||||
|  |       step(5) | ||||||
|  |       v = { value = false, type = "boolean" } | ||||||
|  |     else | ||||||
|  |       err("Invalid primitive") | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     skipWhitespace() | ||||||
|  |     if char() == "#" then | ||||||
|  |       while (not char():match(nl)) do | ||||||
|  |         step() | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     return v | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- figure out the type and get the next value in the document | ||||||
|  |   function getValue() | ||||||
|  |     if char() == '"' or char() == "'" then | ||||||
|  |       return parseString() | ||||||
|  |     elseif char():match("[%+%-0-9]") then | ||||||
|  |       return parseNumber() | ||||||
|  |     elseif char() == "[" then | ||||||
|  |       return parseArray() | ||||||
|  |     elseif char() == "{" then | ||||||
|  |       return parseInlineTable() | ||||||
|  |     else | ||||||
|  |       return parseBoolean() | ||||||
|  |     end | ||||||
|  |     -- date regex (for possible future support): | ||||||
|  |     -- %d%d%d%d%-[0-1][0-9]%-[0-3][0-9]T[0-2][0-9]%:[0-6][0-9]%:[0-6][0-9][Z%:%+%-%.0-9]* | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   -- track whether the current key was quoted or not | ||||||
|  |   local quotedKey = false | ||||||
|  |  | ||||||
|  |   -- parse the document! | ||||||
|  |   while (cursor <= toml:len()) do | ||||||
|  |     -- skip comments and whitespace | ||||||
|  |     if char() == "#" then | ||||||
|  |       while (not char():match(nl)) do | ||||||
|  |         step() | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     if char():match(nl) then | ||||||
|  |       -- skip | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     if char() == "=" then | ||||||
|  |       step() | ||||||
|  |       skipWhitespace() | ||||||
|  |  | ||||||
|  |       -- trim key name | ||||||
|  |       buffer = trim(buffer) | ||||||
|  |  | ||||||
|  |       if buffer:match("^[0-9]*$") and not quotedKey then | ||||||
|  |         buffer = tonumber(buffer) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       if buffer == "" and not quotedKey then | ||||||
|  |         err("Empty key name") | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       local v = getValue() | ||||||
|  |       if v then | ||||||
|  |         -- if the key already exists in the current object, throw an error | ||||||
|  |         if obj[buffer] then | ||||||
|  |           err('Cannot redefine key "' .. buffer .. '"', true) | ||||||
|  |         end | ||||||
|  |         obj[buffer] = v.value | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       -- clear the buffer | ||||||
|  |       buffer = "" | ||||||
|  |       quotedKey = false | ||||||
|  |  | ||||||
|  |       -- skip whitespace and comments | ||||||
|  |       skipWhitespace() | ||||||
|  |       if char() == "#" then | ||||||
|  |         while (bounds() and not char():match(nl)) do | ||||||
|  |           step() | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       -- if there is anything left on this line after parsing a key and its value, | ||||||
|  |       -- throw an error | ||||||
|  |       if not char():match(nl) and cursor < toml:len() then | ||||||
|  |         err("Invalid primitive") | ||||||
|  |       end | ||||||
|  |     elseif char() == "[" then | ||||||
|  |       buffer = "" | ||||||
|  |       step() | ||||||
|  |       local tableArray = false | ||||||
|  |  | ||||||
|  |       -- if there are two brackets in a row, it's a table array! | ||||||
|  |       if char() == "[" then | ||||||
|  |         tableArray = true | ||||||
|  |         step() | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       obj = out | ||||||
|  |  | ||||||
|  |       local function processKey(isLast) | ||||||
|  |         isLast = isLast or false | ||||||
|  |         buffer = trim(buffer) | ||||||
|  |  | ||||||
|  |         if not quotedKey and buffer == "" then | ||||||
|  |           err("Empty table name") | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         if isLast and obj[buffer] and not tableArray and #obj[buffer] > 0 then | ||||||
|  |           err("Cannot redefine table", true) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         -- set obj to the appropriate table so we can start | ||||||
|  |         -- filling it with values! | ||||||
|  |         if tableArray then | ||||||
|  |           -- push onto cache | ||||||
|  |           if obj[buffer] then | ||||||
|  |             obj = obj[buffer] | ||||||
|  |             if isLast then | ||||||
|  |               table.insert(obj, {}) | ||||||
|  |             end | ||||||
|  |             obj = obj[#obj] | ||||||
|  |           else | ||||||
|  |             obj[buffer] = {} | ||||||
|  |             obj = obj[buffer] | ||||||
|  |             if isLast then | ||||||
|  |               table.insert(obj, {}) | ||||||
|  |               obj = obj[1] | ||||||
|  |             end | ||||||
|  |           end | ||||||
|  |         else | ||||||
|  |           obj[buffer] = obj[buffer] or {} | ||||||
|  |           obj = obj[buffer] | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       while (bounds()) do | ||||||
|  |         if char() == "]" then | ||||||
|  |           if tableArray then | ||||||
|  |             if char(1) ~= "]" then | ||||||
|  |               err("Mismatching brackets") | ||||||
|  |             else | ||||||
|  |               step() -- skip inside bracket | ||||||
|  |             end | ||||||
|  |           end | ||||||
|  |           step() -- skip outside bracket | ||||||
|  |  | ||||||
|  |           processKey(true) | ||||||
|  |           buffer = "" | ||||||
|  |           break | ||||||
|  |         elseif char() == '"' or char() == "'" then | ||||||
|  |           buffer = parseString().value | ||||||
|  |           quotedKey = true | ||||||
|  |         elseif char() == "." then | ||||||
|  |           step() -- skip period | ||||||
|  |           processKey() | ||||||
|  |           buffer = "" | ||||||
|  |         else | ||||||
|  |           buffer = buffer .. char() | ||||||
|  |           step() | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       buffer = "" | ||||||
|  |       quotedKey = false | ||||||
|  |     elseif (char() == '"' or char() == "'") then | ||||||
|  |       -- quoted key | ||||||
|  |       buffer = parseString().value | ||||||
|  |       quotedKey = true | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     buffer = buffer .. (char():match(nl) and "" or char()) | ||||||
|  |     step() | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   return out | ||||||
|  | end | ||||||
|  |  | ||||||
|  | TOML.encode = function(tbl) | ||||||
|  |   local toml = "" | ||||||
|  |  | ||||||
|  |   local cache = {} | ||||||
|  |  | ||||||
|  |   local function parse(tbl) | ||||||
|  |     for k, v in pairs(tbl) do | ||||||
|  |       if type(v) == "boolean" then | ||||||
|  |         toml = toml .. k .. " = " .. tostring(v) .. "\n" | ||||||
|  |       elseif type(v) == "number" then | ||||||
|  |         toml = toml .. k .. " = " .. tostring(v) .. "\n" | ||||||
|  |       elseif type(v) == "string" then | ||||||
|  |         local quote = '"' | ||||||
|  |         v = v:gsub("\\", "\\\\") | ||||||
|  |  | ||||||
|  |         -- if the string has any line breaks, make it multiline | ||||||
|  |         if v:match("^\n(.*)$") then | ||||||
|  |           quote = quote:rep(3) | ||||||
|  |           v = "\\n" .. v | ||||||
|  |         elseif v:match("\n") then | ||||||
|  |           quote = quote:rep(3) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         v = v:gsub("\b", "\\b") | ||||||
|  |         v = v:gsub("\t", "\\t") | ||||||
|  |         v = v:gsub("\f", "\\f") | ||||||
|  |         v = v:gsub("\r", "\\r") | ||||||
|  |         v = v:gsub('"', '\\"') | ||||||
|  |         v = v:gsub("/", "\\/") | ||||||
|  |         toml = toml .. k .. " = " .. quote .. v .. quote .. "\n" | ||||||
|  |       elseif type(v) == "table" then | ||||||
|  |         local array, arrayTable = true, true | ||||||
|  |         local first = {} | ||||||
|  |         for kk, vv in pairs(v) do | ||||||
|  |           if type(kk) ~= "number" then array = false end | ||||||
|  |           if type(vv) ~= "table" then | ||||||
|  |             v[kk] = nil | ||||||
|  |             first[kk] = vv | ||||||
|  |             arrayTable = false | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         if array then | ||||||
|  |           if arrayTable then | ||||||
|  |             -- double bracket syntax go! | ||||||
|  |             table.insert(cache, k) | ||||||
|  |             for kk, vv in pairs(v) do | ||||||
|  |               toml = toml .. "[[" .. table.concat(cache, ".") .. "]]\n" | ||||||
|  |               for k3, v3 in pairs(vv) do | ||||||
|  |                 if type(v3) ~= "table" then | ||||||
|  |                   vv[k3] = nil | ||||||
|  |                   first[k3] = v3 | ||||||
|  |                 end | ||||||
|  |               end | ||||||
|  |               parse(first) | ||||||
|  |               parse(vv) | ||||||
|  |             end | ||||||
|  |             table.remove(cache) | ||||||
|  |           else | ||||||
|  |             -- plain ol boring array | ||||||
|  |             toml = toml .. k .. " = [\n" | ||||||
|  |             for kk, vv in pairs(first) do | ||||||
|  |               toml = toml .. tostring(vv) .. ",\n" | ||||||
|  |             end | ||||||
|  |             toml = toml .. "]\n" | ||||||
|  |           end | ||||||
|  |         else | ||||||
|  |           -- just a key/value table, folks | ||||||
|  |           table.insert(cache, k) | ||||||
|  |           toml = toml .. "[" .. table.concat(cache, ".") .. "]\n" | ||||||
|  |           parse(first) | ||||||
|  |           parse(v) | ||||||
|  |           table.remove(cache) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   parse(tbl) | ||||||
|  |  | ||||||
|  |   return toml:sub(1, -2) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | return TOML | ||||||
		Reference in New Issue
	
	Block a user
	 u80864958
					u80864958