Compare commits

...

3 Commits

Author SHA1 Message Date
Pavel Shevaev d56e6dceea Improving line error reporter
Publish PHP Package / docker (push) Successful in 7s Details
2025-02-28 01:15:44 +03:00
Pavel Shevaev 1a24065c72 Fixing minor bugs in parser error line reporter
Publish PHP Package / docker (push) Failing after 3s Details
2025-02-26 09:52:24 +03:00
Pavel Shevaev 4d080bacd8 Better parsing error reporting
Publish PHP Package / docker (push) Successful in 6s Details
2025-02-25 20:03:53 +03:00
1 changed files with 37 additions and 20 deletions

View File

@ -6,29 +6,44 @@ if(!defined('JZON_VERSION'))
define('JZON_EXT_VERSION', phpversion('jzon')); define('JZON_EXT_VERSION', phpversion('jzon'));
} }
function jzon_show_position($p, $in, $context_chars) function jzon_show_position(int $p, string $in, int $context_lines = 5) : string
{ {
$pre = str_replace("\n", '', jzon_get_past_input($p, $in, $context_chars)); $out = array();
$c = str_repeat('-', max(0, strlen($pre) - 1)); $lines = explode("\n", $in);
foreach($lines as $line_idx => $line)
{
//normalizing tabs by converting them to spaces
$out[] = str_replace("\t", " ", $line);
$left = $p - (strlen($line) + 1/*taking into account \n*/);
if($left <= 0)
{
$arrow = '';
//let's take into account tabs
for($i=0;$i<$p;++$i)
$arrow .= ($line[$i] === "\t" ? '----' : '-');
$arrow .= '^';
return $pre . str_replace("\n", '', jzon_get_upcoming_input($p, $in, $context_chars)) . "\n" . $c . "^"; $out[] = $arrow;
} break;
}
$p = $left;
}
if(count($out) > $context_lines)
$out = array_slice($out, -$context_lines);
function jzon_get_past_input($c, $in, $context_chars) //let's add line numbers
{ end($out);
$past = substr($in, 0, $c+1); //let's find out the maximum leading zeros for line numbers
return (strlen($past) > $context_chars ? '...' : '') . substr($past, -$context_chars); $fmt_num = (int)round(log10(key($out)))+1;
} foreach($out as $idx => $line)
$out[$idx] = sprintf('%0'.$fmt_num.'d', ($idx+1)) . ' ' . $line;
function jzon_get_upcoming_input($c, $in, $context_chars) return implode("\n", $out);
{
$next = substr($in, $c+1);
return substr($next, 0, $context_chars) . (strlen($next) > $context_chars ? '...' : '');
} }
class jzonParser class jzonParser
{ {
const ERR_CONTEXT_CHARS = 200; const ERR_CONTEXT_LINES = 5;
private $in; private $in;
private $len; private $len;
@ -86,9 +101,9 @@ class jzonParser
private function _error($error) private function _error($error)
{ {
if($this->c < $this->len) if($this->c < $this->len)
throw new Exception("Parse error: $error\n" . jzon_show_position($this->c, $this->in, self::ERR_CONTEXT_CHARS)); throw new Exception("Parse error: $error\n" . jzon_show_position($this->c, $this->in, self::ERR_CONTEXT_LINES));
else else
throw new Exception("Parse error: $error\n" . jzon_show_position($this->len-1, $this->in, self::ERR_CONTEXT_CHARS)); throw new Exception("Parse error: $error\n" . jzon_show_position($this->len-1, $this->in, self::ERR_CONTEXT_LINES));
} }
private function skip_whitespace() private function skip_whitespace()
@ -337,13 +352,13 @@ class jzonParser
else else
$this->_error("'null' expected"); $this->_error("'null' expected");
} }
} }
function jzon_parse($str) function jzon_parse($str, ?int &$parser = null) : array
{ {
//NOTE: using super fast built-in implementation and making a gracefull fallback to a slower and //NOTE: using super fast built-in implementation and making a gracefull fallback to a slower and
// more relaxed implementation // more relaxed implementation
$parser = 1;
$res = json_decode($str, true); $res = json_decode($str, true);
if(is_array($res)) if(is_array($res))
return $res; return $res;
@ -351,13 +366,15 @@ function jzon_parse($str)
//NOTE: only allowing extension implementation if versions match //NOTE: only allowing extension implementation if versions match
if(defined('JZON_EXT_VERSION') && JZON_EXT_VERSION === JZON_VERSION) if(defined('JZON_EXT_VERSION') && JZON_EXT_VERSION === JZON_VERSION)
{ {
$parser = 2;
list($ok, $err, $err_pos, $res) = jzon_parse_c($str); list($ok, $err, $err_pos, $res) = jzon_parse_c($str);
if(!$ok) if(!$ok)
throw new Exception($err . "\n" . jzon_show_position($err_pos, $str, jzonParser::ERR_CONTEXT_CHARS)); throw new Exception($err . "\n" . jzon_show_position($err_pos, $str, jzonParser::ERR_CONTEXT_LINES));
return $res; return $res;
} }
else else
{ {
$parser = 3;
$p = new jzonParser($str); $p = new jzonParser($str);
$res = $p->parse(); $res = $p->parse();
return $res; return $res;