Skip to content

Commit c261cf4

Browse files
committed
JSON: Script Module
1 parent 303fe6b commit c261cf4

File tree

5 files changed

+202
-3
lines changed

5 files changed

+202
-3
lines changed

VCMP-LUA/LuaPlugin.vcxproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
<PrecompiledHeader>NotUsing</PrecompiledHeader>
6868
<WarningLevel>Level3</WarningLevel>
6969
<PreprocessorDefinitions>_x32;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
70-
<AdditionalIncludeDirectories>.;include;vcmpWrap;vendor;vendor\Lua;vendor\sol;vendor\spdlog\include;modules\sqlite3\sqliteCpp\include;modules\mariadb\include;modules\mariadb\include\mysql;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
70+
<AdditionalIncludeDirectories>.;include;vcmpWrap;vendor;vendor\Lua;vendor\sol;vendor\spdlog\include;modules\sqlite3\sqliteCpp\include;modules\json;modules\mariadb\include;modules\mariadb\include\mysql;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
7171
<Optimization>Full</Optimization>
7272
<FunctionLevelLinking>true</FunctionLevelLinking>
7373
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -90,7 +90,7 @@
9090
<PrecompiledHeader>NotUsing</PrecompiledHeader>
9191
<WarningLevel>Level3</WarningLevel>
9292
<PreprocessorDefinitions>WIN32;_RELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
93-
<AdditionalIncludeDirectories>.;include;vcmpWrap;vendor;vendor\Lua;vendor\sol;vendor\spdlog\include;modules\sqlite3\sqliteCpp\include;modules\mariadb\include;modules\mariadb\include\mysql;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
93+
<AdditionalIncludeDirectories>.;include;vcmpWrap;vendor;vendor\Lua;vendor\sol;vendor\spdlog\include;modules\sqlite3\sqliteCpp\include;modules\json;modules\mariadb\include;modules\mariadb\include\mysql;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
9494
<Optimization>Full</Optimization>
9595
<FunctionLevelLinking>true</FunctionLevelLinking>
9696
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -639,6 +639,9 @@
639639
<Project>{8D2AE635-7923-0588-A255-EA0C8EADE493}</Project>
640640
</ProjectReference>
641641
</ItemGroup>
642+
<ItemGroup>
643+
<None Include="vcmpWrap\Modules\JSON\JSON.hpp" />
644+
</ItemGroup>
642645
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
643646
<ImportGroup Label="ExtensionTargets">
644647
</ImportGroup>

VCMP-LUA/LuaPlugin.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,4 +1599,7 @@
15991599
<Filter>vcmpWrap\Timer</Filter>
16001600
</ClCompile>
16011601
</ItemGroup>
1602+
<ItemGroup>
1603+
<None Include="vcmpWrap\Modules\JSON\JSON.hpp" />
1604+
</ItemGroup>
16021605
</Project>
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#pragma once
2+
#include "pch.h"
3+
4+
//-- [[ JSON.lua
5+
//A compact pure - Lua JSON library.
6+
//The main functions are : JSON.stringify, JSON.parse.
7+
//## JSON.stringify:
8+
//This expects the following to be true of any tables being encoded :
9+
//*They only have string or number keys.Number keys must be represented as
10+
//strings in JSON; this is part of the JSON spec.
11+
//* They are not recursive.Such a structure cannot be specified in JSON.
12+
//A Lua table is considered to be an array ifand only if its set of keys is a
13+
//consecutive sequence of positive integers starting at 1. Arrays are encoded like
14+
//so : `[2, 3, false, "hi"]`. Any other type of Lua table is encoded as a JSON
15+
//object, encoded like so : `{"key1": 2, "key2" : false}`.
16+
//Because the Lua nil value cannot be a key, and as a table value is considerd
17+
//equivalent to a missing key, there is no way to express the JSON "null" value in
18+
//a Lua table.The only way this will output "null" is if your entire input obj is
19+
//nil itself.
20+
//An empty Lua table, {}, could be considered either a JSON object or array -
21+
//it's an ambiguous edge case. We choose to treat this as an object as it is the
22+
//more general type.
23+
//To be clear, none of the above considerations is a limitation of this code.
24+
//Rather, it is what we get when we completely observe the JSON specification for
25+
//as arbitrary a Lua object as JSON is capable of expressing.
26+
//## JSON.parse:
27+
//This function parses JSON, with the exception that it does not pay attention to
28+
//\u - escaped unicode code points in strings.
29+
//It is difficult for Lua to return null as a value.In order to prevent the loss
30+
//of keys with a null value in a JSON string, this function uses the one - off
31+
//table value JSON.null(which is just an empty table) to indicate null values.
32+
//This way you can check if a value is null with the conditional
33+
//`val == JSON.null`.
34+
//If you have control over the data and are using Lua, I would recommend just
35+
//avoiding null values in your data to begin with.
36+
//--]]
37+
38+
static void LoadScriptModule_JSON(sol::state* L)
39+
{
40+
L->script(R"(
41+
JSON = {}
42+
43+
-- Internal functions.
44+
45+
local function kind_of(obj)
46+
if type(obj) ~= 'table' then return type(obj) end
47+
local i = 1
48+
for _ in pairs(obj) do
49+
if obj[i] ~= nil then i = i + 1 else return 'table' end
50+
end
51+
if i == 1 then return 'table' else return 'array' end
52+
end
53+
54+
local function escape_str(s)
55+
local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
56+
local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
57+
for i, c in ipairs(in_char) do
58+
s = s:gsub(c, '\\' .. out_char[i])
59+
end
60+
return s
61+
end
62+
63+
-- Returns pos, did_find; there are two cases:
64+
-- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
65+
-- 2. Delimiter not found: pos = pos after leading space; did_find = false.
66+
-- This throws an error if err_if_missing is true and the delim is not found.
67+
local function skip_delim(str, pos, delim, err_if_missing)
68+
pos = pos + #str:match('^%s*', pos)
69+
if str:sub(pos, pos) ~= delim then
70+
if err_if_missing then
71+
error('Expected ' .. delim .. ' near position ' .. pos)
72+
end
73+
return pos, false
74+
end
75+
return pos + 1, true
76+
end
77+
78+
-- Expects the given pos to be the first character after the opening quote.
79+
-- Returns val, pos; the returned pos is after the closing quote character.
80+
local function parse_str_val(str, pos, val)
81+
val = val or ''
82+
local early_end_error = 'End of input found while parsing string.'
83+
if pos > #str then error(early_end_error) end
84+
local c = str:sub(pos, pos)
85+
if c == '"' then return val, pos + 1 end
86+
if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
87+
-- We must have a \ character.
88+
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
89+
local nextc = str:sub(pos + 1, pos + 1)
90+
if not nextc then error(early_end_error) end
91+
return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
92+
end
93+
94+
-- Returns val, pos; the returned pos is after the number's final character.
95+
local function parse_num_val(str, pos)
96+
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
97+
local val = tonumber(num_str)
98+
if not val then error('Error parsing number at position ' .. pos .. '.') end
99+
return val, pos + #num_str
100+
end
101+
102+
103+
-- Public values and functions.
104+
105+
function JSON.stringify(obj, as_key)
106+
local s = {} -- We'll build the string as an array of strings to be concatenated.
107+
local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj) otherwise.
108+
if kind == 'array' then
109+
if as_key then error('Can\'t encode array as key.') end
110+
s[#s + 1] = '['
111+
for i, val in ipairs(obj) do
112+
if i > 1 then s[#s + 1] = ', ' end
113+
s[#s + 1] = JSON.stringify(val)
114+
end
115+
s[#s + 1] = ']'
116+
elseif kind == 'table' then
117+
if as_key then error('Can\'t encode table as key.') end
118+
s[#s + 1] = '{'
119+
for k, v in pairs(obj) do
120+
if #s > 1 then s[#s + 1] = ', ' end
121+
s[#s + 1] = JSON.stringify(k, true)
122+
s[#s + 1] = ':'
123+
s[#s + 1] = JSON.stringify(v)
124+
end
125+
s[#s + 1] = '}'
126+
elseif kind == 'string' then
127+
return '"' .. escape_str(obj) .. '"'
128+
elseif kind == 'number' then
129+
if as_key then return '"' .. tostring(obj) .. '"' end
130+
return tostring(obj)
131+
elseif kind == 'boolean' then
132+
return tostring(obj)
133+
elseif kind == 'nil' then
134+
return 'null'
135+
else
136+
error('Unjsonifiable type: ' .. kind .. '.')
137+
end
138+
return table.concat(s)
139+
end
140+
141+
JSON.null = {} -- This is a one-off table to represent the null value.
142+
143+
function JSON.parse(str, pos, end_delim)
144+
pos = pos or 1
145+
if pos > #str then error('Reached unexpected end of input.') end
146+
local pos = pos + #str:match('^%s*', pos) -- Skip whitespace.
147+
local first = str:sub(pos, pos)
148+
if first == '{' then -- Parse an object.
149+
local obj, key, delim_found = {}, true, true
150+
pos = pos + 1
151+
while true do
152+
key, pos = JSON.parse(str, pos, '}')
153+
if key == nil then return obj, pos end
154+
if not delim_found then error('Comma missing between object items.') end
155+
pos = skip_delim(str, pos, ':', true) -- true -> error if missing.
156+
obj[key], pos = JSON.parse(str, pos)
157+
pos, delim_found = skip_delim(str, pos, ',')
158+
end
159+
elseif first == '[' then -- Parse an array.
160+
local arr, val, delim_found = {}, true, true
161+
pos = pos + 1
162+
while true do
163+
val, pos = JSON.parse(str, pos, ']')
164+
if val == nil then return arr, pos end
165+
if not delim_found then error('Comma missing between array items.') end
166+
arr[#arr + 1] = val
167+
pos, delim_found = skip_delim(str, pos, ',')
168+
end
169+
elseif first == '"' then -- Parse a string.
170+
return parse_str_val(str, pos + 1)
171+
elseif first == '-' or first:match('%d') then -- Parse a number.
172+
return parse_num_val(str, pos)
173+
elseif first == end_delim then -- End of an object or array.
174+
return nil, pos + 1
175+
else -- Parse true, false, or null.
176+
local literals = {['true'] = true, ['false'] = false, ['null'] = JSON.null}
177+
for lit_str, lit_val in pairs(literals) do
178+
local lit_end = pos + #lit_str - 1
179+
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
180+
end
181+
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
182+
error('Invalid JSON syntax starting at ' .. pos_info_str)
183+
end
184+
end
185+
)");
186+
}

VCMP-LUA/vcmpWrap/globalTables.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include "Modules/Crypto/Hash.h"
1919
#include "Modules/SqLite3/SqLite.h"
2020

21+
// Script modules
22+
#include "Modules/JSON/JSON.hpp"
23+
2124
void InitGlobals(sol::state*);
2225

2326
void RegisterClasses(sol::state* Lua) {
@@ -44,6 +47,10 @@ void RegisterClasses(sol::state* Lua) {
4447
Checkpoint::Init(Lua);
4548
Pickup::Init(Lua);
4649

50+
// Lua Script-Modules
51+
// JSON
52+
LoadScriptModule_JSON(Lua);
53+
4754
Lua->script(R"(
4855
function INTERNAL__tostring(x, intend)
4956
intend = tonumber(intend) or 1

VCMPLua.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,6 @@ Global
7676
HideSolutionNode = FALSE
7777
EndGlobalSection
7878
GlobalSection(ExtensibilityGlobals) = postSolution
79-
SolutionGuid = {3064B8BE-DA0A-4ADC-B3C0-47F386F816AE}
79+
SolutionGuid = {860F20D1-F095-4E02-AD7B-E1E2F982800B}
8080
EndGlobalSection
8181
EndGlobal

0 commit comments

Comments
 (0)