Skip to content

Commit

Permalink
add support for single quoted strings
Browse files Browse the repository at this point in the history
see #5
  • Loading branch information
laktak committed Jul 15, 2017
1 parent 85f065e commit d5d40e7
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 30 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2016 Christian Zangl
Copyright (c) 2016, 2017 Christian Zangl

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
14 changes: 8 additions & 6 deletions assets/charset_test.hjson
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
ql-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
js-ascii: "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
ml-ascii:
'''
! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
'''
{
ql-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
js-ascii: "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
ml-ascii:
'''
! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
'''
}
3 changes: 3 additions & 0 deletions assets/keys_result.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"foo\"bar": test
"'''": test
"foo'''bar": test
"'": test
"'foo": test
"foo'bar": test
":": test
"foo:bar": test
"{": test
Expand Down
3 changes: 3 additions & 0 deletions assets/keys_result.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"foo\"bar": "test",
"'''": "test",
"foo'''bar": "test",
"'": "test",
"'foo": "test",
"foo'bar": "test",
":": "test",
"foo:bar": "test",
"{": "test",
Expand Down
3 changes: 3 additions & 0 deletions assets/keys_test.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
"foo\"bar": test
"'''": test
"foo'''bar": test
"'": test
"'foo": test
"foo'bar": test
# control char in key name
":": test
"foo:bar": test
Expand Down
3 changes: 3 additions & 0 deletions assets/sorted/keys_result.hjson
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"\"": test
"#c1": test
"'": test
"'''": test
"'foo": test
-test: test
.key: test
"/*": test
Expand All @@ -14,6 +16,7 @@
"foo\"bar": test
"foo#bar": test
"foo'''bar": test
"foo'bar": test
"foo/*bar": test
"foo/*foo*/bar": test
"foo//bar": test
Expand Down
3 changes: 3 additions & 0 deletions assets/sorted/keys_result.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"\"": "test",
"#c1": "test",
"'": "test",
"'''": "test",
"'foo": "test",
"-test": "test",
".key": "test",
"/*": "test",
Expand All @@ -14,6 +16,7 @@
"foo\"bar": "test",
"foo#bar": "test",
"foo'''bar": "test",
"foo'bar": "test",
"foo/*bar": "test",
"foo/*foo*/bar": "test",
"foo//bar": "test",
Expand Down
29 changes: 29 additions & 0 deletions assets/sorted/strings2_result.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
foo3a: asdf'''
foo3b: "'''asdf"
foo4a: "asdf'''\nasdf"
foo4b: "asdf\n'''asdf"
"key \"": a key in single quotes
"key 2": a key in single quotes
key1: a key in single quotes
text:
[
single quoted string
'''You need quotes for escapes'''
" untrimmed "
"untrimmed "
containing " double quotes
containing " double quotes
containing " double quotes
'''"containing more " double quotes"'''
containing ' single quotes
containing ' single quotes
containing ' single quotes
"'containing more ' single quotes'"
"'containing more ' single quotes'"
"\n"
" \n"
"\n \n \n \n"
"\t\n"
]
}
28 changes: 28 additions & 0 deletions assets/sorted/strings2_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"foo3a": "asdf'''",
"foo3b": "'''asdf",
"foo4a": "asdf'''\nasdf",
"foo4b": "asdf\n'''asdf",
"key \"": "a key in single quotes",
"key 2": "a key in single quotes",
"key1": "a key in single quotes",
"text": [
"single quoted string",
"You need quotes\tfor escapes",
" untrimmed ",
"untrimmed ",
"containing \" double quotes",
"containing \" double quotes",
"containing \" double quotes",
"\"containing more \" double quotes\"",
"containing ' single quotes",
"containing ' single quotes",
"containing ' single quotes",
"'containing more ' single quotes'",
"'containing more ' single quotes'",
"\n",
" \n",
"\n \n \n \n",
"\t\n"
]
}
29 changes: 29 additions & 0 deletions assets/strings2_result.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
key1: a key in single quotes
"key 2": a key in single quotes
"key \"": a key in single quotes
text:
[
single quoted string
'''You need quotes for escapes'''
" untrimmed "
"untrimmed "
containing " double quotes
containing " double quotes
containing " double quotes
'''"containing more " double quotes"'''
containing ' single quotes
containing ' single quotes
containing ' single quotes
"'containing more ' single quotes'"
"'containing more ' single quotes'"
"\n"
" \n"
"\n \n \n \n"
"\t\n"
]
foo3a: asdf'''
foo3b: "'''asdf"
foo4a: "asdf'''\nasdf"
foo4b: "asdf\n'''asdf"
}
28 changes: 28 additions & 0 deletions assets/strings2_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"key1": "a key in single quotes",
"key 2": "a key in single quotes",
"key \"": "a key in single quotes",
"text": [
"single quoted string",
"You need quotes\tfor escapes",
" untrimmed ",
"untrimmed ",
"containing \" double quotes",
"containing \" double quotes",
"containing \" double quotes",
"\"containing more \" double quotes\"",
"containing ' single quotes",
"containing ' single quotes",
"containing ' single quotes",
"'containing more ' single quotes'",
"'containing more ' single quotes'",
"\n",
" \n",
"\n \n \n \n",
"\t\n"
],
"foo3a": "asdf'''",
"foo3b": "'''asdf",
"foo4a": "asdf'''\nasdf",
"foo4b": "asdf\n'''asdf"
}
36 changes: 36 additions & 0 deletions assets/strings2_test.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
# Hjson 3 allows the use of single quotes

'key1': a key in single quotes
'key 2': a key in single quotes
'key "': a key in single quotes

text: [
'single quoted string'
'You need quotes\tfor escapes'
' untrimmed '
'untrimmed '
'containing " double quotes'
'containing \" double quotes'
"containing \" double quotes"
'"containing more " double quotes"'
'containing \' single quotes'
"containing ' single quotes"
"containing \' single quotes"
"'containing more ' single quotes'"
"\'containing more \' single quotes\'"

'\n'
' \n'
'\n \n \n \n'
'\t\n'
]

# escapes/no escape

foo3a: 'asdf\'\'\''
foo3b: '\'\'\'asdf'

foo4a: 'asdf\'\'\'\nasdf'
foo4b: 'asdf\n\'\'\'asdf'
}
4 changes: 2 additions & 2 deletions assets/testlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ failJSON20_test.json
failJSON21_test.json
failJSON22_test.json
failJSON23_test.json
failJSON24_test.json
failJSON26_test.json
failJSON28_test.json
failJSON29_test.json
Expand Down Expand Up @@ -71,8 +70,8 @@ pass2_test.json
pass3_test.json
pass4_test.json
passSingle_test.hjson
root_test.hjson
stringify1_test.hjson
strings2_test.hjson
strings_test.hjson
trail_test.hjson
stringify/quotes_all_test.hjson
Expand All @@ -81,4 +80,5 @@ stringify/quotes_keys_test.hjson
stringify/quotes_strings_ml_test.json
stringify/quotes_strings_test.hjson
extra/notabs_test.json
extra/root_test.hjson
extra/separator_test.json
6 changes: 4 additions & 2 deletions assets/trail_test.hjson
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
// the following line contains trailing whitespace:
foo: 0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1
{
// the following line contains trailing whitespace:
foo: 0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1
}
33 changes: 16 additions & 17 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func (p *hjsonParser) peek(offs int) byte {

var escapee = map[byte]byte{
'"': '"',
'\'': '\'',
'\\': '\\',
'/': '/',
'b': '\b',
Expand All @@ -72,19 +73,24 @@ var escapee = map[byte]byte{
't': '\t',
}

func (p *hjsonParser) readString() (string, error) {
func (p *hjsonParser) readString(allowML bool) (string, error) {

// Parse a string value.
res := new(bytes.Buffer)

// callers make sure that (ch === '"' || ch === "'")
// When parsing for string values, we must look for " and \ characters.
if p.ch != '"' {
return "", p.errAt("Bad string")
}
exitCh := p.ch
for p.next() {
if p.ch == '"' {
if p.ch == exitCh {
p.next()
return res.String(), nil
if allowML && p.ch == '\'' && res.Len() == 0 {
// ''' indicates a multiline string
p.next()
return p.readMLString()
} else {
return res.String(), nil
}
}
if p.ch == '\\' {
p.next()
Expand Down Expand Up @@ -195,8 +201,8 @@ func (p *hjsonParser) readKeyname() (string, error) {
// quotes for keys are optional in Hjson
// unless they include {}[],: or whitespace.

if p.ch == '"' {
return p.readString()
if p.ch == '"' || p.ch == '\'' {
return p.readString(false)
}

name := new(bytes.Buffer)
Expand Down Expand Up @@ -264,13 +270,6 @@ func (p *hjsonParser) readTfnns() (interface{}, error) {
return nil, p.errAt("Found a punctuator character '" + string(p.ch) + "' when expecting a quoteless string (check your syntax)")
}
chf := p.ch
if chf == '\'' && p.peek(0) == '\'' && p.peek(1) == '\'' {
p.next()
p.next()
p.next()
return p.readMLString()
}

value := new(bytes.Buffer)
value.WriteByte(p.ch)

Expand Down Expand Up @@ -407,8 +406,8 @@ func (p *hjsonParser) readValue() (interface{}, error) {
return p.readObject(false)
case '[':
return p.readArray()
case '"':
return p.readString()
case '"', '\'':
return p.readString(true)
default:
return p.readTfnns()
}
Expand Down
4 changes: 2 additions & 2 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ func init() {
// needsEscape tests if the string can be written without escapes
needsEscape = regexp.MustCompile(`[\\\"\x00-\x1f` + commonRange + `]`)
// needsQuotes tests if the string can be written as a quoteless string (includes needsEscape but without \\ and \")
needsQuotes = regexp.MustCompile(`^\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}]`)
needsQuotes = regexp.MustCompile(`^\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}]`)
// needsEscapeML tests if the string can be written as a multiline string (like needsEscape but without \n, \r, \\, \", \t)
var x08Or9 = `\x08` // `\x09` for the old behavior
needsEscapeML = regexp.MustCompile(`'''|^[\s]+$|[\x00-` + x08Or9 + `\x0b\x0c\x0e-\x1f` + commonRange + `]`)
// starts with a keyword and optionally is followed by a comment
startsWithKeyword = regexp.MustCompile(`^(true|false|null)\s*((,|\]|\}|#|//|/\*).*)?$`)
needsEscapeName = regexp.MustCompile(`[,\{\[\}\]\s:#"]|//|/\*|'''`)
needsEscapeName = regexp.MustCompile(`[,\{\[\}\]\s:#"']|//|/\*`)
}

var meta = map[byte][]byte{
Expand Down

0 comments on commit d5d40e7

Please sign in to comment.