RFC8259 8259
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +61 -0
- data/.rspec +4 -0
- data/.yardopts +2 -0
- data/Gemfile +58 -0
- data/LICENSE.txt +40 -0
- data/README.txt +1 -0
- data/RFC8259 +899 -0
- data/RFC8259.gemspec +89 -0
- data/Rakefile +86 -0
- data/lib/RFC8259.rb +94 -0
- data/lib/RFC8259/array.rb +136 -0
- data/lib/RFC8259/dumper.rb +255 -0
- data/lib/RFC8259/false.rb +79 -0
- data/lib/RFC8259/null.rb +79 -0
- data/lib/RFC8259/number.rb +149 -0
- data/lib/RFC8259/object.rb +137 -0
- data/lib/RFC8259/parser.ry +275 -0
- data/lib/RFC8259/string.rb +226 -0
- data/lib/RFC8259/true.rb +79 -0
- data/lib/RFC8259/value.rb +96 -0
- data/lib/RFC8259/version.rb +46 -0
- data/spec/RFC8259_spec.rb +305 -0
- data/spec/acceptance/README.txt +66 -0
- data/spec/acceptance/invalid/0001-ws/0001-verical-tab.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0002-null.txt +0 -0
- data/spec/acceptance/invalid/0001-ws/0003-space-in-number-1.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0004-space-in-number-2.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0005-space-in-number-3.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0006-space-in-number-4.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0007-space-in-number-5.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0008-space-in-number-6.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0009-space-in-literal.txt +1 -0
- data/spec/acceptance/invalid/0001-ws/0010-Unicode-LINE_SEPARATOR.txt +1 -0
- data/spec/acceptance/invalid/0002-comments/0001-C-style.txt +1 -0
- data/spec/acceptance/invalid/0002-comments/0002-C-plusplus-style.txt +2 -0
- data/spec/acceptance/invalid/0002-comments/0003-sh-style.txt +2 -0
- data/spec/acceptance/invalid/0002-comments/0004-python-docstring.txt +3 -0
- data/spec/acceptance/invalid/0002-comments/0005-SQL-style.txt +2 -0
- data/spec/acceptance/invalid/0002-comments/0006-BASIC-style.txt +2 -0
- data/spec/acceptance/invalid/0003-literals/0001-FALSE.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0002-NULL.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0003-TRUE.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0004-NUL.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0005-nil.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0006-undef.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0007-modifier.txt +1 -0
- data/spec/acceptance/invalid/0003-literals/0008-undefined.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0001-omit-zero.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0002-minus-dot.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0003-missing-frac.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0004-missing-exp.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0005-octal.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0006-hexadecimal.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0007-comma.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0008-perl-underscore.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0009-NaN.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0010-Inf.txt +1 -0
- data/spec/acceptance/invalid/0004-numbers/0011-Infinity.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0000-NUL.txt +0 -0
- data/spec/acceptance/invalid/0005-strings/0001-not-terminated.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0002-single-quote.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0003-back-quote.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0004-carriage-return.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0005-line-feed.txt +2 -0
- data/spec/acceptance/invalid/0005-strings/0006-unknown-escape-a.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0007-unknown-escape-perl-style.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0008-unknown-escape-C-style.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0009-unknown-escape-ruby-style.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0010-escape-too-short.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0011-C-string-concat.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0012-perl-string-concat.txt +1 -0
- data/spec/acceptance/invalid/0005-strings/0013-Java-string-concat.txt +1 -0
- data/spec/acceptance/invalid/0006-encodings/0001-CESU-8.txt +1 -0
- data/spec/acceptance/invalid/0006-encodings/0002-Windows-31J.txt +1 -0
- data/spec/acceptance/invalid/0006-encodings/0003-EBCDIC.txt +1 -0
- data/spec/acceptance/invalid/0006-encodings/0004-overlong-utf8.txt +1 -0
- data/spec/acceptance/invalid/0007-arrays/0001-lacks-open.txt +1 -0
- data/spec/acceptance/invalid/0007-arrays/0002-lacks-close.txt +1 -0
- data/spec/acceptance/invalid/0007-arrays/0003-interleaving-parens.txt +1 -0
- data/spec/acceptance/invalid/0007-arrays/0004-dangling-comma.txt +1 -0
- data/spec/acceptance/invalid/0007-arrays/0005-missing-comma.txt +1 -0
- data/spec/acceptance/invalid/0007-arrays/0006-colon-instead-of-comma.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0001-key-missing.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0002-value-missing.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0003-true-key.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0004-false-key.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0005-null-key.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0006-numeric-key.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0007-array-key.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0008-hash-key.txt +1 -0
- data/spec/acceptance/invalid/0008-hashes/0009-key-not-escaped.txt +4 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0001-JSONP.txt +1 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0002-new-Array.txt +3 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0003-new-Date.txt +1 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0004-new-Error.txt +1 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0005-Math.txt +1 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0006-regular-expression.txt +1 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0007-function.txt +7 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0008-this.txt +1 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0009-plusplus.txt +3 -0
- data/spec/acceptance/invalid/0009-javascriptisms/0010-ternary-operator.txt +1 -0
- data/spec/acceptance/valid/0001-ws/0001-space.json +1 -0
- data/spec/acceptance/valid/0001-ws/0002-tab.json +1 -0
- data/spec/acceptance/valid/0001-ws/0003-lf.json +1 -0
- data/spec/acceptance/valid/0001-ws/0004-cr.json +1 -0
- data/spec/acceptance/valid/0001-ws/0005-before.json +1 -0
- data/spec/acceptance/valid/0001-ws/0006-after.json +1 -0
- data/spec/acceptance/valid/0001-ws/0007-around-comma.json +3 -0
- data/spec/acceptance/valid/0001-ws/0008-around-colon.json +3 -0
- data/spec/acceptance/valid/0002-bare-values/0001-false.json +1 -0
- data/spec/acceptance/valid/0002-bare-values/0002-null.json +1 -0
- data/spec/acceptance/valid/0002-bare-values/0003-true.json +1 -0
- data/spec/acceptance/valid/0002-bare-values/0004-number.json +1 -0
- data/spec/acceptance/valid/0002-bare-values/0005-string.json +1 -0
- data/spec/acceptance/valid/0003-literals/0001-false.json +1 -0
- data/spec/acceptance/valid/0003-literals/0002-null.json +1 -0
- data/spec/acceptance/valid/0003-literals/0003-true.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0000-zero.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0001-one.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0002-two.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0003-three.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0004-four.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0005-five.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0006-six.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0007-seven.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0008-eight.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0009-nine.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0010-ten.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0011-minus.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0012-fraction.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0013-exponent.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0014-exponent-minus.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0015-exponent-plus.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0016-complex.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0017-DBL_MAX.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0018-DBL_MIN.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0019-subnormal-number.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0020-1E400.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0021-pi.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0022-UINT32_MAX.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0023-UINT64_MAX.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0024-INT64_MIN.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0025-high-resolution-zero.json +1 -0
- data/spec/acceptance/valid/0004-numbers/0026-high-resolution-100.json +1 -0
- data/spec/acceptance/valid/0005-strings/0001-empty.json +1 -0
- data/spec/acceptance/valid/0005-strings/0002-basic-latin.json +1 -0
- data/spec/acceptance/valid/0005-strings/0003-escapes.json +1 -0
- data/spec/acceptance/valid/0005-strings/0004-raw-unicode.json +1 -0
- data/spec/acceptance/valid/0005-strings/0005-escaped-unicode.json +1 -0
- data/spec/acceptance/valid/0005-strings/0006-escaped-NUL.json +1 -0
- data/spec/acceptance/valid/0005-strings/0007-escaped-invalid-unicode-still-valid-as-json.json +1 -0
- data/spec/acceptance/valid/0005-strings/0008-ruby-json-gem-cant-handle-this.json +1 -0
- data/spec/acceptance/valid/0005-strings/0009-unescaped-invalid-javascript-still-valid-as-json.json +1 -0
- data/spec/acceptance/valid/0005-strings/0010-escaped-separated-surrogate.json +1 -0
- data/spec/acceptance/valid/0005-strings/0011-escaped-surrogate-then-normal.json +1 -0
- data/spec/acceptance/valid/0006-m17n/0001-genesis.json +6 -0
- data/spec/acceptance/valid/0006-m17n/0002-heart-sutra.json +5 -0
- data/spec/acceptance/valid/0006-m17n/0003-escaped-valid-surrogate-pair.json +1 -0
- data/spec/acceptance/valid/0006-m17n/0004-unescaped-valid-supplementary-multilingual-plane.json +1 -0
- data/spec/acceptance/valid/0007-arrays/0000-empty.json +1 -0
- data/spec/acceptance/valid/0007-arrays/0001-one-element.json +1 -0
- data/spec/acceptance/valid/0007-arrays/0002-multiple-elements.json +33 -0
- data/spec/acceptance/valid/0007-arrays/0003-various-types.json +1 -0
- data/spec/acceptance/valid/0007-arrays/0004-nested.json +17 -0
- data/spec/acceptance/valid/0008-hashes/0000-empty.json +1 -0
- data/spec/acceptance/valid/0008-hashes/0001-onekey.json +1 -0
- data/spec/acceptance/valid/0008-hashes/0002-many-keys.json +5 -0
- data/spec/acceptance/valid/0008-hashes/0003-empty-key.json +3 -0
- data/spec/acceptance/valid/0008-hashes/0004-true-value.json +3 -0
- data/spec/acceptance/valid/0008-hashes/0005-false-value.json +3 -0
- data/spec/acceptance/valid/0008-hashes/0006-null-value.json +3 -0
- data/spec/acceptance/valid/0008-hashes/0007-string-value.json +3 -0
- data/spec/acceptance/valid/0008-hashes/0008-numeric-value.json +3 -0
- data/spec/acceptance/valid/0008-hashes/0009-array-value.json +8 -0
- data/spec/acceptance/valid/0008-hashes/0010-hash-value.json +20 -0
- data/spec/acceptance/valid/0008-hashes/0011-duplicate-key.json +4 -0
- data/spec/acceptance/valid/0008-hashes/0012-duplicate-key-in-different-representations.json +4 -0
- data/spec/acceptance/valid/0009-complicated/0001-jsonschema.json +46 -0
- data/spec/acceptance/valid/0009-complicated/0002-example-in-RFC7159-section-13.json +14 -0
- data/spec/acceptance/valid/0009-complicated/0003-example-in-RFC7159-section-13.json +22 -0
- data/spec/spec_helper.rb +60 -0
- metadata +530 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei. All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# - Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in
|
14
|
+
# the documentation and/or other materials provided with the
|
15
|
+
# distribution.
|
16
|
+
#
|
17
|
+
# - Neither the name of Internet Society, IETF or IETF Trust, nor the
|
18
|
+
# names of specific contributors, may be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior written
|
20
|
+
# permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
|
34
|
+
# The false, as defined in RFC8259 section 3.
|
35
|
+
class RFC8259::False < RFC8259::Value
|
36
|
+
|
37
|
+
# Parse the AST from parser, and convert into corrsponding value.
|
38
|
+
# @param [::Array] ast the AST, generated by the parser
|
39
|
+
# @return [False] evaluated instance
|
40
|
+
# @raise [ArgumentError] malformed input
|
41
|
+
def self.from_ast ast
|
42
|
+
raise ArgumentError "garbage included: #{ast.inspect}" if ast.length > 1
|
43
|
+
raise ArgumentError "not a false: #{ast.inspect}" if ast[0] != :false
|
44
|
+
return new
|
45
|
+
end
|
46
|
+
|
47
|
+
# convert to Ruby's false
|
48
|
+
# @return [false] the ruby counter part.
|
49
|
+
def plain_old_ruby_object
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
|
53
|
+
# JSON gem compat
|
54
|
+
# @return [::String] JSONified string representation
|
55
|
+
def to_json
|
56
|
+
return 'false'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Equality. All false instances are equal each other, plus a FalseClass
|
60
|
+
# instance is also equal to this.
|
61
|
+
def == other
|
62
|
+
case other when FalseClass, self.class
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Local Variables:
|
72
|
+
# mode: ruby
|
73
|
+
# coding: utf-8-unix
|
74
|
+
# indent-tabs-mode: t
|
75
|
+
# tab-width: 3
|
76
|
+
# ruby-indent-level: 3
|
77
|
+
# fill-column: 79
|
78
|
+
# default-justification: full
|
79
|
+
# End:
|
data/lib/RFC8259/null.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei. All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# - Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in
|
14
|
+
# the documentation and/or other materials provided with the
|
15
|
+
# distribution.
|
16
|
+
#
|
17
|
+
# - Neither the name of Internet Society, IETF or IETF Trust, nor the
|
18
|
+
# names of specific contributors, may be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior written
|
20
|
+
# permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
|
34
|
+
# The null, as defined in RFC8259 section 3.
|
35
|
+
class RFC8259::Null < RFC8259::Value
|
36
|
+
|
37
|
+
# Parse the AST from parser, and convert into corrsponding value.
|
38
|
+
# @param [::Array] ast the AST, generated by the parser
|
39
|
+
# @return [Null] evaluated instance
|
40
|
+
# @raise [ArgumentError] malformed input
|
41
|
+
def self.from_ast ast
|
42
|
+
raise ArgumentError "garbage included: #{ast.inspect}" if ast.length > 1
|
43
|
+
raise ArgumentError "not a false: #{ast.inspect}" if ast[0] != :null
|
44
|
+
return new
|
45
|
+
end
|
46
|
+
|
47
|
+
# convert to Ruby's nil
|
48
|
+
# @return [nil] the ruby counter part.
|
49
|
+
def plain_old_ruby_object
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# JSON gem compat
|
54
|
+
# @return [::String] JSONified string representation
|
55
|
+
def to_json
|
56
|
+
return 'null'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Equality. All null instances are equal each other, plus a NilClass
|
60
|
+
# instance is also equal to this.
|
61
|
+
def == other
|
62
|
+
case other when NilClass, self.class
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Local Variables:
|
72
|
+
# mode: ruby
|
73
|
+
# coding: utf-8-unix
|
74
|
+
# indent-tabs-mode: t
|
75
|
+
# tab-width: 3
|
76
|
+
# ruby-indent-level: 3
|
77
|
+
# fill-column: 79
|
78
|
+
# default-justification: full
|
79
|
+
# End:
|
@@ -0,0 +1,149 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei. All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# - Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in
|
14
|
+
# the documentation and/or other materials provided with the
|
15
|
+
# distribution.
|
16
|
+
#
|
17
|
+
# - Neither the name of Internet Society, IETF or IETF Trust, nor the
|
18
|
+
# names of specific contributors, may be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior written
|
20
|
+
# permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
|
34
|
+
require 'bigdecimal'
|
35
|
+
|
36
|
+
# The Numbers, as described in RFC8259 section 6.
|
37
|
+
class RFC8259::Number < RFC8259::Value
|
38
|
+
|
39
|
+
# Notie about technical design: this class _could_ have been made much
|
40
|
+
# faster if we implement the whole type-conversion thing by hand. But that
|
41
|
+
# is very bug-prone. So to avoid unnecessary complexity here we took an
|
42
|
+
# approach to first let everything be BigDecimal, then convert to others
|
43
|
+
# like Float.
|
44
|
+
|
45
|
+
# Parse the AST from parser, and convert into corrsponding values.
|
46
|
+
# @param [::Array] ast the AST, generated by the parser
|
47
|
+
# @return [Number] evaluated instance
|
48
|
+
# @raise [ArgumentError] malformed input
|
49
|
+
def self.from_ast ast
|
50
|
+
type, sign, int, frac, exp = *ast
|
51
|
+
raise ArgumentError, "not an object: #{ast.inspect}" if type != :number
|
52
|
+
raise ArgumentError, "not a number: #{ast.inspect}" if int.nil?
|
53
|
+
new sign, int, frac, exp
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Numeric] converted numeric
|
57
|
+
# @note this conversion might lose precision. Use `to_d` if you want
|
58
|
+
# something that fully represents this number.
|
59
|
+
def plain_old_ruby_object
|
60
|
+
if /\A[+-]?\d+\z/ =~ @to_s
|
61
|
+
return to_i
|
62
|
+
else
|
63
|
+
return to_f
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [BigDecimal] lossless conversion to numeric
|
68
|
+
def to_d
|
69
|
+
return @to_d
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [::String] the original string
|
73
|
+
def to_s
|
74
|
+
return @to_s.dup # dup just in case.
|
75
|
+
end
|
76
|
+
|
77
|
+
# JSON gem compat
|
78
|
+
# @return [::String] the original string
|
79
|
+
def to_json *;
|
80
|
+
return to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Float] conversion to float
|
84
|
+
def to_f
|
85
|
+
# This method must be ideoponent so the result is cached
|
86
|
+
unless @to_f
|
87
|
+
num = to_d.to_f
|
88
|
+
@to_f ||= num # ||= to avoid race
|
89
|
+
end
|
90
|
+
return @to_f
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Integer] conversion to integer
|
94
|
+
def to_i
|
95
|
+
# This method must be ideoponent so the result is cached
|
96
|
+
unless @to_i
|
97
|
+
num = to_d.to_i
|
98
|
+
@to_i ||= num # ||= to avoid race
|
99
|
+
end
|
100
|
+
return @to_i
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [::String] the value in string
|
104
|
+
def inspect
|
105
|
+
sprintf "#<%p:%p>", self.class, plain_old_ruby_object
|
106
|
+
end
|
107
|
+
|
108
|
+
# For pretty print (require 'pp' beforehand)
|
109
|
+
# @param [PP] pp the pp
|
110
|
+
def pretty_print pp
|
111
|
+
pp.object_group self do
|
112
|
+
pp.text ':'
|
113
|
+
plain_old_ruby_object.pretty_print pp
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Number equality is _not_ defined in the RFC so we take liberty of defining
|
118
|
+
# that to be mathematical comparison
|
119
|
+
def == other
|
120
|
+
other == @to_d
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
private_class_method:new
|
126
|
+
# @private
|
127
|
+
def initialize sign, int, frac, exp
|
128
|
+
@sign = sign # nil, '-', or '+'
|
129
|
+
@int = int.join
|
130
|
+
@frac = frac && frac.join # nil, or '.dddd..'
|
131
|
+
@exp = exp && exp.join # nil, or 'e+ddd..'
|
132
|
+
|
133
|
+
# pre-cache common computations
|
134
|
+
@to_s = [@sign, @int, @frac, @exp].join.encode(Encoding::US_ASCII) # this must be OK
|
135
|
+
@to_s.freeze # just in case
|
136
|
+
@to_d = BigDecimal.new @to_s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Local Variables:
|
142
|
+
# mode: ruby
|
143
|
+
# coding: utf-8-unix
|
144
|
+
# indent-tabs-mode: t
|
145
|
+
# tab-width: 3
|
146
|
+
# ruby-indent-level: 3
|
147
|
+
# fill-column: 79
|
148
|
+
# default-justification: full
|
149
|
+
# End:
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#! /your/favourite/path/to/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei. All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# - Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in
|
14
|
+
# the documentation and/or other materials provided with the
|
15
|
+
# distribution.
|
16
|
+
#
|
17
|
+
# - Neither the name of Internet Society, IETF or IETF Trust, nor the
|
18
|
+
# names of specific contributors, may be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior written
|
20
|
+
# permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
|
34
|
+
# The Objects, as described in RFC8259 section 4.
|
35
|
+
class RFC8259::Object < RFC8259::Value
|
36
|
+
|
37
|
+
# Parse the AST from parser, and convert into corrsponding values.
|
38
|
+
# @param [::Array] ast the AST, generated by the parser
|
39
|
+
# @return [Object] evaluated instance
|
40
|
+
# @raise [ArgumentError] malformed input
|
41
|
+
def self.from_ast ast
|
42
|
+
type, *assoc = *ast
|
43
|
+
raise ArgumentError, "not an object: #{ast.inspect}" if type != :object
|
44
|
+
assoc.map! do |a|
|
45
|
+
a.map! do |b|
|
46
|
+
RFC8259::Value.from_ast b
|
47
|
+
end
|
48
|
+
end
|
49
|
+
new assoc
|
50
|
+
end
|
51
|
+
|
52
|
+
# fetch the key.
|
53
|
+
# @note RFC8259 allows identical key to appear multiple times in an object.
|
54
|
+
# @note This is O(1)
|
55
|
+
# @param [::String, String] key key to look at
|
56
|
+
# @return [ [Value] ] corresponding value(s)
|
57
|
+
def [] key
|
58
|
+
ret = @assoc.select do |(k, _)| k == key end
|
59
|
+
ret.map! do |(_, v)| v end
|
60
|
+
return ret
|
61
|
+
end
|
62
|
+
|
63
|
+
# iterates over the pairs.
|
64
|
+
# @yield [key, value] the pair.
|
65
|
+
def each_pair &b
|
66
|
+
e = Enumerator.new do |y|
|
67
|
+
@assoc.each do |a|
|
68
|
+
y << a
|
69
|
+
end
|
70
|
+
end
|
71
|
+
return block_given? ? e.each(&b) : e
|
72
|
+
end
|
73
|
+
|
74
|
+
alias each each_pair
|
75
|
+
|
76
|
+
# @raise [RuntimeError] keys conflict
|
77
|
+
# @return [::Hash] converted object
|
78
|
+
def plain_old_ruby_object
|
79
|
+
ret = Hash.new
|
80
|
+
@assoc.each do |(k, v)|
|
81
|
+
kk = k.plain_old_ruby_object
|
82
|
+
if ret.include? kk
|
83
|
+
raise RuntimeError, "key #{kk} conflict."
|
84
|
+
else
|
85
|
+
vv = v.plain_old_ruby_object
|
86
|
+
ret.store kk, vv
|
87
|
+
end
|
88
|
+
end
|
89
|
+
return ret
|
90
|
+
end
|
91
|
+
|
92
|
+
alias to_h plain_old_ruby_object
|
93
|
+
alias to_hash plain_old_ruby_object
|
94
|
+
|
95
|
+
# @return [::String] the object in string
|
96
|
+
def inspect
|
97
|
+
hdr = sprintf "#<%p:%#016x {", self.class, self.object_id << 1
|
98
|
+
map = @assoc.map do |(k, v)|
|
99
|
+
sprintf '%p: %p', k.to_s, v
|
100
|
+
end.join ', '
|
101
|
+
hdr << map << '}>'
|
102
|
+
end
|
103
|
+
|
104
|
+
# For pretty print
|
105
|
+
# @param [PP] pp the pp
|
106
|
+
def pretty_print pp
|
107
|
+
hdr = sprintf '#<%p:%#016x', self.class, self.object_id << 1
|
108
|
+
pp.group 1, hdr, '>' do
|
109
|
+
pp.text ' '
|
110
|
+
RFC8259::Dumper.kandr pp, 1, @assoc.each, '{', '}' do |(i, j)|
|
111
|
+
i.pretty_print pp
|
112
|
+
pp.text ': '
|
113
|
+
j.pretty_print pp
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
private_class_method:new
|
120
|
+
# @private
|
121
|
+
def initialize assoc
|
122
|
+
@assoc = assoc
|
123
|
+
@assoc.each {|i| i.freeze }
|
124
|
+
@assoc.freeze
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Local Variables:
|
130
|
+
# mode: ruby
|
131
|
+
# coding: utf-8-unix
|
132
|
+
# indent-tabs-mode: t
|
133
|
+
# tab-width: 3
|
134
|
+
# ruby-indent-level: 3
|
135
|
+
# fill-column: 79
|
136
|
+
# default-justification: full
|
137
|
+
# End:
|
@@ -0,0 +1,275 @@
|
|
1
|
+
#! /your/favourite/path/to/racc
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# Copyright (c) 2014 Urabe, Shyouhei. All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# - Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in
|
14
|
+
# the documentation and/or other materials provided with the
|
15
|
+
# distribution.
|
16
|
+
#
|
17
|
+
# - Neither the name of Internet Society, IETF or IETF Trust, nor the
|
18
|
+
# names of specific contributors, may be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior written
|
20
|
+
# permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
|
34
|
+
# This is almost one-to-one translation of RFC8259 section 2 through 7, from
|
35
|
+
# Augmented BNF to Racc BNF. Should be the easiest to verify implementation
|
36
|
+
# against the spec.
|
37
|
+
#
|
38
|
+
# @note This parser has several shift/reduct conflicts. They are all around
|
39
|
+
# handling of white spaces (called "ws"), so can silently be ignored. I also
|
40
|
+
# checked the parser internal and made sure they are OK.
|
41
|
+
class RFC8259::Parser
|
42
|
+
|
43
|
+
options no_result_var
|
44
|
+
expect 28
|
45
|
+
rule
|
46
|
+
|
47
|
+
# Notes about nonterminal's names: in order to make manual verification
|
48
|
+
# easy, all the nonterminals that appear in the RFC are named as such. ABNF
|
49
|
+
# is much concise than plain BNF, so here we added several helper
|
50
|
+
# nonterminals; they are prefixed with "__" so you can distinguish if a
|
51
|
+
# nonterminal is RFC-origin or not.
|
52
|
+
|
53
|
+
# RFC8259 section 2
|
54
|
+
|
55
|
+
JSON_text : ws value ws { val[1] }
|
56
|
+
begin_array : ws "\x5B" ws # [ left square bracket
|
57
|
+
begin_object : ws "\x7B" ws # { left curly bracket
|
58
|
+
end_array : ws "\x5D" ws # ] right square bracket
|
59
|
+
end_object : ws "\x7D" ws # } right curly bracket
|
60
|
+
name_separator : ws "\x3A" ws # : colon
|
61
|
+
value_separator : ws "\x2C" ws # , comma
|
62
|
+
ws : # <- this is the '*' in the ABNF
|
63
|
+
| ws "\x20" # Space
|
64
|
+
| ws "\x09" # Horizontal tab
|
65
|
+
| ws "\x0A" # Line feed or New line
|
66
|
+
| ws "\x0D" # Carriage return
|
67
|
+
|
68
|
+
# RFC8259 section 3
|
69
|
+
|
70
|
+
value : false | null | true | object | array | number | string
|
71
|
+
false : "\x66" "\x61" "\x6c" "\x73" "\x65" { [ :false ] } # false
|
72
|
+
null : "\x6e" "\x75" "\x6c" "\x6c" { [ :null ] } # null
|
73
|
+
true : "\x74" "\x72" "\x75" "\x65" { [ :true ] } # true
|
74
|
+
|
75
|
+
# RFC8259 section 4
|
76
|
+
|
77
|
+
object : begin_object end_object { [ :object ] }
|
78
|
+
| begin_object __members__ end_object { [ :object, *val[1] ] }
|
79
|
+
__members__ : member { val }
|
80
|
+
| __members__ value_separator member { [ *val[0], val[2] ] }
|
81
|
+
member : string name_separator value { [ val[0], val[2] ] }
|
82
|
+
|
83
|
+
# RFC8259 section 5
|
84
|
+
|
85
|
+
array : begin_array end_array { [ :array ] }
|
86
|
+
| begin_array __list__ end_array { [ :array, *val[1] ] }
|
87
|
+
__list__ : value { val }
|
88
|
+
| __list__ value_separator value { [ *val[0], val[2] ] }
|
89
|
+
|
90
|
+
# RFC8259 section 6
|
91
|
+
|
92
|
+
number : __minus_p__ int __frac_p__ __exp_p__ { [ :number, *val ] }
|
93
|
+
__minus_p__ : | minus
|
94
|
+
__frac_p__ : | frac
|
95
|
+
__exp_p__ : | exp
|
96
|
+
decimal_point : "\x2E" # .
|
97
|
+
digit1_9 : "\x31" | "\x32" | "\x33" | "\x34" | "\x35"
|
98
|
+
| "\x36" | "\x37" | "\x38" | "\x39"
|
99
|
+
e : "\x65" | "\x45" # e E
|
100
|
+
exp : e __sign__ __digit_plus__ { val }
|
101
|
+
frac : decimal_point __digit_plus__ { val }
|
102
|
+
int : zero { val }
|
103
|
+
| digit1_9 { val }
|
104
|
+
| digit1_9 __digit_plus__ { [ val[0], *val[1] ] }
|
105
|
+
minus : "\x2D" # -
|
106
|
+
plus : "\x2B" # +
|
107
|
+
zero : "\x30" # 0
|
108
|
+
DIGIT : zero | digit1_9
|
109
|
+
__sign__ : | plus | minus
|
110
|
+
__digit_plus__ : DIGIT { val }
|
111
|
+
| __digit_plus__ DIGIT { [ *val[0], val[1] ] }
|
112
|
+
|
113
|
+
# RFC8259 section 7
|
114
|
+
|
115
|
+
string : quotation_mark quotation_mark { [ :string ] }
|
116
|
+
| quotation_mark __chars__ quotation_mark { [ :string, *val[1] ] }
|
117
|
+
__chars__ : char { val }
|
118
|
+
| __chars__ char { [ *val[0], val[1] ] }
|
119
|
+
char : unescaped | escape __ctrl__ { val.flatten }
|
120
|
+
__ctrl__ : "\x22" # " quotation mark U+0022
|
121
|
+
| "\x5C" # \ reverse solidus U+005C
|
122
|
+
| "\x2F" # / solidus U+002F
|
123
|
+
| "\x62" # b backspace U+0008
|
124
|
+
| "\x66" # f form feed U+000C
|
125
|
+
| "\x6E" # n line feed U+000A
|
126
|
+
| "\x72" # r carriage return U+000D
|
127
|
+
| "\x74" # t tab U+0009
|
128
|
+
| "\x75" # uXXXX U+XXXX
|
129
|
+
HEXDIG HEXDIG HEXDIG HEXDIG { val }
|
130
|
+
escape : "\x5C" # \
|
131
|
+
quotation_mark : "\x22" # "
|
132
|
+
HEXDIG : DIGIT
|
133
|
+
| "\x61" | "\x62" | "\x63" | "\x64" | "\x65" | "\x66"
|
134
|
+
| "\x41" | "\x42" | "\x43" | "\x44" | "\x45" | "\x46"
|
135
|
+
|
136
|
+
# "unescaped" is too much to list up here; use lexer instead.
|
137
|
+
# unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
138
|
+
end
|
139
|
+
|
140
|
+
---- inner
|
141
|
+
|
142
|
+
# @param [true, false] accept_bom Whether to accept BOMs
|
143
|
+
# @param [true, false] yydebug Whether to enable debug mode
|
144
|
+
def initialize accept_bom: false, yydebug: false
|
145
|
+
@accept_bom = accept_bom
|
146
|
+
@yydebug = yydebug
|
147
|
+
end
|
148
|
+
|
149
|
+
# Parses str and generates AST. The str must consist of _a_ valid JSON
|
150
|
+
# text, otherwise an exception shall raise.
|
151
|
+
#
|
152
|
+
# @param [#each_char] str IO or String or something to parse
|
153
|
+
# @return [::Array] Parsed AST
|
154
|
+
# @raise [Racc::ParseError] The input is invalid
|
155
|
+
# @raise [Encoding::CompatibilityError] The input is invalid
|
156
|
+
def parse str
|
157
|
+
@state = :init
|
158
|
+
@enum = str.enum_for:each_char
|
159
|
+
firstchar = @enum.peek
|
160
|
+
@lineno = 1
|
161
|
+
@column = 1
|
162
|
+
|
163
|
+
case @enc = firstchar.encoding
|
164
|
+
when Encoding::UTF_8,
|
165
|
+
Encoding::US_ASCII, # true subset of UTF-8
|
166
|
+
Encoding::UTF8_MAC # true subset of UTF-8
|
167
|
+
# RFC8259 sectoin 8.1 explicitly states that the input string must be
|
168
|
+
# UTF-8 encoded. That point is as clear as the sky. All other
|
169
|
+
# encodings are NG. However, what we call the ASCII encoding is the
|
170
|
+
# true subset of UTF-8. A string of ASCII must also be valid as
|
171
|
+
# UTF-8. So we allow this.
|
172
|
+
#
|
173
|
+
# There are disucssions about parsing BOMs. The original RFC4627 said
|
174
|
+
# nothing about BOMs, however its section 3 ("Encoding") cannot be
|
175
|
+
# read as if it expected BOMs. Current RFC8259 _prohibits_ to
|
176
|
+
# generate JSON texts with BOMs but _allows_ to accept.
|
177
|
+
#
|
178
|
+
# This parser can control whether to accept BOMs.
|
179
|
+
if @accept_bom and firstchar == "\u{feff}".encode(@enc)
|
180
|
+
@enum.next # consume
|
181
|
+
end
|
182
|
+
return do_parse
|
183
|
+
else
|
184
|
+
raise Encoding::CompatibilityError, <<-"end".gsub(/[\n\s]+/, ' ')
|
185
|
+
``JSON text exchanged between systems that are not part of a closed
|
186
|
+
ecosystem MUST be encoded using UTF-8'', said RFC8259 section 8.1.
|
187
|
+
The given string is NOT in the encoding (but #{@enc.inspect}).
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
def nl
|
194
|
+
@nl ||= Regexp.new('[\r\n]'.encode(@enc))
|
195
|
+
end
|
196
|
+
|
197
|
+
def sp
|
198
|
+
@nl ||= Regexp.new('\s'.encode(@enc))
|
199
|
+
end
|
200
|
+
|
201
|
+
def nm
|
202
|
+
@nl ||= Regexp.new('\d'.encode(@enc))
|
203
|
+
end
|
204
|
+
|
205
|
+
def next_token
|
206
|
+
chr = @enum.next
|
207
|
+
tok = chr.encode(Encoding::UTF_8) # dfault
|
208
|
+
newline, @newline = @newline, nl.match(chr)
|
209
|
+
if newline
|
210
|
+
@lineno += 1
|
211
|
+
@column = 1
|
212
|
+
else
|
213
|
+
@column += 1
|
214
|
+
end
|
215
|
+
case @state
|
216
|
+
when :string then # recap: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
217
|
+
case chr.ord
|
218
|
+
when 0x20..0x21 then tok = :unescaped
|
219
|
+
when 0x22 then @state = :init # "
|
220
|
+
when 0x23..0x5B then tok = :unescaped
|
221
|
+
when 0x5C then @state = :escaped # \
|
222
|
+
when 0x5D..0x10FFFF then tok = :unescaped
|
223
|
+
else @state = :string # NG unicode
|
224
|
+
end
|
225
|
+
when :init then @state = (chr.ord == '"'.ord) ? :string : :init
|
226
|
+
when :escaped then @state = (chr.ord == 'u'.ord) ? :u1 : :string
|
227
|
+
when :u1 then @state = :u2
|
228
|
+
when :u2 then @state = :u3
|
229
|
+
when :u3 then @state = :u4
|
230
|
+
when :u4 then @state = :string
|
231
|
+
end
|
232
|
+
return tok, chr
|
233
|
+
rescue StopIteration
|
234
|
+
return false, @enum
|
235
|
+
end
|
236
|
+
|
237
|
+
def on_error id, val, stack
|
238
|
+
reason = case @state
|
239
|
+
when :string
|
240
|
+
'this character is not allowed in a string; escape it.'
|
241
|
+
when :u1, :u2, :u3, :u4
|
242
|
+
'\uXXXX must exactly be a four-letter hexadecimal sequence.'
|
243
|
+
else
|
244
|
+
case val
|
245
|
+
when "'"
|
246
|
+
'you must use " to quote strings'
|
247
|
+
when '}', ']', ','
|
248
|
+
'possible extra (dangling) comma?'
|
249
|
+
when ':'
|
250
|
+
'possible confusion of {} vs []?'
|
251
|
+
when sp
|
252
|
+
'possible space inside of a number?'
|
253
|
+
when nm
|
254
|
+
'possible lack of +/- in exponent?'
|
255
|
+
else
|
256
|
+
'unexpected character'
|
257
|
+
end
|
258
|
+
end
|
259
|
+
msg = sprintf 'Syntax error near line %d, char %d (%p) @ %p: %s',
|
260
|
+
@lineno, @column, val, @enum, reason
|
261
|
+
raise Racc::ParseError, msg
|
262
|
+
end
|
263
|
+
|
264
|
+
---- footer
|
265
|
+
|
266
|
+
#
|
267
|
+
# Local Variables:
|
268
|
+
# mode: ruby
|
269
|
+
# coding: utf-8-unix
|
270
|
+
# indent-tabs-mode: t
|
271
|
+
# tab-width: 3
|
272
|
+
# ruby-indent-level: 3
|
273
|
+
# fill-column: 79
|
274
|
+
# default-justification: full
|
275
|
+
# End:
|