fjson 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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