Skip to content

Commit 602b46e

Browse files
authored
Merge pull request #21 from hjson/fix-18
Add support for single-quoted strings and fix stringifying whitespace
2 parents 97a35e9 + 890f034 commit 602b46e

15 files changed

Lines changed: 150 additions & 12 deletions

src/HJSON/HJSONParser.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function __construct()
1515
{
1616
$this->escapee = [
1717
'"' => '"',
18+
'\'' => '\'',
1819
"\\" => "\\",
1920
'/' => '/',
2021
'b' => chr(8),
@@ -99,23 +100,28 @@ private function value()
99100
case '[':
100101
return $this->_array();
101102
case '"':
102-
return $this->string();
103+
return $this->string('"');
104+
case '\'':
105+
if ($this->peek(0) !== '\'' || $this->peek(1) !== '\'') {
106+
return $this->string('\'');
107+
}
108+
// Falls through on multiline strings
103109
default:
104110
return $this->tfnns();
105111
}
106112
}
107113

108-
private function string()
114+
private function string($quote)
109115
{
110116
// Parse a string value.
111117
$hex;
112118
$string = '';
113119
$uffff;
114120

115121
// When parsing for string values, we must look for " and \ characters.
116-
if ($this->ch === '"') {
122+
if ($this->ch === $quote) {
117123
while ($this->next() !== null) {
118-
if ($this->ch === '"') {
124+
if ($this->ch === $quote) {
119125
$this->next();
120126
return $string;
121127
}
@@ -410,7 +416,9 @@ private function keyname()
410416
// unless they include {}[],: or whitespace.
411417

412418
if ($this->ch === '"') {
413-
return $this->string();
419+
return $this->string('"');
420+
} else if ($this->ch === '\'') {
421+
return $this->string('\'');
414422
}
415423

416424
$name = "";

src/HJSON/HJSONStringifier.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
namespace HJSON;
44

5+
/**
6+
* NOTE: this may return an empty string at the end of the array when the input
7+
* string ends with a newline character
8+
*/
59
function mb_str_split($string)
610
{
7-
return preg_split('/(?<!^)(?!$)/u', $string);
11+
return preg_split('/(?<!^)/u', $string);
812
}
913

1014
class HJSONStringifier
@@ -13,11 +17,11 @@ class HJSONStringifier
1317
// needsEscape tests if the string can be written without escapes
1418
private $needsEscape = '/[\\\"\x00-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}\x]/u';
1519
// needsQuotes tests if the string can be written as a quoteless string (includes needsEscape but without \\ and \")
16-
private $needsQuotes = '/^\\s|^"|^\'\'\'|^#|^\\/\\*|^\\/\\/|^\\{|^\\}|^\\[|^\\]|^:|^,|\\s$|[\x00-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}\x]/u';
20+
private $needsQuotes = '/^\\s|^"|^\'|^\'\'\'|^#|^\\/\\*|^\\/\\/|^\\{|^\\}|^\\[|^\\]|^:|^,|\\s$|[\x00-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}\x]/u';
1721
// needsEscapeML tests if the string can be written as a multiline string (includes needsEscape but without \n, \r, \\ and \")
18-
private $needsEscapeML = '/\'\'\'|[\x00-\x09\x0b\x0c\x0e-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}\x]/u';
22+
private $needsEscapeML = '/^\\s+$|\'\'\'|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}\x]/u';
1923
private $startsWithKeyword = '/^(true|false|null)\s*((,|\]|\}|#|\/\/|\/\*).*)?$/';
20-
private $needsEscapeName = '/[,\{\[\}\]\s:#"]|\/\/|\/\*|\'\'\'/';
24+
private $needsEscapeName = '/[,\{\[\}\]\s:#"\']|\/\/|\/\*|\'\'\'/';
2125
private $gap = '';
2226
private $indent = ' ';
2327

@@ -38,6 +42,7 @@ public function __construct()
3842
"\n" => "\\n",
3943
"\r" => "\\r",
4044
'"' => '\\"',
45+
'\'' => '\\\'',
4146
'\\' => "\\\\"
4247
];
4348
$this->meta[chr(8)] = '\\b';

tests/assets/failJSON24_test.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/assets/failKey5_test.hjson

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
# invalid name
3+
'''foo''': 0
4+
}

tests/assets/failStr8a_test.hjson

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
# invalid ml-string
3+
foo : ""'text'''
4+
}

tests/assets/keys_result.hjson

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
"foo\"bar": test
1919
"'''": test
2020
"foo'''bar": test
21+
"'": test
22+
"'foo": test
23+
"foo'bar": test
2124
":": test
2225
"foo:bar": test
2326
"{": test

tests/assets/keys_result.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
"foo\"bar": "test",
1919
"'''": "test",
2020
"foo'''bar": "test",
21+
"'": "test",
22+
"'foo": "test",
23+
"foo'bar": "test",
2124
":": "test",
2225
"foo:bar": "test",
2326
"{": "test",

tests/assets/keys_test.hjson

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"foo\"bar": test
2323
"'''": test
2424
"foo'''bar": test
25+
"'": test
26+
"'foo": test
27+
"foo'bar": test
2528
# control char in key name
2629
":": test
2730
"foo:bar": test

tests/assets/strings2_result.hjson

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
key1: a key in single quotes
3+
"key 2": a key in single quotes
4+
"key \"": a key in single quotes
5+
text:
6+
[
7+
single quoted string
8+
'''You need quotes for escapes'''
9+
" untrimmed "
10+
"untrimmed "
11+
containing " double quotes
12+
containing " double quotes
13+
containing " double quotes
14+
'''"containing more " double quotes"'''
15+
containing ' single quotes
16+
containing ' single quotes
17+
containing ' single quotes
18+
"'containing more ' single quotes'"
19+
"'containing more ' single quotes'"
20+
"\n"
21+
" \n"
22+
"\n \n \n \n"
23+
"\t\n"
24+
]
25+
foo3a: asdf'''
26+
foo3b: "'''asdf"
27+
foo4a: "asdf'''\nasdf"
28+
foo4b: "asdf\n'''asdf"
29+
}

tests/assets/strings2_result.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"key1": "a key in single quotes",
3+
"key 2": "a key in single quotes",
4+
"key \"": "a key in single quotes",
5+
"text": [
6+
"single quoted string",
7+
"You need quotes\tfor escapes",
8+
" untrimmed ",
9+
"untrimmed ",
10+
"containing \" double quotes",
11+
"containing \" double quotes",
12+
"containing \" double quotes",
13+
"\"containing more \" double quotes\"",
14+
"containing ' single quotes",
15+
"containing ' single quotes",
16+
"containing ' single quotes",
17+
"'containing more ' single quotes'",
18+
"'containing more ' single quotes'",
19+
"\n",
20+
" \n",
21+
"\n \n \n \n",
22+
"\t\n"
23+
],
24+
"foo3a": "asdf'''",
25+
"foo3b": "'''asdf",
26+
"foo4a": "asdf'''\nasdf",
27+
"foo4b": "asdf\n'''asdf"
28+
}

0 commit comments

Comments
 (0)