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.
- 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
data/lib/hammock.rb
ADDED
File without changes
|
data/lib/hammock/aref.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'hammock/map'
|
2
|
+
require 'hammock/errors'
|
3
|
+
require 'hammock/ireference'
|
4
|
+
|
5
|
+
module Hammock
|
6
|
+
class ARef
|
7
|
+
include IReference
|
8
|
+
|
9
|
+
attr_writer :validator
|
10
|
+
|
11
|
+
def initialize(meta=nil)
|
12
|
+
@meta = meta
|
13
|
+
@watches = Map.new
|
14
|
+
@validator = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(fn_or_obj, obj=nil)
|
18
|
+
if obj
|
19
|
+
fn = fn_or_obj
|
20
|
+
else
|
21
|
+
obj = fn_or_obj
|
22
|
+
fn = validator
|
23
|
+
end
|
24
|
+
|
25
|
+
if fn && !fn.call(obj)
|
26
|
+
raise Error, "Illegal reference state"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_watch(key, cb)
|
31
|
+
@watches = watches.assoc(key, cb)
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove_watch(key)
|
35
|
+
@watches = watches.without(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def notify_watches(oldval, newval)
|
39
|
+
return if watches.count == 0
|
40
|
+
watches.each do |key, fn|
|
41
|
+
if fn
|
42
|
+
fn.call(key, self, oldval, newval)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def watches
|
48
|
+
@watches
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def validator
|
54
|
+
@validator
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'hammock/errors'
|
2
|
+
|
3
|
+
module Hammock
|
4
|
+
class ArrayChunk
|
5
|
+
Undefined = Object.new
|
6
|
+
|
7
|
+
def initialize(array, offset, endi=nil)
|
8
|
+
@array = array
|
9
|
+
@offset = offset
|
10
|
+
@end = endi || array.length
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch(i, not_found=Undefined)
|
14
|
+
if not_found == Undefined || (i >= 0 && i < count)
|
15
|
+
@array[@offset + i]
|
16
|
+
else
|
17
|
+
not_found
|
18
|
+
end
|
19
|
+
end
|
20
|
+
alias nth fetch
|
21
|
+
|
22
|
+
def count
|
23
|
+
@end - @offset
|
24
|
+
end
|
25
|
+
alias size count
|
26
|
+
alias length count
|
27
|
+
|
28
|
+
def drop_first
|
29
|
+
if @offset == @end
|
30
|
+
raise Error, "drop_first of empty chunk"
|
31
|
+
else
|
32
|
+
self.class.new(@array, @offset + 1, @end)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def reduce(fn, start)
|
37
|
+
ret = fn.call(start, @array[@offset])
|
38
|
+
return ret if RT.reduced?(ret)
|
39
|
+
|
40
|
+
x = @offset + 1
|
41
|
+
while x < @end
|
42
|
+
ret = fn.call(ret, @array[x])
|
43
|
+
return ret if RT.reduced?(ret)
|
44
|
+
x += 1
|
45
|
+
end
|
46
|
+
ret
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/hammock/atom.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
require 'hammock/aref'
|
3
|
+
require 'hammock/ideref'
|
4
|
+
|
5
|
+
module Hammock
|
6
|
+
class Atom < ARef
|
7
|
+
include IDeref
|
8
|
+
def initialize(obj, meta=nil)
|
9
|
+
super(meta)
|
10
|
+
@state = Atomic.new(obj)
|
11
|
+
end
|
12
|
+
|
13
|
+
def deref
|
14
|
+
@state.get
|
15
|
+
end
|
16
|
+
|
17
|
+
def swap(fn, *args)
|
18
|
+
loop do
|
19
|
+
v = deref
|
20
|
+
new_value = fn.call(v, *args)
|
21
|
+
validate(new_value)
|
22
|
+
if @state.compare_and_set(v, new_value)
|
23
|
+
notify_watches(v, new_value)
|
24
|
+
return new_value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def compare_and_set(old_value, new_value)
|
30
|
+
validate(new_value)
|
31
|
+
ret = @state.compare_and_set(old_value, new_value)
|
32
|
+
notify_watches(old_value, new_value) if ret
|
33
|
+
ret
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset(new_value)
|
37
|
+
old_value = deref
|
38
|
+
validate(new_value)
|
39
|
+
@state.value = new_value
|
40
|
+
notify_watches(old_value, new_value)
|
41
|
+
new_value
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
prefix = "#<#{self.class}:0x#{self.__id__.to_s(16)}"
|
46
|
+
"#{prefix} #{deref.inspect}>"
|
47
|
+
end
|
48
|
+
alias to_s inspect
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hammock/array_chunk'
|
2
|
+
|
3
|
+
module Hammock
|
4
|
+
class ChunkBuffer
|
5
|
+
def initialize(capacity)
|
6
|
+
@buffer = ::Array.new(capacity)
|
7
|
+
@end = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(obj)
|
11
|
+
@buffer[@end] = obj
|
12
|
+
@end += 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def chunk
|
16
|
+
ret = ArrayChunk.new(@buffer, 0, @end)
|
17
|
+
@buffer = nil
|
18
|
+
ret
|
19
|
+
end
|
20
|
+
|
21
|
+
def count
|
22
|
+
@end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'hammock/meta'
|
2
|
+
require 'hammock/ichunked_seq'
|
3
|
+
require 'hammock/iseq'
|
4
|
+
|
5
|
+
module Hammock
|
6
|
+
class ChunkedCons
|
7
|
+
include Meta
|
8
|
+
include IChunkedSeq
|
9
|
+
include ISeq
|
10
|
+
|
11
|
+
def initialize(chunk, more, meta=nil)
|
12
|
+
@chunk = chunk
|
13
|
+
@_more = more
|
14
|
+
@meta = meta
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_meta(meta)
|
18
|
+
if @meta == meta
|
19
|
+
self
|
20
|
+
else
|
21
|
+
self.class.new(@chunk, @_more, meta)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
@chunk.nth(0)
|
27
|
+
end
|
28
|
+
alias head first
|
29
|
+
|
30
|
+
def rest
|
31
|
+
if @chunk.count > 1
|
32
|
+
self.class.new(@chunk.drop_first, @_more)
|
33
|
+
elsif @_more.nil?
|
34
|
+
EmptyList.new
|
35
|
+
else
|
36
|
+
@_more
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def next
|
41
|
+
if @chunk.count > 1
|
42
|
+
self.class.new(@chunk.drop_first, @_more)
|
43
|
+
else
|
44
|
+
chunked_next
|
45
|
+
end
|
46
|
+
end
|
47
|
+
alias tail next
|
48
|
+
|
49
|
+
def chunked_first
|
50
|
+
@chunk
|
51
|
+
end
|
52
|
+
|
53
|
+
def chunked_next
|
54
|
+
chunked_rest.seq
|
55
|
+
end
|
56
|
+
|
57
|
+
def chunked_rest
|
58
|
+
if @_more.nil?
|
59
|
+
EmptyList.new
|
60
|
+
else
|
61
|
+
@_more
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def each
|
66
|
+
return self unless block_given?
|
67
|
+
list = self
|
68
|
+
until list.nil? || list.empty?
|
69
|
+
yield(list.head)
|
70
|
+
list = list.tail
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_a
|
75
|
+
ret = []
|
76
|
+
each do |i|
|
77
|
+
ret << i
|
78
|
+
end
|
79
|
+
ret
|
80
|
+
end
|
81
|
+
|
82
|
+
def empty?
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
def seq
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def empty
|
91
|
+
EmptyList.new
|
92
|
+
end
|
93
|
+
|
94
|
+
def dup
|
95
|
+
self
|
96
|
+
end
|
97
|
+
alias clone dup
|
98
|
+
|
99
|
+
def to_list
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
"(#{to_a.map(&:inspect).join(' ')})"
|
105
|
+
end
|
106
|
+
alias to_s inspect
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'hammock/meta'
|
2
|
+
require 'hammock/array_chunk'
|
3
|
+
require 'hammock/ichunked_seq'
|
4
|
+
require 'hammock/iseq'
|
5
|
+
|
6
|
+
module Hammock
|
7
|
+
class ChunkedSeq
|
8
|
+
include IChunkedSeq
|
9
|
+
include ISeq
|
10
|
+
include Meta
|
11
|
+
|
12
|
+
def initialize(vec, i, offset, node=nil, meta=nil)
|
13
|
+
@vec = vec
|
14
|
+
@i = i
|
15
|
+
@offset = offset
|
16
|
+
@meta = meta
|
17
|
+
@node = node || @vec.array_for(i)
|
18
|
+
end
|
19
|
+
|
20
|
+
def chunked_first
|
21
|
+
ArrayChunk.new(@node, @offset)
|
22
|
+
end
|
23
|
+
|
24
|
+
def chunked_next
|
25
|
+
if @i + @node.length < @vec.count
|
26
|
+
ChunkedSeq.new(vec, @i + @node.length, 0)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def chunked_rest
|
31
|
+
seq = chunked_next
|
32
|
+
return EmptyList.new if seq.nil?
|
33
|
+
seq
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_meta(meta)
|
37
|
+
self.class.new(@vec, @i, @offset, @node, meta)
|
38
|
+
end
|
39
|
+
|
40
|
+
def first
|
41
|
+
@node[@offset]
|
42
|
+
end
|
43
|
+
alias head first
|
44
|
+
|
45
|
+
def next?
|
46
|
+
(@offset + 1 < @node.length) || (@i + @node.length < @vec.count)
|
47
|
+
end
|
48
|
+
|
49
|
+
def each
|
50
|
+
return self unless block_given?
|
51
|
+
list = self
|
52
|
+
while list.next?
|
53
|
+
yield(list.head)
|
54
|
+
list = list.next
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_a
|
59
|
+
ret = []
|
60
|
+
each do |v|
|
61
|
+
ret << v
|
62
|
+
end
|
63
|
+
ret
|
64
|
+
end
|
65
|
+
|
66
|
+
def next
|
67
|
+
if @offset + 1 < @node.length
|
68
|
+
ChunkedSeq.new(@vec, @i, @offset + 1, @node)
|
69
|
+
else
|
70
|
+
chunked_next
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def rest
|
75
|
+
t = self.next
|
76
|
+
return EmptyList.new if t.nil?
|
77
|
+
t
|
78
|
+
end
|
79
|
+
alias tail rest
|
80
|
+
|
81
|
+
def seq
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def empty?
|
86
|
+
seq.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
def count
|
90
|
+
@vec.count - (@i + @offset)
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"[#{to_a.map(&:inspect).join(' ')}]"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'hammock/symbol'
|
2
|
+
require 'hammock/errors'
|
3
|
+
|
4
|
+
module Hammock
|
5
|
+
module Compiler
|
6
|
+
extend self
|
7
|
+
DOT = Symbol.intern(".")
|
8
|
+
NEW = Symbol.intern("new")
|
9
|
+
|
10
|
+
def namespace(env, sym)
|
11
|
+
env["__namespace__"] || sym.namespace || RT::CURRENT_NS.deref
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_var(env, sym)
|
15
|
+
namespace(env, sym).find_var(sym)
|
16
|
+
end
|
17
|
+
|
18
|
+
def macro?(form)
|
19
|
+
Meta === form && form.meta && form.meta[:macro]
|
20
|
+
end
|
21
|
+
|
22
|
+
def expand_method(form)
|
23
|
+
meta = form.meta
|
24
|
+
method, target, *args = *form
|
25
|
+
args = Sequence.from_array(args)
|
26
|
+
method = Symbol.intern(method.name[1..-1])
|
27
|
+
if args
|
28
|
+
args = args.cons(method)
|
29
|
+
else
|
30
|
+
args = method
|
31
|
+
end
|
32
|
+
Sequence.from_array([DOT, target, args], meta)
|
33
|
+
end
|
34
|
+
|
35
|
+
def expand_new(form)
|
36
|
+
meta = form.meta
|
37
|
+
klass, *args = *form
|
38
|
+
args = Sequence.from_array(args)
|
39
|
+
klass = Symbol.intern(klass.name[0..-2])
|
40
|
+
if args
|
41
|
+
args = args.cons(NEW)
|
42
|
+
else
|
43
|
+
args = NEW
|
44
|
+
end
|
45
|
+
Sequence.from_array([DOT, klass, args], meta)
|
46
|
+
end
|
47
|
+
|
48
|
+
def macroexpand1(form, env=RT.global_env)
|
49
|
+
form, _ = _macroexpand1(env, form)
|
50
|
+
form
|
51
|
+
end
|
52
|
+
|
53
|
+
def _macroexpand1(env, form)
|
54
|
+
return form, false unless form.is_a?(List)
|
55
|
+
sym = form.car
|
56
|
+
return form, false unless Hammock::Symbol === sym
|
57
|
+
meta = form.meta
|
58
|
+
|
59
|
+
if item = find_var(env, sym)
|
60
|
+
dreffed = item.deref
|
61
|
+
if macro?(item) || macro?(dreffed)
|
62
|
+
begin
|
63
|
+
form = dreffed.call(form, env, *form.tail)
|
64
|
+
if meta && Meta === form
|
65
|
+
form = form.with_meta(meta)
|
66
|
+
end
|
67
|
+
rescue => e
|
68
|
+
raise Hammock::CompileError.new(form), "Error compiling: #{e}", e.backtrace
|
69
|
+
end
|
70
|
+
return form, true
|
71
|
+
else
|
72
|
+
return form, false
|
73
|
+
end
|
74
|
+
elsif sym.name == DOT.name
|
75
|
+
return form, false
|
76
|
+
elsif sym.name.start_with?(DOT.name)
|
77
|
+
form = expand_method(form)
|
78
|
+
return form, false
|
79
|
+
elsif sym.name.end_with?(DOT.name)
|
80
|
+
form = expand_new(form)
|
81
|
+
return form, false
|
82
|
+
else
|
83
|
+
return form, false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def special(form)
|
88
|
+
return unless form.is_a?(List)
|
89
|
+
RT.special(form.car)
|
90
|
+
end
|
91
|
+
|
92
|
+
def macroexpand(env, form)
|
93
|
+
return form unless List === form && Hammock::Symbol === form.first
|
94
|
+
ret = true
|
95
|
+
spec = nil
|
96
|
+
while ret && !spec
|
97
|
+
form, ret = _macroexpand1(env, form)
|
98
|
+
spec = special(form) if form
|
99
|
+
end
|
100
|
+
form
|
101
|
+
end
|
102
|
+
|
103
|
+
def compile(env, form)
|
104
|
+
return form unless IPersistentCollection === form
|
105
|
+
meta = form.meta
|
106
|
+
new_form = form.to_a.map do |f|
|
107
|
+
compile(env, f)
|
108
|
+
end
|
109
|
+
case form
|
110
|
+
when List
|
111
|
+
list = Sequence.from_array(new_form, meta)
|
112
|
+
macroexpand(env, list)
|
113
|
+
when Vector
|
114
|
+
Vector.from_array(new_form, meta)
|
115
|
+
when Map
|
116
|
+
Map.from_pairs(new_form).with_meta(meta)
|
117
|
+
when Set
|
118
|
+
Set.from_array(new_form, meta)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def evaluate(env, form)
|
123
|
+
form = macroexpand(env, form)
|
124
|
+
|
125
|
+
case form
|
126
|
+
when Var
|
127
|
+
form.root
|
128
|
+
when EmptyList
|
129
|
+
form
|
130
|
+
when Hammock::Symbol
|
131
|
+
return env[form.name] if env.key?(form.name) && !form.ns
|
132
|
+
namespace = form.namespace(env["__namespace__"])
|
133
|
+
if namespace.has_var?(form.name) && (v = namespace.find_var(form.name))
|
134
|
+
v.deref
|
135
|
+
elsif form.constant
|
136
|
+
form.constant
|
137
|
+
else
|
138
|
+
raise "Unable to resolve symbol #{form.name} in this context"
|
139
|
+
end
|
140
|
+
when List
|
141
|
+
if s = special(form)
|
142
|
+
return s.call(form, env, *form.tail)
|
143
|
+
end
|
144
|
+
|
145
|
+
head = form.car
|
146
|
+
|
147
|
+
fn = evaluate(env, head)
|
148
|
+
|
149
|
+
if Var === fn
|
150
|
+
fn = fn.deref
|
151
|
+
end
|
152
|
+
|
153
|
+
if fn.respond_to?(:trace) && (t = fn.trace)
|
154
|
+
env = env.bind("__stack__", env["__stack__"].add(t))
|
155
|
+
end
|
156
|
+
|
157
|
+
case fn
|
158
|
+
when IFn
|
159
|
+
args = (form.tail || []).to_a.map { |elem| evaluate(env, elem) }
|
160
|
+
begin
|
161
|
+
fn.call *args
|
162
|
+
rescue => e
|
163
|
+
raise e.class, e.message, (env["__stack__"].to_a.reverse)
|
164
|
+
end
|
165
|
+
when ::Symbol
|
166
|
+
args = (form.tail || []).to_a.map { |elem| evaluate(env, elem) }
|
167
|
+
if args.count > 2
|
168
|
+
raise ArgumentError, "more than one arg passed as argument to Keyword #{fn}", env["__stack__"].to_a
|
169
|
+
end
|
170
|
+
map, default = *args
|
171
|
+
if ILookup === map
|
172
|
+
map.fetch(fn, default)
|
173
|
+
end
|
174
|
+
else
|
175
|
+
raise Error, "What? #{head.inspect}, #{fn.inspect}, #{form}, #{form.meta}"
|
176
|
+
end
|
177
|
+
when Map
|
178
|
+
ret = []
|
179
|
+
form.each do |k,v|
|
180
|
+
ret << [evaluate(env, k), evaluate(env, v)]
|
181
|
+
end
|
182
|
+
Map.from_pairs(ret, form.meta)
|
183
|
+
when RT::Finally, RT::Catch
|
184
|
+
RT::Do.new.call(nil, env, *form.body)
|
185
|
+
when Hammock::Set, Vector
|
186
|
+
klass = form.class
|
187
|
+
ret = []
|
188
|
+
form.each do |v|
|
189
|
+
ret << evaluate(env, v)
|
190
|
+
end
|
191
|
+
klass.from_array(ret, form.meta)
|
192
|
+
else # un-evaluable
|
193
|
+
form
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|