mirror of https://github.com/g012/l65.git
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_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
set(L65_VERSION_MAJOR "1")
|
set(L65_VERSION_MAJOR "1")
|
||||||
set(L65_VERSION_MINOR "0")
|
set(L65_VERSION_MINOR "1")
|
||||||
set(L65_VERSION_REVISION "0")
|
set(L65_VERSION_REVISION "0")
|
||||||
set(L65_VERSION "${L65_VERSION_MAJOR}.${L65_VERSION_MINOR}.${L65_VERSION_REVISION}")
|
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")
|
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_BINARY_DIR}/scripts.h
|
||||||
${L65_SOURCE_DIR}/stb_image.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)
|
file(GLOB L65_FILES ${L65_SOURCE_DIR}/*.l65)
|
||||||
set(L65_SCRIPTS
|
set(L65_SCRIPTS
|
||||||
${L65_SOURCE_DIR}/6502.lua
|
${L65_SOURCE_DIR}/6502.lua
|
||||||
|
${L65_SOURCE_DIR}/dkjson.lua
|
||||||
${L65_SOURCE_DIR}/l65.lua
|
${L65_SOURCE_DIR}/l65.lua
|
||||||
${L65_BINARY_DIR}/l65cfg.lua
|
${L65_BINARY_DIR}/l65cfg.lua
|
||||||
|
${L65_SOURCE_DIR}/re.lua
|
||||||
${L65_FILES}
|
${L65_FILES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -100,7 +96,7 @@ add_custom_command(
|
||||||
DEPENDS embed ${L65_SCRIPTS}
|
DEPENDS embed ${L65_SCRIPTS}
|
||||||
)
|
)
|
||||||
add_custom_target(prereq DEPENDS ${L65_BINARY_DIR}/scripts.h)
|
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)
|
add_dependencies(${PROJECT_NAME} prereq)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
|
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE "${L65_SOURCE_DIR}" "${L65_BINARY_DIR}")
|
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
|
* [k65](http://devkk.net/wiki/index.php?title=K65) style syntax
|
||||||
* helpers to inter-operate with cc/ca65 and dasm
|
* helpers to inter-operate with cc/ca65 and dasm
|
||||||
|
* import TIATracker \*.ttt files directly
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Developed by g012, using:
|
Developed by g012, using:
|
||||||
* [Lua 5.3.4](https://www.lua.org)
|
* [Lua 5.3.4](https://www.lua.org)
|
||||||
* [LuaMinify](https://github.com/stravant/LuaMinify)
|
* [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)
|
* [LuaFileSystem](https://keplerproject.github.io/luafilesystem)
|
||||||
* [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg)
|
* [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:
|
Projects which inspired l65:
|
||||||
* [nimble65](https://bitbucket.org/kylearan/nimble65)
|
* [nimble65](https://bitbucket.org/kylearan/nimble65)
|
||||||
|
|
|
@ -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;
|
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)
|
static int pmain(lua_State* L)
|
||||||
{
|
{
|
||||||
int argc = (int)lua_tointeger(L, 1);
|
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);
|
compile = fnl > 4 && !strcmp(".lua", filename + fnl - 4);
|
||||||
if (compile)
|
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);
|
fprintf(f, "static const char %s[] = {", name);
|
||||||
w_o = 0;
|
w_o = 0;
|
||||||
lua_dump(L, writer, f, 0);
|
lua_dump(L, writer, f, 0);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.major = 1
|
M.major = 0
|
||||||
M.minor = 0
|
M.minor = 0
|
||||||
M.revision = 0
|
M.revision = 0
|
||||||
M.version = "1.0.0"
|
M.version = "0.0.0"
|
||||||
|
|
||||||
return M
|
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 int luaB_collectgarbage (lua_State *L) {
|
||||||
static const char *const opts[] = {"stop", "restart", "collect",
|
static const char *const opts[] = {"stop", "restart", "collect",
|
||||||
"count", "step", "luai_setpause", "setstepmul",
|
"count", "step", "setpause", "setstepmul",
|
||||||
"isrunning", NULL};
|
"isrunning", NULL};
|
||||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
||||||
|
@ -6643,7 +6643,7 @@ static const luaL_Reg luai_base_funcs[] = {
|
||||||
#if defined(LUA_COMPAT_LOADSTRING)
|
#if defined(LUA_COMPAT_LOADSTRING)
|
||||||
{"loadstring", luaB_load},
|
{"loadstring", luaB_load},
|
||||||
#endif
|
#endif
|
||||||
{"luai_next", luaB_next},
|
{"next", luaB_next},
|
||||||
{"pairs", luaB_pairs},
|
{"pairs", luaB_pairs},
|
||||||
{"pcall", luaB_pcall},
|
{"pcall", luaB_pcall},
|
||||||
{"print", luaB_print},
|
{"print", luaB_print},
|
||||||
|
@ -6653,7 +6653,7 @@ static const luaL_Reg luai_base_funcs[] = {
|
||||||
{"rawset", luaB_rawset},
|
{"rawset", luaB_rawset},
|
||||||
{"select", luaB_select},
|
{"select", luaB_select},
|
||||||
{"setmetatable", luaB_setmetatable},
|
{"setmetatable", luaB_setmetatable},
|
||||||
{"luai_tonumber", luaB_tonumber},
|
{"tonumber", luaB_tonumber},
|
||||||
{"tostring", luaB_tostring},
|
{"tostring", luaB_tostring},
|
||||||
{"type", luaB_type},
|
{"type", luaB_type},
|
||||||
{"xpcall", luaB_xpcall},
|
{"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) {
|
static int lua_fieldargs (lua_State *L, int farg, int *width) {
|
||||||
lua_Integer f = luaL_checkinteger(L, farg);
|
lua_Integer f = luaL_checkinteger(L, farg);
|
||||||
lua_Integer w = luaL_optinteger(L, farg + 1, 1);
|
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");
|
luaL_argcheck(L, 0 < w, farg + 1, "width must be positive");
|
||||||
if (f + w > LUA_NBITS)
|
if (f + w > LUA_NBITS)
|
||||||
luaL_error(L, "trying to access non-existent bits");
|
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);
|
luai_codeextraarg(fs, c);
|
||||||
}
|
}
|
||||||
else
|
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 */
|
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[] = {
|
static const luaL_Reg luai_co_funcs[] = {
|
||||||
{"create", luaB_cocreate},
|
{"create", luaB_cocreate},
|
||||||
{"luai_resume", luaB_coresume},
|
{"resume", luaB_coresume},
|
||||||
{"running", luaB_corunning},
|
{"running", luaB_corunning},
|
||||||
{"status", luaB_costatus},
|
{"status", luaB_costatus},
|
||||||
{"wrap", luaB_cowrap},
|
{"wrap", luaB_cowrap},
|
||||||
|
@ -8416,7 +8416,7 @@ static int luai_db_getinfo (lua_State *L) {
|
||||||
luai_settabss(L, "what", ar.what);
|
luai_settabss(L, "what", ar.what);
|
||||||
}
|
}
|
||||||
if (strchr(options, 'l'))
|
if (strchr(options, 'l'))
|
||||||
luai_settabsi(L, "luai_currentline", ar.luai_currentline);
|
luai_settabsi(L, "currentline", ar.luai_currentline);
|
||||||
if (strchr(options, 'u')) {
|
if (strchr(options, 'u')) {
|
||||||
luai_settabsi(L, "nups", ar.nups);
|
luai_settabsi(L, "nups", ar.nups);
|
||||||
luai_settabsi(L, "nparams", ar.nparams);
|
luai_settabsi(L, "nparams", ar.nparams);
|
||||||
|
@ -8676,7 +8676,7 @@ static int luai_db_traceback (lua_State *L) {
|
||||||
|
|
||||||
static const luaL_Reg luai_dblib[] = {
|
static const luaL_Reg luai_dblib[] = {
|
||||||
{"debug", luai_db_debug},
|
{"debug", luai_db_debug},
|
||||||
{"luai_getuservalue", luai_db_getuservalue},
|
{"getuservalue", luai_db_getuservalue},
|
||||||
{"gethook", luai_db_gethook},
|
{"gethook", luai_db_gethook},
|
||||||
{"getinfo", luai_db_getinfo},
|
{"getinfo", luai_db_getinfo},
|
||||||
{"getlocal", luai_db_getlocal},
|
{"getlocal", luai_db_getlocal},
|
||||||
|
@ -8685,7 +8685,7 @@ static const luaL_Reg luai_dblib[] = {
|
||||||
{"getupvalue", luai_db_getupvalue},
|
{"getupvalue", luai_db_getupvalue},
|
||||||
{"upvaluejoin", luai_db_upvaluejoin},
|
{"upvaluejoin", luai_db_upvaluejoin},
|
||||||
{"upvalueid", luai_db_upvalueid},
|
{"upvalueid", luai_db_upvalueid},
|
||||||
{"luai_setuservalue", luai_db_setuservalue},
|
{"setuservalue", luai_db_setuservalue},
|
||||||
{"sethook", luai_db_sethook},
|
{"sethook", luai_db_sethook},
|
||||||
{"setlocal", luai_db_setlocal},
|
{"setlocal", luai_db_setlocal},
|
||||||
{"setmetatable", luai_db_setmetatable},
|
{"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)
|
? luaF_getlocalname(p, t + 1, pc)
|
||||||
: luai_upvalname(p, t);
|
: luai_upvalname(p, t);
|
||||||
luai_kname(p, pc, k, name);
|
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: {
|
case luai_OP_GETUPVAL: {
|
||||||
*name = luai_upvalname(p, luai_GETARG_B(i));
|
*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.
|
** given thread.
|
||||||
*/
|
*/
|
||||||
static luai_CallInfo *luai_findpcall (lua_State *L) {
|
static luai_CallInfo *luai_findpcall (lua_State *L) {
|
||||||
|
@ -13264,7 +13264,7 @@ static const luaL_Reg luai_mathlib[] = {
|
||||||
{"cos", luai_math_cos},
|
{"cos", luai_math_cos},
|
||||||
{"deg", luai_math_deg},
|
{"deg", luai_math_deg},
|
||||||
{"exp", luai_math_exp},
|
{"exp", luai_math_exp},
|
||||||
{"luai_tointeger", luai_math_toint},
|
{"tointeger", luai_math_toint},
|
||||||
{"floor", luai_math_floor},
|
{"floor", luai_math_floor},
|
||||||
{"fmod", luai_math_fmod},
|
{"fmod", luai_math_fmod},
|
||||||
{"ult", luai_math_ult},
|
{"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);
|
lua_Integer res = lua_tointegerx(L, -1, &isnum);
|
||||||
if (!isnum) { /* luai_field is not an integer? */
|
if (!isnum) { /* luai_field is not an integer? */
|
||||||
if (t != LUA_TNIL) /* some other value? */
|
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? */
|
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;
|
res = d;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!(-LUAI_L_MAXDATEFIELD <= res && res <= LUAI_L_MAXDATEFIELD))
|
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;
|
res -= delta;
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
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
|
** tag methods
|
||||||
*/
|
*/
|
||||||
const luai_TValue *luaT_gettm (luai_Table *events, luai_TMS event, luai_TString *ename) {
|
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;
|
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[] = {
|
static struct script { const char *name; int t; const char *data; size_t sz; } embedded[] = {
|
||||||
{ "l65cfg", 0, script_l65cfg_lua, sizeof(script_l65cfg_lua) },
|
SRC_LUA(dkjson),
|
||||||
{ "vcs", 1, script_vcs_l65, sizeof(script_vcs_l65) },
|
SRC_LUA(l65cfg),
|
||||||
|
SRC_LUA(re),
|
||||||
|
SRC_L65(vcs),
|
||||||
};
|
};
|
||||||
|
#undef SRC_LUA
|
||||||
|
#undef SRC_L65
|
||||||
|
|
||||||
static int getembedded(lua_State *L)
|
static int getembedded(lua_State *L)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
137
vcs.l65
137
vcs.l65
|
@ -454,3 +454,140 @@ sprite = function(opt)
|
||||||
end
|
end
|
||||||
return sp
|
return sp
|
||||||
end
|
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…
Reference in New Issue