json-next 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a4bc912a3a1cfbfee8f003ec071192735ec5f638
4
+ data.tar.gz: 493c00f3d34dc0c3f868f28d52e254b07686b4c5
5
+ SHA512:
6
+ metadata.gz: 717d9c8b9bdc8f63fb8abbc2de0e76b6d1355bb17edf30702c61449ff5ce9f739d1f9f3bac5c11eeb169d2488a04ef17f9624c63fdb1e614d713ab7bbdb19b1a
7
+ data.tar.gz: a57f3d1a612907827b3ae7decbff6bd55d963f214499a551fe166d6a427029d02f5a0ceee7b28ded0ee269a31282b14ca731beae9f007174ae6f7197626f1cb2
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2017-07-06
2
+
3
+ * Everything is new. First release.
@@ -0,0 +1,15 @@
1
+ HISTORY.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/json/next.rb
6
+ lib/json/next/commata.rb
7
+ lib/json/next/parser/hanson.rb
8
+ lib/json/next/parser/son.rb
9
+ lib/json/next/pattern.rb
10
+ lib/json/next/version.rb
11
+ test/helper.rb
12
+ test/test_commata.rb
13
+ test/test_parser_hanson.rb
14
+ test/test_parser_son.rb
15
+ test/test_version.rb
@@ -0,0 +1,256 @@
1
+ # jasony
2
+
3
+ jasony gem - read generation y / next generation json versions (HanSON, SON, etc.) with comments, unquoted keys, multi-line strings, trailing commas, optional commas, and more
4
+
5
+
6
+ * home :: [github.com/datatxt/jasony](https://github.com/datatxt/jasony)
7
+ * bugs :: [github.com/datatxt/jasony/issues](https://github.com/datatxt/jasony/issues)
8
+ * gem :: [rubygems.org/gems/jasony](https://rubygems.org/gems/jasony)
9
+ * rdoc :: [rubydoc.info/gems/jasony](http://rubydoc.info/gems/jasony)
10
+
11
+
12
+
13
+
14
+ ## Usage - `HANSON.parse`, `SON.parse`
15
+
16
+ [HanSON](#hanson) •
17
+ [SON](#son)
18
+
19
+
20
+ ### HanSON
21
+
22
+ _HanSON - JSON for Humans by Tim Jansen et al_
23
+
24
+ HanSON is an extension of JSON with a few simple additions to the spec:
25
+
26
+ - quotes for strings are optional if they follow JavaScript identifier rules.
27
+ - you can alternatively use backticks, as in ES6's template string literal, as quotes for strings.
28
+ A backtick-quoted string may span several lines and you are not required to escape regular quote characters,
29
+ only backticks. Backslashes still need to be escaped, and all other backslash-escape sequences work like in
30
+ regular JSON.
31
+ - for single-line strings, single quotes (`''`) are supported in addition to double quotes (`""`)
32
+ - you can use JavaScript comments, both single line (`//`) and multi-line comments (`/* */`), in all places where JSON allows whitespace.
33
+ - Commas after the last list element or object property will be ignored.
34
+
35
+
36
+ Example:
37
+
38
+ ``` js
39
+ {
40
+ listName: "Sesame Street Monsters", // note that listName needs no quotes
41
+ content: [
42
+ {
43
+ name: "Cookie Monster",
44
+ /* Note the template quotes and unescaped regular quotes in the next string */
45
+ background: `Cookie Monster used to be a
46
+ monster that ate everything, especially cookies.
47
+ These days he is forced to eat "healthy" food.`
48
+ }, {
49
+ // You can single-quote strings too:
50
+ name: 'Herry Monster',
51
+ background: `Herry Monster is a furry blue monster with a purple nose.
52
+ He's mostly retired today.`
53
+ }, // don't worry, the trailing comma will be ignored
54
+ ]
55
+ }
56
+ ```
57
+
58
+ Use `HANSON.convert` to convert HanSON text to ye old' JSON text:
59
+
60
+ ``` json
61
+ {
62
+ "listName": "Sesame Street Monsters",
63
+ "content": [
64
+ { "name": "Cookie Monster",
65
+ "background": "Cookie Monster used to be a\n ... to eat \"healthy\" food."
66
+ },
67
+ { "name": "Herry Monster",
68
+ "background": "Herry Monster is a furry blue monster with a purple nose.\n ... today."
69
+ }
70
+ ]
71
+ }
72
+ ```
73
+
74
+ Use `HANSON.parse` instead of `JSON.parse` to parse text to ruby hash / array / etc.:
75
+
76
+ ``` ruby
77
+ {
78
+ "listName" => "Sesame Street Monsters",
79
+ "content" => [
80
+ { "name" => "Cookie Monster",
81
+ "background" => "Cookie Monster used to be a\n ... to eat \"healthy\" food."
82
+ },
83
+ { "name" => "Herry Monster",
84
+ "background" => "Herry Monster is a furry blue monster with a purple nose.\n ... today."
85
+ }
86
+ ]
87
+ }
88
+ ```
89
+
90
+
91
+
92
+ ### SON
93
+
94
+ _SON - Simple Object Notation by Aleksander Gurin et al_
95
+
96
+ Simple data format similar to JSON, but with some minor changes:
97
+
98
+ - comments starts with `#` sign and ends with newline (`\n`)
99
+ - comma after an object key-value pair is optional
100
+ - comma after an array item is optional
101
+
102
+ JSON is compatible with SON in a sense that
103
+ JSON data is also SON data, but not vise versa.
104
+
105
+ Example:
106
+
107
+ ```
108
+ {
109
+ # Personal information
110
+
111
+ "name": "Alexander Grothendieck"
112
+ "fields": "mathematics"
113
+ "main_topics": [
114
+ "Etale cohomology"
115
+ "Motives"
116
+ "Topos theory"
117
+ "Schemes"
118
+ ]
119
+ "numbers": [1 2 3 4]
120
+ "mixed": [1.1 -2 true false null]
121
+ }
122
+ ```
123
+
124
+ Use `SON.convert` to convert SON text to ye old' JSON text:
125
+
126
+ ``` json
127
+ {
128
+ "name": "Alexander Grothendieck",
129
+ "fields": "mathematics",
130
+ "main_topics": [
131
+ "Etale cohomology",
132
+ "Motives",
133
+ "Topos theory",
134
+ "Schemes"
135
+ ],
136
+ "numbers": [1, 2, 3, 4],
137
+ "mixed": [1.1, -2, true, false, null]
138
+ }
139
+ ```
140
+
141
+ Use `SON.parse` instead of `JSON.parse` to parse text to ruby hash / array / etc.:
142
+
143
+ ``` ruby
144
+
145
+ {
146
+ "name" => "Alexander Grothendieck",
147
+ "fields" => "mathematics",
148
+ "main_topics" =>
149
+ ["Etale cohomology", "Motives", "Topos theory", "Schemes"],
150
+ "numbers" => [1, 2, 3, 4],
151
+ "mixed" => [1.1, -2, true, false, nil]
152
+ }
153
+ ```
154
+
155
+
156
+
157
+ ### Live Examples
158
+
159
+
160
+ ``` ruby
161
+ require 'jasony'
162
+
163
+ text1 =<<TXT
164
+ {
165
+ listName: "Sesame Street Monsters", // note that listName needs no quotes
166
+ content: [
167
+ {
168
+ name: "Cookie Monster",
169
+ /* Note the template quotes and unescaped regular quotes in the next string */
170
+ background: `Cookie Monster used to be a
171
+ monster that ate everything, especially cookies.
172
+ These days he is forced to eat "healthy" food.`
173
+ }, {
174
+ // You can single-quote strings too:
175
+ name: 'Herry Monster',
176
+ background: `Herry Monster is a furry blue monster with a purple nose.
177
+ He's mostly retired today.`
178
+ }, // don't worry, the trailing comma will be ignored
179
+ ]
180
+ }
181
+ TXT
182
+
183
+ pp HANSON.parse( text1 ) # note: is the same as JSON.parse( HANSON.convert( text ))
184
+ ```
185
+
186
+ resulting in:
187
+
188
+ ``` ruby
189
+ {
190
+ "listName" => "Sesame Street Monsters",
191
+ "content" => [
192
+ { "name" => "Cookie Monster",
193
+ "background" => "Cookie Monster used to be a\n ... to eat \"healthy\" food."
194
+ },
195
+ { "name" => "Herry Monster",
196
+ "background" => "Herry Monster is a furry blue monster with a purple nose.\n ... today."
197
+ }
198
+ ]
199
+ }
200
+ ```
201
+
202
+ and
203
+
204
+ ``` ruby
205
+
206
+ text2 =<<TXT
207
+ {
208
+ # Personal information
209
+
210
+ "name": "Alexander Grothendieck"
211
+ "fields": "mathematics"
212
+ "main_topics": [
213
+ "Etale cohomology"
214
+ "Motives"
215
+ "Topos theory"
216
+ "Schemes"
217
+ ]
218
+ "numbers": [1 2 3 4]
219
+ "mixed": [1.1 -2 true false null]
220
+ }
221
+ TXT
222
+
223
+ pp SON.parse( text2 ) # note: is the same as JSON.parse( SON.convert( text ))
224
+ ```
225
+
226
+ resulting in:
227
+
228
+ ``` ruby
229
+ {
230
+ "name" => "Alexander Grothendieck",
231
+ "fields" => "mathematics",
232
+ "main_topics" =>
233
+ ["Etale cohomology", "Motives", "Topos theory", "Schemes"],
234
+ "numbers" => [1, 2, 3, 4],
235
+ "mixed" => [1.1, -2, true, false, nil]
236
+ }
237
+ ```
238
+
239
+
240
+
241
+ ## More JSON Formats
242
+
243
+ See the [Awesome JSON (What's Next?)](https://github.com/datatxt/awesome-json-next) collection / page.
244
+
245
+
246
+
247
+ ## License
248
+
249
+ ![](https://publicdomainworks.github.io/buttons/zero88x31.png)
250
+
251
+ The `jasony` scripts are dedicated to the public domain.
252
+ Use it as you please with no restrictions whatsoever.
253
+
254
+ ## Questions? Comments?
255
+
256
+ Post them to the [wwwmake forum](http://groups.google.com/group/wwwmake). Thanks!
@@ -0,0 +1,26 @@
1
+ require 'hoe'
2
+ require './lib/json/next/version.rb'
3
+
4
+ Hoe.spec 'json-next' do
5
+
6
+ self.version = JSON::Next::VERSION
7
+
8
+ self.summary = 'json-next - read generation y / next generation json versions (HanSON, SON, etc.) with comments, unquoted keys, multi-line strings, trailing commas, optional commas, and more'
9
+ self.description = summary
10
+
11
+ self.urls = ['https://github.com/datatxt/json-next']
12
+
13
+ self.author = 'Gerald Bauer'
14
+ self.email = 'ruby-talk@ruby-lang.org'
15
+
16
+ # switch extension to .markdown for gihub formatting
17
+ self.readme_file = 'README.md'
18
+ self.history_file = 'HISTORY.md'
19
+
20
+ self.licenses = ['Public Domain']
21
+
22
+ self.spec_extras = {
23
+ required_ruby_version: '>= 1.9.2'
24
+ }
25
+
26
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'json'
4
+ require 'strscan' ## StringScanner
5
+ require 'pp'
6
+
7
+
8
+ # our own code
9
+ require 'json/next/version' # note: let version always go first
10
+ require 'json/next/pattern' # shared utils for "custom" parser
11
+ require 'json/next/commata'
12
+
13
+ require 'json/next/parser/hanson'
14
+ require 'json/next/parser/son'
15
+
16
+
17
+
18
+ # say hello
19
+ puts JSON::Next.banner if defined?( $RUBYLIBS_DEBUG )
@@ -0,0 +1,171 @@
1
+ # encoding: utf-8
2
+
3
+ module JSON
4
+ module Next
5
+
6
+
7
+ ### auto-add commas for objects and arrays
8
+ class Commata
9
+
10
+
11
+ ## convenience helper
12
+ def self.convert( str, opts={} )
13
+ self.new.convert( str, opts )
14
+ end
15
+
16
+
17
+ def convert( str, opts={} )
18
+ @debug = opts.fetch( :debug, false )
19
+ @out = ""
20
+ @buffer = StringScanner.new( str )
21
+
22
+ skip_whitespaces
23
+ parse_value
24
+ @out
25
+ end
26
+
27
+
28
+ def debug?() @debug; end
29
+
30
+
31
+
32
+ def skip_whitespaces
33
+ @buffer.skip( /[ \t\n]*/ ) ## skip trailing WHITESPACE
34
+ end
35
+
36
+
37
+ def parse_string
38
+
39
+ if @buffer.peek(1) == '"' ## double quote
40
+ @buffer.getch # consume double quote
41
+ value = @buffer.scan_until( /(?=")/) ## fix: allow escaped double quote e.g. \" too!!!
42
+ @buffer.getch # consume double quote
43
+
44
+ puts %{string value >>#{value}<<} if debug?
45
+ @out << ' "'
46
+ @out << value
47
+ @out << '" '
48
+
49
+ skip_whitespaces
50
+ else
51
+ puts "!! format error: string literal - expected opening quote (\") - rest is >>#{@buffer.rest}<<"
52
+ end
53
+ end
54
+
55
+
56
+
57
+ def parse_object
58
+
59
+ if @buffer.peek(1) == '{'
60
+ @buffer.getch # consume '{'
61
+ @out << ' { '
62
+ skip_whitespaces
63
+
64
+ if @buffer.peek(1) == '}' ## empty object?
65
+ @buffer.getch # consume '{'
66
+ @out << ' } '
67
+ skip_whitespaces
68
+ return
69
+ end
70
+
71
+ loop do
72
+ parse_string
73
+ if @buffer.peek(1) == ':'
74
+ @buffer.getch # consume ':'
75
+ @out << ' : '
76
+ skip_whitespaces
77
+
78
+ parse_value
79
+ if @buffer.peek(1) == '}'
80
+ @buffer.getch # consume '}'
81
+ @out << ' } '
82
+ skip_whitespaces
83
+ return ## use break - why? why not?
84
+ else
85
+ if @buffer.peek(1) == ','
86
+ @buffer.getch # consume ','
87
+ @out << ' , '
88
+ skip_whitespaces
89
+ else
90
+ puts "object literal - auto-add comma for key-value pair" if debug?
91
+ @out << ' , '
92
+ end
93
+ end
94
+ else
95
+ puts "!! format error: object literal - expected colon (:) - rest is >>#{@buffer.rest}<<"
96
+ end
97
+ end
98
+ else
99
+ puts "!! format error: object literal - expected curly open bracket ({) - rest is >>#{@buffer.rest}<<"
100
+ end
101
+ end # method parse_object
102
+
103
+
104
+
105
+ def parse_array
106
+
107
+ if @buffer.peek(1) == '['
108
+ @buffer.getch # consume '['
109
+ @out << ' [ '
110
+ skip_whitespaces
111
+
112
+ if @buffer.peek(1) == ']' ## empty array?
113
+ @buffer.getch # consume ']'
114
+ @out << ' ] '
115
+ skip_whitespaces
116
+ return
117
+ end
118
+
119
+ loop do
120
+ parse_value
121
+ if @buffer.peek(1) == ']'
122
+ @buffer.getch # consume ']'
123
+ @out << ' ] '
124
+ skip_whitespaces
125
+ return ## use break - why? why not?
126
+ else
127
+ if @buffer.peek(1) == ','
128
+ @buffer.getch # consume ','
129
+ @out << ' , '
130
+ skip_whitespaces
131
+ else
132
+ puts "array literal - auto-add comma for value" if debug?
133
+ @out << ' , '
134
+ end
135
+ end
136
+ end
137
+ else
138
+ puts "!! format error: array literal - expected square open bracket ([) - rest is >>#{@buffer.rest}<<"
139
+ end
140
+ end # method parse_array
141
+
142
+
143
+
144
+ def parse_value
145
+ if @buffer.peek(1) == '{'
146
+ parse_object
147
+ elsif @buffer.peek(1) == '['
148
+ parse_array
149
+ elsif @buffer.peek(1) == '"'
150
+ parse_string
151
+ else
152
+ ## assume number or literal/identifier
153
+ value = @buffer.scan( /[_$a-zA-Z0-9.+\-]+/ )
154
+ puts %{literal value >>#{value}<<} if debug?
155
+ @out << " "
156
+ @out << value
157
+ @out << " "
158
+
159
+ skip_whitespaces
160
+ end
161
+
162
+ ## todo/fix: check if eof reached ?? if not report warning - more data available??
163
+ ## wrap in object ({}) or array ([])
164
+ end
165
+
166
+
167
+ end # class Commata
168
+
169
+
170
+ end # module Next
171
+ end # module JSON
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # based on github.com/timjansen/hanson
5
+ # Thanks to Tim Jansen
6
+
7
+
8
+ module HANSON
9
+
10
+
11
+ BACKTICK_ML_QUOTE = JSON::Next::BACKTICK_ML_QUOTE
12
+ SINGLE_QUOTE = JSON::Next::SINGLE_QUOTE
13
+ DOUBLE_QUOTE = JSON::Next::DOUBLE_QUOTE
14
+
15
+ CLANG_ML_COMMENT = JSON::Next::CLANG_ML_COMMENT
16
+ CLANG_COMMENT = JSON::Next::CLANG_COMMENT
17
+
18
+ KEYWORDS = JSON::Next::KEYWORDS
19
+ IDENTIFIER = JSON::Next::IDENTIFIER
20
+ TRAILING_COMMA = JSON::Next::TRAILING_COMMA
21
+
22
+ UNESCAPE_MAP = JSON::Next::UNESCAPE_MAP
23
+ ML_ESCAPE_MAP = JSON::Next::ML_ESCAPE_MAP
24
+
25
+
26
+
27
+ def self.strip_comments( text ) ## pass 1
28
+ text.gsub( /#{BACKTICK_ML_QUOTE}|#{SINGLE_QUOTE}|#{DOUBLE_QUOTE}|#{CLANG_ML_COMMENT}|#{CLANG_COMMENT}/ox ) do |match|
29
+ ## puts "match: >>#{match}<< : #{match.class.name}"
30
+ if match[0] == ?/ ## comments start with // or /*
31
+ ## puts "!!! removing comments"
32
+ '' ## remove / strip comments
33
+ else
34
+ match
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ def self.normalize_quotes( text ) ## pass 2
41
+ text.gsub( /#{BACKTICK_ML_QUOTE}|#{SINGLE_QUOTE}|#{DOUBLE_QUOTE}/ox ) do |match|
42
+ ## puts "match: >>#{match}<< : #{match.class.name}"
43
+
44
+ m = Regexp.last_match
45
+ if m[:backtick_ml_quote]
46
+ ## puts "!!! ml_quote -- convert to double quotes"
47
+ str = m[:backtick_ml_quote]
48
+ str = str.gsub( /\\./ ) {|r| UNESCAPE_MAP[r] || r }
49
+ str = str.gsub( /[\n\r\t"]/ ) { |r| ML_ESCAPE_MAP[r] }
50
+ '"' + str + '"'
51
+ elsif m[:single_quote]
52
+ ## puts "!!! single_quote -- convert to double quotes"
53
+ str = m[:single_quote]
54
+ str = str.gsub( /\\./ ) {|r| UNESCAPE_MAP[r] || r }
55
+ str = str.gsub( /"/, %{\\"} )
56
+ '"' + str + '"'
57
+ else
58
+ match
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ def self.convert( text )
65
+
66
+ # text is the HanSON string to convert.
67
+
68
+ # todo: add keep_line_numbers options - why? why not?
69
+ # see github.com/timjansen/hanson
70
+
71
+ ## pass 1: remove/strip comments
72
+ text = strip_comments( text )
73
+
74
+ ## pass 2: requote/normalize quotes
75
+ text = normalize_quotes( text )
76
+
77
+ ## pass 3: quote unquoted and remove trailing commas
78
+ text = text.gsub( /#{KEYWORDS}|#{IDENTIFIER}|#{DOUBLE_QUOTE}|#{TRAILING_COMMA}/ox ) do |match|
79
+ ## puts "match: >>#{match}<< : #{match.class.name}"
80
+
81
+ m = Regexp.last_match
82
+ if m[:identifier]
83
+ ## puts "!!! identfier -- wrap in double quotes"
84
+ '"' + m[:identifier] + '"'
85
+ elsif m[:trailing_comma]
86
+ ## puts "!!! trailing comma -- remove"
87
+ ''
88
+ else
89
+ match
90
+ end
91
+ end
92
+
93
+ text
94
+ end
95
+
96
+
97
+ def self.parse( text )
98
+ JSON.parse( self.convert( text ) )
99
+ end
100
+
101
+ end # module HANSON
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # based on github.com/aleksandergurin/simple-object-notation
5
+ # Thanks to Aleksander Gurin
6
+
7
+
8
+ module SON
9
+
10
+
11
+ DOUBLE_QUOTE = JSON::Next::DOUBLE_QUOTE
12
+
13
+ SHELL_COMMENT = JSON::Next::SHELL_COMMENT
14
+
15
+
16
+ def self.strip_comments( text ) ## pass 1
17
+ text.gsub( /#{DOUBLE_QUOTE}|#{SHELL_COMMENT}/ox ) do |match|
18
+ ## puts "match: >>#{match}<< : #{match.class.name}"
19
+ if match[0] == ?# ## comments start with #
20
+ ## puts "!!! removing comments"
21
+ '' ## remove / strip comments
22
+ else
23
+ match
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+
30
+
31
+ def self.convert( text )
32
+
33
+ # text is the SON string to convert.
34
+
35
+ text = strip_comments( text ) ## pass 1
36
+ text = JSON::Next::Commata.convert( text ) ## pass 2 - auto-add (missing optional) commas
37
+ text
38
+ end
39
+
40
+
41
+ def self.parse( text )
42
+ JSON.parse( self.convert( text ) )
43
+ end
44
+
45
+ end # module SON
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+
3
+ module JSON
4
+ module Next
5
+
6
+ ## note: regex pattern \\ needs to get escaped twice, thus, \\.
7
+ ## and for literal \\ use \\\\\.
8
+
9
+ ## todo: check for newlines [^] incl. newline ?
10
+
11
+ ## note: add named captures for "inner" unquotes string values
12
+
13
+ ###############
14
+ ##### quotes
15
+
16
+
17
+ BACKTICK_ML_QUOTE = %< ` (?<backtick_ml_quote>
18
+ (?:
19
+ \\\\. | [^`]
20
+ )*
21
+ )
22
+ `
23
+ >
24
+
25
+ ## => \\\\. -- allow backslash escapes e.g. \n \t \\ etc.
26
+ ## => [^`] -- everything except backquote
27
+
28
+ ## todo/fix - check if [^`] includes/matches newline too (yes)? -- add \n for multi-line!
29
+
30
+
31
+ SINGLE_QUOTE = %< ' (?<single_quote>
32
+ (?:
33
+ \\\\. | [^']
34
+ )*
35
+ )
36
+ '
37
+ >
38
+
39
+ DOUBLE_QUOTE = %< " (?<double_quote>
40
+ (?:
41
+ \\\\. | [^"]
42
+ )*
43
+ )
44
+ "
45
+ >
46
+
47
+
48
+ #######################
49
+ #### comments
50
+
51
+ CLANG_ML_COMMENT = %< /\\*
52
+ .*?
53
+ \\*/
54
+ >
55
+
56
+ ## use . instead of [^] - why? why not?
57
+ ## note: check if . incl. newlines too (only in multi-line (m) option - why? why not??
58
+ ## fix/todo: include newline!!! \n - for multi-line!!!
59
+
60
+ ## note: *? is NON-greedy
61
+
62
+
63
+ CLANG_COMMENT = %< //
64
+ .*?
65
+ (?:
66
+ \\n | $
67
+ )
68
+ >
69
+
70
+ ## note: check if . incl. newlines too (only in multi-line (m) option - why? why not??
71
+ ## note: *? is NON-greedy
72
+
73
+
74
+ SHELL_COMMENT = %< [#]
75
+ .*?
76
+ (?:
77
+ \\n | $
78
+ )
79
+ >
80
+
81
+ ## note: use [#] instead of # to avoid confusion with # comment in regex
82
+
83
+
84
+
85
+
86
+ KEYWORDS = %<
87
+ (?:
88
+ true | false | null
89
+ )
90
+ (?=
91
+ [^\\w_$] | $
92
+ )
93
+ >
94
+
95
+ IDENTIFIER = %<
96
+ (?<identifier>
97
+ [a-zA-Z_$]
98
+ [\\w_$]*
99
+ )
100
+ >
101
+
102
+ TRAILING_COMMA = %<
103
+ (?<trailing_comma>,)
104
+ (?=
105
+ \\s*
106
+ [}\\]]
107
+ )
108
+ >
109
+
110
+
111
+
112
+ UNESCAPE_MAP = {
113
+ %<\\"> => %<">, ## "\\\"" => "\"",
114
+ %<\\`> => %<`>, ## "\\`" => "`",
115
+ %<\\'> => %<'> ## "\\'" => "'"
116
+ }
117
+
118
+ ML_ESCAPE_MAP = {
119
+ %<\n> => %<\\n>, ## "\n" => "\\n",
120
+ %<\r> => %<\\r>, ## "\r" => "\\r",
121
+ %<\t> => %<\\t>, ## "\t" => "\\t",
122
+ %<"> => %<\\"> ## "\"" => "\\\""
123
+ }
124
+
125
+
126
+ end # module Next
127
+ end # module JSON
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ module JSON
4
+ module Next
5
+
6
+ MAJOR = 1 ## todo: namespace inside version or something - why? why not??
7
+ MINOR = 1
8
+ PATCH = 0
9
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
10
+
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ def self.banner
16
+ "json-next/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
17
+ end
18
+
19
+ def self.root
20
+ "#{File.expand_path( File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) )}"
21
+ end
22
+
23
+ end # module Next
24
+ end # module JSON
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ ## minitest setup
4
+ require 'minitest/autorun'
5
+
6
+
7
+ $RUBYLIBS_DEBUG = true
8
+
9
+ ## our own code
10
+ require 'json/next'
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_commata.rb
6
+
7
+
8
+ require 'helper'
9
+
10
+
11
+ class TestCommata < MiniTest::Test
12
+
13
+ def test_commata
14
+
15
+ sample1a =<<TXT
16
+ {
17
+ "name": "Alexander Grothendieck"
18
+ "fields": "mathematics"
19
+ "main_topics": [
20
+ "Etale cohomology"
21
+ "Motives"
22
+ "Topos theory"
23
+ "Schemes"
24
+ ]
25
+ }
26
+ TXT
27
+
28
+ exp_sample1b = {
29
+ "name" => "Alexander Grothendieck",
30
+ "fields" => "mathematics",
31
+ "main_topics" =>
32
+ ["Etale cohomology", "Motives", "Topos theory", "Schemes"]
33
+ }
34
+
35
+
36
+ sample2a =<<TXT
37
+ {
38
+ "nested": {
39
+ "name": "Alexander Grothendieck"
40
+ "fields": "mathematics"
41
+ "array": ["one" "two"] }
42
+ "numbers": [1 2 3 4 5]
43
+ "more_numbers": [1.1 2.0 0.3 4.444 -5.1]
44
+ "mixed": [1 true false null]
45
+ }
46
+ TXT
47
+
48
+ sample2a1 =<<TXT
49
+ {
50
+ "nested": {
51
+ "name": "Alexander Grothendieck",
52
+ "fields": "mathematics",
53
+ "array": ["one", "two"] },
54
+ "numbers": [1, 2, 3, 4, 5],
55
+ "more_numbers": [1.1, 2.0, 0.3, 4.444, -5.1],
56
+ "mixed": [1, true, false, null]
57
+ }
58
+ TXT
59
+
60
+
61
+
62
+ exp_sample2b = {
63
+ "nested" => {
64
+ "name" => "Alexander Grothendieck",
65
+ "fields" => "mathematics",
66
+ "array" => ["one", "two"]
67
+ },
68
+ "numbers" => [1, 2, 3, 4, 5],
69
+ "more_numbers" => [1.1, 2.0, 0.3, 4.444, -5.1],
70
+ "mixed" => [1, true, false, nil]
71
+ }
72
+
73
+
74
+ sample1b = JSON::Next::Commata.convert( sample1a, debug: true )
75
+ puts sample1b
76
+
77
+ assert_equal exp_sample1b, JSON.parse( sample1b )
78
+
79
+
80
+ sample2b = JSON::Next::Commata.convert( sample2a, debug: true )
81
+ puts sample2b
82
+
83
+ assert_equal exp_sample2b, JSON.parse( sample2b )
84
+
85
+
86
+ sample2b1 = JSON::Next::Commata.convert( sample2a1, debug: true )
87
+ puts sample2b1
88
+
89
+ assert_equal exp_sample2b, JSON.parse( sample2b1 )
90
+ end
91
+
92
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_parser_hanson.rb
6
+
7
+
8
+ require 'helper'
9
+
10
+
11
+ class TestParser < MiniTest::Test
12
+
13
+ def test_hanson
14
+ exp_sample1 = {
15
+ "listName"=>"Sesame Street Monsters",
16
+ "content"=>
17
+ [{"name"=>"Cookie Monster",
18
+ "background"=> "Cookie Monster used to be a\n monster that ate everything, especially cookies.\n These days he is forced to eat \"healthy\" food."
19
+ },
20
+ {"name"=>"Herry Monster",
21
+ "background"=> "Herry Monster is a furry blue monster with a purple nose.\n He's mostly retired today."
22
+ }
23
+ ]}
24
+
25
+ sample1 =<<TXT
26
+ {
27
+ listName: "Sesame Street Monsters", // note that listName needs no quotes
28
+ content: [
29
+ {
30
+ name: "Cookie Monster",
31
+ /* Note the template quotes and unescaped regular quotes in the next string */
32
+ background: `Cookie Monster used to be a
33
+ monster that ate everything, especially cookies.
34
+ These days he is forced to eat "healthy" food.`
35
+ }, {
36
+ // You can single-quote strings too:
37
+ name: 'Herry Monster',
38
+ background: `Herry Monster is a furry blue monster with a purple nose.
39
+ He's mostly retired today.`
40
+ }, // don't worry, the trailing comma will be ignored
41
+ ]
42
+ }
43
+ TXT
44
+
45
+ puts HANSON.convert( sample1 )
46
+
47
+ pp HANSON.parse( sample1 )
48
+
49
+ assert_equal exp_sample1, HANSON.parse( sample1 )
50
+ end
51
+
52
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_parser_son.rb
6
+
7
+
8
+ require 'helper'
9
+
10
+
11
+ class TestParserSon < MiniTest::Test
12
+
13
+ def test_son
14
+ exp_sample1 = {
15
+ "name"=>"Alexander Grothendieck",
16
+ "fields"=>"mathematics",
17
+ "main_topics"=>
18
+ ["Etale cohomology", "Motives", "Topos theory", "Schemes"]
19
+ }
20
+
21
+ sample1 =<<TXT
22
+ {
23
+ # Personal information
24
+
25
+ "name": "Alexander Grothendieck"
26
+ "fields": "mathematics"
27
+ "main_topics": [
28
+ "Etale cohomology"
29
+ "Motives"
30
+ "Topos theory"
31
+ "Schemes"
32
+ ]
33
+ }
34
+ TXT
35
+
36
+ puts SON.convert( sample1 )
37
+
38
+ assert_equal exp_sample1, SON.parse( sample1 )
39
+ end
40
+
41
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_version.rb
6
+
7
+
8
+ require 'helper'
9
+
10
+
11
+ class TestVersion < MiniTest::Test
12
+
13
+
14
+ def test_version
15
+
16
+ pp JSON::Next::UNESCAPE_MAP
17
+ pp JSON::Next::ML_ESCAPE_MAP
18
+
19
+ pp JSON::Next::BACKTICK_ML_QUOTE
20
+ puts JSON::Next::BACKTICK_ML_QUOTE
21
+ pp JSON::Next::SINGLE_QUOTE
22
+ puts JSON::Next::SINGLE_QUOTE
23
+ pp JSON::Next::DOUBLE_QUOTE
24
+ puts JSON::Next::DOUBLE_QUOTE
25
+
26
+ pp JSON::Next::CLANG_ML_COMMENT
27
+ puts JSON::Next::CLANG_ML_COMMENT
28
+ pp JSON::Next::CLANG_COMMENT
29
+ puts JSON::Next::CLANG_COMMENT
30
+
31
+
32
+ puts JSON::Next::VERSION
33
+ assert true
34
+ ## assume everything ok if get here
35
+ end
36
+
37
+ end # class TestVersion
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-next
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hoe
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.15'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.15'
41
+ description: json-next - read generation y / next generation json versions (HanSON,
42
+ SON, etc.) with comments, unquoted keys, multi-line strings, trailing commas, optional
43
+ commas, and more
44
+ email: ruby-talk@ruby-lang.org
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files:
48
+ - HISTORY.md
49
+ - Manifest.txt
50
+ - README.md
51
+ files:
52
+ - HISTORY.md
53
+ - Manifest.txt
54
+ - README.md
55
+ - Rakefile
56
+ - lib/json/next.rb
57
+ - lib/json/next/commata.rb
58
+ - lib/json/next/parser/hanson.rb
59
+ - lib/json/next/parser/son.rb
60
+ - lib/json/next/pattern.rb
61
+ - lib/json/next/version.rb
62
+ - test/helper.rb
63
+ - test/test_commata.rb
64
+ - test/test_parser_hanson.rb
65
+ - test/test_parser_son.rb
66
+ - test/test_version.rb
67
+ homepage: https://github.com/datatxt/json-next
68
+ licenses:
69
+ - Public Domain
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options:
73
+ - "--main"
74
+ - README.md
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 1.9.2
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.6.7
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: json-next - read generation y / next generation json versions (HanSON, SON,
93
+ etc.) with comments, unquoted keys, multi-line strings, trailing commas, optional
94
+ commas, and more
95
+ test_files: []