hammock-ruby 0.0.1.alpha

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 (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