1- -- MIT License - Copyright (c) 2023 V (alis.is)
1+ -- MIT License - Copyright (c) 2025 V (alis.is)
22local DEFAULT_MAX_DEPTH = 1000
33
44local WHITESPACE = " \t\n\r "
@@ -25,7 +25,7 @@ local function charAt(s, pos)
2525 return s :sub (pos , pos )
2626end
2727
28- local function decodeError (str , idx , msg )
28+ local function decode_error (str , idx , msg )
2929 local line_count = 1
3030 local col_count = 1
3131 for i = 1 , idx - 1 do
4545--- @field max_depth number ?
4646
4747--- @class HjsonDecoder
48- --- @field decode fun ( self : HjsonDecoder , s : string ): any
48+ --- @field decode fun ( self : HjsonDecoder , s : string ): any , string ?, boolean
4949
5050local HjsonDecoder = {}
5151--- Hjson decoder
@@ -160,7 +160,7 @@ function HjsonDecoder:new(options)
160160 elseif n <= 0x10ffff then
161161 return string.char (f (n / 262144 ) + 240 , f (n % 262144 / 4096 ) + 128 , f (n % 4096 / 64 ) + 128 , n % 64 + 128 )
162162 end
163- decodeError (s , _end , string.format (" invalid unicode codepoint '%x'" , n ))
163+ decode_error (s , _end , string.format (" invalid unicode codepoint '%x'" , n ))
164164 end
165165
166166 local function parseString (s , _end )
@@ -176,7 +176,7 @@ function HjsonDecoder:new(options)
176176 local function scan_string ()
177177 local content , terminator = s :match (' (.-)([\' "\\ %z\001 -\031 ])' , _end )
178178 if not content then
179- decodeError (s , begin , " Unterminated string" )
179+ decode_error (s , begin , " Unterminated string" )
180180 end
181181
182182 _end = _end + # content + # terminator
@@ -189,22 +189,22 @@ function HjsonDecoder:new(options)
189189 return -- continue
190190 elseif terminator ~= " \\ " then
191191 if options .strict then
192- decodeError (s , begin , " Invalid control character " .. terminator )
192+ decode_error (s , begin , " Invalid control character " .. terminator )
193193 else
194194 chunks = chunks .. terminator
195195 return -- continue
196196 end
197197 end
198198
199199 if # s < _end then
200- decodeError (s , _end , " Unterminated string" )
200+ decode_error (s , _end , " Unterminated string" )
201201 end
202202
203203 local chars
204204 local esc = charAt (s , _end )
205205 if esc ~= " u" then
206206 if not esc or not BACKSLASH [esc ] then
207- decodeError (s , _end , " Invalid \\ X escape sequence" )
207+ decode_error (s , _end , " Invalid \\ X escape sequence" )
208208 end
209209 chars = BACKSLASH [esc ]
210210 _end = _end + 1
@@ -213,7 +213,7 @@ function HjsonDecoder:new(options)
213213 local msg = " Invalid \\ uXXXX escape sequence"
214214 esc = s :sub (_end + 1 , _end + 4 )
215215 if # esc ~= 4 or not esc :find (" %x%x%x%x" ) then
216- decodeError (s , _end - 1 , msg )
216+ decode_error (s , _end - 1 , msg )
217217 end
218218 _end = _end + 5
219219 if esc :find (" ^[dD][89aAbB]" ) and s :sub (_end , _end + 1 ) == " \\ u" then
@@ -259,7 +259,7 @@ function HjsonDecoder:new(options)
259259 local function scan_mlstring ()
260260 ch = charAt (s , _end )
261261 if ch == " " then
262- decodeError (s , _end , " Bad multiline string" )
262+ decode_error (s , _end , " Bad multiline string" )
263263 end
264264 if ch == " '" then
265265 triple = triple + 1
@@ -302,7 +302,7 @@ function HjsonDecoder:new(options)
302302 local chf , begin = getNext (s , _end )
303303 _end = begin
304304 if PUNCTUATOR :find (chf , 1 , true ) then
305- decodeError (s , _end , " Found a punctuator character when expecting a quoteless string (check your syntax)" )
305+ decode_error (s , _end , " Found a punctuator character when expecting a quoteless string (check your syntax)" )
306306 end
307307
308308 while true do
@@ -359,14 +359,14 @@ function HjsonDecoder:new(options)
359359 while true do
360360 ch = charAt (s , _end )
361361 if ch == " " then
362- decodeError (s , _end , " Bad key name (eof)" )
362+ decode_error (s , _end , " Bad key name (eof)" )
363363 end
364364 if ch == " :" then
365365 if begin == _end then
366- decodeError (s , begin , " Found ':' but no key name (for an empty key name use quotes)" )
366+ decode_error (s , begin , " Found ':' but no key name (for an empty key name use quotes)" )
367367 elseif space >= 0 then
368368 if space ~= _end - 1 then
369- decodeError (s , _end , " Found whitespace in your key name (use quotes to include)" )
369+ decode_error (s , _end , " Found whitespace in your key name (use quotes to include)" )
370370 end
371371 return trim (s :sub (begin , _end - 1 )), _end
372372 else
@@ -377,7 +377,7 @@ function HjsonDecoder:new(options)
377377 space = _end
378378 end
379379 elseif ch == " {" or ch == " }" or ch == " [" or ch == " ]" or ch == " ," then
380- decodeError (
380+ decode_error (
381381 s ,
382382 begin ,
383383 " Found '" ..
@@ -441,7 +441,7 @@ function HjsonDecoder:new(options)
441441
442442 ch , _end = getNext (s , _end )
443443 if ch ~= " :" then
444- decodeError (s , _end , " Expecting ':' delimiter" )
444+ decode_error (s , _end , " Expecting ':' delimiter" )
445445 end
446446
447447 ch , _end = getNext (s , _end + 1 )
@@ -485,7 +485,7 @@ function HjsonDecoder:new(options)
485485 ch , _end = getNext (s , _end )
486486
487487 if ch == " " then
488- decodeError (s , _end , " End of input while parsing an array (did you forget a closing ']'?)" )
488+ decode_error (s , _end , " End of input while parsing an array (did you forget a closing ']'?)" )
489489 end
490490 -- Look-ahead for trivial empty array
491491 if ch == " ]" then
@@ -517,12 +517,12 @@ function HjsonDecoder:new(options)
517517 end
518518 depth = depth + 1
519519 if depth > options .max_depth then
520- decodeError (string , idx , " Exceeded max depth" )
520+ decode_error (string , idx , " Exceeded max depth" )
521521 end
522522
523523 local ch = charAt (string , idx )
524524 if not ch then
525- decodeError (string , idx , " Expecting value" )
525+ decode_error (string , idx , " Expecting value" )
526526 end
527527
528528 if ch == ' "' or ch == " '" then
@@ -541,7 +541,7 @@ function HjsonDecoder:new(options)
541541
542542 local function scan_once (string , idx )
543543 if idx <= 0 then
544- decodeError (string , idx , " Expecting value" )
544+ decode_error (string , idx , " expecting value" )
545545 end
546546 local status , result , _end = pcall (_scan_once , string , idx )
547547 memo = {}
@@ -554,7 +554,7 @@ function HjsonDecoder:new(options)
554554
555555 local function scan_object_once (string , idx )
556556 if idx <= 0 then
557- decodeError (string , idx , " Expecting value" )
557+ decode_error (string , idx , " expecting value" )
558558 end
559559 local status , result , _end = pcall (parse_object , {s = string , _end = idx }, _scan_once , true )
560560 memo = {}
@@ -577,29 +577,39 @@ function HjsonDecoder:new(options)
577577 return hd
578578end
579579
580+ --- @param s any
581+ --- @return any , string ?, boolean
580582function HjsonDecoder :decode (s )
581583 --[[
582584 Returns the Lua representation of ``s`` (a ``ascii`` or ``utf-8`` string
583585 instance containing a JSON document)
584586 ]]
585- local ch
586- local obj , _end = self :raw_decode (s )
587- ch , _end = self .get_next (s , _end )
588- if _end ~= # s + 1 then
589- decodeError (s , _end , " Extra data" )
587+ assert (type (s ) == " string" , " HjsonDecoder:decode expects a string as input" )
588+
589+ local status , objOrError = pcall (function ()
590+ local obj , _end = self :__raw_decode (s )
591+ _ , _end = self .get_next (s , _end )
592+ if _end ~= # s + 1 then
593+ decode_error (s , _end , " extra data" )
594+ end
595+ return obj
596+ end )
597+
598+ if not status then
599+ return nil , objOrError , false
590600 end
591601
592- return obj
602+ return objOrError , nil , true
593603end
594604
595- function HjsonDecoder :raw_decode (s , idx )
605+ function HjsonDecoder :__raw_decode (s , idx )
596606 local ch
597607 if idx == nil then
598608 idx = 1
599609 end
600610
601611 if idx <= 0 then
602- decodeError (s , idx , " Expecting value" )
612+ decode_error (s , idx , " expecting value" )
603613 end
604614
605615 -- Strip UTF-8 bom
0 commit comments