heist 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +17 -0
- data/Manifest.txt +23 -19
- data/README.txt +84 -52
- data/lib/builtin/library.scm +208 -10
- data/lib/builtin/primitives.rb +154 -92
- data/lib/builtin/syntax.scm +22 -5
- data/lib/heist.rb +49 -17
- data/lib/parser/nodes.rb +47 -24
- data/lib/parser/ruby.rb +29 -0
- data/lib/parser/scheme.rb +455 -143
- data/lib/parser/scheme.tt +23 -5
- data/lib/repl.rb +19 -16
- data/lib/runtime/binding.rb +24 -2
- data/lib/runtime/callable/continuation.rb +23 -2
- data/lib/runtime/callable/function.rb +122 -21
- data/lib/runtime/callable/macro.rb +169 -123
- data/lib/runtime/callable/macro/expansion.rb +137 -2
- data/lib/runtime/callable/macro/matches.rb +125 -41
- data/lib/runtime/callable/macro/tree.rb +141 -0
- data/lib/runtime/callable/syntax.rb +44 -0
- data/lib/runtime/data/cons.rb +234 -0
- data/lib/runtime/data/expression.rb +15 -6
- data/lib/runtime/data/identifier.rb +19 -2
- data/lib/runtime/frame.rb +102 -35
- data/lib/runtime/runtime.rb +44 -19
- data/lib/runtime/scope.rb +145 -30
- data/lib/runtime/stack.rb +103 -1
- data/lib/runtime/stackless.rb +48 -6
- data/test/arithmetic.scm +11 -2
- data/test/continuations.scm +16 -2
- data/test/equivalence.scm +34 -0
- data/test/functional.scm +4 -0
- data/test/lists.scm +78 -0
- data/test/macro-helpers.scm +1 -0
- data/test/macros.scm +111 -24
- data/test/numbers.scm +30 -8
- data/test/test_heist.rb +67 -12
- metadata +25 -21
- data/lib/builtin/syntax.rb +0 -166
- data/lib/runtime/callable/macro/splice.rb +0 -56
- data/lib/runtime/data/list.rb +0 -36
data/test/numbers.scm
CHANGED
@@ -1,19 +1,41 @@
|
|
1
|
-
(assert (eqv? 42 42))
|
2
|
-
(assert (not (eqv? 42 #f)))
|
3
|
-
(assert (not (eqv? 42 42.0)))
|
4
1
|
(assert (= 42 42))
|
5
2
|
(assert (= 42 42.0))
|
6
3
|
|
7
4
|
(assert (number? 42))
|
8
5
|
(assert (not (number? #t)))
|
9
|
-
|
10
|
-
|
6
|
+
(assert (complex? 2+3i))
|
7
|
+
(assert (not (real? 2+3i)))
|
11
8
|
(assert (real? 3.1416))
|
12
9
|
(assert (real? 22/7))
|
13
10
|
(assert (real? 42))
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
(assert (not (rational? 2+3i)))
|
12
|
+
(assert (not (rational? 3.1416)))
|
13
|
+
(assert (rational? 22/7))
|
17
14
|
(assert (not (integer? 22/7)))
|
18
15
|
(assert (integer? 42))
|
19
16
|
|
17
|
+
(assert (zero? 0))
|
18
|
+
(assert (not (zero? 1)))
|
19
|
+
(assert (odd? 1))
|
20
|
+
(assert (not (odd? 2)))
|
21
|
+
(assert (even? 6))
|
22
|
+
(assert (not (even? 7)))
|
23
|
+
(assert (positive? 5))
|
24
|
+
(assert (not (positive? -4)))
|
25
|
+
(assert (negative? -13))
|
26
|
+
(assert (not (negative? 0)))
|
27
|
+
|
28
|
+
(assert-equal 9 (max 8 2 7 3 9 5))
|
29
|
+
(assert-equal 2 (min 8 2 7 3 9 5))
|
30
|
+
|
31
|
+
(assert (exact? 8))
|
32
|
+
(assert (exact? 4/3))
|
33
|
+
(assert (exact? 5+3i))
|
34
|
+
(assert (inexact? 8.5))
|
35
|
+
(assert (inexact? 8.5+4i))
|
36
|
+
|
37
|
+
(assert (rational? (/ 4 3)))
|
38
|
+
(assert (rational? (/ 4)))
|
39
|
+
|
40
|
+
(assert-equal 1 (make-polar 1 0))
|
41
|
+
|
data/test/test_heist.rb
CHANGED
@@ -13,7 +13,7 @@ Class.new(Test::Unit::TestCase) do
|
|
13
13
|
def setup
|
14
14
|
return @@env if @@env
|
15
15
|
@@env = Heist::Runtime.new(Heist::BIN_SPEC.parse($args))
|
16
|
-
|
16
|
+
puts @@env.info
|
17
17
|
|
18
18
|
@@env.define('assert') do |value|
|
19
19
|
assert(value)
|
@@ -21,18 +21,21 @@ Class.new(Test::Unit::TestCase) do
|
|
21
21
|
@@env.define('assert-equal') do |expected, actual|
|
22
22
|
assert_equal(expected, actual)
|
23
23
|
end
|
24
|
-
@@env.syntax('assert-raise') do |scope,
|
25
|
-
exception = Heist.const_get(
|
26
|
-
assert_raise(exception) {
|
24
|
+
@@env.syntax('assert-raise') do |scope, cells|
|
25
|
+
exception = Heist.const_get(cells.car.to_s)
|
26
|
+
assert_raise(exception) { scope.eval(cells.cdr.car) }
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
%w[ booleans
|
31
31
|
numbers
|
32
|
+
lists
|
33
|
+
equivalence
|
32
34
|
arithmetic
|
33
35
|
define_values
|
34
36
|
define_functions
|
35
37
|
closures
|
38
|
+
functional
|
36
39
|
let
|
37
40
|
conditionals
|
38
41
|
file_loading
|
@@ -45,6 +48,39 @@ Class.new(Test::Unit::TestCase) do
|
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
51
|
+
def test_cons
|
52
|
+
cons = Heist::Runtime::Cons
|
53
|
+
|
54
|
+
empty = cons[[]]
|
55
|
+
assert empty.list?
|
56
|
+
assert !empty.pair?
|
57
|
+
assert_equal cons::NULL, empty
|
58
|
+
assert_equal 0, empty.length
|
59
|
+
|
60
|
+
single = cons[[4]]
|
61
|
+
assert single.list?
|
62
|
+
assert single.pair?
|
63
|
+
assert_equal 1, single.length
|
64
|
+
|
65
|
+
multi = cons[[2,4,7]]
|
66
|
+
assert multi.list?
|
67
|
+
assert multi.pair?
|
68
|
+
assert_equal 3, multi.length
|
69
|
+
|
70
|
+
multi.tail.cdr = 8
|
71
|
+
assert multi.pair?
|
72
|
+
assert !multi.list?
|
73
|
+
assert_raise(Heist::TypeError) { multi.size }
|
74
|
+
|
75
|
+
nested = cons[[2,4,6,cons.new(7,8)]]
|
76
|
+
assert nested.list?
|
77
|
+
assert nested.pair?
|
78
|
+
assert_equal 4, nested.length
|
79
|
+
assert !nested.cdr.cdr.cdr.car.list?
|
80
|
+
assert nested.cdr.cdr.cdr.car.pair?
|
81
|
+
assert_equal 8, nested.cdr.cdr.cdr.car.cdr
|
82
|
+
end
|
83
|
+
|
48
84
|
def test_macro_hygiene
|
49
85
|
@@env.run($dir + '/' + (@@env.hygienic? ? 'hygienic' : 'unhygienic'))
|
50
86
|
end
|
@@ -55,14 +91,25 @@ Class.new(Test::Unit::TestCase) do
|
|
55
91
|
end
|
56
92
|
|
57
93
|
def test_quotes
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
assert_equal 7,
|
62
|
-
assert_equal [:+,
|
63
|
-
|
64
|
-
|
65
|
-
assert_equal [
|
94
|
+
cons = Heist::Runtime::Cons
|
95
|
+
c = cons.method(:new)
|
96
|
+
|
97
|
+
assert_equal 7, @@env.eval("(+ 3 4)")
|
98
|
+
assert_equal [:+, 3, 4], @@env.eval("'(+ 3 4)").to_ruby
|
99
|
+
assert cons === @@env.eval("'(+ 3 4)")
|
100
|
+
assert_equal 7, @@env.eval("(+ '3 4)")
|
101
|
+
assert_equal c[1,8], @@env.eval("'(1 . 8)")
|
102
|
+
assert_equal c[1,8], @@env.eval("`(1 . 8)")
|
103
|
+
assert_equal [:+, [:-, 7, 9], 4], @@env.eval("'(+ (- 7 9) 4)").to_ruby
|
104
|
+
assert_equal [7, 9, 6], @@env.eval("`(7 ,(+ 4 5) 6)").to_ruby
|
105
|
+
assert cons === @@env.eval("`(7 ,(+ 4 5) 6)")
|
106
|
+
assert_equal [3, 7, 6, 2, 6, 9], @@env.eval("`(3 7 6 ,@((lambda () '(2 6))) 9)").to_ruby
|
107
|
+
assert_equal [1, 2, 10], @@env.eval("`(1 2 ,(+ 4 6))").to_ruby
|
108
|
+
assert_equal [3, 2, 9, 8], @@env.eval("`(,(/ 9 3) 2 ,@(list 9 8))").to_ruby
|
109
|
+
assert_equal [1, 2, 4, 9, 8, 5], @@env.eval("`(,@(list 1 2) 4 ,@(list 9 8) 5)").to_ruby
|
110
|
+
assert_equal c[9,c[8,c[5,7]]], @@env.eval("`(,@(list 9 8) 5 . 7)")
|
111
|
+
assert_equal c[9,c[8,24]], @@env.eval("`(,@(list 9 8) . ,(* 4 6))")
|
112
|
+
assert_equal [:quote, []], @@env.eval("''()").to_ruby
|
66
113
|
end
|
67
114
|
|
68
115
|
def test_birds
|
@@ -80,5 +127,13 @@ Class.new(Test::Unit::TestCase) do
|
|
80
127
|
|
81
128
|
assert_equal 45, @@env.eval("((K 45) 6)")
|
82
129
|
end
|
130
|
+
|
131
|
+
def test_ruby_execution
|
132
|
+
expr = [[:lambda, [:x], [:+, 1, :x]], 3]
|
133
|
+
assert_equal 4, @@env.exec(expr)
|
134
|
+
list = Heist.parse(expr)
|
135
|
+
assert Heist::Runtime::Cons === list
|
136
|
+
assert_equal expr, list.to_ruby
|
137
|
+
end
|
83
138
|
end
|
84
139
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Coglan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-04-01 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -57,37 +57,37 @@ extra_rdoc_files:
|
|
57
57
|
files:
|
58
58
|
- History.txt
|
59
59
|
- Manifest.txt
|
60
|
-
- README.txt
|
61
60
|
- Rakefile
|
61
|
+
- README.txt
|
62
62
|
- bin/heist
|
63
|
-
- lib/heist.rb
|
64
63
|
- lib/bin_spec.rb
|
64
|
+
- lib/heist.rb
|
65
65
|
- lib/repl.rb
|
66
|
+
- lib/builtin/library.scm
|
66
67
|
- lib/builtin/primitives.rb
|
67
|
-
- lib/builtin/syntax.rb
|
68
68
|
- lib/builtin/syntax.scm
|
69
|
-
- lib/builtin/library.scm
|
70
|
-
- lib/parser/scheme.tt
|
71
|
-
- lib/parser/scheme.rb
|
72
69
|
- lib/parser/nodes.rb
|
73
|
-
- lib/
|
74
|
-
- lib/
|
75
|
-
- lib/
|
76
|
-
- lib/runtime/
|
70
|
+
- lib/parser/ruby.rb
|
71
|
+
- lib/parser/scheme.rb
|
72
|
+
- lib/parser/scheme.tt
|
73
|
+
- lib/runtime/callable/continuation.rb
|
77
74
|
- lib/runtime/callable/function.rb
|
75
|
+
- lib/runtime/callable/syntax.rb
|
78
76
|
- lib/runtime/callable/macro.rb
|
79
77
|
- lib/runtime/callable/macro/matches.rb
|
80
|
-
- lib/runtime/callable/macro/
|
78
|
+
- lib/runtime/callable/macro/tree.rb
|
81
79
|
- lib/runtime/callable/macro/expansion.rb
|
82
|
-
- lib/runtime/
|
83
|
-
- lib/runtime/
|
84
|
-
- lib/runtime/
|
80
|
+
- lib/runtime/data/cons.rb
|
81
|
+
- lib/runtime/data/expression.rb
|
82
|
+
- lib/runtime/data/identifier.rb
|
83
|
+
- lib/runtime/binding.rb
|
85
84
|
- lib/runtime/frame.rb
|
85
|
+
- lib/runtime/runtime.rb
|
86
86
|
- lib/runtime/scope.rb
|
87
|
-
- lib/runtime/
|
87
|
+
- lib/runtime/stack.rb
|
88
|
+
- lib/runtime/stackless.rb
|
88
89
|
- lib/stdlib/benchmark.scm
|
89
90
|
- lib/stdlib/birdhouse.scm
|
90
|
-
- test/test_heist.rb
|
91
91
|
- test/arithmetic.scm
|
92
92
|
- test/benchmarks.scm
|
93
93
|
- test/booleans.scm
|
@@ -97,15 +97,19 @@ files:
|
|
97
97
|
- test/define_functions.scm
|
98
98
|
- test/define_values.scm
|
99
99
|
- test/delay.scm
|
100
|
+
- test/equivalence.scm
|
100
101
|
- test/file_loading.scm
|
102
|
+
- test/functional.scm
|
103
|
+
- test/hygienic.scm
|
101
104
|
- test/let.scm
|
102
105
|
- test/lib.scm
|
106
|
+
- test/lists.scm
|
103
107
|
- test/macro-helpers.scm
|
104
108
|
- test/macros.scm
|
105
|
-
- test/hygienic.scm
|
106
|
-
- test/unhygienic.scm
|
107
|
-
- test/plt-macros.txt
|
108
109
|
- test/numbers.scm
|
110
|
+
- test/plt-macros.txt
|
111
|
+
- test/test_heist.rb
|
112
|
+
- test/unhygienic.scm
|
109
113
|
- test/vars.scm
|
110
114
|
has_rdoc: true
|
111
115
|
homepage: Heist is a Scheme interpreter written in Ruby. It provides an executable
|
data/lib/builtin/syntax.rb
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
# Control structures
|
2
|
-
|
3
|
-
# (cond) goes through a list of tests, evaluating each one
|
4
|
-
# in order of appearance. Once a matching precondition is
|
5
|
-
# found, its consequent is tail-called and no further
|
6
|
-
# preconditions are evaluated.
|
7
|
-
syntax('cond') do |scope, *pairs|
|
8
|
-
result = nil
|
9
|
-
pairs.each do |list|
|
10
|
-
next if result
|
11
|
-
test = list.first.to_s == 'else' || Heist.evaluate(list.first, scope)
|
12
|
-
next unless test
|
13
|
-
result = list[1].to_s == '=>' ?
|
14
|
-
Heist.evaluate(list[2], scope).call(scope, [test]) :
|
15
|
-
Body.new(list.rest, scope)
|
16
|
-
end
|
17
|
-
result
|
18
|
-
end
|
19
|
-
|
20
|
-
# (case) acts like Ruby's case statement. The value of the
|
21
|
-
# given expression is compared against a series of lists;
|
22
|
-
# once a list is found to include the value, the expressions
|
23
|
-
# following the list are evaluated and no further lists
|
24
|
-
# are tested.
|
25
|
-
syntax('case') do |scope, key, *clauses|
|
26
|
-
value = Heist.evaluate(key, scope)
|
27
|
-
result = nil
|
28
|
-
clauses.each do |list|
|
29
|
-
next if result
|
30
|
-
values = call('quote', scope, list.first)
|
31
|
-
result = Body.new(list.rest, scope) if values == :else or
|
32
|
-
values.include?(value)
|
33
|
-
end
|
34
|
-
result
|
35
|
-
end
|
36
|
-
|
37
|
-
#----------------------------------------------------------------
|
38
|
-
|
39
|
-
# Binding constructs
|
40
|
-
|
41
|
-
# (let), (let*) and (letrec) each create a new scope and bind
|
42
|
-
# values to some symbols before executing a series of lists.
|
43
|
-
# They differ according to how they evaluate the bound values.
|
44
|
-
|
45
|
-
# (let) evaluates values in the enclosing scope, so lambdas will
|
46
|
-
# not be able to refer to other values assigned using the (let).
|
47
|
-
syntax('let') do |scope, assignments, *body|
|
48
|
-
if Identifier === assignments
|
49
|
-
name = assignments
|
50
|
-
assignments = body.first
|
51
|
-
formals = assignments.map { |pair| pair.first }
|
52
|
-
values = assignments.map { |pair| pair.last }
|
53
|
-
closure = Scope.new(scope)
|
54
|
-
closure[name] = Function.new(closure, formals, body[1..-1])
|
55
|
-
closure[name].call(scope, values)
|
56
|
-
else
|
57
|
-
closure = Scope.new(scope)
|
58
|
-
assignments.each do |assign|
|
59
|
-
closure[assign.first] = Heist.evaluate(assign.last, scope)
|
60
|
-
end
|
61
|
-
call('begin', closure, *body)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# (let*) creates a new scope for each variable and evaluates
|
66
|
-
# each expression in its enclosing scope. Basically a shorthand
|
67
|
-
# for several nested (let)s. Variables may refer to those that
|
68
|
-
# preceed them but not vice versa.
|
69
|
-
syntax('let*') do |scope, assignments, *body|
|
70
|
-
closure = assignments.inject(scope) do |outer, assign|
|
71
|
-
inner = Scope.new(outer)
|
72
|
-
inner[assign.first] = Heist.evaluate(assign.last, outer)
|
73
|
-
inner
|
74
|
-
end
|
75
|
-
call('begin', closure, *body)
|
76
|
-
end
|
77
|
-
|
78
|
-
# (letrec) evaluates values in the inner scope, so lambdas are
|
79
|
-
# able to refer to other values assigned using the (letrec).
|
80
|
-
syntax('letrec') do |scope, assignments, *body|
|
81
|
-
closure = Scope.new(scope)
|
82
|
-
assignments.each do |assign|
|
83
|
-
closure[assign.first] = Heist.evaluate(assign.last, closure)
|
84
|
-
end
|
85
|
-
call('begin', closure, *body)
|
86
|
-
end
|
87
|
-
|
88
|
-
syntax('let-syntax') do |*args|
|
89
|
-
call('let', *args)
|
90
|
-
end
|
91
|
-
|
92
|
-
syntax('letrec-syntax') do |*args|
|
93
|
-
call('letrec', *args)
|
94
|
-
end
|
95
|
-
|
96
|
-
#----------------------------------------------------------------
|
97
|
-
|
98
|
-
# Iteration
|
99
|
-
|
100
|
-
# (do) is similar to the 'while' construct in procedural
|
101
|
-
# languages. It assigns initial values to a set of variables,
|
102
|
-
# then performs the list of given commands in a loop. If
|
103
|
-
# before any iteration the test is found to be false, the
|
104
|
-
# loop is halted and the value of the expression following
|
105
|
-
# the test is returned.
|
106
|
-
syntax('do') do |scope, assignments, test, *commands|
|
107
|
-
closure = Scope.new(scope)
|
108
|
-
assignments.each do |assign|
|
109
|
-
closure[assign.first] = Heist.evaluate(assign[1], scope)
|
110
|
-
end
|
111
|
-
while not Heist.evaluate(test.first, closure)
|
112
|
-
commands.each { |expr| Heist.evaluate(expr, closure) }
|
113
|
-
temp = {}
|
114
|
-
assignments.each do |assign|
|
115
|
-
step = assign[2] || assign[0]
|
116
|
-
temp[assign.first] = Heist.evaluate(step, closure)
|
117
|
-
end
|
118
|
-
assignments.each do |assign|
|
119
|
-
closure[assign.first] = temp[assign.first]
|
120
|
-
end
|
121
|
-
end
|
122
|
-
call('begin', closure, *test.rest)
|
123
|
-
end
|
124
|
-
|
125
|
-
#----------------------------------------------------------------
|
126
|
-
|
127
|
-
# Boolean combinators
|
128
|
-
|
129
|
-
# (and) returns the first falsey value returned by the list
|
130
|
-
# of expressions, or returns the value of the last expression
|
131
|
-
# if all values are truthy.
|
132
|
-
syntax('and') do |scope, *args|
|
133
|
-
result = true
|
134
|
-
args.each do |arg|
|
135
|
-
next if not result
|
136
|
-
result = Heist.evaluate(arg, scope)
|
137
|
-
end
|
138
|
-
result
|
139
|
-
end
|
140
|
-
|
141
|
-
# (or) returns the first truthy value returned by the list
|
142
|
-
# of expressions, or returns the value of the last expression
|
143
|
-
# if all values are falsey.
|
144
|
-
syntax('or') do |scope, *args|
|
145
|
-
result = false
|
146
|
-
args.each do |arg|
|
147
|
-
next if result
|
148
|
-
result = Heist.evaluate(arg, scope)
|
149
|
-
end
|
150
|
-
result
|
151
|
-
end
|
152
|
-
|
153
|
-
#----------------------------------------------------------------
|
154
|
-
|
155
|
-
# Delayed evaluation
|
156
|
-
|
157
|
-
# (delay) allows the evaluation of an expression to be delayed
|
158
|
-
# by wrapping it in a promise. Use (force) to evaluate the promise
|
159
|
-
# at a later time. The expression inside a promise is only
|
160
|
-
# ever evaluated once, so a promise can be implemented as a
|
161
|
-
# memoized closure.
|
162
|
-
syntax('delay') do |scope, expression|
|
163
|
-
promise = Binding.new(expression, scope)
|
164
|
-
Function.new(scope) { promise.extract }
|
165
|
-
end
|
166
|
-
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module Heist
|
2
|
-
class Runtime
|
3
|
-
class Macro
|
4
|
-
|
5
|
-
class Splice
|
6
|
-
attr_reader :name, :depth
|
7
|
-
|
8
|
-
def initialize(name, depth)
|
9
|
-
@name, @depth = name, depth
|
10
|
-
@data = []
|
11
|
-
(0...@depth).inject(@data) { |list, d| list << []; list.last }
|
12
|
-
@indexes = (0..@depth).map { 0 }
|
13
|
-
@stack = []
|
14
|
-
end
|
15
|
-
|
16
|
-
def <<(value)
|
17
|
-
@stack.pop.call() while not @stack.empty?
|
18
|
-
tail(@depth) << value
|
19
|
-
end
|
20
|
-
|
21
|
-
def mark!(depth)
|
22
|
-
@stack << lambda { tail(depth) << [] }
|
23
|
-
end
|
24
|
-
|
25
|
-
def size(depth)
|
26
|
-
current(depth).size
|
27
|
-
end
|
28
|
-
|
29
|
-
def read
|
30
|
-
current(@depth)[@indexes[@depth]]
|
31
|
-
end
|
32
|
-
|
33
|
-
def shift!(depth)
|
34
|
-
@indexes[depth] += 1
|
35
|
-
@indexes[depth] = 0 if @indexes[depth] >= current(depth).size
|
36
|
-
end
|
37
|
-
|
38
|
-
def to_s(depth = 0)
|
39
|
-
"#{@name}#{' ...' * (@depth - depth)}"
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def tail(depth)
|
45
|
-
(0...depth).inject(@data) { |list, d| list.last }
|
46
|
-
end
|
47
|
-
|
48
|
-
def current(depth)
|
49
|
-
@indexes[0...depth].inject(@data) { |list, i| list[i] }
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|