hammock-ruby 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +132 -0
- data/bin/hammock +55 -0
- data/lib/clojure/core.clj +6872 -0
- data/lib/hammock.rb +0 -0
- data/lib/hammock/aref.rb +57 -0
- data/lib/hammock/array_chunk.rb +49 -0
- data/lib/hammock/atom.rb +50 -0
- data/lib/hammock/block.rb +8 -0
- data/lib/hammock/chunk_buffer.rb +25 -0
- data/lib/hammock/chunked_cons.rb +108 -0
- data/lib/hammock/chunked_seq.rb +97 -0
- data/lib/hammock/compiler.rb +197 -0
- data/lib/hammock/environment.rb +40 -0
- data/lib/hammock/errors.rb +14 -0
- data/lib/hammock/function.rb +187 -0
- data/lib/hammock/ichunked_seq.rb +23 -0
- data/lib/hammock/ideref.rb +5 -0
- data/lib/hammock/ifn.rb +10 -0
- data/lib/hammock/ilookup.rb +6 -0
- data/lib/hammock/interfaces.rb +80 -0
- data/lib/hammock/ipersistent_collection.rb +15 -0
- data/lib/hammock/ireference.rb +15 -0
- data/lib/hammock/iseq.rb +12 -0
- data/lib/hammock/lazy_sequence.rb +82 -0
- data/lib/hammock/lazy_transformer.rb +169 -0
- data/lib/hammock/list.rb +232 -0
- data/lib/hammock/loop_locals.rb +34 -0
- data/lib/hammock/map.rb +205 -0
- data/lib/hammock/meta.rb +12 -0
- data/lib/hammock/multi_method.rb +52 -0
- data/lib/hammock/namespace.rb +185 -0
- data/lib/hammock/reader.rb +570 -0
- data/lib/hammock/recur_locals.rb +16 -0
- data/lib/hammock/reduced.rb +15 -0
- data/lib/hammock/repl.rb +29 -0
- data/lib/hammock/rt.rb +580 -0
- data/lib/hammock/sequence.rb +59 -0
- data/lib/hammock/set.rb +144 -0
- data/lib/hammock/stream.rb +40 -0
- data/lib/hammock/symbol.rb +65 -0
- data/lib/hammock/var.rb +186 -0
- data/lib/hammock/vector.rb +309 -0
- data/lib/hammock/version.rb +3 -0
- data/lib/hammock/volatile.rb +19 -0
- data/spec/examples/data.hmk +4 -0
- data/spec/hammock/reader_spec.rb +242 -0
- data/spec/hammock/rt_spec.rb +10 -0
- data/spec/hammock/sequence_spec.rb +24 -0
- metadata +139 -0
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'hamster/immutable'
|
2
|
+
|
3
|
+
require 'hammock/ipersistent_collection'
|
4
|
+
require 'hammock/meta'
|
5
|
+
require 'hammock/ifn'
|
6
|
+
require 'hammock/ilookup'
|
7
|
+
require 'hammock/chunked_seq'
|
8
|
+
require 'hammock/iseq'
|
9
|
+
|
10
|
+
module Hammock
|
11
|
+
class Vector
|
12
|
+
include Hamster::Immutable
|
13
|
+
include IPersistentCollection
|
14
|
+
include Meta
|
15
|
+
include IFn
|
16
|
+
include ILookup
|
17
|
+
include ISeq
|
18
|
+
|
19
|
+
Undefined = Object.new
|
20
|
+
|
21
|
+
BLOCK_SIZE = 32
|
22
|
+
INDEX_MASK = BLOCK_SIZE - 1
|
23
|
+
BITS_PER_LEVEL = 5
|
24
|
+
|
25
|
+
def self.alloc_from(vec, meta = nil)
|
26
|
+
vec.send(:transform) { @meta = meta }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.create(coll)
|
30
|
+
if Vector === coll
|
31
|
+
coll
|
32
|
+
else
|
33
|
+
from_array(coll.to_a)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.from_array(items, meta=nil)
|
38
|
+
items.reduce(new(meta)) { |vector, item| vector.add(item) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(meta=nil)
|
42
|
+
@meta = meta
|
43
|
+
@levels = 0
|
44
|
+
@root = []
|
45
|
+
@size = 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty?
|
49
|
+
@size == 0
|
50
|
+
end
|
51
|
+
alias null? empty?
|
52
|
+
|
53
|
+
def size
|
54
|
+
@size
|
55
|
+
end
|
56
|
+
alias count size
|
57
|
+
alias length size
|
58
|
+
|
59
|
+
def first
|
60
|
+
get(0)
|
61
|
+
end
|
62
|
+
alias head first
|
63
|
+
|
64
|
+
def last
|
65
|
+
get(-1)
|
66
|
+
end
|
67
|
+
|
68
|
+
def add(item)
|
69
|
+
transform do
|
70
|
+
update_leaf_node(@size, item)
|
71
|
+
@size += 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
alias cons add
|
75
|
+
alias conj add
|
76
|
+
|
77
|
+
def concat(arraylike)
|
78
|
+
arraylike.to_a.reduce(self) do |v, item|
|
79
|
+
v.add(item)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def set(index, item = Undefined)
|
84
|
+
return set(index, yield(get(index))) if item.equal?(Undefined)
|
85
|
+
raise IndexError if empty? or index == @size
|
86
|
+
raise IndexError if index.abs > @size
|
87
|
+
return set(@size + index, item) if index < 0
|
88
|
+
transform do
|
89
|
+
update_leaf_node(index, item)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def get(index)
|
94
|
+
return nil if empty? or index == @size
|
95
|
+
return nil if index.abs > @size
|
96
|
+
return get(@size + index) if index < 0
|
97
|
+
leaf_node_for(@root, root_index_bits, index)[index & INDEX_MASK]
|
98
|
+
end
|
99
|
+
alias nth get
|
100
|
+
|
101
|
+
def fetch(n, missing=Undefined)
|
102
|
+
unless Integer === n
|
103
|
+
raise "Index must be an Integer. Received #{n.inspect}"
|
104
|
+
end
|
105
|
+
if n >= count
|
106
|
+
if missing == Undefined
|
107
|
+
raise IndexError
|
108
|
+
else
|
109
|
+
missing
|
110
|
+
end
|
111
|
+
else
|
112
|
+
get(n)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
alias val_at fetch
|
116
|
+
|
117
|
+
def call(n, missing=nil)
|
118
|
+
fetch(n, missing)
|
119
|
+
end
|
120
|
+
|
121
|
+
def each(&block)
|
122
|
+
return self unless block_given?
|
123
|
+
traverse_depth_first(&block)
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def map(&block)
|
128
|
+
return self unless block_given?
|
129
|
+
reduce(EmptyVector) { |vector, item| vector.add(yield(item)) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def reduce(memo = Undefined)
|
133
|
+
each do |item|
|
134
|
+
memo = memo.equal?(Undefined) ? item : yield(memo, item)
|
135
|
+
end if block_given?
|
136
|
+
memo unless memo.equal?(Undefined)
|
137
|
+
end
|
138
|
+
|
139
|
+
def empty
|
140
|
+
self.class.new(meta)
|
141
|
+
end
|
142
|
+
|
143
|
+
def eql?(other)
|
144
|
+
return true if other.equal?(self)
|
145
|
+
return false unless instance_of?(other.class) && @size == other.size
|
146
|
+
@root.eql?(other.instance_variable_get(:@root))
|
147
|
+
end
|
148
|
+
alias == eql?
|
149
|
+
|
150
|
+
def to_a
|
151
|
+
ret = []
|
152
|
+
each do |obj|
|
153
|
+
ret << obj
|
154
|
+
end
|
155
|
+
ret
|
156
|
+
end
|
157
|
+
|
158
|
+
def assoc_n(idx, obj)
|
159
|
+
if idx == count
|
160
|
+
add(obj)
|
161
|
+
else
|
162
|
+
set(idx, obj)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
alias assoc assoc_n
|
167
|
+
|
168
|
+
def seq
|
169
|
+
return if count == 0
|
170
|
+
ChunkedSeq.new(self, 0, 0)
|
171
|
+
end
|
172
|
+
|
173
|
+
def array_for(i)
|
174
|
+
leaf_node_for(@root, root_index_bits, i)
|
175
|
+
end
|
176
|
+
|
177
|
+
def inspect
|
178
|
+
"[#{self.map(&:inspect).to_a.join(' ')}]"
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def traverse_depth_first(node = @root, level = @levels, &block)
|
184
|
+
return node.each(&block) if level == 0
|
185
|
+
node.each { |child| traverse_depth_first(child, level - 1, &block) }
|
186
|
+
end
|
187
|
+
|
188
|
+
def leaf_node_for(node, child_index_bits, index)
|
189
|
+
return node if child_index_bits == 0
|
190
|
+
child_index = (index >> child_index_bits) & INDEX_MASK
|
191
|
+
leaf_node_for(node[child_index], child_index_bits - BITS_PER_LEVEL, index)
|
192
|
+
end
|
193
|
+
|
194
|
+
def update_leaf_node(index, item)
|
195
|
+
copy_leaf_node_for(new_root, root_index_bits, index)[index & INDEX_MASK] = item
|
196
|
+
end
|
197
|
+
|
198
|
+
def copy_leaf_node_for(node, child_index_bits, index)
|
199
|
+
return node if child_index_bits == 0
|
200
|
+
child_index = (index >> child_index_bits) & INDEX_MASK
|
201
|
+
if child_node = node[child_index]
|
202
|
+
child_node = child_node.dup
|
203
|
+
else
|
204
|
+
child_node = []
|
205
|
+
end
|
206
|
+
node[child_index] = child_node
|
207
|
+
copy_leaf_node_for(child_node, child_index_bits - BITS_PER_LEVEL, index)
|
208
|
+
end
|
209
|
+
|
210
|
+
def new_root
|
211
|
+
if full?
|
212
|
+
@levels += 1
|
213
|
+
@root = [@root]
|
214
|
+
else
|
215
|
+
@root = @root.dup
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def full?
|
220
|
+
(@size >> root_index_bits) > 0
|
221
|
+
end
|
222
|
+
|
223
|
+
def root_index_bits
|
224
|
+
@levels * BITS_PER_LEVEL
|
225
|
+
end
|
226
|
+
|
227
|
+
class SubVector
|
228
|
+
include IPersistentCollection
|
229
|
+
include Meta
|
230
|
+
include IFn
|
231
|
+
include ILookup
|
232
|
+
attr_reader :start_idx, :end_idx, :v
|
233
|
+
|
234
|
+
def self.alloc_from(subvec, meta)
|
235
|
+
new(meta, subvec.v, subvec.start_idx, subvec.end_idx)
|
236
|
+
end
|
237
|
+
|
238
|
+
def initialize(meta, vec, start_idx, end_idx)
|
239
|
+
@meta = meta
|
240
|
+
if SubVector === vec
|
241
|
+
start_idx += vec.start_idx
|
242
|
+
end_idx += vec.end_idx
|
243
|
+
vec = vec.v
|
244
|
+
end
|
245
|
+
@v, @start_idx, @end_idx = vec, start_idx, end_idx
|
246
|
+
end
|
247
|
+
|
248
|
+
def to_a
|
249
|
+
a = @v.to_a
|
250
|
+
a[start_idx..end_idx]
|
251
|
+
end
|
252
|
+
|
253
|
+
def size
|
254
|
+
@end_idx - @start_idx
|
255
|
+
end
|
256
|
+
alias count size
|
257
|
+
alias length size
|
258
|
+
|
259
|
+
def add(obj)
|
260
|
+
self.class.new(meta, v.assoc_n(@end_idx, obj), @start_idx, @end_idx + 1)
|
261
|
+
end
|
262
|
+
alias conj add
|
263
|
+
alias cons add
|
264
|
+
|
265
|
+
def get(i)
|
266
|
+
if (@start_idx + i) >= @end_idx || i < 0
|
267
|
+
raise IndexError, "Index #{i} out of bounds."
|
268
|
+
end
|
269
|
+
return v.nth(@start_idx + i);
|
270
|
+
end
|
271
|
+
alias nth get
|
272
|
+
alias val_at get
|
273
|
+
|
274
|
+
def fetch(n, missing=Undefined)
|
275
|
+
if n >= count
|
276
|
+
if missing == Undefined
|
277
|
+
raise IndexError
|
278
|
+
else
|
279
|
+
missing
|
280
|
+
end
|
281
|
+
else
|
282
|
+
get(n)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def call(n, missing=nil)
|
287
|
+
fetch(n, missing)
|
288
|
+
end
|
289
|
+
|
290
|
+
def assoc_n(i, obj)
|
291
|
+
if (@start_idx + i) > @end_idx
|
292
|
+
raise IndexError, "Index #{i} out of bounds."
|
293
|
+
elsif (@start_idx + i) == @end_idx
|
294
|
+
cons(val)
|
295
|
+
else
|
296
|
+
self.class.new v.assoc_n(@start_idx + i, obj), @start_idx, @end_idx
|
297
|
+
end
|
298
|
+
end
|
299
|
+
alias assoc assoc_n
|
300
|
+
|
301
|
+
def inspect
|
302
|
+
"[#{to_a.map(&:inspect).join(' ')}]"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
EmptyVector = Vector.new
|
309
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'hammock/reader'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe Hammock::Reader do
|
5
|
+
def read_string(string)
|
6
|
+
io = StringIO.new(string)
|
7
|
+
Hammock::Reader.new.read(io)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "reads a list of numbers" do
|
11
|
+
result = read_string "(1 21 331)"
|
12
|
+
expect(result.to_a).to eq [1, 21, 331]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "reads nested lists of numbers" do
|
16
|
+
result = read_string "(1 (21 331))"
|
17
|
+
expect(result.to_a).to eq [1, Hammock::Sequence.from_array([21, 331])]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "reads bare numbers" do
|
21
|
+
result = read_string "324"
|
22
|
+
expect(result).to eq 324
|
23
|
+
end
|
24
|
+
|
25
|
+
it "reads floats" do
|
26
|
+
result = read_string "3.24"
|
27
|
+
expect(result).to eq 3.24
|
28
|
+
end
|
29
|
+
|
30
|
+
it "reads negative numbers" do
|
31
|
+
result = read_string "-3.24"
|
32
|
+
expect(result).to be_a Numeric
|
33
|
+
expect(result).to eq -3.24
|
34
|
+
end
|
35
|
+
|
36
|
+
it "reads symbols prefixed with -" do
|
37
|
+
result = read_string "-foo"
|
38
|
+
expect(result).to eq Hammock::Symbol.intern("-foo")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "reads strings" do
|
42
|
+
result = read_string '"Hello"'
|
43
|
+
expect(result).to eq "Hello"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "reads strings with escaped double quotes" do
|
47
|
+
result = read_string '"Hello \"Mate\""'
|
48
|
+
expect(result).to eq 'Hello "Mate"'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "reads strings with escapes" do
|
52
|
+
result = read_string '"Hello \\\\"'
|
53
|
+
expect(result).to eq 'Hello \\'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "reads strings with newlines" do
|
57
|
+
result = read_string '"Hello\nThere"'
|
58
|
+
expect(result).to eq "Hello\nThere"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "reads strings with unicode escape sequences" do
|
62
|
+
result = read_string '"Hello snowman \u2603"'
|
63
|
+
expect(result).to eq "Hello snowman ☃"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "reads character literals" do
|
67
|
+
result = read_string '[\u2603]'
|
68
|
+
expect(result.to_a.first).to eq "☃"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "reads character literal newlines" do
|
72
|
+
result = read_string '[\newline]'
|
73
|
+
expect(result.to_a.first).to eq "\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "reads lists with strings and numbers" do
|
77
|
+
result = read_string '(1.2 ("Foo" 3))'
|
78
|
+
expect(result.to_a).to eq [1.2, Hammock::Sequence.from_array(["Foo", 3])]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "reads vectors" do
|
82
|
+
result = read_string '["foo" ["bar"]]'
|
83
|
+
expect(result.to_a).to eq ["foo", Hammock::Vector.from_array(["bar"])]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "reads maps" do
|
87
|
+
result = read_string '{"key" "value"}'
|
88
|
+
expect(result).to eq Hammock::Map.from_array ["key", "value"]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "reads nested maps" do
|
92
|
+
result = read_string '{"key1" {"key2" "value"}}'
|
93
|
+
expect(result).to eq Hammock::Map.from_array(["key1", Hammock::Map.from_array(["key2", "value"])])
|
94
|
+
end
|
95
|
+
|
96
|
+
it "ignores optional commas" do
|
97
|
+
result = read_string '{"key1" "val", "key2", "val"}'
|
98
|
+
expect(result).to eq Hammock::Map.from_array ["key1", "val", "key2", "val"]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "parses basic keywords" do
|
102
|
+
result = read_string ':foo'
|
103
|
+
expect(result).to eq :foo
|
104
|
+
end
|
105
|
+
|
106
|
+
it "parses namespaced keywords" do
|
107
|
+
result = read_string ':foo/bar'
|
108
|
+
expect(result).to eq :"foo/bar"
|
109
|
+
end
|
110
|
+
|
111
|
+
it "parses implicitly namespaced keywords" do
|
112
|
+
result = read_string '::foo'
|
113
|
+
expect(result).to eq :"clojure.core/foo"
|
114
|
+
end
|
115
|
+
|
116
|
+
it "parses complex nested data structures" do
|
117
|
+
|
118
|
+
result = read_string '{:foo [1 2 3]
|
119
|
+
:bar "Baz"
|
120
|
+
:quux {:a 1 :b 2}}'
|
121
|
+
expect(result).to eq Hammock::Map.from_array([:foo,
|
122
|
+
Hammock::Vector.from_array([1,2,3]),
|
123
|
+
:bar, "Baz",
|
124
|
+
:quux,
|
125
|
+
Hammock::Map.from_array([:a, 1, :b, 2])])
|
126
|
+
end
|
127
|
+
|
128
|
+
it "parses character literals" do
|
129
|
+
result = read_string '\\a'
|
130
|
+
expect(result).to eq "a"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "parses nil, true, and false" do
|
134
|
+
result = read_string '[true false nil]'
|
135
|
+
expect(result.to_a).to eq [true, false, nil]
|
136
|
+
end
|
137
|
+
|
138
|
+
it "ignores line-ending comments" do
|
139
|
+
result = read_string '["foo" ; ignore me
|
140
|
+
"bar"] ; me too'
|
141
|
+
expect(result.to_a).to eq ["foo", "bar"]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "reads set literals" do
|
145
|
+
result = read_string '#{1 2 3}'
|
146
|
+
expect(result).to eq Hammock::Set.from_array([1, 2, 3])
|
147
|
+
end
|
148
|
+
|
149
|
+
it "reads regex literals" do
|
150
|
+
str = '#"[\\\\d]+"'
|
151
|
+
result = read_string str
|
152
|
+
expect(result).to eq Regexp.new "[\\d]+"
|
153
|
+
end
|
154
|
+
|
155
|
+
it "reads symbols" do
|
156
|
+
str = '(map foo)'
|
157
|
+
result = read_string str
|
158
|
+
expect(result).to eq Hammock::Sequence.from_array [
|
159
|
+
Hammock::Symbol.intern("map"),
|
160
|
+
Hammock::Symbol.intern("foo")]
|
161
|
+
end
|
162
|
+
|
163
|
+
it "reads symbols with ticks too" do
|
164
|
+
str = "(map' foo)"
|
165
|
+
result = read_string str
|
166
|
+
expect(result).to eq Hammock::Sequence.from_array [
|
167
|
+
Hammock::Symbol.intern("map'"),
|
168
|
+
Hammock::Symbol.intern("foo")]
|
169
|
+
end
|
170
|
+
|
171
|
+
it "reads Ruby constants" do
|
172
|
+
str = 'Object'
|
173
|
+
result = read_string str
|
174
|
+
expect(result).to eq Hammock::Symbol.intern("Object")
|
175
|
+
end
|
176
|
+
|
177
|
+
it "reads quoted symbols" do
|
178
|
+
str = "'foo"
|
179
|
+
result = read_string str
|
180
|
+
expect(result).to eq Hammock::Sequence.from_array [Hammock::Symbol.intern("quote"), Hammock::Symbol.intern("foo")]
|
181
|
+
end
|
182
|
+
|
183
|
+
it "reads quoted lists" do
|
184
|
+
str = "'(foo bar)"
|
185
|
+
result = read_string str
|
186
|
+
expected = Hammock::Sequence.from_array([Hammock::Symbol.intern("quote"), Hammock::Sequence.from_array([Hammock::Symbol.intern("foo"), Hammock::Symbol.intern("bar")])])
|
187
|
+
expect(result).to eq expected
|
188
|
+
end
|
189
|
+
|
190
|
+
it "assigns metadata to the following form" do
|
191
|
+
str = "^{:foo true} [1 2]"
|
192
|
+
result = read_string str
|
193
|
+
expected_meta = Hammock::Map.from_array [:foo, true]
|
194
|
+
expect(result.meta).to eq expected_meta
|
195
|
+
end
|
196
|
+
|
197
|
+
it "assigns keyword metadata" do
|
198
|
+
str = "^:foo [1 2]"
|
199
|
+
result = read_string str
|
200
|
+
expected_meta = Hammock::Map.from_array [:foo, true]
|
201
|
+
expect(result.meta).to eq expected_meta
|
202
|
+
end
|
203
|
+
|
204
|
+
it "assigns keyword metadata" do
|
205
|
+
str = "^:foo ^:bar [1 2]"
|
206
|
+
result = read_string str
|
207
|
+
expected_meta = Hammock::Map.from_array [:foo, true, :bar, true]
|
208
|
+
expect(result.meta).to eq expected_meta
|
209
|
+
end
|
210
|
+
|
211
|
+
it "raises error when attempting to apply meta-data to non-meta-data objects" do
|
212
|
+
str = '^:foo ^:bar "hello"'
|
213
|
+
expect { read_string(str) }.to raise_error
|
214
|
+
end
|
215
|
+
|
216
|
+
it "reads syntax quoted clojure.core things" do
|
217
|
+
form = read_string("(def foo 1)")
|
218
|
+
Hammock::Compiler.evaluate(Hammock::RT.global_env, form)
|
219
|
+
str = '`foo'
|
220
|
+
result = read_string(str)
|
221
|
+
expected = Hammock::Symbol.intern("clojure.core", "foo")
|
222
|
+
expect(result.tail.first).to eq expected
|
223
|
+
end
|
224
|
+
|
225
|
+
it "reads unquotes" do
|
226
|
+
str = '`~blah'
|
227
|
+
result = read_string(str)
|
228
|
+
expected = Hammock::Symbol.intern("blah")
|
229
|
+
expect(result).to eq expected
|
230
|
+
end
|
231
|
+
|
232
|
+
it "reads from a file" do
|
233
|
+
reader = Hammock::Reader.new
|
234
|
+
result = nil
|
235
|
+
File.open(File.expand_path("../../examples/data.hmk", __FILE__)) do |f|
|
236
|
+
result = reader.read(f)
|
237
|
+
end
|
238
|
+
expect(result.to_a).to eq [Hammock::Symbol.intern("map"),
|
239
|
+
Hammock::Symbol.intern("foo"),
|
240
|
+
Hammock::Map.from_array([:foo, "bar", :bar, 1, :quux, 1.4])]
|
241
|
+
end
|
242
|
+
end
|