hjson 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c0a9dccdbbdf54033ab8704f7155633b6e646a70
4
+ data.tar.gz: 54d9147a318ef371f91d61fe4c0e30bd6e26d2ef
5
+ SHA512:
6
+ metadata.gz: 5db57fcc6a3d2410af1c8def6b1f4bee8cbdb6a65277a444bedc982ccb85bce9f57e8f3834e85316e337ef1afe9e13a949f0084e4ab6bb19b0306b7dad5298f7
7
+ data.tar.gz: 08c3eb7ab497ab4ff5798ffa4ad6d745e9dafca6ea13fa8c0cb0357c1e86912306a70234e50be57ed65fa2efd643361495cad2e0d1f03b811afd46ac7d91d15c
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hjson.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 namusyaka
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,205 @@
1
+ # Hjson, the Human JSON written in Ruby
2
+
3
+ A configuration file format for humans. Relaxed syntax, fewer mistakes, more comments for Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hjson'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install hjson
20
+
21
+ ## Usage
22
+
23
+ You can use Hjson as `JSON.parse` of standard library.
24
+
25
+ Please check it out.
26
+
27
+ ```ruby
28
+ require 'hjson'
29
+
30
+ hjson = <<HJSON
31
+ // for your config
32
+ // use #, // or /**/ comments,
33
+ // omit quotes for keys
34
+ key: 1
35
+ // omit quotes for strings
36
+ string: contains everything until LF
37
+ // omit commas at the end of a line
38
+ cool: {
39
+ foo: 1
40
+ bar: 2
41
+ }
42
+ // allow trailing commas
43
+ list: [
44
+ 1,
45
+ 2,
46
+ ]
47
+ // and use multiline strings
48
+ realist:
49
+ '''
50
+ My half empty glass,
51
+ I will fill your empty half.
52
+ Now you are half full.
53
+ '''
54
+ HJSON
55
+
56
+ Hjson.parse(hjson)
57
+ ```
58
+
59
+ ### Comments
60
+
61
+ Hjson allows you to use comments in your JSON.
62
+
63
+ ```ruby
64
+ require 'hjson'
65
+
66
+ hjson =<<HJSON
67
+ {
68
+ # specify rate in requests/second
69
+ "rate": 1000
70
+
71
+ // prefer c-style comments?
72
+ /* feeling old fashioned? */
73
+ }
74
+ HJSON
75
+
76
+ Hjson.parse(hjson) #=> {"rate"=>1000}
77
+ ```
78
+
79
+ ### Quotes
80
+
81
+ You don't need to quote keyname.
82
+
83
+ ```ruby
84
+ require 'hjson'
85
+
86
+ hjson =<<HJSON
87
+ {
88
+ key: "value"
89
+ }
90
+ HJSON
91
+
92
+ Hjson.parse(hjson) #=> {"key"=>"value"}
93
+ ```
94
+
95
+ ### Commas
96
+
97
+ You can forget the comma at the end, Hjson recognizes the end automatically.
98
+
99
+
100
+ ```ruby
101
+ require 'hjson'
102
+
103
+ hjson =<<HJSON
104
+ {
105
+ one: 1
106
+ two: 2
107
+ three: 4 # oops
108
+ }
109
+ HJSON
110
+
111
+ Hjson.parse(hjson) #=> {"one"=>1, "two"=>2, "three"=>4}
112
+ ```
113
+
114
+ ### Quoteless
115
+
116
+ Hjson makes quotes for strings optional as well.
117
+
118
+ ```ruby
119
+ require 'hjson'
120
+
121
+ hjson =<<HJSON
122
+ {
123
+ text: look ma, no quotes!
124
+
125
+ # To make your life easy, put the next
126
+ # value or comment on a new line.
127
+ # It's also easier to read!
128
+ }
129
+ HJSON
130
+
131
+ Hjson.parse(hjson) #=> {"text"=>"look ma, no quotes!"}
132
+ ```
133
+
134
+ ### Escapes
135
+
136
+ You don't need to escape in unquoted strings.
137
+
138
+ ```ruby
139
+ require 'hjson'
140
+
141
+ hjson = <<HJSON
142
+ {
143
+ # write a regex without escaping the escape
144
+ regex: ^\d*\.{0,1}\d+$
145
+
146
+ # quotes in the content need no escapes
147
+ inject: <div class="important"></div>
148
+
149
+ # inside quotes, escapes work
150
+ # just like in JSON
151
+ escape: "\\\\ \n \t\\""
152
+ }
153
+ HJSON
154
+
155
+ Hjson.parse(hjson)
156
+ #=> {"regex"=>"^d*.{0,1}d+$",
157
+ "inject"=>"<div class=\"important\"></div>",
158
+ "escape"=>"\\ \n \t\""}
159
+ ```
160
+
161
+ ### Multiline
162
+
163
+ Hjson allows you to use `'''` for writing multiline strings.
164
+
165
+ ```ruby
166
+ require 'hjson'
167
+
168
+ hjson =<<HJSON
169
+ {
170
+ haiku:
171
+ '''
172
+ JSON I love you.
173
+ But strangled is my data.
174
+ This, so much better.
175
+ '''
176
+ }
177
+ HJSON
178
+
179
+ Hjson.parse(hjson) #=> {"haiku"=>"JSON I love you.\nBut strangled is my data.\nThis, so much better."}
180
+ ```
181
+
182
+ ### Braces
183
+
184
+ You can omit the braces for the root object.
185
+
186
+ ```ruby
187
+ require 'hjson'
188
+
189
+ hjson =<<HJSON
190
+ // this is a valid config file
191
+ joke: My backslash escaped!
192
+ HJSON
193
+
194
+ Hjson.parse(hjson) #=> {"joke"=>"My backslash escaped!"}
195
+ ```
196
+
197
+ ## Contributing
198
+
199
+ Bug reports and pull requests are welcome on GitHub at https://github.com/namusyaka/hjson. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
200
+
201
+
202
+ ## License
203
+
204
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
205
+
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task default: :test
11
+ task spec: :test
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hjson/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'hjson'
8
+ spec.version = Hjson::VERSION
9
+ spec.authors = ['namusyaka']
10
+ spec.email = ['namusyaka@gmail.com']
11
+
12
+ spec.summary = %q{Human JSON (Hjson) implementation for Ruby}
13
+ spec.description = %q{Human JSON (Hjson) implementation for Ruby}
14
+ spec.homepage = 'https://github.com/namusyaka/hjson'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_runtime_dependency 'json', '~> 2.0.2'
23
+ spec.add_development_dependency 'hashdiff'
24
+ spec.add_development_dependency 'bundler', '~> 1.12'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'test-unit'
27
+ end
@@ -0,0 +1,14 @@
1
+ require "hjson/version"
2
+ require "hjson/parser"
3
+
4
+ module Hjson
5
+ def parser
6
+ @parser ||= Parser
7
+ end
8
+
9
+ def parse(source, **options)
10
+ parser.new(source, **options).parse
11
+ end
12
+
13
+ module_function :parser, :parse
14
+ end
@@ -0,0 +1,43 @@
1
+ require 'hjson/ast/parser'
2
+ require 'hjson/ast/number_parser'
3
+
4
+ module Hjson
5
+ module AST
6
+ class Node < Parser
7
+ def self.[](type)
8
+ @names ||= {}
9
+ @names[type] ||= begin
10
+ namespace = name.split(/::/).slice(0..-2).join('::')
11
+ const_name = parser_name(type, namespace)
12
+ Object.const_get(const_name) if Object.const_defined?(const_name)
13
+ end
14
+ end
15
+
16
+ def self.parser_name(type, namespace)
17
+ type = type.to_s.split(?_).map(&:capitalize).join
18
+ "#{namespace}::#{type}"
19
+ end
20
+
21
+ def with_whitespaces
22
+ node(:whitespace)
23
+ yield
24
+ node(:whitespace)
25
+ end
26
+
27
+ def node(type, *args, &block)
28
+ args.unshift(buffer)
29
+ Node[type].new(*args).parse
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ require 'hjson/ast/nodes/root'
36
+ require 'hjson/ast/nodes/object'
37
+ require 'hjson/ast/nodes/array'
38
+ require 'hjson/ast/nodes/any'
39
+ require 'hjson/ast/nodes/string'
40
+ require 'hjson/ast/nodes/multiline'
41
+ require 'hjson/ast/nodes/value'
42
+ require 'hjson/ast/nodes/whitespace'
43
+ require 'hjson/ast/nodes/keyname'
@@ -0,0 +1,42 @@
1
+ module Hjson
2
+ module AST
3
+ class Any < Node
4
+ rule(:eol) { |c| [?\r, ?\n, nil].include?(c) }
5
+ rule(:number) { |c| (?0..?9).include?(c) }
6
+ rule(:comment) { |c| c == ?/ && (peek(1) == ?/ || peek(1) == ?*) }
7
+ rule(:special) { |c| [?,, ?}, ?], ?#].include?(c) }
8
+ rule(:punctuator) { |c| [?{, ?}, ?[, ?], ?,, ?:].include?(c) }
9
+ rule(:multiline) { |c| payload.length == 3 && payload == "'''" }
10
+
11
+ declare :payload, :char
12
+
13
+ parser do
14
+ if punctuator?
15
+ fail SyntaxError, 'Any value does not expected %p' % char
16
+ end
17
+ end
18
+ parser do
19
+ until eos?
20
+ read
21
+ halt(node(:multiline)) if multiline?
22
+ if eol? || special? || comment?
23
+ chf = payload[0]
24
+ stripped = payload.strip
25
+ case chf
26
+ when ?f then halt(false) if stripped == 'false'
27
+ when ?n then halt(nil) if stripped == 'null'
28
+ when ?t then halt(true) if stripped == 'true'
29
+ else
30
+ if chf == ?- || number?(chf)
31
+ number = NumberParser.new(payload).parse
32
+ halt(number) if number
33
+ end
34
+ end
35
+ halt(payload.strip) if eol?
36
+ end
37
+ payload << char if char
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ module Hjson
2
+ module AST
3
+ class Array < Node
4
+ rule(:comma) { |c| c == ?, }
5
+ rule(:finished) { |c| c == ?] }
6
+
7
+ declare(:array_class) { options[:array_class] || ::Array }
8
+ declare(:payload) { @array_class.new }
9
+
10
+ parser { read && node(:whitespace) }
11
+ parser { halt(read && payload) if finished? }
12
+ parser do
13
+ while char?
14
+ payload << node(:value)
15
+ with_whitespaces do
16
+ read && node(:whitespace) if comma?
17
+ halt(read && payload) if finished?
18
+ end
19
+ end
20
+ end
21
+ parser { fail EndOfInputError }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ module Hjson
2
+ module AST
3
+ class Keyname < Node
4
+ attr_accessor :start, :space
5
+
6
+ rule(:double_quote) { |c| c == ?" }
7
+ rule(:colon) { |c| c == ?: }
8
+ rule(:punctuator) { |c| [?{, ?}, ?[, ?], ?,, ?:].include?(c) }
9
+
10
+ declare :space, -1
11
+ declare :start, :charpos
12
+ declare :payload, ''
13
+
14
+ parser { halt(node(:string)) if double_quote? }
15
+ parser do
16
+ until eos?
17
+ if colon?
18
+ fail SyntaxError if payload.empty?
19
+ if space.positive? && space != payload.length
20
+ buffer.pos = start + space
21
+ fail SyntaxError
22
+ end
23
+ halt(payload)
24
+ elsif char <= ' '
25
+ fail SyntaxError if eos?
26
+ self.space = payload.length if space.negative?
27
+ elsif punctuator?
28
+ fail SyntaxError
29
+ else
30
+ payload << char
31
+ end
32
+ read
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,70 @@
1
+ module Hjson
2
+ module AST
3
+ class Multiline < Node
4
+ attr_accessor :triple, :indent
5
+
6
+ rule(:single_quote) { |c| c == ?' }
7
+ rule(:double_quote) { |c| c == ?" }
8
+ rule(:new_line) { |c| c == ?\n }
9
+
10
+ declare :triple, 0
11
+ declare :indent, 0
12
+ declare :payload, ''
13
+
14
+ parser do
15
+ loop do
16
+ current = peek(-@indent - 4)
17
+ break if !current || current == ?\n
18
+ self.indent += 1
19
+ end
20
+ end
21
+
22
+ parser { read_while { char <= ' ' && !new_line? } }
23
+ parser { read && skip_indent if new_line? }
24
+
25
+ parser do
26
+ loop do
27
+ fail SyntaxError unless char
28
+ if single_quote?
29
+ self.triple += 1
30
+ read
31
+ if triple == 3
32
+ @payload = payload.slice(0..-2) if payload.slice(-1) == ?\n
33
+ halt(payload)
34
+ else
35
+ next
36
+ end
37
+ else
38
+ while triple > 0
39
+ payload << ?'
40
+ self.triple -= 1
41
+ end
42
+ end
43
+
44
+ if new_line?
45
+ payload << ?\n
46
+ read
47
+ skip_indent
48
+ else
49
+ payload << char if char != ?\r
50
+ read
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def skip_indent
58
+ skip = self.indent
59
+ read_while do
60
+ cond = char <= ' ' && !new_line?
61
+ if cond
62
+ result = skip > 0
63
+ skip -= 1
64
+ result
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,40 @@
1
+ module Hjson
2
+ module AST
3
+ class Object < Node
4
+ rule(:without_braces) { !!@without_braces }
5
+ rule(:finished) { |c| c == ?} && !without_braces? }
6
+ rule(:comma) { |c| c == ?, }
7
+
8
+ declare(:object_class) { options[:object_class] || Hash }
9
+ declare(:payload) { @object_class.new }
10
+ declare(:without_braces) { !!options[:without_braces] }
11
+
12
+ parser { read unless without_braces? }
13
+ parser { node(:whitespace) }
14
+ parser { halt(read && payload) if finished? }
15
+ parser do
16
+ while char?
17
+ key = node(:keyname)
18
+ with_whitespaces do
19
+ validate ?:
20
+ read
21
+ payload[key] = node(:value)
22
+ end
23
+ if comma?
24
+ node(:whitespace)
25
+ read
26
+ end
27
+ halt(read && payload) if finished?
28
+ node(:whitespace)
29
+ end
30
+ end
31
+ parser do
32
+ if without_braces?
33
+ halt(payload)
34
+ else
35
+ fail EndOfInputError
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ module Hjson
2
+ module AST
3
+ class Root < Node
4
+ # Reads useless whitespaces such as \s, \t and any comments
5
+ parser { node(:whitespace) }
6
+ parser do
7
+ hjson =
8
+ case char
9
+ when ?{ then node(:object, class_constant: options[:object_class])
10
+ when ?[ then node(:array, class_constant: options[:array_class])
11
+ end
12
+ halt(hjson) if hjson
13
+
14
+ begin
15
+ node(:object, without_braces: true)
16
+ rescue SyntaxError
17
+ reset && node(:value)
18
+ end
19
+ end
20
+
21
+ def initialize(buffer, **options)
22
+ @buffer = buffer
23
+ @options = options
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ module Hjson
2
+ module AST
3
+ class String < Node
4
+ ESCAPEE = {
5
+ ?" => ?",
6
+ ?\\ => ?\\,
7
+ ?/ => ?/,
8
+ ?b => ?\b,
9
+ ?f => ?\f,
10
+ ?n => ?\n,
11
+ ?r => ?\r,
12
+ ?t => ?\t
13
+ }.freeze
14
+
15
+ rule(:double_quote) { |c| c == ?" }
16
+ rule(:escaped) { |c| c == ?\\ }
17
+ rule(:unicode) { |c| c == ?u }
18
+ rule(:escapee) { |c| !!ESCAPEE[c] }
19
+
20
+ declare :payload, ''
21
+
22
+ parser { fail SyntaxError unless double_quote? }
23
+ parser do
24
+ while read
25
+ halt(read && payload) if double_quote?
26
+ if escaped?
27
+ read
28
+ if unicode?
29
+ payload << read_unicode
30
+ elsif escapee?
31
+ payload << read_escapee
32
+ else
33
+ break
34
+ end
35
+ else
36
+ payload << char
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def read_escapee
44
+ ESCAPEE[char]
45
+ end
46
+
47
+ def read_unicode
48
+ uffff = 4.times.reduce(0) do |uffff, _|
49
+ read
50
+ hex = char.to_i(16)
51
+ break if hex.infinite?
52
+ uffff * 16 + hex
53
+ end
54
+ uffff.chr(Encoding::UTF_8)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,15 @@
1
+ module Hjson
2
+ module AST
3
+ class Value < Node
4
+ parser { node(:whitespace) }
5
+ parser do
6
+ case char
7
+ when ?{ then node(:object)
8
+ when ?[ then node(:array)
9
+ when ?" then node(:string)
10
+ else node(:any)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module Hjson
2
+ module AST
3
+ class Whitespace < Node
4
+ rule(:simple_comment) { |c| c == ?# || (char == ?/ && peek(1) == ?/) }
5
+ rule(:multi_comment) { |c| c == ?/ && peek(1) == ?* }
6
+
7
+ parser do
8
+ until eos?
9
+ read_while { char <= ' ' }
10
+ if simple_comment?
11
+ read_while { char != ?\n }
12
+ elsif multi_comment?
13
+ read
14
+ read_while { !(char == ?* && peek(1) == ?/) }
15
+ current = char
16
+ skip(2) if current
17
+ else
18
+ break
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def skip(n)
26
+ n.times { read }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,66 @@
1
+ require 'hjson/ast/parser'
2
+
3
+ module Hjson
4
+ module AST
5
+ class NumberParser < Parser
6
+ attr_accessor :leading, :zero_size, :payload
7
+
8
+ rule(:zero) { |c| c == ?0 }
9
+ rule(:symbol) { |c| negative? || c == ?+ }
10
+ rule(:period) { |c| c == ?. }
11
+ rule(:number) { |c| (?0..?9).include?(c) }
12
+ rule(:negative) { |c| c == ?- }
13
+ rule(:exponential) { |c| c == ?e || c == ?E }
14
+
15
+ rule(:leading) { !!@leading }
16
+ rule(:payload_float) { payload.include?(?.) || payload.include?(?e) || payload.include?(?E) }
17
+
18
+ parser { (self.payload = ?-) && read if negative? }
19
+
20
+ parser do
21
+ read_while if: :number? do
22
+ if leading?
23
+ if char == ?0
24
+ self.zero_size += 1
25
+ else
26
+ self.leading = false
27
+ end
28
+ end
29
+ payload << char
30
+ end
31
+ end
32
+
33
+ parser { self.zero_size -= 1 if leading? }
34
+
35
+ parser do
36
+ if period?
37
+ (payload << ?.) && read
38
+ read_while(if: :number?) { payload << char }
39
+ end
40
+ end
41
+
42
+ parser do
43
+ if exponential?
44
+ (payload << char) && read
45
+ (payload << char) && read if symbol?
46
+ read_while(if: :number?) { payload << char }
47
+ end
48
+ end
49
+
50
+ parser do
51
+ read_while { char <= ' ' }
52
+ halt if char || !zero_size.zero?
53
+ payload.public_send(payload_float? ? :to_f : :to_i)
54
+ end
55
+
56
+ def initialize(payload, **options)
57
+ @source = payload
58
+ @buffer = StringScanner.new(source)
59
+ @payload = ''
60
+ @leading = true
61
+ @zero_size = 0
62
+ @options = options
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,102 @@
1
+ require 'strscan'
2
+ require 'forwardable'
3
+ require 'hjson/errors'
4
+
5
+ module Hjson
6
+ module AST
7
+ class Parser
8
+ attr_reader :payload, :source, :buffer, :options
9
+
10
+ extend Forwardable
11
+ def_delegators :buffer, :eos?, :getch, :pos, :string, :reset, :charpos
12
+ alias_method :read, :getch
13
+
14
+ def self.parsers
15
+ @parsers ||= []
16
+ end
17
+
18
+ def self.parser(&parser)
19
+ parsers << parser
20
+ end
21
+
22
+ def self.declared_vars
23
+ @declared_vars ||= {}
24
+ end
25
+
26
+ def self.declare(var, val = nil, &block)
27
+ declared_vars[var] = block_given? ? block : val
28
+ end
29
+
30
+ def self.rule(name, &block)
31
+ define_method(:"#{name}?") do |c = nil|
32
+ c = char unless c
33
+ instance_exec(c, &block)
34
+ end
35
+ end
36
+
37
+ def parse
38
+ catch(:halt) { parsers.reduce(nil) { |_, parser| instance_eval(&parser) } }
39
+ end
40
+
41
+ def initialize(buffer, **options)
42
+ @buffer = buffer
43
+ @payload = nil
44
+ @options = options
45
+ assign_declared_vars!
46
+ end
47
+
48
+ private
49
+
50
+ def assign_declared_vars!
51
+ self.class.declared_vars.each do |var, val|
52
+ val =
53
+ case val
54
+ when Proc then instance_eval(&val)
55
+ when Symbol then send(val)
56
+ when Fixnum then val
57
+ else val.dup
58
+ end
59
+ instance_variable_set(:"@#{var}", val)
60
+ end
61
+ end
62
+
63
+ def validate(expected)
64
+ current = current_char
65
+ fail SyntaxError,
66
+ "Expected %p instead of %p" % [expected, current] if expected && expected != current
67
+ end
68
+
69
+ def halt(data = nil)
70
+ throw :halt, data
71
+ end
72
+
73
+ def parsers
74
+ self.class.parsers
75
+ end
76
+
77
+ def read_while(**options, &block)
78
+ if options[:if] && respond_to?(options[:if], true)
79
+ while char && send(options[:if])
80
+ instance_eval(&block)
81
+ read
82
+ end
83
+ else
84
+ read while char && instance_exec(char, &block)
85
+ end
86
+ end
87
+
88
+ def peek(offset = 0)
89
+ string[charpos + offset]
90
+ end
91
+
92
+ def current_char
93
+ string[charpos]
94
+ end
95
+ alias_method :char, :current_char
96
+
97
+ def char?
98
+ !!char
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,5 @@
1
+ module Hjson
2
+ Error = Class.new(StandardError)
3
+ SyntaxError = Class.new(Error)
4
+ EndOfInputError = Class.new(Error)
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'hjson/ast/parser'
2
+ require 'hjson/ast/node'
3
+
4
+ module Hjson
5
+ class Parser < AST::Node
6
+ parser { node(:root) }
7
+
8
+ def initialize(source, **options)
9
+ @source = source
10
+ @buffer = StringScanner.new(source)
11
+ @options = options
12
+ end
13
+
14
+ private
15
+
16
+ def node(*args)
17
+ args.push(**@options)
18
+ super(*args)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Hjson
2
+ VERSION = "0.1.0".freeze
3
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hjson
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - namusyaka
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashdiff
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: test-unit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Human JSON (Hjson) implementation for Ruby
84
+ email:
85
+ - namusyaka@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - hjson.gemspec
96
+ - lib/hjson.rb
97
+ - lib/hjson/ast/node.rb
98
+ - lib/hjson/ast/nodes/any.rb
99
+ - lib/hjson/ast/nodes/array.rb
100
+ - lib/hjson/ast/nodes/keyname.rb
101
+ - lib/hjson/ast/nodes/multiline.rb
102
+ - lib/hjson/ast/nodes/object.rb
103
+ - lib/hjson/ast/nodes/root.rb
104
+ - lib/hjson/ast/nodes/string.rb
105
+ - lib/hjson/ast/nodes/value.rb
106
+ - lib/hjson/ast/nodes/whitespace.rb
107
+ - lib/hjson/ast/number_parser.rb
108
+ - lib/hjson/ast/parser.rb
109
+ - lib/hjson/errors.rb
110
+ - lib/hjson/parser.rb
111
+ - lib/hjson/version.rb
112
+ homepage: https://github.com/namusyaka/hjson
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.5.1
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Human JSON (Hjson) implementation for Ruby
136
+ test_files: []