hammock-ruby 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +132 -0
  3. data/bin/hammock +55 -0
  4. data/lib/clojure/core.clj +6872 -0
  5. data/lib/hammock.rb +0 -0
  6. data/lib/hammock/aref.rb +57 -0
  7. data/lib/hammock/array_chunk.rb +49 -0
  8. data/lib/hammock/atom.rb +50 -0
  9. data/lib/hammock/block.rb +8 -0
  10. data/lib/hammock/chunk_buffer.rb +25 -0
  11. data/lib/hammock/chunked_cons.rb +108 -0
  12. data/lib/hammock/chunked_seq.rb +97 -0
  13. data/lib/hammock/compiler.rb +197 -0
  14. data/lib/hammock/environment.rb +40 -0
  15. data/lib/hammock/errors.rb +14 -0
  16. data/lib/hammock/function.rb +187 -0
  17. data/lib/hammock/ichunked_seq.rb +23 -0
  18. data/lib/hammock/ideref.rb +5 -0
  19. data/lib/hammock/ifn.rb +10 -0
  20. data/lib/hammock/ilookup.rb +6 -0
  21. data/lib/hammock/interfaces.rb +80 -0
  22. data/lib/hammock/ipersistent_collection.rb +15 -0
  23. data/lib/hammock/ireference.rb +15 -0
  24. data/lib/hammock/iseq.rb +12 -0
  25. data/lib/hammock/lazy_sequence.rb +82 -0
  26. data/lib/hammock/lazy_transformer.rb +169 -0
  27. data/lib/hammock/list.rb +232 -0
  28. data/lib/hammock/loop_locals.rb +34 -0
  29. data/lib/hammock/map.rb +205 -0
  30. data/lib/hammock/meta.rb +12 -0
  31. data/lib/hammock/multi_method.rb +52 -0
  32. data/lib/hammock/namespace.rb +185 -0
  33. data/lib/hammock/reader.rb +570 -0
  34. data/lib/hammock/recur_locals.rb +16 -0
  35. data/lib/hammock/reduced.rb +15 -0
  36. data/lib/hammock/repl.rb +29 -0
  37. data/lib/hammock/rt.rb +580 -0
  38. data/lib/hammock/sequence.rb +59 -0
  39. data/lib/hammock/set.rb +144 -0
  40. data/lib/hammock/stream.rb +40 -0
  41. data/lib/hammock/symbol.rb +65 -0
  42. data/lib/hammock/var.rb +186 -0
  43. data/lib/hammock/vector.rb +309 -0
  44. data/lib/hammock/version.rb +3 -0
  45. data/lib/hammock/volatile.rb +19 -0
  46. data/spec/examples/data.hmk +4 -0
  47. data/spec/hammock/reader_spec.rb +242 -0
  48. data/spec/hammock/rt_spec.rb +10 -0
  49. data/spec/hammock/sequence_spec.rb +24 -0
  50. 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,3 @@
1
+ module Hammock
2
+ VERSION = "0.0.1.alpha"
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'hammock/ideref'
2
+
3
+ module Hammock
4
+ class Volatile
5
+ include IDeref
6
+
7
+ def initialize(val)
8
+ @value = val
9
+ end
10
+
11
+ def deref
12
+ @value
13
+ end
14
+
15
+ def reset(newval)
16
+ @value = newval
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ (map foo
2
+ {:foo "bar"
3
+ :bar 1
4
+ :quux 1.4})
@@ -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