fjson 0.0.1

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.
data/lib/json_ext.so ADDED
Binary file
data/lib/parser.rb ADDED
@@ -0,0 +1,165 @@
1
+ module JSON
2
+ # This class implements the JSON parser that is used to parse a JSON string
3
+ # into a Ruby data structure.
4
+ class Parser < StringScanner
5
+ STRING = /"((?:[^"\\]|\\.)*)"/
6
+ INTEGER = /-?\d+/
7
+ FLOAT = /-?\d+\.(\d*)(?i:e[+-]?\d+)?/
8
+ OBJECT_OPEN = /\{/
9
+ OBJECT_CLOSE = /\}/
10
+ ARRAY_OPEN = /\[/
11
+ ARRAY_CLOSE = /\]/
12
+ PAIR_DELIMITER = /:/
13
+ COLLECTION_DELIMITER = /,/
14
+ TRUE = /true/
15
+ FALSE = /false/
16
+ NULL = /null/
17
+ IGNORE = %r(
18
+ (?:
19
+ //[^\n\r]*[\n\r]| # line comments
20
+ /\* # c-style comments
21
+ (?:
22
+ [^*/]| # normal chars
23
+ /[^*]| # slashes that do not start a nested comment
24
+ \*[^/]| # asterisks that do not end this comment
25
+ /(?=\*/) # single slash before this comment's end
26
+ )*
27
+ \*/ # the end of this comment
28
+ |\s+ # whitespaces
29
+ )+
30
+ )mx
31
+
32
+ UNPARSED = Object.new
33
+
34
+ # Parses the current JSON string and returns the complete data structure
35
+ # as a result.
36
+ def parse
37
+ reset
38
+ until eos?
39
+ case
40
+ when scan(ARRAY_OPEN)
41
+ return parse_array
42
+ when scan(OBJECT_OPEN)
43
+ return parse_object
44
+ when skip(IGNORE)
45
+ ;
46
+ when !((value = parse_value).equal? UNPARSED)
47
+ return value
48
+ else
49
+ raise ParserError, "source '#{peek(20)}' not in JSON!"
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def parse_string
57
+ if scan(STRING)
58
+ return '' if self[1].empty?
59
+ self[1].gsub(/\\(?:[\\bfnrt"]|u([A-Fa-f\d]{4}))/) do
60
+ case $~[0]
61
+ when '\\\\' then '\\'
62
+ when '\\b' then "\b"
63
+ when '\\f' then "\f"
64
+ when '\\n' then "\n"
65
+ when '\\r' then "\r"
66
+ when '\\t' then "\t"
67
+ when '\\"' then '"'
68
+ else
69
+ if JSON.support_unicode? and $KCODE == 'UTF8'
70
+ JSON.utf16_to_utf8($~[1])
71
+ else
72
+ # if utf8 mode is switched off or unicode not supported, try to
73
+ # transform unicode \u-notation to bytes directly:
74
+ $~[1].to_i(16).chr
75
+ end
76
+ end
77
+ end
78
+ else
79
+ UNPARSED
80
+ end
81
+ end
82
+
83
+ def parse_value
84
+ case
85
+ when scan(FLOAT)
86
+ Float(self[0])
87
+ when scan(INTEGER)
88
+ Integer(self[0])
89
+ when scan(TRUE)
90
+ true
91
+ when scan(FALSE)
92
+ false
93
+ when scan(NULL)
94
+ nil
95
+ when (string = parse_string) != UNPARSED
96
+ string
97
+ when scan(ARRAY_OPEN)
98
+ parse_array
99
+ when scan(OBJECT_OPEN)
100
+ parse_object
101
+ else
102
+ UNPARSED
103
+ end
104
+ end
105
+
106
+ def parse_array
107
+ result = []
108
+ until eos?
109
+ case
110
+ when (value = parse_value) != UNPARSED
111
+ result << value
112
+ skip(IGNORE)
113
+ unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
114
+ raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
115
+ end
116
+ when scan(ARRAY_CLOSE)
117
+ break
118
+ when skip(IGNORE)
119
+ ;
120
+ else
121
+ raise ParserError, "unexpected token in array at '#{peek(20)}'!"
122
+ end
123
+ end
124
+ result
125
+ end
126
+
127
+ def parse_object
128
+ result = {}
129
+ until eos?
130
+ case
131
+ when (string = parse_string) != UNPARSED
132
+ skip(IGNORE)
133
+ unless scan(PAIR_DELIMITER)
134
+ raise ParserError, "expected ':' in object at '#{peek(20)}'!"
135
+ end
136
+ skip(IGNORE)
137
+ unless (value = parse_value).equal? UNPARSED
138
+ result[string] = value
139
+ skip(IGNORE)
140
+ unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
141
+ raise ParserError,
142
+ "expected ',' or '}' in object at '#{peek(20)}'!"
143
+ end
144
+ else
145
+ raise ParserError, "expected value in object at '#{peek(20)}'!"
146
+ end
147
+ when scan(OBJECT_CLOSE)
148
+ if klassname = result['json_class']
149
+ klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
150
+ p.const_get(k) rescue nil
151
+ end
152
+ break unless klass and klass.json_creatable?
153
+ result = klass.json_create(result)
154
+ end
155
+ break
156
+ when skip(IGNORE)
157
+ ;
158
+ else
159
+ raise ParserError, "unexpected token in object at '#{peek(20)}'!"
160
+ end
161
+ end
162
+ result
163
+ end
164
+ end
165
+ end
data/lib/state_ext.so ADDED
Binary file
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A one dimensional Array" do
8
+ specify "should create json" do
9
+ array = [1, 'hello', :hello]
10
+ array.to_json.should_equal '[1,"hello","hello"]'
11
+ end
12
+ end
13
+
14
+ context "A multi-dimensional Array" do
15
+ specify "should create json" do
16
+ array = [1, ['hello', [:hello]]]
17
+ array.to_json.should_equal '[1,["hello",["hello"]]]'
18
+ end
19
+
20
+ specify "should not support circular dependencies" do
21
+ array = []
22
+ array << 1
23
+ array << ['hello', array]
24
+ proc {array.to_json}.should_raise JSON::CircularDatastructure
25
+ end
26
+ end
27
+
28
+ #context "Array performance" do
29
+ # specify "" do
30
+ # begin_time = Time.now
31
+ # 100000.times do
32
+ # [1, ['hello', [:hello]]].to_json
33
+ # end
34
+ # puts "#{Time.now - begin_time} seconds"
35
+ # end
36
+ #end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A false object" do
8
+ specify "should convert to json" do
9
+ false.to_json.should_equal "false"
10
+ end
11
+
12
+ specify "to_json should accept any number of arguments" do
13
+ false.to_json(1, 2)
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A float number" do
8
+ specify "should convert to json" do
9
+ (3.14).to_json.should_equal "3.14"
10
+ end
11
+
12
+ specify "to_json should accept any number of arguments" do
13
+ (3.14).to_json(1, 2)
14
+ end
15
+ end
data/spec/hash_spec.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A one dimensional Hash" do
8
+ specify "should create json" do
9
+ hash = {
10
+ :one => 1,
11
+ "hello" => :hello
12
+ }
13
+ json = hash.to_json
14
+ json.should_include "\"one\":1"
15
+ json.should_include "\"hello\":\"hello\""
16
+ end
17
+ end
18
+
19
+ context "A multi-dimensional Hash" do
20
+ specify "should create json" do
21
+ hash = {
22
+ :one => 1,
23
+ "hello" => {
24
+ :hello2 => 2
25
+ },
26
+ }
27
+ json = hash.to_json
28
+ json.should_include "\"one\":1"
29
+ json.should_include "\"hello\":{\"hello2\":2}"
30
+ end
31
+
32
+ specify "should not support circular dependencies" do
33
+ hash = { :one => 1 }
34
+ hash["hello"] = hash
35
+ proc {hash.to_json}.should_raise JSON::CircularDatastructure
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "An integer" do
8
+ specify "should convert to json" do
9
+ 5.to_json.should_equal "5"
10
+ end
11
+
12
+ specify "to_json should accept any number of arguments" do
13
+ 5.to_json(1, 2)
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A nil object" do
8
+ specify "should convert to json" do
9
+ nil.to_json.should_equal "null"
10
+ end
11
+
12
+ specify "to_json should accept any number of arguments" do
13
+ nil.to_json(1, 2)
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "An object" do
8
+ specify "should convert its string representation to json" do
9
+ obj = Object.new
10
+ obj_str = obj.to_s
11
+ obj.to_json.should_equal '"' + obj_str + '"'
12
+ end
13
+
14
+ specify "to_json should accept any number of arguments" do
15
+ Object.new.to_json(1, 2)
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec'
2
+ dir = File.dirname(__FILE__)
3
+ args = ARGV.dup
4
+
5
+ class Object
6
+ def metaclass
7
+ class << self; self; end
8
+ end
9
+
10
+ def define_instance_method(sym, &block)
11
+ metaclass.__send__(:define_method, sym, &block)
12
+ end
13
+ end
14
+
15
+ unless args.include?("-f") || args.include?("--format")
16
+ args << "--format"
17
+ args << "specdoc"
18
+ end
19
+ args << $0
20
+
21
+ $context_runner = ::Spec::Runner::OptionParser.create_context_runner(args, false, STDERR, STDOUT)
22
+
23
+ at_exit do
24
+ unless context_runner.instance_eval {@reporter}.instance_eval {@start_time}
25
+ context_runner.run(false)
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ dir = File.dirname(__FILE__)
2
+
3
+ Dir["#{dir}/**/*_spec.rb"].each do |file|
4
+ require File.expand_path(file)
5
+ end
@@ -0,0 +1,67 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A JSON state" do
8
+ specify "method from_state should return the passed in object when a state is passed in" do
9
+ state = JSON::State.new
10
+ JSON::State.from_state(state).should_equal state
11
+ end
12
+
13
+ specify "method from_state should return a new state with hash values when a hash is passed in" do
14
+ shim_state_class = Object.new
15
+ shim_state_class.extend JSON::State::ClassMethods
16
+ passed_params = nil
17
+ shim_state_class.define_instance_method(:new) {|params| passed_params = params}
18
+
19
+ args = {}
20
+ shim_state_class.from_state(args)
21
+ passed_params.should_equal args
22
+ end
23
+
24
+ specify "method from_state should return a new state with default parameters when an object that is not " +
25
+ "a state or a hash is passed in" do
26
+ state = JSON::State.from_state(Object.new)
27
+ state.should_be_instance_of JSON::State
28
+ end
29
+
30
+ specify "should initialize its attributes to their defaults" do
31
+ state = JSON::State.new
32
+ state.indent.should_equal ""
33
+ state.space.should_equal ""
34
+ state.object_nl.should_equal ""
35
+ state.array_nl.should_equal ""
36
+ state.instance_eval {@seen}.should_equal({})
37
+ end
38
+
39
+ specify "should initialize its attributes with passed in values" do
40
+ params = {
41
+ :indent => Object.new,
42
+ :space => Object.new,
43
+ :object_nl => Object.new,
44
+ :array_nl => Object.new
45
+ }
46
+ state = JSON::State.new(params)
47
+ state.indent.should_equal params[:indent]
48
+ state.space.should_equal params[:space]
49
+ state.object_nl.should_equal params[:object_nl]
50
+ state.array_nl.should_equal params[:array_nl]
51
+ end
52
+
53
+ specify "should remember objects" do
54
+ state = JSON::State.new
55
+ obj = Object.new
56
+ state.remember(obj)
57
+ state.should_be_seen(obj)
58
+ end
59
+
60
+ specify "should forget objects" do
61
+ state = JSON::State.new
62
+ obj = Object.new
63
+ state.remember(obj)
64
+ state.forget(obj)
65
+ state.should_not_be_seen(obj)
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ dir = File.dirname(__FILE__)
4
+ require File.expand_path("#{dir}/spec_helper")
5
+ require File.expand_path("#{dir}/../lib/fjson")
6
+
7
+ context "A string" do
8
+ specify "should convert to json" do
9
+ "test".to_json.should_equal '"test"'
10
+ end
11
+
12
+ specify "to_json should accept any number of arguments" do
13
+ "test".to_json(1, 2)
14
+ end
15
+ end