atomy 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|