Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
|
d56e6dceea | |
|
1a24065c72 | |
|
4d080bacd8 | |
|
dd1c8ad1ba | |
|
8221b4c151 | |
|
ce8fe5c99b | |
|
dde7df8320 | |
|
1f6c89684f | |
|
8cbbdd94cb | |
|
bf41349ad0 | |
|
81adf6af0a | |
|
a88f703367 |
|
@ -0,0 +1,29 @@
|
|||
|
||||
name: Publish PHP Package
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Get tag name
|
||||
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: zip and send
|
||||
run: |
|
||||
ls -la
|
||||
apt-get update -y
|
||||
apt-get install -y zip
|
||||
cd ../
|
||||
zip -r ${{ gitea.event.repository.name }}.zip ${{ gitea.event.repository.name }} -x '*.git*'
|
||||
curl -v \
|
||||
--user composer-pbl:${{ secrets.COMPOSER_PSWD }} \
|
||||
--upload-file ${{ gitea.event.repository.name }}.zip \
|
||||
https://git.bit5.ru/api/packages/bit/composer?version=${{ env.TAG }}
|
|
@ -0,0 +1 @@
|
|||
tags
|
|
@ -0,0 +1,8 @@
|
|||
## v1.0.8
|
||||
|
||||
- Parse number as floating-point if it doesn't fit in integer; trying hard to
|
||||
use integer when possible though to not lose precision
|
||||
|
||||
## v1.0.7
|
||||
|
||||
- Removing slashes which escape quotes inside strings
|
63
README.md
63
README.md
|
@ -0,0 +1,63 @@
|
|||
# PHP JZON #
|
||||
|
||||
This is a small PHP library for parsing JZON documents.
|
||||
|
||||
|
||||
## What is JZON? ##
|
||||
|
||||
JZON is a *superset* of JSON which is designed to be actively edited by *humans*.
|
||||
|
||||
PHP JZON features:
|
||||
|
||||
* no need to worry about trailing commas
|
||||
|
||||
* one line comments starting with #
|
||||
|
||||
* simple array keys don't need to be quoted with ""
|
||||
|
||||
* parsing errors are shown in a friendly manner
|
||||
|
||||
### Quick Example ###
|
||||
|
||||
Say, you have the following JZON file example.json:
|
||||
|
||||
```
|
||||
["foo",
|
||||
"bar",
|
||||
{meaning_of_life: 42,
|
||||
#NOTE: no comma ahead
|
||||
# and yes comments are supported!
|
||||
hey: "bar"
|
||||
"thatscool": 1
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Now you can parse it this way:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
$str = file_get_contents('example.json');
|
||||
var_dump(jzon_parse($str));
|
||||
```
|
||||
|
||||
And it should output something as follows:
|
||||
|
||||
```
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
[2]=>
|
||||
array(3) {
|
||||
["thatscool"]=>
|
||||
int(1)
|
||||
["meaning_of_life"]=>
|
||||
int(42)
|
||||
["hey"]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
```
|
10
json.inc.php
10
json.inc.php
|
@ -105,7 +105,7 @@ class JsonParser
|
|||
|
||||
/**
|
||||
* @param string $input JSON string
|
||||
* @return null|ParsingException null if no error is found, a ParsingException containing all details otherwise
|
||||
* @return null|JsonParsingException null if no error is found, a ParsingException containing all details otherwise
|
||||
*/
|
||||
public function lint($input)
|
||||
{
|
||||
|
@ -114,12 +114,13 @@ class JsonParser
|
|||
} catch (JsonParsingException $e) {
|
||||
return $e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input JSON string
|
||||
* @return mixed
|
||||
* @throws ParsingException
|
||||
* @throws JsonParsingException
|
||||
*/
|
||||
public function parse($input, $flags = 0)
|
||||
{
|
||||
|
@ -289,7 +290,7 @@ class JsonParser
|
|||
'first_column' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_column'],
|
||||
'last_column' => $this->lstack[count($this->lstack) - 1]['last_column'],
|
||||
);
|
||||
$r = $this->performAction($yyval, $yytext, $yyleng, $yylineno, $action[1], $this->vstack, $this->lstack);
|
||||
$r = $this->performAction($yyval, $yytext, $yyleng, $yylineno, $action[1], $this->vstack);
|
||||
|
||||
if (!$r instanceof JsonUndefined) {
|
||||
return $r;
|
||||
|
@ -311,8 +312,6 @@ class JsonParser
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function parseError($str, $hash)
|
||||
|
@ -664,7 +663,6 @@ class JsonLexer
|
|||
break;
|
||||
case 1:
|
||||
return 6;
|
||||
break;
|
||||
case 2:
|
||||
$this->yytext = substr($this->yytext, 1, $this->yyleng-2);
|
||||
|
||||
|
|
75
jzon.inc.php
75
jzon.inc.php
|
@ -6,29 +6,44 @@ if(!defined('JZON_VERSION'))
|
|||
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));
|
||||
$c = str_repeat('-', max(0, strlen($pre) - 1));
|
||||
$out = array();
|
||||
$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;
|
||||
}
|
||||
|
||||
function jzon_get_past_input($c, $in, $context_chars)
|
||||
{
|
||||
$past = substr($in, 0, $c+1);
|
||||
return (strlen($past) > $context_chars ? '...' : '') . substr($past, -$context_chars);
|
||||
$p = $left;
|
||||
}
|
||||
if(count($out) > $context_lines)
|
||||
$out = array_slice($out, -$context_lines);
|
||||
|
||||
function jzon_get_upcoming_input($c, $in, $context_chars)
|
||||
{
|
||||
$next = substr($in, $c+1);
|
||||
return substr($next, 0, $context_chars) . (strlen($next) > $context_chars ? '...' : '');
|
||||
//let's add line numbers
|
||||
end($out);
|
||||
//let's find out the maximum leading zeros for line numbers
|
||||
$fmt_num = (int)round(log10(key($out)))+1;
|
||||
foreach($out as $idx => $line)
|
||||
$out[$idx] = sprintf('%0'.$fmt_num.'d', ($idx+1)) . ' ' . $line;
|
||||
|
||||
return implode("\n", $out);
|
||||
}
|
||||
|
||||
class jzonParser
|
||||
{
|
||||
const ERR_CONTEXT_CHARS = 200;
|
||||
const ERR_CONTEXT_LINES = 5;
|
||||
|
||||
private $in;
|
||||
private $len;
|
||||
|
@ -86,9 +101,9 @@ class jzonParser
|
|||
private function _error($error)
|
||||
{
|
||||
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
|
||||
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()
|
||||
|
@ -249,7 +264,9 @@ class jzonParser
|
|||
{
|
||||
$end = $this->c;
|
||||
++$this->c;
|
||||
return substr($this->in, $start, $end - $start);
|
||||
$str = substr($this->in, $start, $end - $start);
|
||||
//let's remove escape slashes before "
|
||||
return str_replace('\\"', '"', $str);
|
||||
}
|
||||
$prev = $this->in[$this->c];
|
||||
++$this->c;
|
||||
|
@ -290,10 +307,17 @@ class jzonParser
|
|||
++$this->c;
|
||||
}
|
||||
|
||||
$str_num = substr($this->in, $start, $this->c - $start);
|
||||
|
||||
$fval = floatval($str_num);
|
||||
$ival = intval($str_num);
|
||||
|
||||
if($is_float)
|
||||
$out = 1*substr($this->in, $start, $this->c - $start);
|
||||
$out = $fval;
|
||||
else if(($ival < PHP_INT_MAX && $ival > PHP_INT_MIN) || strval($ival) === $str_num)
|
||||
$out = $ival;
|
||||
else
|
||||
$out = (int)(1*substr($this->in, $start, $this->c - $start));
|
||||
$out = $fval;
|
||||
}
|
||||
|
||||
private function parse_true(&$out)
|
||||
|
@ -328,28 +352,31 @@ class jzonParser
|
|||
else
|
||||
$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
|
||||
// more relaxed implementation
|
||||
$parser = 1;
|
||||
$res = json_decode($str, true);
|
||||
if(is_array($res))
|
||||
return $res;
|
||||
|
||||
//NOTE: only allowing extension implementation if versions match
|
||||
if(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);
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
$parser = 3;
|
||||
$p = new jzonParser($str);
|
||||
return $p->parse();
|
||||
$res = $p->parse();
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue