11-- MIT License - Copyright (c) 2023 V (alis.is)
2+ local DEFAULT_MAX_DEPTH = 1000
23
34local WHITESPACE = " \t\n\r "
45local 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 ))
3839end
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+
4050local 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