RFC8259 8259

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.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +61 -0
  3. data/.rspec +4 -0
  4. data/.yardopts +2 -0
  5. data/Gemfile +58 -0
  6. data/LICENSE.txt +40 -0
  7. data/README.txt +1 -0
  8. data/RFC8259 +899 -0
  9. data/RFC8259.gemspec +89 -0
  10. data/Rakefile +86 -0
  11. data/lib/RFC8259.rb +94 -0
  12. data/lib/RFC8259/array.rb +136 -0
  13. data/lib/RFC8259/dumper.rb +255 -0
  14. data/lib/RFC8259/false.rb +79 -0
  15. data/lib/RFC8259/null.rb +79 -0
  16. data/lib/RFC8259/number.rb +149 -0
  17. data/lib/RFC8259/object.rb +137 -0
  18. data/lib/RFC8259/parser.ry +275 -0
  19. data/lib/RFC8259/string.rb +226 -0
  20. data/lib/RFC8259/true.rb +79 -0
  21. data/lib/RFC8259/value.rb +96 -0
  22. data/lib/RFC8259/version.rb +46 -0
  23. data/spec/RFC8259_spec.rb +305 -0
  24. data/spec/acceptance/README.txt +66 -0
  25. data/spec/acceptance/invalid/0001-ws/0001-verical-tab.txt +1 -0
  26. data/spec/acceptance/invalid/0001-ws/0002-null.txt +0 -0
  27. data/spec/acceptance/invalid/0001-ws/0003-space-in-number-1.txt +1 -0
  28. data/spec/acceptance/invalid/0001-ws/0004-space-in-number-2.txt +1 -0
  29. data/spec/acceptance/invalid/0001-ws/0005-space-in-number-3.txt +1 -0
  30. data/spec/acceptance/invalid/0001-ws/0006-space-in-number-4.txt +1 -0
  31. data/spec/acceptance/invalid/0001-ws/0007-space-in-number-5.txt +1 -0
  32. data/spec/acceptance/invalid/0001-ws/0008-space-in-number-6.txt +1 -0
  33. data/spec/acceptance/invalid/0001-ws/0009-space-in-literal.txt +1 -0
  34. data/spec/acceptance/invalid/0001-ws/0010-Unicode-LINE_SEPARATOR.txt +1 -0
  35. data/spec/acceptance/invalid/0002-comments/0001-C-style.txt +1 -0
  36. data/spec/acceptance/invalid/0002-comments/0002-C-plusplus-style.txt +2 -0
  37. data/spec/acceptance/invalid/0002-comments/0003-sh-style.txt +2 -0
  38. data/spec/acceptance/invalid/0002-comments/0004-python-docstring.txt +3 -0
  39. data/spec/acceptance/invalid/0002-comments/0005-SQL-style.txt +2 -0
  40. data/spec/acceptance/invalid/0002-comments/0006-BASIC-style.txt +2 -0
  41. data/spec/acceptance/invalid/0003-literals/0001-FALSE.txt +1 -0
  42. data/spec/acceptance/invalid/0003-literals/0002-NULL.txt +1 -0
  43. data/spec/acceptance/invalid/0003-literals/0003-TRUE.txt +1 -0
  44. data/spec/acceptance/invalid/0003-literals/0004-NUL.txt +1 -0
  45. data/spec/acceptance/invalid/0003-literals/0005-nil.txt +1 -0
  46. data/spec/acceptance/invalid/0003-literals/0006-undef.txt +1 -0
  47. data/spec/acceptance/invalid/0003-literals/0007-modifier.txt +1 -0
  48. data/spec/acceptance/invalid/0003-literals/0008-undefined.txt +1 -0
  49. data/spec/acceptance/invalid/0004-numbers/0001-omit-zero.txt +1 -0
  50. data/spec/acceptance/invalid/0004-numbers/0002-minus-dot.txt +1 -0
  51. data/spec/acceptance/invalid/0004-numbers/0003-missing-frac.txt +1 -0
  52. data/spec/acceptance/invalid/0004-numbers/0004-missing-exp.txt +1 -0
  53. data/spec/acceptance/invalid/0004-numbers/0005-octal.txt +1 -0
  54. data/spec/acceptance/invalid/0004-numbers/0006-hexadecimal.txt +1 -0
  55. data/spec/acceptance/invalid/0004-numbers/0007-comma.txt +1 -0
  56. data/spec/acceptance/invalid/0004-numbers/0008-perl-underscore.txt +1 -0
  57. data/spec/acceptance/invalid/0004-numbers/0009-NaN.txt +1 -0
  58. data/spec/acceptance/invalid/0004-numbers/0010-Inf.txt +1 -0
  59. data/spec/acceptance/invalid/0004-numbers/0011-Infinity.txt +1 -0
  60. data/spec/acceptance/invalid/0005-strings/0000-NUL.txt +0 -0
  61. data/spec/acceptance/invalid/0005-strings/0001-not-terminated.txt +1 -0
  62. data/spec/acceptance/invalid/0005-strings/0002-single-quote.txt +1 -0
  63. data/spec/acceptance/invalid/0005-strings/0003-back-quote.txt +1 -0
  64. data/spec/acceptance/invalid/0005-strings/0004-carriage-return.txt +1 -0
  65. data/spec/acceptance/invalid/0005-strings/0005-line-feed.txt +2 -0
  66. data/spec/acceptance/invalid/0005-strings/0006-unknown-escape-a.txt +1 -0
  67. data/spec/acceptance/invalid/0005-strings/0007-unknown-escape-perl-style.txt +1 -0
  68. data/spec/acceptance/invalid/0005-strings/0008-unknown-escape-C-style.txt +1 -0
  69. data/spec/acceptance/invalid/0005-strings/0009-unknown-escape-ruby-style.txt +1 -0
  70. data/spec/acceptance/invalid/0005-strings/0010-escape-too-short.txt +1 -0
  71. data/spec/acceptance/invalid/0005-strings/0011-C-string-concat.txt +1 -0
  72. data/spec/acceptance/invalid/0005-strings/0012-perl-string-concat.txt +1 -0
  73. data/spec/acceptance/invalid/0005-strings/0013-Java-string-concat.txt +1 -0
  74. data/spec/acceptance/invalid/0006-encodings/0001-CESU-8.txt +1 -0
  75. data/spec/acceptance/invalid/0006-encodings/0002-Windows-31J.txt +1 -0
  76. data/spec/acceptance/invalid/0006-encodings/0003-EBCDIC.txt +1 -0
  77. data/spec/acceptance/invalid/0006-encodings/0004-overlong-utf8.txt +1 -0
  78. data/spec/acceptance/invalid/0007-arrays/0001-lacks-open.txt +1 -0
  79. data/spec/acceptance/invalid/0007-arrays/0002-lacks-close.txt +1 -0
  80. data/spec/acceptance/invalid/0007-arrays/0003-interleaving-parens.txt +1 -0
  81. data/spec/acceptance/invalid/0007-arrays/0004-dangling-comma.txt +1 -0
  82. data/spec/acceptance/invalid/0007-arrays/0005-missing-comma.txt +1 -0
  83. data/spec/acceptance/invalid/0007-arrays/0006-colon-instead-of-comma.txt +1 -0
  84. data/spec/acceptance/invalid/0008-hashes/0001-key-missing.txt +1 -0
  85. data/spec/acceptance/invalid/0008-hashes/0002-value-missing.txt +1 -0
  86. data/spec/acceptance/invalid/0008-hashes/0003-true-key.txt +1 -0
  87. data/spec/acceptance/invalid/0008-hashes/0004-false-key.txt +1 -0
  88. data/spec/acceptance/invalid/0008-hashes/0005-null-key.txt +1 -0
  89. data/spec/acceptance/invalid/0008-hashes/0006-numeric-key.txt +1 -0
  90. data/spec/acceptance/invalid/0008-hashes/0007-array-key.txt +1 -0
  91. data/spec/acceptance/invalid/0008-hashes/0008-hash-key.txt +1 -0
  92. data/spec/acceptance/invalid/0008-hashes/0009-key-not-escaped.txt +4 -0
  93. data/spec/acceptance/invalid/0009-javascriptisms/0001-JSONP.txt +1 -0
  94. data/spec/acceptance/invalid/0009-javascriptisms/0002-new-Array.txt +3 -0
  95. data/spec/acceptance/invalid/0009-javascriptisms/0003-new-Date.txt +1 -0
  96. data/spec/acceptance/invalid/0009-javascriptisms/0004-new-Error.txt +1 -0
  97. data/spec/acceptance/invalid/0009-javascriptisms/0005-Math.txt +1 -0
  98. data/spec/acceptance/invalid/0009-javascriptisms/0006-regular-expression.txt +1 -0
  99. data/spec/acceptance/invalid/0009-javascriptisms/0007-function.txt +7 -0
  100. data/spec/acceptance/invalid/0009-javascriptisms/0008-this.txt +1 -0
  101. data/spec/acceptance/invalid/0009-javascriptisms/0009-plusplus.txt +3 -0
  102. data/spec/acceptance/invalid/0009-javascriptisms/0010-ternary-operator.txt +1 -0
  103. data/spec/acceptance/valid/0001-ws/0001-space.json +1 -0
  104. data/spec/acceptance/valid/0001-ws/0002-tab.json +1 -0
  105. data/spec/acceptance/valid/0001-ws/0003-lf.json +1 -0
  106. data/spec/acceptance/valid/0001-ws/0004-cr.json +1 -0
  107. data/spec/acceptance/valid/0001-ws/0005-before.json +1 -0
  108. data/spec/acceptance/valid/0001-ws/0006-after.json +1 -0
  109. data/spec/acceptance/valid/0001-ws/0007-around-comma.json +3 -0
  110. data/spec/acceptance/valid/0001-ws/0008-around-colon.json +3 -0
  111. data/spec/acceptance/valid/0002-bare-values/0001-false.json +1 -0
  112. data/spec/acceptance/valid/0002-bare-values/0002-null.json +1 -0
  113. data/spec/acceptance/valid/0002-bare-values/0003-true.json +1 -0
  114. data/spec/acceptance/valid/0002-bare-values/0004-number.json +1 -0
  115. data/spec/acceptance/valid/0002-bare-values/0005-string.json +1 -0
  116. data/spec/acceptance/valid/0003-literals/0001-false.json +1 -0
  117. data/spec/acceptance/valid/0003-literals/0002-null.json +1 -0
  118. data/spec/acceptance/valid/0003-literals/0003-true.json +1 -0
  119. data/spec/acceptance/valid/0004-numbers/0000-zero.json +1 -0
  120. data/spec/acceptance/valid/0004-numbers/0001-one.json +1 -0
  121. data/spec/acceptance/valid/0004-numbers/0002-two.json +1 -0
  122. data/spec/acceptance/valid/0004-numbers/0003-three.json +1 -0
  123. data/spec/acceptance/valid/0004-numbers/0004-four.json +1 -0
  124. data/spec/acceptance/valid/0004-numbers/0005-five.json +1 -0
  125. data/spec/acceptance/valid/0004-numbers/0006-six.json +1 -0
  126. data/spec/acceptance/valid/0004-numbers/0007-seven.json +1 -0
  127. data/spec/acceptance/valid/0004-numbers/0008-eight.json +1 -0
  128. data/spec/acceptance/valid/0004-numbers/0009-nine.json +1 -0
  129. data/spec/acceptance/valid/0004-numbers/0010-ten.json +1 -0
  130. data/spec/acceptance/valid/0004-numbers/0011-minus.json +1 -0
  131. data/spec/acceptance/valid/0004-numbers/0012-fraction.json +1 -0
  132. data/spec/acceptance/valid/0004-numbers/0013-exponent.json +1 -0
  133. data/spec/acceptance/valid/0004-numbers/0014-exponent-minus.json +1 -0
  134. data/spec/acceptance/valid/0004-numbers/0015-exponent-plus.json +1 -0
  135. data/spec/acceptance/valid/0004-numbers/0016-complex.json +1 -0
  136. data/spec/acceptance/valid/0004-numbers/0017-DBL_MAX.json +1 -0
  137. data/spec/acceptance/valid/0004-numbers/0018-DBL_MIN.json +1 -0
  138. data/spec/acceptance/valid/0004-numbers/0019-subnormal-number.json +1 -0
  139. data/spec/acceptance/valid/0004-numbers/0020-1E400.json +1 -0
  140. data/spec/acceptance/valid/0004-numbers/0021-pi.json +1 -0
  141. data/spec/acceptance/valid/0004-numbers/0022-UINT32_MAX.json +1 -0
  142. data/spec/acceptance/valid/0004-numbers/0023-UINT64_MAX.json +1 -0
  143. data/spec/acceptance/valid/0004-numbers/0024-INT64_MIN.json +1 -0
  144. data/spec/acceptance/valid/0004-numbers/0025-high-resolution-zero.json +1 -0
  145. data/spec/acceptance/valid/0004-numbers/0026-high-resolution-100.json +1 -0
  146. data/spec/acceptance/valid/0005-strings/0001-empty.json +1 -0
  147. data/spec/acceptance/valid/0005-strings/0002-basic-latin.json +1 -0
  148. data/spec/acceptance/valid/0005-strings/0003-escapes.json +1 -0
  149. data/spec/acceptance/valid/0005-strings/0004-raw-unicode.json +1 -0
  150. data/spec/acceptance/valid/0005-strings/0005-escaped-unicode.json +1 -0
  151. data/spec/acceptance/valid/0005-strings/0006-escaped-NUL.json +1 -0
  152. data/spec/acceptance/valid/0005-strings/0007-escaped-invalid-unicode-still-valid-as-json.json +1 -0
  153. data/spec/acceptance/valid/0005-strings/0008-ruby-json-gem-cant-handle-this.json +1 -0
  154. data/spec/acceptance/valid/0005-strings/0009-unescaped-invalid-javascript-still-valid-as-json.json +1 -0
  155. data/spec/acceptance/valid/0005-strings/0010-escaped-separated-surrogate.json +1 -0
  156. data/spec/acceptance/valid/0005-strings/0011-escaped-surrogate-then-normal.json +1 -0
  157. data/spec/acceptance/valid/0006-m17n/0001-genesis.json +6 -0
  158. data/spec/acceptance/valid/0006-m17n/0002-heart-sutra.json +5 -0
  159. data/spec/acceptance/valid/0006-m17n/0003-escaped-valid-surrogate-pair.json +1 -0
  160. data/spec/acceptance/valid/0006-m17n/0004-unescaped-valid-supplementary-multilingual-plane.json +1 -0
  161. data/spec/acceptance/valid/0007-arrays/0000-empty.json +1 -0
  162. data/spec/acceptance/valid/0007-arrays/0001-one-element.json +1 -0
  163. data/spec/acceptance/valid/0007-arrays/0002-multiple-elements.json +33 -0
  164. data/spec/acceptance/valid/0007-arrays/0003-various-types.json +1 -0
  165. data/spec/acceptance/valid/0007-arrays/0004-nested.json +17 -0
  166. data/spec/acceptance/valid/0008-hashes/0000-empty.json +1 -0
  167. data/spec/acceptance/valid/0008-hashes/0001-onekey.json +1 -0
  168. data/spec/acceptance/valid/0008-hashes/0002-many-keys.json +5 -0
  169. data/spec/acceptance/valid/0008-hashes/0003-empty-key.json +3 -0
  170. data/spec/acceptance/valid/0008-hashes/0004-true-value.json +3 -0
  171. data/spec/acceptance/valid/0008-hashes/0005-false-value.json +3 -0
  172. data/spec/acceptance/valid/0008-hashes/0006-null-value.json +3 -0
  173. data/spec/acceptance/valid/0008-hashes/0007-string-value.json +3 -0
  174. data/spec/acceptance/valid/0008-hashes/0008-numeric-value.json +3 -0
  175. data/spec/acceptance/valid/0008-hashes/0009-array-value.json +8 -0
  176. data/spec/acceptance/valid/0008-hashes/0010-hash-value.json +20 -0
  177. data/spec/acceptance/valid/0008-hashes/0011-duplicate-key.json +4 -0
  178. data/spec/acceptance/valid/0008-hashes/0012-duplicate-key-in-different-representations.json +4 -0
  179. data/spec/acceptance/valid/0009-complicated/0001-jsonschema.json +46 -0
  180. data/spec/acceptance/valid/0009-complicated/0002-example-in-RFC7159-section-13.json +14 -0
  181. data/spec/acceptance/valid/0009-complicated/0003-example-in-RFC7159-section-13.json +22 -0
  182. data/spec/spec_helper.rb +60 -0
  183. 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:
@@ -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: