mirror of
https://github.com/g012/l65.git
synced 2025-01-13 11:30:03 +00:00
Fixed erroneous function name in lua.h.
Added lpeg's re.lua lib. Added dkjson.lua. Added parser for TIATracker .ttt files.
This commit is contained in:
parent
6bdc61e300
commit
ce2737c353
@ -8,7 +8,7 @@ set(L65_SOURCE_DIR ${PROJECT_SOURCE_DIR})
|
||||
set(L65_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||
|
||||
set(L65_VERSION_MAJOR "1")
|
||||
set(L65_VERSION_MINOR "0")
|
||||
set(L65_VERSION_MINOR "1")
|
||||
set(L65_VERSION_REVISION "0")
|
||||
set(L65_VERSION "${L65_VERSION_MAJOR}.${L65_VERSION_MINOR}.${L65_VERSION_REVISION}")
|
||||
configure_file("${L65_SOURCE_DIR}/l65cfg.lua.in" "${L65_BINARY_DIR}/l65cfg.lua")
|
||||
@ -77,18 +77,14 @@ set(L65_HEADERS
|
||||
${L65_BINARY_DIR}/scripts.h
|
||||
${L65_SOURCE_DIR}/stb_image.h
|
||||
)
|
||||
set(L65_RESOURCES
|
||||
${L65_SOURCE_DIR}/6502.lua
|
||||
${L65_SOURCE_DIR}/l65.lua
|
||||
${L65_BINARY_DIR}/l65cfg.lua
|
||||
${L65_SOURCE_DIR}/vcs.l65
|
||||
)
|
||||
|
||||
file(GLOB L65_FILES ${L65_SOURCE_DIR}/*.l65)
|
||||
set(L65_SCRIPTS
|
||||
${L65_SOURCE_DIR}/6502.lua
|
||||
${L65_SOURCE_DIR}/dkjson.lua
|
||||
${L65_SOURCE_DIR}/l65.lua
|
||||
${L65_BINARY_DIR}/l65cfg.lua
|
||||
${L65_SOURCE_DIR}/re.lua
|
||||
${L65_FILES}
|
||||
)
|
||||
|
||||
@ -100,7 +96,7 @@ add_custom_command(
|
||||
DEPENDS embed ${L65_SCRIPTS}
|
||||
)
|
||||
add_custom_target(prereq DEPENDS ${L65_BINARY_DIR}/scripts.h)
|
||||
add_executable(${PROJECT_NAME} ${L65_SOURCES} ${L65_HEADERS} ${L65_RESOURCES})
|
||||
add_executable(${PROJECT_NAME} ${L65_SOURCES} ${L65_HEADERS} ${L65_FILES})
|
||||
add_dependencies(${PROJECT_NAME} prereq)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${L65_SOURCE_DIR}" "${L65_BINARY_DIR}")
|
||||
|
10
README.md
10
README.md
@ -617,19 +617,19 @@ Note that the syntax file includes some highlighting for features only activated
|
||||
|
||||
* [k65](http://devkk.net/wiki/index.php?title=K65) style syntax
|
||||
* helpers to inter-operate with cc/ca65 and dasm
|
||||
* import TIATracker \*.ttt files directly
|
||||
|
||||
## Credits
|
||||
|
||||
Developed by g012, using:
|
||||
* [Lua 5.3.4](https://www.lua.org)
|
||||
* [LuaMinify](https://github.com/stravant/LuaMinify)
|
||||
* Lua syntax file shipping with [vim](http://www.vim.org)
|
||||
* Lua indent file from [vim-lua](https://github.com/tbastos/vim-lua)
|
||||
* [stb_image](https://github.com/nothings/stb)
|
||||
|
||||
Not using, but integrated for end-user convenience:
|
||||
* [LuaFileSystem](https://keplerproject.github.io/luafilesystem)
|
||||
* [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg)
|
||||
* [stb_image](https://github.com/nothings/stb)
|
||||
* [dkjson](http://dkolf.de/src/dkjson-lua.fsl)
|
||||
* Lua syntax file shipping with [vim](http://www.vim.org)
|
||||
* Lua indent file from [vim-lua](https://github.com/tbastos/vim-lua)
|
||||
|
||||
Projects which inspired l65:
|
||||
* [nimble65](https://bitbucket.org/kylearan/nimble65)
|
||||
|
713
dkjson.lua
Normal file
713
dkjson.lua
Normal file
@ -0,0 +1,713 @@
|
||||
-- Module options:
|
||||
local always_try_using_lpeg = true
|
||||
local register_global_module_table = false
|
||||
local global_module_name = 'json'
|
||||
|
||||
--[==[
|
||||
|
||||
David Kolf's JSON module for Lua 5.1/5.2
|
||||
|
||||
Version 2.5
|
||||
|
||||
|
||||
For the documentation see the corresponding readme.txt or visit
|
||||
<http://dkolf.de/src/dkjson-lua.fsl/>.
|
||||
|
||||
You can contact the author by sending an e-mail to 'david' at the
|
||||
domain 'dkolf.de'.
|
||||
|
||||
|
||||
Copyright (C) 2010-2013 David Heiko Kolf
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
--]==]
|
||||
|
||||
-- global dependencies:
|
||||
local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset =
|
||||
pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset
|
||||
local error, require, pcall, select = error, require, pcall, select
|
||||
local floor, huge = math.floor, math.huge
|
||||
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
|
||||
string.rep, string.gsub, string.sub, string.byte, string.char,
|
||||
string.find, string.len, string.format
|
||||
local strmatch = string.match
|
||||
local concat = table.concat
|
||||
|
||||
local json = { version = "dkjson 2.5" }
|
||||
|
||||
if register_global_module_table then
|
||||
_G[global_module_name] = json
|
||||
end
|
||||
|
||||
local _ENV = nil -- blocking globals in Lua 5.2
|
||||
|
||||
pcall (function()
|
||||
-- Enable access to blocked metatables.
|
||||
-- Don't worry, this module doesn't change anything in them.
|
||||
local debmeta = require "debug".getmetatable
|
||||
if debmeta then getmetatable = debmeta end
|
||||
end)
|
||||
|
||||
json.null = setmetatable ({}, {
|
||||
__tojson = function () return "null" end
|
||||
})
|
||||
|
||||
local function isarray (tbl)
|
||||
local max, n, arraylen = 0, 0, 0
|
||||
for k,v in pairs (tbl) do
|
||||
if k == 'n' and type(v) == 'number' then
|
||||
arraylen = v
|
||||
if v > max then
|
||||
max = v
|
||||
end
|
||||
else
|
||||
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
|
||||
return false
|
||||
end
|
||||
if k > max then
|
||||
max = k
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
if max > 10 and max > arraylen and max > n * 2 then
|
||||
return false -- don't create an array with too many holes
|
||||
end
|
||||
return true, max
|
||||
end
|
||||
|
||||
local escapecodes = {
|
||||
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
|
||||
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"
|
||||
}
|
||||
|
||||
local function escapeutf8 (uchar)
|
||||
local value = escapecodes[uchar]
|
||||
if value then
|
||||
return value
|
||||
end
|
||||
local a, b, c, d = strbyte (uchar, 1, 4)
|
||||
a, b, c, d = a or 0, b or 0, c or 0, d or 0
|
||||
if a <= 0x7f then
|
||||
value = a
|
||||
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
|
||||
value = (a - 0xc0) * 0x40 + b - 0x80
|
||||
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
|
||||
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
|
||||
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
|
||||
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
|
||||
else
|
||||
return ""
|
||||
end
|
||||
if value <= 0xffff then
|
||||
return strformat ("\\u%.4x", value)
|
||||
elseif value <= 0x10ffff then
|
||||
-- encode as UTF-16 surrogate pair
|
||||
value = value - 0x10000
|
||||
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
|
||||
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
local function fsub (str, pattern, repl)
|
||||
-- gsub always builds a new string in a buffer, even when no match
|
||||
-- exists. First using find should be more efficient when most strings
|
||||
-- don't contain the pattern.
|
||||
if strfind (str, pattern) then
|
||||
return gsub (str, pattern, repl)
|
||||
else
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
local function quotestring (value)
|
||||
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
|
||||
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
|
||||
if strfind (value, "[\194\216\220\225\226\239]") then
|
||||
value = fsub (value, "\194[\128-\159\173]", escapeutf8)
|
||||
value = fsub (value, "\216[\128-\132]", escapeutf8)
|
||||
value = fsub (value, "\220\143", escapeutf8)
|
||||
value = fsub (value, "\225\158[\180\181]", escapeutf8)
|
||||
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
|
||||
value = fsub (value, "\226\129[\160-\175]", escapeutf8)
|
||||
value = fsub (value, "\239\187\191", escapeutf8)
|
||||
value = fsub (value, "\239\191[\176-\191]", escapeutf8)
|
||||
end
|
||||
return "\"" .. value .. "\""
|
||||
end
|
||||
json.quotestring = quotestring
|
||||
|
||||
local function replace(str, o, n)
|
||||
local i, j = strfind (str, o, 1, true)
|
||||
if i then
|
||||
return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
|
||||
else
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
-- locale independent num2str and str2num functions
|
||||
local decpoint, numfilter
|
||||
|
||||
local function updatedecpoint ()
|
||||
decpoint = strmatch(tostring(0.5), "([^05+])")
|
||||
-- build a filter that can be used to remove group separators
|
||||
numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
|
||||
end
|
||||
|
||||
updatedecpoint()
|
||||
|
||||
local function num2str (num)
|
||||
return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
|
||||
end
|
||||
|
||||
local function str2num (str)
|
||||
local num = tonumber(replace(str, ".", decpoint))
|
||||
if not num then
|
||||
updatedecpoint()
|
||||
num = tonumber(replace(str, ".", decpoint))
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
local function addnewline2 (level, buffer, buflen)
|
||||
buffer[buflen+1] = "\n"
|
||||
buffer[buflen+2] = strrep (" ", level)
|
||||
buflen = buflen + 2
|
||||
return buflen
|
||||
end
|
||||
|
||||
function json.addnewline (state)
|
||||
if state.indent then
|
||||
state.bufferlen = addnewline2 (state.level or 0,
|
||||
state.buffer, state.bufferlen or #(state.buffer))
|
||||
end
|
||||
end
|
||||
|
||||
local encode2 -- forward declaration
|
||||
|
||||
local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||
local kt = type (key)
|
||||
if kt ~= 'string' and kt ~= 'number' then
|
||||
return nil, "type '" .. kt .. "' is not supported as a key by JSON."
|
||||
end
|
||||
if prev then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = ","
|
||||
end
|
||||
if indent then
|
||||
buflen = addnewline2 (level, buffer, buflen)
|
||||
end
|
||||
buffer[buflen+1] = quotestring (key)
|
||||
buffer[buflen+2] = ":"
|
||||
return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
|
||||
end
|
||||
|
||||
local function appendcustom(res, buffer, state)
|
||||
local buflen = state.bufferlen
|
||||
if type (res) == 'string' then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = res
|
||||
end
|
||||
return buflen
|
||||
end
|
||||
|
||||
local function exception(reason, value, state, buffer, buflen, defaultmessage)
|
||||
defaultmessage = defaultmessage or reason
|
||||
local handler = state.exception
|
||||
if not handler then
|
||||
return nil, defaultmessage
|
||||
else
|
||||
state.bufferlen = buflen
|
||||
local ret, msg = handler (reason, value, state, defaultmessage)
|
||||
if not ret then return nil, msg or defaultmessage end
|
||||
return appendcustom(ret, buffer, state)
|
||||
end
|
||||
end
|
||||
|
||||
function json.encodeexception(reason, value, state, defaultmessage)
|
||||
return quotestring("<" .. defaultmessage .. ">")
|
||||
end
|
||||
|
||||
encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
|
||||
local valtype = type (value)
|
||||
local valmeta = getmetatable (value)
|
||||
valmeta = type (valmeta) == 'table' and valmeta -- only tables
|
||||
local valtojson = valmeta and valmeta.__tojson
|
||||
if valtojson then
|
||||
if tables[value] then
|
||||
return exception('reference cycle', value, state, buffer, buflen)
|
||||
end
|
||||
tables[value] = true
|
||||
state.bufferlen = buflen
|
||||
local ret, msg = valtojson (value, state)
|
||||
if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
|
||||
tables[value] = nil
|
||||
buflen = appendcustom(ret, buffer, state)
|
||||
elseif value == nil then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "null"
|
||||
elseif valtype == 'number' then
|
||||
local s
|
||||
if value ~= value or value >= huge or -value >= huge then
|
||||
-- This is the behaviour of the original JSON implementation.
|
||||
s = "null"
|
||||
else
|
||||
s = num2str (value)
|
||||
end
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = s
|
||||
elseif valtype == 'boolean' then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = value and "true" or "false"
|
||||
elseif valtype == 'string' then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = quotestring (value)
|
||||
elseif valtype == 'table' then
|
||||
if tables[value] then
|
||||
return exception('reference cycle', value, state, buffer, buflen)
|
||||
end
|
||||
tables[value] = true
|
||||
level = level + 1
|
||||
local isa, n = isarray (value)
|
||||
if n == 0 and valmeta and valmeta.__jsontype == 'object' then
|
||||
isa = false
|
||||
end
|
||||
local msg
|
||||
if isa then -- JSON array
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "["
|
||||
for i = 1, n do
|
||||
buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
|
||||
if not buflen then return nil, msg end
|
||||
if i < n then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = ","
|
||||
end
|
||||
end
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "]"
|
||||
else -- JSON object
|
||||
local prev = false
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "{"
|
||||
local order = valmeta and valmeta.__jsonorder or globalorder
|
||||
if order then
|
||||
local used = {}
|
||||
n = #order
|
||||
for i = 1, n do
|
||||
local k = order[i]
|
||||
local v = value[k]
|
||||
if v then
|
||||
used[k] = true
|
||||
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||
prev = true -- add a seperator before the next element
|
||||
end
|
||||
end
|
||||
for k,v in pairs (value) do
|
||||
if not used[k] then
|
||||
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||
if not buflen then return nil, msg end
|
||||
prev = true -- add a seperator before the next element
|
||||
end
|
||||
end
|
||||
else -- unordered
|
||||
for k,v in pairs (value) do
|
||||
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||
if not buflen then return nil, msg end
|
||||
prev = true -- add a seperator before the next element
|
||||
end
|
||||
end
|
||||
if indent then
|
||||
buflen = addnewline2 (level - 1, buffer, buflen)
|
||||
end
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "}"
|
||||
end
|
||||
tables[value] = nil
|
||||
else
|
||||
return exception ('unsupported type', value, state, buffer, buflen,
|
||||
"type '" .. valtype .. "' is not supported by JSON.")
|
||||
end
|
||||
return buflen
|
||||
end
|
||||
|
||||
function json.encode (value, state)
|
||||
state = state or {}
|
||||
local oldbuffer = state.buffer
|
||||
local buffer = oldbuffer or {}
|
||||
state.buffer = buffer
|
||||
updatedecpoint()
|
||||
local ret, msg = encode2 (value, state.indent, state.level or 0,
|
||||
buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
|
||||
if not ret then
|
||||
error (msg, 2)
|
||||
elseif oldbuffer == buffer then
|
||||
state.bufferlen = ret
|
||||
return true
|
||||
else
|
||||
state.bufferlen = nil
|
||||
state.buffer = nil
|
||||
return concat (buffer)
|
||||
end
|
||||
end
|
||||
|
||||
local function loc (str, where)
|
||||
local line, pos, linepos = 1, 1, 0
|
||||
while true do
|
||||
pos = strfind (str, "\n", pos, true)
|
||||
if pos and pos < where then
|
||||
line = line + 1
|
||||
linepos = pos
|
||||
pos = pos + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return "line " .. line .. ", column " .. (where - linepos)
|
||||
end
|
||||
|
||||
local function unterminated (str, what, where)
|
||||
return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
|
||||
end
|
||||
|
||||
local function scanwhite (str, pos)
|
||||
while true do
|
||||
pos = strfind (str, "%S", pos)
|
||||
if not pos then return nil end
|
||||
local sub2 = strsub (str, pos, pos + 1)
|
||||
if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
|
||||
-- UTF-8 Byte Order Mark
|
||||
pos = pos + 3
|
||||
elseif sub2 == "//" then
|
||||
pos = strfind (str, "[\n\r]", pos + 2)
|
||||
if not pos then return nil end
|
||||
elseif sub2 == "/*" then
|
||||
pos = strfind (str, "*/", pos + 2)
|
||||
if not pos then return nil end
|
||||
pos = pos + 2
|
||||
else
|
||||
return pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local escapechars = {
|
||||
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
|
||||
["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
|
||||
}
|
||||
|
||||
local function unichar (value)
|
||||
if value < 0 then
|
||||
return nil
|
||||
elseif value <= 0x007f then
|
||||
return strchar (value)
|
||||
elseif value <= 0x07ff then
|
||||
return strchar (0xc0 + floor(value/0x40),
|
||||
0x80 + (floor(value) % 0x40))
|
||||
elseif value <= 0xffff then
|
||||
return strchar (0xe0 + floor(value/0x1000),
|
||||
0x80 + (floor(value/0x40) % 0x40),
|
||||
0x80 + (floor(value) % 0x40))
|
||||
elseif value <= 0x10ffff then
|
||||
return strchar (0xf0 + floor(value/0x40000),
|
||||
0x80 + (floor(value/0x1000) % 0x40),
|
||||
0x80 + (floor(value/0x40) % 0x40),
|
||||
0x80 + (floor(value) % 0x40))
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function scanstring (str, pos)
|
||||
local lastpos = pos + 1
|
||||
local buffer, n = {}, 0
|
||||
while true do
|
||||
local nextpos = strfind (str, "[\"\\]", lastpos)
|
||||
if not nextpos then
|
||||
return unterminated (str, "string", pos)
|
||||
end
|
||||
if nextpos > lastpos then
|
||||
n = n + 1
|
||||
buffer[n] = strsub (str, lastpos, nextpos - 1)
|
||||
end
|
||||
if strsub (str, nextpos, nextpos) == "\"" then
|
||||
lastpos = nextpos + 1
|
||||
break
|
||||
else
|
||||
local escchar = strsub (str, nextpos + 1, nextpos + 1)
|
||||
local value
|
||||
if escchar == "u" then
|
||||
value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
|
||||
if value then
|
||||
local value2
|
||||
if 0xD800 <= value and value <= 0xDBff then
|
||||
-- we have the high surrogate of UTF-16. Check if there is a
|
||||
-- low surrogate escaped nearby to combine them.
|
||||
if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
|
||||
value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
|
||||
if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
|
||||
value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
|
||||
else
|
||||
value2 = nil -- in case it was out of range for a low surrogate
|
||||
end
|
||||
end
|
||||
end
|
||||
value = value and unichar (value)
|
||||
if value then
|
||||
if value2 then
|
||||
lastpos = nextpos + 12
|
||||
else
|
||||
lastpos = nextpos + 6
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not value then
|
||||
value = escapechars[escchar] or escchar
|
||||
lastpos = nextpos + 2
|
||||
end
|
||||
n = n + 1
|
||||
buffer[n] = value
|
||||
end
|
||||
end
|
||||
if n == 1 then
|
||||
return buffer[1], lastpos
|
||||
elseif n > 1 then
|
||||
return concat (buffer), lastpos
|
||||
else
|
||||
return "", lastpos
|
||||
end
|
||||
end
|
||||
|
||||
local scanvalue -- forward declaration
|
||||
|
||||
local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
|
||||
local len = strlen (str)
|
||||
local tbl, n = {}, 0
|
||||
local pos = startpos + 1
|
||||
if what == 'object' then
|
||||
setmetatable (tbl, objectmeta)
|
||||
else
|
||||
setmetatable (tbl, arraymeta)
|
||||
end
|
||||
while true do
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
local char = strsub (str, pos, pos)
|
||||
if char == closechar then
|
||||
return tbl, pos + 1
|
||||
end
|
||||
local val1, err
|
||||
val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||
if err then return nil, pos, err end
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
char = strsub (str, pos, pos)
|
||||
if char == ":" then
|
||||
if val1 == nil then
|
||||
return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
|
||||
end
|
||||
pos = scanwhite (str, pos + 1)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
local val2
|
||||
val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||
if err then return nil, pos, err end
|
||||
tbl[val1] = val2
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
char = strsub (str, pos, pos)
|
||||
else
|
||||
n = n + 1
|
||||
tbl[n] = val1
|
||||
end
|
||||
if char == "," then
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
|
||||
pos = pos or 1
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then
|
||||
return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
|
||||
end
|
||||
local char = strsub (str, pos, pos)
|
||||
if char == "{" then
|
||||
return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
|
||||
elseif char == "[" then
|
||||
return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
|
||||
elseif char == "\"" then
|
||||
return scanstring (str, pos)
|
||||
else
|
||||
local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
|
||||
if pstart then
|
||||
local number = str2num (strsub (str, pstart, pend))
|
||||
if number then
|
||||
return number, pend + 1
|
||||
end
|
||||
end
|
||||
pstart, pend = strfind (str, "^%a%w*", pos)
|
||||
if pstart then
|
||||
local name = strsub (str, pstart, pend)
|
||||
if name == "true" then
|
||||
return true, pend + 1
|
||||
elseif name == "false" then
|
||||
return false, pend + 1
|
||||
elseif name == "null" then
|
||||
return nullval, pend + 1
|
||||
end
|
||||
end
|
||||
return nil, pos, "no valid JSON value at " .. loc (str, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function optionalmetatables(...)
|
||||
if select("#", ...) > 0 then
|
||||
return ...
|
||||
else
|
||||
return {__jsontype = 'object'}, {__jsontype = 'array'}
|
||||
end
|
||||
end
|
||||
|
||||
function json.decode (str, pos, nullval, ...)
|
||||
local objectmeta, arraymeta = optionalmetatables(...)
|
||||
return scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||
end
|
||||
|
||||
function json.use_lpeg ()
|
||||
local g = require ("lpeg")
|
||||
|
||||
if g.version() == "0.11" then
|
||||
error "due to a bug in LPeg 0.11, it cannot be used for JSON matching"
|
||||
end
|
||||
|
||||
local pegmatch = g.match
|
||||
local P, S, R = g.P, g.S, g.R
|
||||
|
||||
local function ErrorCall (str, pos, msg, state)
|
||||
if not state.msg then
|
||||
state.msg = msg .. " at " .. loc (str, pos)
|
||||
state.pos = pos
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function Err (msg)
|
||||
return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
|
||||
end
|
||||
|
||||
local SingleLineComment = P"//" * (1 - S"\n\r")^0
|
||||
local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/"
|
||||
local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0
|
||||
|
||||
local PlainChar = 1 - S"\"\\\n\r"
|
||||
local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
|
||||
local HexDigit = R("09", "af", "AF")
|
||||
local function UTF16Surrogate (match, pos, high, low)
|
||||
high, low = tonumber (high, 16), tonumber (low, 16)
|
||||
if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
|
||||
return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function UTF16BMP (hex)
|
||||
return unichar (tonumber (hex, 16))
|
||||
end
|
||||
local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
|
||||
local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
|
||||
local Char = UnicodeEscape + EscapeSequence + PlainChar
|
||||
local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string")
|
||||
local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
|
||||
local Fractal = P"." * R"09"^0
|
||||
local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
|
||||
local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num
|
||||
local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
|
||||
local SimpleValue = Number + String + Constant
|
||||
local ArrayContent, ObjectContent
|
||||
|
||||
-- The functions parsearray and parseobject parse only a single value/pair
|
||||
-- at a time and store them directly to avoid hitting the LPeg limits.
|
||||
local function parsearray (str, pos, nullval, state)
|
||||
local obj, cont
|
||||
local npos
|
||||
local t, nt = {}, 0
|
||||
repeat
|
||||
obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
|
||||
if not npos then break end
|
||||
pos = npos
|
||||
nt = nt + 1
|
||||
t[nt] = obj
|
||||
until cont == 'last'
|
||||
return pos, setmetatable (t, state.arraymeta)
|
||||
end
|
||||
|
||||
local function parseobject (str, pos, nullval, state)
|
||||
local obj, key, cont
|
||||
local npos
|
||||
local t = {}
|
||||
repeat
|
||||
key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
|
||||
if not npos then break end
|
||||
pos = npos
|
||||
t[key] = obj
|
||||
until cont == 'last'
|
||||
return pos, setmetatable (t, state.objectmeta)
|
||||
end
|
||||
|
||||
local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected")
|
||||
local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected")
|
||||
local Value = Space * (Array + Object + SimpleValue)
|
||||
local ExpectedValue = Value + Space * Err "value expected"
|
||||
ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
|
||||
local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue)
|
||||
ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
|
||||
local DecodeValue = ExpectedValue * g.Cp ()
|
||||
|
||||
function json.decode (str, pos, nullval, ...)
|
||||
local state = {}
|
||||
state.objectmeta, state.arraymeta = optionalmetatables(...)
|
||||
local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
|
||||
if state.msg then
|
||||
return nil, state.pos, state.msg
|
||||
else
|
||||
return obj, retpos
|
||||
end
|
||||
end
|
||||
|
||||
-- use this function only once:
|
||||
json.use_lpeg = function () return json end
|
||||
|
||||
json.using_lpeg = true
|
||||
|
||||
return json -- so you can get the module using json = require "dkjson".use_lpeg()
|
||||
end
|
||||
|
||||
if always_try_using_lpeg then
|
||||
pcall (json.use_lpeg)
|
||||
end
|
||||
|
||||
return json
|
50
embed.c
50
embed.c
@ -80,6 +80,44 @@ static int writer(lua_State* L, const void* p, size_t size, void* f)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// use custom version so that filename is not used for debug info
|
||||
LUALIB_API int luaL_loadfilex2 (lua_State *L, const char *filename,
|
||||
const char *mode, const char *chunkname) {
|
||||
luai_LoadF lf;
|
||||
int status, readstatus;
|
||||
int c;
|
||||
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
|
||||
if (filename == NULL) {
|
||||
lua_pushliteral(L, "=stdin");
|
||||
lf.f = stdin;
|
||||
}
|
||||
else {
|
||||
lua_pushfstring(L, "@%s", filename);
|
||||
lf.f = fopen(filename, "r");
|
||||
if (lf.f == NULL) return luai_errfile(L, "open", fnameindex);
|
||||
}
|
||||
if (luai_skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return luai_errfile(L, "reopen", fnameindex);
|
||||
luai_skipcomment(&lf, &c); /* re-read initial portion */
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, chunkname);
|
||||
status = lua_load(L, luai_getF, &lf, lua_tostring(L, -1), mode);
|
||||
readstatus = ferror(lf.f);
|
||||
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
||||
if (readstatus) {
|
||||
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
||||
return luai_errfile(L, "read", fnameindex);
|
||||
}
|
||||
lua_remove(L, fnameindex);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int pmain(lua_State* L)
|
||||
{
|
||||
int argc = (int)lua_tointeger(L, 1);
|
||||
@ -102,7 +140,17 @@ static int pmain(lua_State* L)
|
||||
compile = fnl > 4 && !strcmp(".lua", filename + fnl - 4);
|
||||
if (compile)
|
||||
{
|
||||
if (luaL_loadfile(L, filename) != LUA_OK) fatal(lua_tostring(L, -1));
|
||||
// do no use luaL_loadfile so that the filename is not used for error printing
|
||||
char *p = strdup(filename), *n = p;
|
||||
for (p = p + strlen(p); p != n && *p != '/' && *p != '\\'; --p) ;
|
||||
if (p != n) ++p;
|
||||
char *chunkname = malloc(strlen(p) + 16);
|
||||
strcpy(chunkname, "=(");
|
||||
strcat(chunkname, p);
|
||||
strcat(chunkname, ")");
|
||||
free(n);
|
||||
if (luaL_loadfilex2(L, filename, 0, chunkname) != LUA_OK) fatal(lua_tostring(L, -1));
|
||||
free(chunkname);
|
||||
fprintf(f, "static const char %s[] = {", name);
|
||||
w_o = 0;
|
||||
lua_dump(L, writer, f, 0);
|
||||
|
@ -1,8 +1,8 @@
|
||||
local M = {}
|
||||
|
||||
M.major = 1
|
||||
M.major = 0
|
||||
M.minor = 0
|
||||
M.revision = 0
|
||||
M.version = "1.0.0"
|
||||
M.version = "0.0.0"
|
||||
|
||||
return M
|
||||
|
32
lua.h
32
lua.h
@ -6353,7 +6353,7 @@ static int luaB_rawset (lua_State *L) {
|
||||
|
||||
static int luaB_collectgarbage (lua_State *L) {
|
||||
static const char *const opts[] = {"stop", "restart", "collect",
|
||||
"count", "step", "luai_setpause", "setstepmul",
|
||||
"count", "step", "setpause", "setstepmul",
|
||||
"isrunning", NULL};
|
||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
||||
@ -6643,7 +6643,7 @@ static const luaL_Reg luai_base_funcs[] = {
|
||||
#if defined(LUA_COMPAT_LOADSTRING)
|
||||
{"loadstring", luaB_load},
|
||||
#endif
|
||||
{"luai_next", luaB_next},
|
||||
{"next", luaB_next},
|
||||
{"pairs", luaB_pairs},
|
||||
{"pcall", luaB_pcall},
|
||||
{"print", luaB_print},
|
||||
@ -6653,7 +6653,7 @@ static const luaL_Reg luai_base_funcs[] = {
|
||||
{"rawset", luaB_rawset},
|
||||
{"select", luaB_select},
|
||||
{"setmetatable", luaB_setmetatable},
|
||||
{"luai_tonumber", luaB_tonumber},
|
||||
{"tonumber", luaB_tonumber},
|
||||
{"tostring", luaB_tostring},
|
||||
{"type", luaB_type},
|
||||
{"xpcall", luaB_xpcall},
|
||||
@ -6831,7 +6831,7 @@ static int lua_b_rrot (lua_State *L) {
|
||||
static int lua_fieldargs (lua_State *L, int farg, int *width) {
|
||||
lua_Integer f = luaL_checkinteger(L, farg);
|
||||
lua_Integer w = luaL_optinteger(L, farg + 1, 1);
|
||||
luaL_argcheck(L, 0 <= f, farg, "luai_field cannot be negative");
|
||||
luaL_argcheck(L, 0 <= f, farg, "field cannot be negative");
|
||||
luaL_argcheck(L, 0 < w, farg + 1, "width must be positive");
|
||||
if (f + w > LUA_NBITS)
|
||||
luaL_error(L, "trying to access non-existent bits");
|
||||
@ -8066,7 +8066,7 @@ void luaK_setlist (luai_FuncState *fs, int base, int nelems, int tostore) {
|
||||
luai_codeextraarg(fs, c);
|
||||
}
|
||||
else
|
||||
luaX_syntaxerror(fs->ls, "luai_constructor too long");
|
||||
luaX_syntaxerror(fs->ls, "constructor too long");
|
||||
fs->luai_freereg = base + 1; /* free registers with list values */
|
||||
}
|
||||
|
||||
@ -8204,7 +8204,7 @@ static int luaB_corunning (lua_State *L) {
|
||||
|
||||
static const luaL_Reg luai_co_funcs[] = {
|
||||
{"create", luaB_cocreate},
|
||||
{"luai_resume", luaB_coresume},
|
||||
{"resume", luaB_coresume},
|
||||
{"running", luaB_corunning},
|
||||
{"status", luaB_costatus},
|
||||
{"wrap", luaB_cowrap},
|
||||
@ -8416,7 +8416,7 @@ static int luai_db_getinfo (lua_State *L) {
|
||||
luai_settabss(L, "what", ar.what);
|
||||
}
|
||||
if (strchr(options, 'l'))
|
||||
luai_settabsi(L, "luai_currentline", ar.luai_currentline);
|
||||
luai_settabsi(L, "currentline", ar.luai_currentline);
|
||||
if (strchr(options, 'u')) {
|
||||
luai_settabsi(L, "nups", ar.nups);
|
||||
luai_settabsi(L, "nparams", ar.nparams);
|
||||
@ -8676,7 +8676,7 @@ static int luai_db_traceback (lua_State *L) {
|
||||
|
||||
static const luaL_Reg luai_dblib[] = {
|
||||
{"debug", luai_db_debug},
|
||||
{"luai_getuservalue", luai_db_getuservalue},
|
||||
{"getuservalue", luai_db_getuservalue},
|
||||
{"gethook", luai_db_gethook},
|
||||
{"getinfo", luai_db_getinfo},
|
||||
{"getlocal", luai_db_getlocal},
|
||||
@ -8685,7 +8685,7 @@ static const luaL_Reg luai_dblib[] = {
|
||||
{"getupvalue", luai_db_getupvalue},
|
||||
{"upvaluejoin", luai_db_upvaluejoin},
|
||||
{"upvalueid", luai_db_upvalueid},
|
||||
{"luai_setuservalue", luai_db_setuservalue},
|
||||
{"setuservalue", luai_db_setuservalue},
|
||||
{"sethook", luai_db_sethook},
|
||||
{"setlocal", luai_db_setlocal},
|
||||
{"setmetatable", luai_db_setmetatable},
|
||||
@ -9125,7 +9125,7 @@ static const char *luai_getobjname (luai_Proto *p, int lastpc, int reg,
|
||||
? luaF_getlocalname(p, t + 1, pc)
|
||||
: luai_upvalname(p, t);
|
||||
luai_kname(p, pc, k, name);
|
||||
return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "luai_field";
|
||||
return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field";
|
||||
}
|
||||
case luai_OP_GETUPVAL: {
|
||||
*name = luai_upvalname(p, luai_GETARG_B(i));
|
||||
@ -9894,7 +9894,7 @@ static void luai_unroll (lua_State *L, void *ud) {
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a suspended protected call (a "luai_recover point") for the
|
||||
** Try to find a suspended protected call (a "recover point") for the
|
||||
** given thread.
|
||||
*/
|
||||
static luai_CallInfo *luai_findpcall (lua_State *L) {
|
||||
@ -13264,7 +13264,7 @@ static const luaL_Reg luai_mathlib[] = {
|
||||
{"cos", luai_math_cos},
|
||||
{"deg", luai_math_deg},
|
||||
{"exp", luai_math_exp},
|
||||
{"luai_tointeger", luai_math_toint},
|
||||
{"tointeger", luai_math_toint},
|
||||
{"floor", luai_math_floor},
|
||||
{"fmod", luai_math_fmod},
|
||||
{"ult", luai_math_ult},
|
||||
@ -14979,14 +14979,14 @@ static int luai_getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
lua_Integer res = lua_tointegerx(L, -1, &isnum);
|
||||
if (!isnum) { /* luai_field is not an integer? */
|
||||
if (t != LUA_TNIL) /* some other value? */
|
||||
return luaL_error(L, "luai_field '%s' is not an integer", key);
|
||||
return luaL_error(L, "field '%s' is not an integer", key);
|
||||
else if (d < 0) /* absent luai_field; no default? */
|
||||
return luaL_error(L, "luai_field '%s' missing in date table", key);
|
||||
return luaL_error(L, "field '%s' missing in date table", key);
|
||||
res = d;
|
||||
}
|
||||
else {
|
||||
if (!(-LUAI_L_MAXDATEFIELD <= res && res <= LUAI_L_MAXDATEFIELD))
|
||||
return luaL_error(L, "luai_field '%s' is out-of-bound", key);
|
||||
return luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
res -= delta;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
@ -19958,7 +19958,7 @@ void luaT_init (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** function to be used with macro "luai_fasttm": optimized for absence of
|
||||
** function to be used with macro "fasttm": optimized for absence of
|
||||
** tag methods
|
||||
*/
|
||||
const luai_TValue *luaT_gettm (luai_Table *events, luai_TMS event, luai_TString *ename) {
|
||||
|
10
main.c
10
main.c
@ -139,10 +139,16 @@ static int luaopen_l65(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define SRC_LUA(name) { #name, 0, script_ ## name ## _lua, sizeof(script_ ## name ## _lua) }
|
||||
#define SRC_L65(name) { #name, 1, script_ ## name ## _l65, sizeof(script_ ## name ## _l65) }
|
||||
static struct script { const char *name; int t; const char *data; size_t sz; } embedded[] = {
|
||||
{ "l65cfg", 0, script_l65cfg_lua, sizeof(script_l65cfg_lua) },
|
||||
{ "vcs", 1, script_vcs_l65, sizeof(script_vcs_l65) },
|
||||
SRC_LUA(dkjson),
|
||||
SRC_LUA(l65cfg),
|
||||
SRC_LUA(re),
|
||||
SRC_L65(vcs),
|
||||
};
|
||||
#undef SRC_LUA
|
||||
#undef SRC_L65
|
||||
|
||||
static int getembedded(lua_State *L)
|
||||
{
|
||||
|
249
re.lua
Normal file
249
re.lua
Normal file
@ -0,0 +1,249 @@
|
||||
-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
|
||||
|
||||
-- imported functions and modules
|
||||
local tonumber, type, print, error = tonumber, type, print, error
|
||||
local setmetatable = setmetatable
|
||||
local m = require"lpeg"
|
||||
|
||||
-- 'm' will be used to parse expressions, and 'mm' will be used to
|
||||
-- create expressions; that is, 're' runs on 'm', creating patterns
|
||||
-- on 'mm'
|
||||
local mm = m
|
||||
|
||||
-- pattern's metatable
|
||||
local mt = getmetatable(mm.P(0))
|
||||
|
||||
local any = m.P(1)
|
||||
|
||||
-- Pre-defined names
|
||||
local Predef = { nl = m.P"\n" }
|
||||
|
||||
|
||||
local mem
|
||||
local fmem
|
||||
local gmem
|
||||
|
||||
|
||||
local function updatelocale ()
|
||||
mm.locale(Predef)
|
||||
Predef.a = Predef.alpha
|
||||
Predef.c = Predef.cntrl
|
||||
Predef.d = Predef.digit
|
||||
Predef.g = Predef.graph
|
||||
Predef.l = Predef.lower
|
||||
Predef.p = Predef.punct
|
||||
Predef.s = Predef.space
|
||||
Predef.u = Predef.upper
|
||||
Predef.w = Predef.alnum
|
||||
Predef.x = Predef.xdigit
|
||||
Predef.A = any - Predef.a
|
||||
Predef.C = any - Predef.c
|
||||
Predef.D = any - Predef.d
|
||||
Predef.G = any - Predef.g
|
||||
Predef.L = any - Predef.l
|
||||
Predef.P = any - Predef.p
|
||||
Predef.S = any - Predef.s
|
||||
Predef.U = any - Predef.u
|
||||
Predef.W = any - Predef.w
|
||||
Predef.X = any - Predef.x
|
||||
mem = {} -- restart memoization
|
||||
fmem = {}
|
||||
gmem = {}
|
||||
local mt = {__mode = "v"}
|
||||
setmetatable(mem, mt)
|
||||
setmetatable(fmem, mt)
|
||||
setmetatable(gmem, mt)
|
||||
end
|
||||
|
||||
|
||||
updatelocale()
|
||||
|
||||
|
||||
|
||||
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
|
||||
|
||||
|
||||
local function getdef (id, defs)
|
||||
local c = defs and defs[id]
|
||||
if not c then error("undefined name: " .. id) end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
local function patt_error (s, i)
|
||||
local msg = (#s < i + 20) and s:sub(i)
|
||||
or s:sub(i,i+20) .. "..."
|
||||
msg = ("pattern error near '%s'"):format(msg)
|
||||
error(msg, 2)
|
||||
end
|
||||
|
||||
local function mult (p, n)
|
||||
local np = mm.P(true)
|
||||
while n >= 1 do
|
||||
if n%2 >= 1 then np = np * p end
|
||||
p = p * p
|
||||
n = n/2
|
||||
end
|
||||
return np
|
||||
end
|
||||
|
||||
local function equalcap (s, i, c)
|
||||
if type(c) ~= "string" then return nil end
|
||||
local e = #c + i
|
||||
if s:sub(i, e - 1) == c then return e else return nil end
|
||||
end
|
||||
|
||||
|
||||
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
||||
|
||||
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
||||
|
||||
local arrow = S * "<-"
|
||||
|
||||
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
||||
|
||||
name = m.C(name)
|
||||
|
||||
|
||||
-- a defined name only have meaning in a given environment
|
||||
local Def = name * m.Carg(1)
|
||||
|
||||
local num = m.C(m.R"09"^1) * S / tonumber
|
||||
|
||||
local String = "'" * m.C((any - "'")^0) * "'" +
|
||||
'"' * m.C((any - '"')^0) * '"'
|
||||
|
||||
|
||||
local defined = "%" * Def / function (c,Defs)
|
||||
local cat = Defs and Defs[c] or Predef[c]
|
||||
if not cat then error ("name '" .. c .. "' undefined") end
|
||||
return cat
|
||||
end
|
||||
|
||||
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
||||
|
||||
local item = defined + Range + m.C(any)
|
||||
|
||||
local Class =
|
||||
"["
|
||||
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
||||
* m.Cf(item * (item - "]")^0, mt.__add) /
|
||||
function (c, p) return c == "^" and any - p or p end
|
||||
* "]"
|
||||
|
||||
local function adddef (t, k, exp)
|
||||
if t[k] then
|
||||
error("'"..k.."' already defined as a rule")
|
||||
else
|
||||
t[k] = exp
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function firstdef (n, r) return adddef({n}, n, r) end
|
||||
|
||||
|
||||
local function NT (n, b)
|
||||
if not b then
|
||||
error("rule '"..n.."' used outside a grammar")
|
||||
else return mm.V(n)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local exp = m.P{ "Exp",
|
||||
Exp = S * ( m.V"Grammar"
|
||||
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
|
||||
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
|
||||
* (#seq_follow + patt_error);
|
||||
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
||||
+ "!" * S * m.V"Prefix" / mt.__unm
|
||||
+ m.V"Suffix";
|
||||
Suffix = m.Cf(m.V"Primary" * S *
|
||||
( ( m.P"+" * m.Cc(1, mt.__pow)
|
||||
+ m.P"*" * m.Cc(0, mt.__pow)
|
||||
+ m.P"?" * m.Cc(-1, mt.__pow)
|
||||
+ "^" * ( m.Cg(num * m.Cc(mult))
|
||||
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
||||
)
|
||||
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
||||
+ m.P"{}" * m.Cc(nil, m.Ct)
|
||||
+ m.Cg(Def / getdef * m.Cc(mt.__div))
|
||||
)
|
||||
+ "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt))
|
||||
) * S
|
||||
)^0, function (a,b,f) return f(a,b) end );
|
||||
Primary = "(" * m.V"Exp" * ")"
|
||||
+ String / mm.P
|
||||
+ Class
|
||||
+ defined
|
||||
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
||||
function (n, p) return mm.Cg(p, n) end
|
||||
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
||||
+ m.P"{}" / mm.Cp
|
||||
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
||||
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
||||
+ "{" * m.V"Exp" * "}" / mm.C
|
||||
+ m.P"." * m.Cc(any)
|
||||
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
|
||||
Definition = name * arrow * m.V"Exp";
|
||||
Grammar = m.Cg(m.Cc(true), "G") *
|
||||
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
|
||||
adddef) / mm.P
|
||||
}
|
||||
|
||||
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
||||
|
||||
|
||||
local function compile (p, defs)
|
||||
if mm.type(p) == "pattern" then return p end -- already compiled
|
||||
local cp = pattern:match(p, 1, defs)
|
||||
if not cp then error("incorrect pattern", 3) end
|
||||
return cp
|
||||
end
|
||||
|
||||
local function match (s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:match(s, i or 1)
|
||||
end
|
||||
|
||||
local function find (s, p, i)
|
||||
local cp = fmem[p]
|
||||
if not cp then
|
||||
cp = compile(p) / 0
|
||||
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
||||
fmem[p] = cp
|
||||
end
|
||||
local i, e = cp:match(s, i or 1)
|
||||
if i then return i, e - 1
|
||||
else return i
|
||||
end
|
||||
end
|
||||
|
||||
local function gsub (s, p, rep)
|
||||
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
||||
gmem[p] = g
|
||||
local cp = g[rep]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
cp = mm.Cs((cp / rep + 1)^0)
|
||||
g[rep] = cp
|
||||
end
|
||||
return cp:match(s)
|
||||
end
|
||||
|
||||
|
||||
-- exported names
|
||||
local re = {
|
||||
compile = compile,
|
||||
match = match,
|
||||
find = find,
|
||||
gsub = gsub,
|
||||
updatelocale = updatelocale,
|
||||
}
|
||||
|
||||
return re
|
2561
samples/Ishtar.ttt
Normal file
2561
samples/Ishtar.ttt
Normal file
File diff suppressed because it is too large
Load Diff
137
vcs.l65
137
vcs.l65
@ -454,3 +454,140 @@ sprite = function(opt)
|
||||
end
|
||||
return sp
|
||||
end
|
||||
|
||||
-- load a TIATracker song and prepare the data for VCS playback
|
||||
ttt = function(filename)
|
||||
local f, str = assert(io.open(filename,'r')) str=f:read('*all') f:close()
|
||||
local s, pos, err = require"dkjson".decode(str)
|
||||
if err then error(string.format("error parsing JSON file %s: %s", filename, err)) end
|
||||
|
||||
local t = {}
|
||||
t.version = s.version
|
||||
t.tvmode = s.tvmode
|
||||
t.rowsperbeat = s.rowsperbeat
|
||||
t.author = s.metaAuthor
|
||||
t.name = s.metaName
|
||||
t.comment = s.metaComment
|
||||
t.globalspeed = (s.globalspeed ~= nil and s.globalspeed or true) and 1 or 0
|
||||
t.speed = s.evenspeed - 1
|
||||
t.oddspeed = s.oddspeed - 1
|
||||
local usefunktempo
|
||||
if t.globalspeed == 1 then usefunktempo = s.evenspeed ~= s.oddspeed
|
||||
else
|
||||
for k,v in ipairs(s.channels[1].sequence) do
|
||||
local pattern = s.patterns[v.patternindex+1]
|
||||
if pattern.evenspeed ~= pattern.oddspeed then usefunktempo=true break end
|
||||
end
|
||||
end
|
||||
t.usefunktempo = usefunktempo and 1 or 0
|
||||
t.usegoto,t.useslide,t.useoverlay,t.startswithnotes = 0,0,0,1
|
||||
|
||||
local patternmap = {}
|
||||
local instrumentmap,instrumentcount = {},0
|
||||
local percussionmap,percussioncount = {},0
|
||||
t.insctrltable, t.insadindexes, t.inssustainindexes, t.insreleaseindexes = {}, {}, {}, {}
|
||||
t.percfreqtable, t.percctrlvoltable, t.percindexes = {}, {}, {}
|
||||
local insEnvelopeIndex,percEnvelopeIndex = 0,0
|
||||
t.insfreqvoltable = {}
|
||||
t.patternspeeds = {}
|
||||
t.patterns,t.patternptr = {},{}
|
||||
t.sequence = { {}, {} }
|
||||
local ins = table.insert
|
||||
for channelindex, channel in ipairs(s.channels) do
|
||||
local gotooffset = 0
|
||||
for sequenceindex,sequence in ipairs(channel.sequence) do
|
||||
local patternindex = sequence.patternindex + 1
|
||||
if not patternmap[patternindex] then
|
||||
patternmap[patternindex] = #t.patterns
|
||||
local pattern = { name = s.patterns[patternindex].name }
|
||||
ins(t.patterns, pattern)
|
||||
for noteindex,note in ipairs(s.patterns[patternindex].notes) do
|
||||
if note.type == 0 then -- Instrument
|
||||
if not instrumentmap[note.number] then
|
||||
instrumentmap[note.number] = instrumentcount
|
||||
local instrument = s.instruments[note.number+1]
|
||||
instrumentcount = instrumentcount + (instrument.waveform == 16 and 2 or 1)
|
||||
local sz = instrument.envelopeLength
|
||||
while sz > instrument.releaseStart+1 and instrument.frequencies[sz] == 0 and instrument.volumes[sz] == 0 do sz = sz-1 end
|
||||
for i=1,sz do
|
||||
ins(t.insfreqvoltable, instrument.frequencies[i]+8 << 4 | instrument.volumes[i])
|
||||
end
|
||||
ins(t.insfreqvoltable, instrument.releaseStart+1, 0)
|
||||
ins(t.insfreqvoltable, 0)
|
||||
for i = 1, instrument.waveform == 16 and 2 or 1 do
|
||||
ins(t.insadindexes, insEnvelopeIndex)
|
||||
ins(t.inssustainindexes, insEnvelopeIndex + instrument.sustainStart)
|
||||
ins(t.insreleaseindexes, insEnvelopeIndex + instrument.releaseStart)
|
||||
end
|
||||
insEnvelopeIndex = insEnvelopeIndex + sz + 2
|
||||
if instrument.waveform == 16 then -- PURE_COMBINED
|
||||
ins(t.insctrltable, 4) -- PURE_HIGH
|
||||
ins(t.insctrltable, 12) -- PURE_LOW
|
||||
else
|
||||
ins(t.insctrltable, instrument.waveform)
|
||||
end
|
||||
end
|
||||
local valueIns = instrumentmap[note.number] + 1
|
||||
if s.instruments[note.number+1].waveform == 16 and note.value > 31 then valueIns=valueIns+1 end
|
||||
ins(pattern, valueIns<<5 | note.value&0x1f)
|
||||
elseif note.type == 1 then -- Percussion
|
||||
if not percussionmap[note.number] then
|
||||
percussionmap[note.number] = percussioncount
|
||||
percussioncount = percussioncount + 1
|
||||
local percussion = s.percussion[note.number+1]
|
||||
local sz = percussion.envelopeLength
|
||||
while sz > 0 and percussion.waveforms[sz] == 0 and percussion.volumes[sz] == 0 do sz = sz-1 end
|
||||
for i=1,sz do
|
||||
local freq = percussion.frequencies[i]
|
||||
if percussion.overlay and i == sz then freq = freq + 128 end
|
||||
ins(t.percfreqtable, freq)
|
||||
ins(t.percctrlvoltable, percussion.waveforms[i] << 4 | percussion.volumes[i])
|
||||
end
|
||||
ins(t.percfreqtable, 0)
|
||||
ins(t.percctrlvoltable, 0)
|
||||
ins(t.percindexes, percEnvelopeIndex+1)
|
||||
percEnvelopeIndex = percEnvelopeIndex + sz + 1
|
||||
if percussion.overlay then t.useoverlay = 1 end
|
||||
end
|
||||
ins(pattern, percussionmap[note.number] + 17) -- NoteFirstPerc
|
||||
elseif note.type == 2 then -- Hold
|
||||
ins(pattern, 8) -- NoteHold
|
||||
if sequenceindex == 1 and noteindex == 1 then t.startswithnotes = 0 end
|
||||
elseif note.type == 3 then -- Pause
|
||||
ins(pattern, 16) -- NotePause
|
||||
elseif note.type == 4 then -- Slide
|
||||
ins(pattern, 8 + note.value) -- NoteHold + note.value
|
||||
t.useslide = 1
|
||||
end
|
||||
end
|
||||
ins(pattern, 0)
|
||||
if t.globalspeed == 0 then
|
||||
ins(t.patternspeeds, usefunktempo and (t.speed << 4 | t.oddspeed) or t.speed)
|
||||
end
|
||||
end
|
||||
ins(t.sequence[channelindex], patternmap[patternindex])
|
||||
if sequence.gototarget ~= -1 then
|
||||
t.usegoto = 1
|
||||
local value = 128 + sequence.gototarget + gotooffset
|
||||
gotooffset = gotooffset + 1
|
||||
if channelindex == 1 then value = value + #channel.sequence[1] end
|
||||
assert(value < 256, "goto target in channel " .. (channelindex-1) .. " is out of range: " .. value)
|
||||
ins(t.sequence[channelindex], value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local i
|
||||
t.c0init,i = s.startpattern0,0
|
||||
while i <= t.c0init do
|
||||
if t.sequence[1][i+1] > 127 then t.c0init = t.c0init + 1 end
|
||||
i = i+1
|
||||
end
|
||||
t.c1init,i = s.startpattern1,0
|
||||
while i <= t.c1init do
|
||||
if t.sequence[2][i+1] > 127 then t.c1init = t.c1init + 1 end
|
||||
i = i+1
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user