atomy 0.1.1
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.
- data/COPYING +30 -0
- data/README.md +1 -0
- data/bin/atomy +134 -0
- data/kernel/block.ay +30 -0
- data/kernel/boot.ay +10 -0
- data/kernel/comparison.ay +61 -0
- data/kernel/concurrency.ay +84 -0
- data/kernel/condition.ay +277 -0
- data/kernel/control-flow.ay +222 -0
- data/kernel/cosmetics.ay +3 -0
- data/kernel/data-delta.ay +105 -0
- data/kernel/data.ay +56 -0
- data/kernel/define.ay +93 -0
- data/kernel/doc.ay +453 -0
- data/kernel/documentation.ay +135 -0
- data/kernel/dynamic.ay +42 -0
- data/kernel/errors.ay +6 -0
- data/kernel/format.ay +13 -0
- data/kernel/format/data.ay +89 -0
- data/kernel/format/formatter.ay +345 -0
- data/kernel/format/parser.ay +13 -0
- data/kernel/hashes.ay +39 -0
- data/kernel/io.ay +244 -0
- data/kernel/namespaces.ay +63 -0
- data/kernel/node.ay +48 -0
- data/kernel/operators.ay +28 -0
- data/kernel/particles.ay +53 -0
- data/kernel/patterns.ay +256 -0
- data/kernel/precision.ay +148 -0
- data/kernel/pretty.ay +283 -0
- data/kernel/repl.ay +140 -0
- data/kernel/therie.ay +204 -0
- data/lib/ast/binary_send.rb +44 -0
- data/lib/ast/block.rb +268 -0
- data/lib/ast/constant.rb +88 -0
- data/lib/ast/internal/assign.rb +19 -0
- data/lib/ast/internal/block_pass.rb +21 -0
- data/lib/ast/internal/catch.rb +247 -0
- data/lib/ast/internal/class.rb +30 -0
- data/lib/ast/internal/class_variable.rb +23 -0
- data/lib/ast/internal/define.rb +174 -0
- data/lib/ast/internal/ensure.rb +135 -0
- data/lib/ast/internal/file.rb +14 -0
- data/lib/ast/internal/global_variable.rb +20 -0
- data/lib/ast/internal/if_then_else.rb +24 -0
- data/lib/ast/internal/instance_variable.rb +17 -0
- data/lib/ast/internal/let_macro.rb +35 -0
- data/lib/ast/internal/macro_quote.rb +23 -0
- data/lib/ast/internal/match.rb +53 -0
- data/lib/ast/internal/module.rb +30 -0
- data/lib/ast/internal/pattern.rb +17 -0
- data/lib/ast/internal/return.rb +29 -0
- data/lib/ast/internal/set.rb +19 -0
- data/lib/ast/internal/singleton_class.rb +18 -0
- data/lib/ast/internal/splat.rb +14 -0
- data/lib/ast/internal/when.rb +24 -0
- data/lib/ast/list.rb +25 -0
- data/lib/ast/macro.rb +37 -0
- data/lib/ast/node.rb +599 -0
- data/lib/ast/operator.rb +21 -0
- data/lib/ast/particle.rb +13 -0
- data/lib/ast/primitive.rb +20 -0
- data/lib/ast/quasi_quote.rb +20 -0
- data/lib/ast/quote.rb +13 -0
- data/lib/ast/send.rb +104 -0
- data/lib/ast/splice.rb +32 -0
- data/lib/ast/string.rb +23 -0
- data/lib/ast/unary.rb +44 -0
- data/lib/ast/unquote.rb +45 -0
- data/lib/ast/variable.rb +64 -0
- data/lib/atomy.kpeg.rb +3995 -0
- data/lib/code_loader.rb +137 -0
- data/lib/compiler/compiler.rb +155 -0
- data/lib/compiler/stages.rb +81 -0
- data/lib/formatter.kpeg.rb +1394 -0
- data/lib/macros.rb +317 -0
- data/lib/method.rb +261 -0
- data/lib/namespace.rb +236 -0
- data/lib/parser.rb +28 -0
- data/lib/patterns.rb +276 -0
- data/lib/patterns/any.rb +21 -0
- data/lib/patterns/attribute.rb +59 -0
- data/lib/patterns/block_pass.rb +54 -0
- data/lib/patterns/constant.rb +33 -0
- data/lib/patterns/default.rb +44 -0
- data/lib/patterns/head_tail.rb +63 -0
- data/lib/patterns/list.rb +77 -0
- data/lib/patterns/match.rb +45 -0
- data/lib/patterns/named.rb +55 -0
- data/lib/patterns/named_class.rb +46 -0
- data/lib/patterns/named_global.rb +46 -0
- data/lib/patterns/named_instance.rb +46 -0
- data/lib/patterns/particle.rb +29 -0
- data/lib/patterns/quasi_quote.rb +184 -0
- data/lib/patterns/quote.rb +33 -0
- data/lib/patterns/singleton_class.rb +31 -0
- data/lib/patterns/splat.rb +57 -0
- data/lib/util.rb +37 -0
- metadata +164 -0
data/kernel/repl.ay
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
namespace(atomy)
|
2
|
+
|
3
|
+
title"The REPL"
|
4
|
+
|
5
|
+
require("readline")
|
6
|
+
|
7
|
+
doc"
|
8
|
+
The basics of a REPL - reading, evaluating, and printing in a loop. More \
|
9
|
+
flexibility is provided by various signals. See \hl{repl} for a fancier \
|
10
|
+
REPL, which builds upon this, and \hl{ReplDebugger}.
|
11
|
+
|
12
|
+
When showing the prompt, \hl{#prompt} is signaled with a \hl{#use-prompt} \
|
13
|
+
restart available. Invoke this restart and pass along a string to \
|
14
|
+
override the prompt, which defaults to \code{>>}.
|
15
|
+
|
16
|
+
Input preceded by a colon (\code{:}) and followed by an alphanumeric \
|
17
|
+
character is assumed to be a \italic{special command}. These are not \
|
18
|
+
evaluated, and are signaled as \hl{#special(\italic{text})}.
|
19
|
+
|
20
|
+
When the user sends \code{EOF} (Ctrl+D) or an interrupt (Ctrl+C), \
|
21
|
+
\hl{#quit} is signaled.
|
22
|
+
|
23
|
+
When the user enters code, \hl{#input(\italic{text})} is signaled. The code\
|
24
|
+
is evaluated with two restarts registered: \code{#retry} for re-attempting \
|
25
|
+
evaluation, and \code{#abort}, for canceling the evaluation. After the code\
|
26
|
+
is evaluated, \hl{#evaluated(\italic{result})} is signaled.
|
27
|
+
|
28
|
+
\hl{#loop} is signaled before the loop starts over again (i.e., after the \
|
29
|
+
input is handled).
|
30
|
+
" spec {
|
31
|
+
bnd is-a?(Binding)
|
32
|
+
} for:
|
33
|
+
basic-repl(bnd = TOPLEVEL_BINDING) :=
|
34
|
+
loop:
|
35
|
+
prompt =
|
36
|
+
with-restarts(use-prompt(p) -> p):
|
37
|
+
signal(#prompt)
|
38
|
+
">> "
|
39
|
+
|
40
|
+
in =
|
41
|
+
{ Readline readline(prompt)
|
42
|
+
} rescue:
|
43
|
+
Interrupt -> do { signal(#quit), "" }
|
44
|
+
|
45
|
+
in match:
|
46
|
+
nil -> signal(#quit)
|
47
|
+
|
48
|
+
"" -> #ok
|
49
|
+
|
50
|
+
String ? (=~ r"^:[[:alnum:]]") ->
|
51
|
+
signal(#special(in [1 .. -1]))
|
52
|
+
|
53
|
+
source -> do:
|
54
|
+
signal(#input(source))
|
55
|
+
|
56
|
+
try = {
|
57
|
+
with-restarts(retry -> try [], abort -> #ok):
|
58
|
+
res = Atomy::Compiler evaluate(source, bnd)
|
59
|
+
signal(#evaluated(res))
|
60
|
+
}
|
61
|
+
|
62
|
+
try []
|
63
|
+
|
64
|
+
signal(#loop)
|
65
|
+
|
66
|
+
|
67
|
+
doc"
|
68
|
+
An interactive debugger REPL for handling errors. This will list the \
|
69
|
+
results along with a number, allow the user to continue evaluating code, \
|
70
|
+
and once they enter a \italic{special command} in the form of \
|
71
|
+
\code{:\italic{number}}, the specified restart will be invoked.
|
72
|
+
" for:
|
73
|
+
class(ReplDebugger):
|
74
|
+
class(<< self):
|
75
|
+
show-backtrace(e) := do:
|
76
|
+
Rubinius::Backtrace backtrace(e backtrace) _/show print
|
77
|
+
debug(e)
|
78
|
+
|
79
|
+
run(e) := do:
|
80
|
+
DefaultDebugger show-error-banner(e)
|
81
|
+
|
82
|
+
with-restarts(backtrace -> show-backtrace(e)):
|
83
|
+
debug(e)
|
84
|
+
|
85
|
+
debug(e) := do:
|
86
|
+
DefaultDebugger show-options-for(e)
|
87
|
+
|
88
|
+
{ basic-repl } bind:
|
89
|
+
#prompt -> restart(#use-prompt, "[!]> ")
|
90
|
+
|
91
|
+
#special(n ? (=~ r"\d+")) ->
|
92
|
+
^restarts [n to-i] invoke
|
93
|
+
|
94
|
+
#quit -> exit(1)
|
95
|
+
|
96
|
+
dynamic(debugger, ReplDebugger)
|
97
|
+
|
98
|
+
doc"
|
99
|
+
A more feature-filled REPL, providing persistent history and setting \
|
100
|
+
\hl{^debugger} to \hl{ReplDebugger}.
|
101
|
+
|
102
|
+
History will be managed and appended to a file specified by \hl{history} \
|
103
|
+
upon termination.
|
104
|
+
" spec {
|
105
|
+
history is-a?(String)
|
106
|
+
bnd is-a?(Binding)
|
107
|
+
} for:
|
108
|
+
repl(history = nil, bnd = TOPLEVEL_BINDING) := do:
|
109
|
+
when(history && File exists?(history)):
|
110
|
+
File open(history, "r") [f]:
|
111
|
+
f readlines each [l]:
|
112
|
+
Readline::HISTORY << l strip
|
113
|
+
|
114
|
+
SANE_HISTORY = []
|
115
|
+
|
116
|
+
{ let(debugger = ReplDebugger):
|
117
|
+
frame = 0
|
118
|
+
|
119
|
+
{ let(atomy/pretty/colored? = true):
|
120
|
+
basic-repl(bnd)
|
121
|
+
} bind:
|
122
|
+
#prompt ->
|
123
|
+
restart(#use-prompt, "[" + frame to-s + "]> ")
|
124
|
+
|
125
|
+
#loop ->
|
126
|
+
(frame += 1)
|
127
|
+
|
128
|
+
#quit -> exit(0)
|
129
|
+
|
130
|
+
#input(str) -> (SANE_HISTORY << str)
|
131
|
+
|
132
|
+
#special("h") ->
|
133
|
+
":h\thelp" print
|
134
|
+
|
135
|
+
#evaluated(r) ->
|
136
|
+
(" => " + r show) print
|
137
|
+
} ensuring:
|
138
|
+
when(history):
|
139
|
+
File open(history, "a") [f]:
|
140
|
+
f puts(*SANE_HISTORY)
|
data/kernel/therie.ay
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
namespace(therie)
|
2
|
+
|
3
|
+
title"Testing with Therie"
|
4
|
+
|
5
|
+
doc"
|
6
|
+
Therie is a small and simple behavioral-style testing suite that comes with \
|
7
|
+
Atomy. To use it, you should use the \hl{therie} namespace, which provides \
|
8
|
+
the following methods.
|
9
|
+
|
10
|
+
\atomy{
|
11
|
+
use(therie)
|
12
|
+
}
|
13
|
+
"
|
14
|
+
|
15
|
+
dynamic(nesting-level, 0)
|
16
|
+
|
17
|
+
doc"
|
18
|
+
A trivial container of \hl{passed} and \hl{failed} counts, with accessors \
|
19
|
+
for both.
|
20
|
+
" for:
|
21
|
+
class(Stats):
|
22
|
+
attr-accessor(#passed, #failed)
|
23
|
+
|
24
|
+
export:
|
25
|
+
initialize := do:
|
26
|
+
@passed = 0
|
27
|
+
@failed = 0
|
28
|
+
|
29
|
+
inspect := f"Stats(@passed = %d, @failed = %d)" [@passed, @failed]
|
30
|
+
|
31
|
+
dynamic(stats, nil)
|
32
|
+
|
33
|
+
|
34
|
+
section("Structure"):
|
35
|
+
doc"\evaluate{use(therie)}"
|
36
|
+
|
37
|
+
doc"
|
38
|
+
Run \hl{tests} and keep track of how many passed and how many failed, \
|
39
|
+
printing the stats at the end and returning them.
|
40
|
+
" spec {
|
41
|
+
=> Stats
|
42
|
+
} for {
|
43
|
+
theorize(&tests) :=
|
44
|
+
let(stats = Stats new):
|
45
|
+
tests call
|
46
|
+
"\n" display
|
47
|
+
|
48
|
+
^stats onto:
|
49
|
+
f"total of %d tests (%s passed, %s failed)" [
|
50
|
+
failed + passed
|
51
|
+
passed to-s colored(#green)
|
52
|
+
if(failed == 0)
|
53
|
+
then: "0"
|
54
|
+
else: failed to-s colored(#red)
|
55
|
+
] display
|
56
|
+
|
57
|
+
"\n" display
|
58
|
+
|
59
|
+
^stats
|
60
|
+
} examples:
|
61
|
+
theorize:
|
62
|
+
describe("foo"):
|
63
|
+
it("does x"): true should-be(false)
|
64
|
+
it("does x"): true should-be(true)
|
65
|
+
|
66
|
+
|
67
|
+
doc"
|
68
|
+
Logically group together a set of behavior.
|
69
|
+
|
70
|
+
Prints out \hl{what}, with each test in \hl{body} indented afterward.
|
71
|
+
" for {
|
72
|
+
describe(what, &body) := do:
|
73
|
+
f"- %s" [what] indented print
|
74
|
+
|
75
|
+
let(nesting-level = ^nesting-level + 1):
|
76
|
+
body call
|
77
|
+
|
78
|
+
nil
|
79
|
+
} examples:
|
80
|
+
describe("foo"):
|
81
|
+
it("does x"): true should-be(false)
|
82
|
+
it("does x"): true should-be(true)
|
83
|
+
|
84
|
+
|
85
|
+
doc"
|
86
|
+
Describe some behavior that the tests in \hl{body} will demonstrate.
|
87
|
+
" for {
|
88
|
+
it(description, &tests) := do:
|
89
|
+
{ tests call
|
90
|
+
f"✓ %s" [description] indented colored(#green) print
|
91
|
+
} rescue {
|
92
|
+
e -> do:
|
93
|
+
when(^stats):
|
94
|
+
^stats failed += 1
|
95
|
+
|
96
|
+
f"✗ %s" [description] indented colored(#red) print
|
97
|
+
f" ` %s: %s" [
|
98
|
+
e class name
|
99
|
+
e message
|
100
|
+
] indented colored(#yellow) print
|
101
|
+
|
102
|
+
e backtrace first(5) each [l]:
|
103
|
+
f" %s" [l colored(#cyan)] indented print
|
104
|
+
} else:
|
105
|
+
when(^stats):
|
106
|
+
^stats passed += 1
|
107
|
+
|
108
|
+
nil
|
109
|
+
} examples:
|
110
|
+
it("adds correctly"): (2 + 2) should-be(4)
|
111
|
+
it("adds correctly"): (1 + 2) should-be(4)
|
112
|
+
|
113
|
+
|
114
|
+
section("Tests"):
|
115
|
+
doc"\evaluate{use(therie)}"
|
116
|
+
|
117
|
+
doc"
|
118
|
+
Test that \hl{predicate} is satisified by \hl{o} by evaluating it with \
|
119
|
+
\hl{o} as \hl{self}.
|
120
|
+
" for {
|
121
|
+
o should(&check) :=
|
122
|
+
unless(o onto(&check)):
|
123
|
+
raise(f"assertion failed for %v" [o])
|
124
|
+
} examples:
|
125
|
+
(2 + 2) should: even?
|
126
|
+
(2 + 2) should: odd?
|
127
|
+
|
128
|
+
|
129
|
+
doc"
|
130
|
+
Test for \hl{x == y}.
|
131
|
+
" for {
|
132
|
+
x should-be(y) :=
|
133
|
+
unless(x == y):
|
134
|
+
raise(f"expected %v, got %v" [y, x])
|
135
|
+
} examples:
|
136
|
+
(2 + 2) should-be(4)
|
137
|
+
(1 + 2) should-be(4)
|
138
|
+
|
139
|
+
|
140
|
+
doc"
|
141
|
+
Test that executing \hl{x} will raise an exception of class \hl{y}.
|
142
|
+
" spec {
|
143
|
+
x respond-to?(#call)
|
144
|
+
y is-a?(Class)
|
145
|
+
} for {
|
146
|
+
x should-raise(y) :=
|
147
|
+
x rescue {
|
148
|
+
e ->
|
149
|
+
unless(e kind-of?(y)):
|
150
|
+
raise(f"expected exception %s, got: %s" [y, e])
|
151
|
+
} else:
|
152
|
+
raise("#should-raise - no exception raised")
|
153
|
+
} examples:
|
154
|
+
{ abc } should-raise(NoMethodError)
|
155
|
+
{ #ok } should-raise(NoMethodError)
|
156
|
+
|
157
|
+
|
158
|
+
doc"
|
159
|
+
Test that executing \hl{x} will signal an error of class \hl{y}.
|
160
|
+
" spec {
|
161
|
+
x respond-to?(#call)
|
162
|
+
y is-a?(Class)
|
163
|
+
} for {
|
164
|
+
x should-error(y) :=
|
165
|
+
{ with-restarts(errored -> nil):
|
166
|
+
x call
|
167
|
+
raise("#should-error - no error signaled")
|
168
|
+
} bind {
|
169
|
+
(e: ExceptionError) ->
|
170
|
+
raise(e exception)
|
171
|
+
|
172
|
+
(e: Error) -> do:
|
173
|
+
when(e kind-of?(y)):
|
174
|
+
restart(#errored)
|
175
|
+
|
176
|
+
raise(f"expected error %s, got: %s" [y, e message])
|
177
|
+
}
|
178
|
+
} examples:
|
179
|
+
{ error(#foo) } should-error(SimpleError)
|
180
|
+
{ #ok } should-error(SimpleError)
|
181
|
+
|
182
|
+
|
183
|
+
-- helpers
|
184
|
+
String indented := " " * (^nesting-level * 2) + self
|
185
|
+
|
186
|
+
String colored(color) := do:
|
187
|
+
-- only makes sense to colorize if we're outputting to a terminal
|
188
|
+
unless(^output-port tty?):
|
189
|
+
return(self)
|
190
|
+
|
191
|
+
codes =
|
192
|
+
[ #black
|
193
|
+
#red
|
194
|
+
#green
|
195
|
+
#yellow
|
196
|
+
#blue
|
197
|
+
#magenta
|
198
|
+
#cyan
|
199
|
+
#white
|
200
|
+
] zip((0 .. 7) to-a)
|
201
|
+
|
202
|
+
hash = Hash [codes]
|
203
|
+
|
204
|
+
"\e[9" + hash [color] to-s + "m" + self + "\e[0m"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Atomy
|
2
|
+
module AST
|
3
|
+
class BinarySend < Node
|
4
|
+
children :lhs, :rhs
|
5
|
+
attributes :operator
|
6
|
+
slots [:private, "false"], :namespace?
|
7
|
+
generate
|
8
|
+
|
9
|
+
alias :method_name :operator
|
10
|
+
|
11
|
+
def register_macro(body, let = false)
|
12
|
+
Atomy::Macro.register(
|
13
|
+
@operator,
|
14
|
+
[@lhs, @rhs].collect do |n|
|
15
|
+
Atomy::Macro.macro_pattern n
|
16
|
+
end,
|
17
|
+
body,
|
18
|
+
let
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def message_name
|
23
|
+
Atomy.namespaced(@namespace, @operator)
|
24
|
+
end
|
25
|
+
|
26
|
+
def prepare
|
27
|
+
resolve.expand
|
28
|
+
end
|
29
|
+
|
30
|
+
def bytecode(g)
|
31
|
+
pos(g)
|
32
|
+
@lhs.compile(g)
|
33
|
+
g.push_literal message_name.to_sym unless @namespace == "_"
|
34
|
+
@rhs.compile(g)
|
35
|
+
if @namespace == "_"
|
36
|
+
g.send @operator.to_sym, 1
|
37
|
+
else
|
38
|
+
g.send :atomy_send, 2
|
39
|
+
#g.call_custom method_name.to_sym, 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/ast/block.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
module Atomy
|
2
|
+
module AST
|
3
|
+
class Block < Rubinius::AST::Iter
|
4
|
+
include NodeLike
|
5
|
+
extend SentientNode
|
6
|
+
|
7
|
+
children [:contents], [:arguments]
|
8
|
+
generate
|
9
|
+
|
10
|
+
def block_arguments
|
11
|
+
BlockArguments.new @arguments
|
12
|
+
end
|
13
|
+
|
14
|
+
def block_body
|
15
|
+
BlockBody.new @line, @contents
|
16
|
+
end
|
17
|
+
|
18
|
+
def body
|
19
|
+
InlinedBody.new @line, @contents
|
20
|
+
end
|
21
|
+
|
22
|
+
alias :caller :body
|
23
|
+
|
24
|
+
def bytecode(g)
|
25
|
+
pos(g)
|
26
|
+
|
27
|
+
state = g.state
|
28
|
+
state.scope.nest_scope self
|
29
|
+
|
30
|
+
blk = new_block_generator g, block_arguments
|
31
|
+
|
32
|
+
blk.push_state self
|
33
|
+
blk.state.push_super state.super
|
34
|
+
blk.state.push_eval state.eval
|
35
|
+
|
36
|
+
blk.state.push_name blk.name
|
37
|
+
|
38
|
+
# Push line info down.
|
39
|
+
pos(blk)
|
40
|
+
|
41
|
+
block_arguments.bytecode(blk)
|
42
|
+
|
43
|
+
blk.state.push_block
|
44
|
+
blk.push_modifiers
|
45
|
+
blk.break = nil
|
46
|
+
blk.next = nil
|
47
|
+
blk.redo = blk.new_label
|
48
|
+
blk.redo.set!
|
49
|
+
|
50
|
+
block_body.compile(blk)
|
51
|
+
|
52
|
+
blk.pop_modifiers
|
53
|
+
blk.state.pop_block
|
54
|
+
blk.ret
|
55
|
+
blk.close
|
56
|
+
blk.pop_state
|
57
|
+
|
58
|
+
blk.splat_index = block_arguments.splat_index
|
59
|
+
blk.local_count = local_count
|
60
|
+
blk.local_names = local_names
|
61
|
+
|
62
|
+
g.create_block blk
|
63
|
+
|
64
|
+
g.push_cpath_top
|
65
|
+
g.find_const :Proc
|
66
|
+
g.swap
|
67
|
+
g.send :__from_block__, 1
|
68
|
+
end
|
69
|
+
|
70
|
+
def as_message(send)
|
71
|
+
case send.receiver
|
72
|
+
when Send
|
73
|
+
if send.receiver.method_name == "[]"
|
74
|
+
dup.tap do |b|
|
75
|
+
b.arguments = send.receiver.arguments
|
76
|
+
end.as_message(send.receiver)
|
77
|
+
else
|
78
|
+
send.receiver.dup.tap do |s|
|
79
|
+
s.block = self
|
80
|
+
end
|
81
|
+
end
|
82
|
+
when Variable, Unquote,
|
83
|
+
Constant, ScopedConstant, ToplevelConstant
|
84
|
+
send.receiver = send.receiver.to_send
|
85
|
+
as_message(send)
|
86
|
+
when List
|
87
|
+
dup.tap do |b|
|
88
|
+
b.arguments = send.receiver.elements
|
89
|
+
end
|
90
|
+
else
|
91
|
+
unless send.method_name
|
92
|
+
raise "unknown receiver for block: #{send.receiver.inspect}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class InlinedBody < Node
|
99
|
+
children [:expressions]
|
100
|
+
generate
|
101
|
+
|
102
|
+
attr_accessor :parent
|
103
|
+
|
104
|
+
def variables
|
105
|
+
@variables ||= {}
|
106
|
+
end
|
107
|
+
|
108
|
+
def local_count
|
109
|
+
@parent.local_names
|
110
|
+
end
|
111
|
+
|
112
|
+
def local_names
|
113
|
+
@parent.local_names
|
114
|
+
end
|
115
|
+
|
116
|
+
def allocate_slot
|
117
|
+
@parent.allocate_slot
|
118
|
+
end
|
119
|
+
|
120
|
+
def nest_scope(scope)
|
121
|
+
scope.parent = self
|
122
|
+
end
|
123
|
+
|
124
|
+
def search_local(name)
|
125
|
+
if variable = variables[name]
|
126
|
+
variable.nested_reference
|
127
|
+
else
|
128
|
+
@parent.search_local(name)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def pseudo_local(name)
|
133
|
+
if variable = variables[name]
|
134
|
+
variable.nested_reference
|
135
|
+
elsif reference = @parent.search_local(name)
|
136
|
+
reference.depth += 1
|
137
|
+
reference
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def new_local(name)
|
142
|
+
variables[name] =
|
143
|
+
@parent.new_local(name + ":" + @parent.allocate_slot.to_s)
|
144
|
+
end
|
145
|
+
|
146
|
+
def new_nested_local(name)
|
147
|
+
@parent.new_local(name).nested_reference
|
148
|
+
end
|
149
|
+
|
150
|
+
def empty?
|
151
|
+
@expressions.empty?
|
152
|
+
end
|
153
|
+
|
154
|
+
def setup(g)
|
155
|
+
g.state.scope.nest_scope self
|
156
|
+
|
157
|
+
blk = g.state.block?
|
158
|
+
ens = g.state.ensure?
|
159
|
+
res = g.state.rescue?
|
160
|
+
lop = g.state.loop?
|
161
|
+
msn = g.state.masgn?
|
162
|
+
|
163
|
+
g.push_state self
|
164
|
+
|
165
|
+
g.state.push_block if blk
|
166
|
+
g.state.push_ensure if ens
|
167
|
+
g.state.push_rescue(res) if res
|
168
|
+
g.state.push_loop if lop
|
169
|
+
g.state.push_masgn if msn
|
170
|
+
end
|
171
|
+
|
172
|
+
def reset(g)
|
173
|
+
g.pop_state
|
174
|
+
end
|
175
|
+
|
176
|
+
def bytecode(g)
|
177
|
+
pos(g)
|
178
|
+
|
179
|
+
setup(g)
|
180
|
+
|
181
|
+
g.push_nil if empty?
|
182
|
+
|
183
|
+
@expressions.each_with_index do |node,idx|
|
184
|
+
g.pop unless idx == 0
|
185
|
+
node.compile(g)
|
186
|
+
end
|
187
|
+
|
188
|
+
reset(g)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class BlockArguments
|
193
|
+
attr_reader :arguments
|
194
|
+
|
195
|
+
def initialize(args)
|
196
|
+
@arguments = args.collect(&:to_pattern)
|
197
|
+
end
|
198
|
+
|
199
|
+
def bytecode(g)
|
200
|
+
return if @arguments.empty?
|
201
|
+
|
202
|
+
if @arguments.last.kind_of?(Patterns::BlockPass)
|
203
|
+
g.push_block_arg
|
204
|
+
@arguments.pop.deconstruct(g)
|
205
|
+
end
|
206
|
+
|
207
|
+
g.cast_for_splat_block_arg
|
208
|
+
@arguments.each do |a|
|
209
|
+
if a.kind_of?(Patterns::Splat)
|
210
|
+
a.pattern.deconstruct(g)
|
211
|
+
return
|
212
|
+
else
|
213
|
+
g.shift_array
|
214
|
+
a.match(g)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
g.pop
|
218
|
+
end
|
219
|
+
|
220
|
+
def local_names
|
221
|
+
@arguments.collect { |a| a.local_names }.flatten
|
222
|
+
end
|
223
|
+
|
224
|
+
def size
|
225
|
+
@arguments.size
|
226
|
+
end
|
227
|
+
|
228
|
+
def locals
|
229
|
+
local_names.size
|
230
|
+
end
|
231
|
+
|
232
|
+
def required_args
|
233
|
+
size
|
234
|
+
end
|
235
|
+
|
236
|
+
def total_args
|
237
|
+
size
|
238
|
+
end
|
239
|
+
|
240
|
+
def splat_index
|
241
|
+
@arguments.each do |a,i|
|
242
|
+
return i if a.kind_of?(Patterns::Splat)
|
243
|
+
end
|
244
|
+
nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class BlockBody < Node
|
249
|
+
children [:expressions]
|
250
|
+
generate
|
251
|
+
|
252
|
+
def empty?
|
253
|
+
@expressions.empty?
|
254
|
+
end
|
255
|
+
|
256
|
+
def bytecode(g)
|
257
|
+
pos(g)
|
258
|
+
|
259
|
+
g.push_nil if empty?
|
260
|
+
|
261
|
+
@expressions.each_with_index do |node,idx|
|
262
|
+
g.pop unless idx == 0
|
263
|
+
node.compile(g)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|