Skip to content

Commit d165cfe

Browse files
committed
Fix #6
1 parent 0794096 commit d165cfe

File tree

2 files changed

+90
-64
lines changed

2 files changed

+90
-64
lines changed

hjson.lua

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@ local encoderH = require "hjson.encoderH"
1111
---
1212
---decodes h/json
1313
---@param str string
14-
---@param strict boolean?
15-
---@param object_hook (fun(obj: table): table)?
16-
---@param object_pairs_hook (fun(pairs: HJsonKeyValuePair[]): HJsonKeyValuePair[])?
14+
---@param options HjsonDecoderOptions
1715
---@return any
18-
local function decode(str, strict, object_hook, object_pairs_hook)
19-
local _decoder = decoder:new(strict, object_hook, object_pairs_hook)
16+
local function decode(str, options)
17+
local _decoder = decoder:new(options)
2018
return _decoder:decode(str)
2119
end
2220

@@ -35,17 +33,17 @@ local function preprocess_encode_options(options)
3533
return result
3634
end
3735

38-
if options.skipkeys == true then
36+
if options.skip_keys == true then
3937
print("skipkeys is deprecated, use skip_keys instead")
4038
options.skip_keys = true
4139
end
4240

43-
if options.sortKeys == true then
41+
if options.sort_keys == true then
4442
print("sortKeys is deprecated, use sort_keys instead")
4543
options.sort_keys = true
4644
end
4745

48-
if options.invalidObjectsAsType == true then
46+
if options.invalid_objects_as_type == true then
4947
print("invalidObjectsAsType is deprecated, use invalid_objects_as_type instead")
5048
options.invalid_objects_as_type = true
5149
end
@@ -103,11 +101,9 @@ local hjson = {
103101
---
104102
---decodes h/json
105103
---@param str string
106-
---@param strict boolean?
107-
---@param object_hook (fun(obj: table): table)?
108-
---@param object_pairs_hook (fun(pairs: HJsonKeyValuePair[]): HJsonKeyValuePair[])?
104+
---@param options HjsonDecoderOptions
109105
---@return any
110-
parse = decode
106+
parse = decode,
111107
}
112108

113109
return hjson

hjson/decoder.lua

Lines changed: 82 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
-- MIT License - Copyright (c) 2023 V (alis.is)
2+
local DEFAULT_MAX_DEPTH = 1000
23

34
local WHITESPACE = " \t\n\r"
45
local PUNCTUATOR = "{}[],:"
@@ -37,34 +38,55 @@ local function decodeError(str, idx, msg)
3738
error(string.format("%s at line %d col %d", msg, line_count, col_count))
3839
end
3940

41+
---@class HjsonDecoderOptions
42+
---@field strict boolean?
43+
---@field object_hook (fun(obj: table): table)?
44+
---@field object_pairs_hook (fun(pairs: HJsonKeyValuePair[]): HJsonKeyValuePair[])?
45+
---@field max_depth number?
46+
47+
---@class HjsonDecoder
48+
---@field decode fun(self: HjsonDecoder, s: string): any
49+
4050
local HjsonDecoder = {}
41-
--[[Hjson decoder
42-
Performs the following translations in decoding by default:
43-
** NOTE: nil is used in lua for reference removal and so arrays with null wont contain nil in lua representation.
44-
Same objects wont contain keys with nil value.
45-
46-
+---------------+-------------------+
47-
| JSON | Lua |
48-
+===============+===================+
49-
| object | table |
50-
+---------------+-------------------+
51-
| array | table |
52-
+---------------+-------------------+
53-
| string | string |
54-
+---------------+-------------------+
55-
| number | number |
56-
+---------------+-------------------+
57-
| true | true |
58-
+---------------+-------------------+
59-
| false | false |
60-
+---------------+-------------------+
61-
| null | nil |
62-
+---------------+-------------------+
63-
]]
64-
function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
65-
if strict == nil then
66-
strict = true
51+
--- Hjson decoder
52+
--- Performs the following translations in decoding by default:
53+
--- ** NOTE: nil is used in lua for reference removal and so arrays with null wont contain nil in lua representation.
54+
--- Same objects wont contain keys with nil value.
55+
--- +---------------+-------------------+
56+
--- | JSON | Lua |
57+
--- +===============+===================+
58+
--- | object | table |
59+
--- +---------------+-------------------+
60+
--- | array | table |
61+
--- +---------------+-------------------+
62+
--- | string | string |
63+
--- +---------------+-------------------+
64+
--- | number | number |
65+
--- +---------------+-------------------+
66+
--- | true | true |
67+
--- +---------------+-------------------+
68+
--- | false | false |
69+
--- +---------------+-------------------+
70+
--- | null | nil |
71+
--- +---------------+-------------------+
72+
73+
--- Creates a new HjsonDecoder instance
74+
---@param options HjsonDecoderOptions
75+
---@return HjsonDecoder
76+
function HjsonDecoder:new(options)
77+
if type(options) ~= "table" then
78+
options = {
79+
strict = type(options) == "boolean" and options or true, -- for backward compatibility
80+
}
81+
end
82+
83+
if type(options.strict) ~= "boolean" then
84+
options.strict = true
85+
end
86+
if type(options.max_depth) ~= "number" then
87+
options.max_depth = DEFAULT_MAX_DEPTH
6788
end
89+
6890
local memo = {}
6991

7092
local function getEol(s, _end)
@@ -166,7 +188,7 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
166188
chunks = chunks .. terminator
167189
return -- continue
168190
elseif terminator ~= "\\" then
169-
if strict then
191+
if options.strict then
170192
decodeError(s, begin, "Invalid control character " .. terminator)
171193
else
172194
chunks = chunks .. terminator
@@ -367,9 +389,9 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
367389
end
368390
end
369391

370-
local function parseObject(state, scanOnce, objectWithoutBraces)
371-
if objectWithoutBraces == nil then
372-
objectWithoutBraces = false
392+
local function parse_object(state, scan_once, is_object_without_braces, depth)
393+
if is_object_without_braces == nil then
394+
is_object_without_braces = false
373395
end
374396
local s = state.s
375397
local _end = state._end
@@ -399,14 +421,14 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
399421
ch, _end = getNext(s, _end)
400422

401423
-- Trivial empty object
402-
if not objectWithoutBraces and ch == "}" then
403-
if type(object_pairs_hook) == "function" then
404-
local result = object_pairs_hook(pairs)
424+
if not is_object_without_braces and ch == "}" then
425+
if type(options.object_pairs_hook) == "function" then
426+
local result = options.object_pairs_hook(pairs)
405427
return result, _end + 1
406428
end
407429
pairs = {}
408-
if type(object_hook) == "function" then
409-
pairs = object_hook(pairs)
430+
if type(options.object_hook) == "function" then
431+
pairs = options.object_hook(pairs)
410432
end
411433
return pairs, _end + 1
412434
end
@@ -423,15 +445,15 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
423445
end
424446

425447
ch, _end = getNext(s, _end + 1)
426-
value, _end = scanOnce(s, _end)
448+
value, _end = scan_once(s, _end, depth)
427449
table.insert(pairs, {[key] = value})
428450

429451
ch, _end = getNext(s, _end)
430452
if ch == "," then
431453
ch, _end = getNext(s, _end + 1)
432454
end
433455

434-
if objectWithoutBraces then
456+
if is_object_without_braces then
435457
if ch == "" then
436458
break
437459
end
@@ -443,19 +465,19 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
443465
end
444466
ch, _end = getNext(s, _end)
445467
end
446-
if type(object_pairs_hook) == "function" then
447-
local result = object_pairs_hook(pairs)
468+
if type(options.object_pairs_hook) == "function" then
469+
local result = options.object_pairs_hook(pairs)
448470
return result, _end
449471
end
450472

451473
local obj = dict(pairs)
452-
if type(object_hook) == "function" then
453-
obj = object_hook(obj)
474+
if type(options.object_hook) == "function" then
475+
obj = options.object_hook(obj)
454476
end
455477
return obj, _end
456478
end
457479

458-
local function parseArray(state, scanOnce)
480+
local function parse_array(state, scan_once, depth)
459481
local ch
460482
local s = state.s
461483
local _end = state._end
@@ -472,7 +494,7 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
472494

473495
while true do
474496
local value
475-
value, _end = scanOnce(s, _end)
497+
value, _end = scan_once(s, _end, depth)
476498
table.insert(values, value)
477499
ch, _end = getNext(s, _end)
478500
if ch == "," then
@@ -489,7 +511,15 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
489511
return values, _end
490512
end
491513

492-
local function _scanOnce(string, idx)
514+
local function _scan_once(string, idx, depth)
515+
if type(depth) ~= "number" then
516+
depth = 0
517+
end
518+
depth = depth + 1
519+
if depth > options.max_depth then
520+
decodeError(string, idx, "Exceeded max depth")
521+
end
522+
493523
local ch = charAt(string, idx)
494524
if not ch then
495525
decodeError(string, idx, "Expecting value")
@@ -502,18 +532,18 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
502532
return parseString(string, idx + 1)
503533
end
504534
elseif ch == "{" then
505-
return parseObject({s = string, _end = idx + 1}, _scanOnce)
535+
return parse_object({s = string, _end = idx + 1}, _scan_once, false, depth)
506536
elseif ch == "[" then
507-
return parseArray({s = string, _end = idx + 1}, _scanOnce)
537+
return parse_array({s = string, _end = idx + 1}, _scan_once, depth)
508538
end
509539
return parsePrimitive(string, idx)
510540
end
511541

512-
local function scanOnce(string, idx)
542+
local function scan_once(string, idx)
513543
if idx <= 0 then
514544
decodeError(string, idx, "Expecting value")
515545
end
516-
local status, result, _end = pcall(_scanOnce, string, idx)
546+
local status, result, _end = pcall(_scan_once, string, idx)
517547
memo = {}
518548
if not status then
519549
error(result)
@@ -522,11 +552,11 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
522552
return result, _end
523553
end
524554

525-
local function scanObjectOnce(string, idx)
555+
local function scan_object_once(string, idx)
526556
if idx <= 0 then
527557
decodeError(string, idx, "Expecting value")
528558
end
529-
local status, result, _end = pcall(parseObject, {s = string, _end = idx}, _scanOnce, true)
559+
local status, result, _end = pcall(parse_object, {s = string, _end = idx}, _scan_once, true)
530560
memo = {}
531561
if not status then
532562
error(result)
@@ -538,8 +568,8 @@ function HjsonDecoder:new(strict, object_hook, object_pairs_hook)
538568
-- Finally create and return instance of decoder
539569
local hd = {
540570
get_next = getNext,
541-
scan_once = scanOnce,
542-
scan_object_once = scanObjectOnce
571+
scan_once = scan_once,
572+
scan_object_once = scan_object_once
543573
}
544574
setmetatable(hd, self)
545575
self.__index = self

0 commit comments

Comments
 (0)