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/COPYING
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Copyright (c)2010, Alex Suraci
|
2
|
+
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
* Redistributions in binary form must reproduce the above
|
12
|
+
copyright notice, this list of conditions and the following
|
13
|
+
disclaimer in the documentation and/or other materials provided
|
14
|
+
with the distribution.
|
15
|
+
|
16
|
+
* Neither the name of Alex Suraci nor the names of other
|
17
|
+
contributors may be used to endorse or promote products derived
|
18
|
+
from this software without specific prior written permission.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
21
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
22
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
23
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
24
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
25
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
26
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
27
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
28
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
29
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Atomo-like language on the Rubinius VM.
|
data/bin/atomy
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
#!/usr/bin/env rbx
|
2
|
+
|
3
|
+
base = File.expand_path "../../lib/", __FILE__
|
4
|
+
kernel = File.expand_path "../../kernel/", __FILE__
|
5
|
+
|
6
|
+
require 'readline'
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
require base + "/macros"
|
10
|
+
require base + "/method"
|
11
|
+
require base + "/util"
|
12
|
+
require base + "/namespace"
|
13
|
+
require base + "/compiler/compiler"
|
14
|
+
require base + "/compiler/stages"
|
15
|
+
require base + "/parser"
|
16
|
+
require base + "/patterns"
|
17
|
+
require base + "/code_loader"
|
18
|
+
|
19
|
+
def require_atomy(*as)
|
20
|
+
before = Atomy::Namespace.get
|
21
|
+
begin
|
22
|
+
Atomy::CodeLoader.load_file *as
|
23
|
+
ensure
|
24
|
+
Atomy::Namespace.set(before)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
options = {}
|
29
|
+
|
30
|
+
unless ARGV.empty? || ARGV[0][0] != ?-
|
31
|
+
OptionParser.new do |o|
|
32
|
+
o.banner = "usage: atomy [options]"
|
33
|
+
|
34
|
+
o.on("-B", "--print-bytecode", "print out compiled bytecode") do |v|
|
35
|
+
options[:debug] = v
|
36
|
+
end
|
37
|
+
|
38
|
+
o.on("-e", "--evaluate EXPR", "evaluate EXPR and print the result") do |v|
|
39
|
+
options[:evaluate] = v
|
40
|
+
end
|
41
|
+
|
42
|
+
o.on("-s", "--before-start EXPR", "evaluate EXPR beforehand") do |v|
|
43
|
+
options[:start] = v
|
44
|
+
end
|
45
|
+
|
46
|
+
o.on("-l", "--load FILE", "load FILENAME and start the REPL") do |v|
|
47
|
+
options[:load] = v
|
48
|
+
end
|
49
|
+
|
50
|
+
o.on("-d", "--documentation DIR", "generate documentation with output to DIR") do |v|
|
51
|
+
Atomy::CodeLoader.documentation = v
|
52
|
+
end
|
53
|
+
|
54
|
+
o.on_tail("-h", "--help", "show this message") do
|
55
|
+
puts o
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
end.parse!
|
59
|
+
end
|
60
|
+
|
61
|
+
a = Time.now
|
62
|
+
puts "loading kernel"
|
63
|
+
require_atomy(kernel + "/boot.ay")
|
64
|
+
puts Time.now - a
|
65
|
+
|
66
|
+
Atomy::Namespace.ensure(:user)
|
67
|
+
|
68
|
+
def run_atomy(options)
|
69
|
+
if str = options[:evaluate] || options[:start]
|
70
|
+
$0 = "(eval)"
|
71
|
+
res = Atomy::Compiler.evaluate(
|
72
|
+
str,
|
73
|
+
nil,
|
74
|
+
"(eval)",
|
75
|
+
1,
|
76
|
+
options[:debug]
|
77
|
+
)
|
78
|
+
|
79
|
+
if options[:evaluate]
|
80
|
+
res.send(Atomy.namespaced("atomy", "write").to_sym)
|
81
|
+
return
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if file = options[:load] || ARGV[0]
|
86
|
+
$0 = file
|
87
|
+
require_atomy(file, ARGV[0] ? :run : :load, options[:debug])
|
88
|
+
return if ARGV[0]
|
89
|
+
end
|
90
|
+
|
91
|
+
if respond_to?(Atomy.namespaced("atomy", "repl").to_sym, true)
|
92
|
+
send(Atomy.namespaced("atomy", "repl").to_sym, File.expand_path("~/.atomy_history"))
|
93
|
+
else
|
94
|
+
$stderr.puts("main REPL startup failed! here's a basic one:")
|
95
|
+
|
96
|
+
history_file = File.expand_path("~/.atomy_history")
|
97
|
+
|
98
|
+
if File.exists?(history_file)
|
99
|
+
File.open(history_file, "r") do |f|
|
100
|
+
f.readlines.each do |l|
|
101
|
+
Readline::HISTORY << l.strip
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
begin
|
107
|
+
sane_history = []
|
108
|
+
while str = Readline.readline("> ")
|
109
|
+
next if str.empty?
|
110
|
+
|
111
|
+
sane_history << str
|
112
|
+
|
113
|
+
begin
|
114
|
+
res = Atomy::Compiler.evaluate str, TOPLEVEL_BINDING
|
115
|
+
if res.respond_to?(:pretty)
|
116
|
+
puts "=> #{res.pretty.render}"
|
117
|
+
else
|
118
|
+
puts "=> #{res.inspect}"
|
119
|
+
end
|
120
|
+
rescue StandardError => e
|
121
|
+
puts "ERROR!"
|
122
|
+
puts "#{e}:\n #{e.message}"
|
123
|
+
puts e.backtrace
|
124
|
+
end
|
125
|
+
end
|
126
|
+
ensure
|
127
|
+
File.open(history_file, "a") do |f|
|
128
|
+
f.puts(*sane_history)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
run_atomy(options)
|
data/kernel/block.ay
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
namespace(atomy)
|
2
|
+
|
3
|
+
macro(x onto(&b)):
|
4
|
+
names [ctx]:
|
5
|
+
shadowed? = false
|
6
|
+
|
7
|
+
blk = b through-quotes [n]:
|
8
|
+
n match:
|
9
|
+
`@~(v: Atomy::AST::Variable) -> do:
|
10
|
+
shadowed? =! true
|
11
|
+
`(~ctx instance-variable-get(#~("@" + v name)))
|
12
|
+
|
13
|
+
'self -> do:
|
14
|
+
shadowed? =! true
|
15
|
+
ctx
|
16
|
+
|
17
|
+
Atomy::AST::Send ? @private ->
|
18
|
+
n dup tap [p]:
|
19
|
+
p receiver = 'self
|
20
|
+
|
21
|
+
Atomy::AST::BinarySend ? @private ->
|
22
|
+
n dup tap [p]:
|
23
|
+
p lhs = 'self
|
24
|
+
|
25
|
+
_ ->
|
26
|
+
n
|
27
|
+
|
28
|
+
if(shadowed?)
|
29
|
+
then: `(do: ~ctx = self, ~blk block call-on-instance(~x))
|
30
|
+
else: `(~b block call-on-instance(~x))
|
data/kernel/boot.ay
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
macro(_LINE): line
|
2
|
+
macro(_FILE): Atomy::AST::File new(line)
|
3
|
+
|
4
|
+
macro(import(name)): `(require-atomy(~name, "load" to-sym))
|
5
|
+
|
6
|
+
"operators define cosmetics data comparison dynamic control-flow namespaces
|
7
|
+
patterns precision data-delta documentation particles hashes node block errors
|
8
|
+
doc pretty format concurrency io condition therie repl" split each [k]:
|
9
|
+
puts("loading " + k)
|
10
|
+
import(File expand-path("../", _FILE) + "/" + k)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module(Atomy::AST):
|
2
|
+
class(And < Node):
|
3
|
+
children(#a, #b)
|
4
|
+
generate
|
5
|
+
|
6
|
+
bytecode(g) := do:
|
7
|
+
pos(g)
|
8
|
+
done = g new-label
|
9
|
+
no = g new-label
|
10
|
+
|
11
|
+
@a compile(g)
|
12
|
+
g dup
|
13
|
+
g gif(done)
|
14
|
+
|
15
|
+
g pop
|
16
|
+
@b compile(g)
|
17
|
+
|
18
|
+
done set!
|
19
|
+
|
20
|
+
class(Or < Node):
|
21
|
+
children(#a, #b)
|
22
|
+
generate
|
23
|
+
|
24
|
+
bytecode(g) := do:
|
25
|
+
pos(g)
|
26
|
+
done = g new-label
|
27
|
+
|
28
|
+
@a compile(g)
|
29
|
+
g dup
|
30
|
+
g git(done)
|
31
|
+
|
32
|
+
g pop
|
33
|
+
@b compile(g)
|
34
|
+
|
35
|
+
done set!
|
36
|
+
|
37
|
+
class(Negate < Node):
|
38
|
+
children(#expression)
|
39
|
+
generate
|
40
|
+
|
41
|
+
bytecode(g) := do:
|
42
|
+
pos(g)
|
43
|
+
done = g new-label
|
44
|
+
yes = g new-label
|
45
|
+
|
46
|
+
@expression compile(g)
|
47
|
+
g git(yes)
|
48
|
+
|
49
|
+
g push-true
|
50
|
+
g goto(done)
|
51
|
+
|
52
|
+
yes set!
|
53
|
+
g push-false
|
54
|
+
|
55
|
+
done set!
|
56
|
+
|
57
|
+
macro(a && b): Atomy::AST::And new(line, a, b)
|
58
|
+
macro(a || b): Atomy::AST::Or new(line, a, b)
|
59
|
+
macro(!a): Atomy::AST::Negate new(line, a)
|
60
|
+
macro(a != b): `!(~a == ~b)
|
61
|
+
macro(a !~ b): `!(~a =~ ~b)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
namespace(atomy)
|
2
|
+
|
3
|
+
require("actor")
|
4
|
+
|
5
|
+
title"Concurrency"
|
6
|
+
|
7
|
+
doc"
|
8
|
+
Get the current actor.
|
9
|
+
" spec {
|
10
|
+
=> Actor
|
11
|
+
} for:
|
12
|
+
me := Actor current
|
13
|
+
|
14
|
+
|
15
|
+
section("Sending & Receiving"):
|
16
|
+
doc"
|
17
|
+
Send message \code{v} to the actor.
|
18
|
+
" spec {
|
19
|
+
=> Actor
|
20
|
+
} for {
|
21
|
+
Actor <- v := send(v)
|
22
|
+
} examples:
|
23
|
+
a = spawn: receive { 42 -> #ok } write
|
24
|
+
a <- 42
|
25
|
+
|
26
|
+
doc"
|
27
|
+
Receive a message sent to the current actor that matches any of the \
|
28
|
+
patterns listed in \hl{body}. Blocks until a matching message is \
|
29
|
+
received. Non-matching messages are consumed and ignored.
|
30
|
+
" spec {
|
31
|
+
body contents all? [x]: x match { `(~_ -> ~_) -> true, _ -> false }
|
32
|
+
=> any
|
33
|
+
} for {
|
34
|
+
macro(receive(&body)):
|
35
|
+
names [e]:
|
36
|
+
bs = body contents collect [`(~pat -> ~exp)]:
|
37
|
+
`(~e when('~pat to-pattern) [~pat]: ~exp)
|
38
|
+
|
39
|
+
`(Actor receive [~e]: ~*bs)
|
40
|
+
} examples:
|
41
|
+
a = spawn: receive { 1 -> #got-1 } write
|
42
|
+
a <- 0
|
43
|
+
a <- 2
|
44
|
+
a <- 1
|
45
|
+
|
46
|
+
doc"
|
47
|
+
Similar to \code{receive}, but with a timeout and an action to \
|
48
|
+
perform if it times out.
|
49
|
+
" spec {
|
50
|
+
body contents all? [x]: x match { `(~_ -> ~_) -> true, _ -> false }
|
51
|
+
timeout match: `(~_ -> ~_) -> true, _ -> false
|
52
|
+
=> any
|
53
|
+
} for {
|
54
|
+
macro(receive(&body) after(timeout)):
|
55
|
+
names [e]:
|
56
|
+
bs = body contents collect [`(~pat -> ~exp)]:
|
57
|
+
`(~e when('~pat to-pattern) [~pat]: ~exp)
|
58
|
+
|
59
|
+
bs << `(~e after(~(timeout lhs)): ~(timeout rhs))
|
60
|
+
|
61
|
+
`(Actor receive [~e]: ~*bs)
|
62
|
+
} examples:
|
63
|
+
receive { 1 -> #ok } after(1 -> #too-slow)
|
64
|
+
|
65
|
+
|
66
|
+
section("Spawning"):
|
67
|
+
doc"
|
68
|
+
Spawn a new actor, performing \code{action}.
|
69
|
+
" spec {
|
70
|
+
=> Actor
|
71
|
+
} for {
|
72
|
+
spawn(&action) := Actor send(#spawn, &action)
|
73
|
+
} examples:
|
74
|
+
spawn: (2 + 2) write
|
75
|
+
|
76
|
+
doc"
|
77
|
+
Spawn a new actor, performing \code{action}, linked to the current \
|
78
|
+
actor.
|
79
|
+
" spec {
|
80
|
+
=> Actor
|
81
|
+
} for {
|
82
|
+
spawn-link(&action) := Actor send(#spawn-link, &action)
|
83
|
+
} examples:
|
84
|
+
spawn-link: (2 + 2) write
|
data/kernel/condition.ay
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
namespace(atomy)
|
2
|
+
|
3
|
+
title"Condition System"
|
4
|
+
|
5
|
+
doc"
|
6
|
+
Rather than traditional exceptions, Atomy sports a condition/restart \
|
7
|
+
system modeled on Common Lisp's design. The native Ruby exception handling \
|
8
|
+
is available, but conditions and restarts are much more flexible.
|
9
|
+
"
|
10
|
+
|
11
|
+
-- registered handlers/restarts
|
12
|
+
dynamic(handlers, [])
|
13
|
+
dynamic(restarts, [])
|
14
|
+
|
15
|
+
for-macro:
|
16
|
+
catcher(x) :=
|
17
|
+
Atomy::AST::Variable new(x line) $:
|
18
|
+
x match:
|
19
|
+
Atomy::AST::Variable ->
|
20
|
+
"restart:" + x name
|
21
|
+
|
22
|
+
Atomy::AST::Send ->
|
23
|
+
"restart:" + x method-name
|
24
|
+
|
25
|
+
data(Object):
|
26
|
+
Restart(@name, @action)
|
27
|
+
|
28
|
+
Restart invoke(*args) :=
|
29
|
+
throw(("restart:" + @name to-s) to-sym, @action [*args])
|
30
|
+
|
31
|
+
symbols(signal, warning, error, restart)
|
32
|
+
|
33
|
+
section("Conditions"):
|
34
|
+
doc"
|
35
|
+
Condition system hierarchy. You should subclass one of these to create \
|
36
|
+
your own conditions.
|
37
|
+
" for:
|
38
|
+
data(Object):
|
39
|
+
Condition:
|
40
|
+
Error(@backtrace):
|
41
|
+
SimpleError(@value)
|
42
|
+
ExceptionError(@exception)
|
43
|
+
NoRestartError(@name)
|
44
|
+
PortError(@port):
|
45
|
+
EndOfFile
|
46
|
+
|
47
|
+
Warning(@backtrace):
|
48
|
+
SimpleWarning(@value)
|
49
|
+
|
50
|
+
doc"
|
51
|
+
Get the name of a condition. By default, this will be the class name, \
|
52
|
+
but you may override this for your own behaviour.
|
53
|
+
" for:
|
54
|
+
Condition name := class name
|
55
|
+
|
56
|
+
ExceptionError name := @exception class name
|
57
|
+
|
58
|
+
doc"
|
59
|
+
A human-friendly message displayed for the condition. Override this.
|
60
|
+
" for:
|
61
|
+
Condition message := inspect
|
62
|
+
|
63
|
+
SimpleError message := @value to-s
|
64
|
+
ExceptionError message := @exception message
|
65
|
+
SimpleWarning message := @value to-s
|
66
|
+
NoRestartError message := "unknown restart " + @name show
|
67
|
+
EndOfFile message := "unexpected end-of-file for " + @port show
|
68
|
+
|
69
|
+
|
70
|
+
section("Handling"):
|
71
|
+
doc"
|
72
|
+
Invoke the \hl{name} restart, passing \hl{args} along to its callback.
|
73
|
+
|
74
|
+
See \hl{with-restarts}.
|
75
|
+
" spec {
|
76
|
+
name is-a?(Symbol)
|
77
|
+
=> any
|
78
|
+
} for {
|
79
|
+
restart(name, *args) := do:
|
80
|
+
^restarts each [r]:
|
81
|
+
when(r name == name):
|
82
|
+
r invoke(*args)
|
83
|
+
|
84
|
+
error(NoRestartError new(name))
|
85
|
+
} examples:
|
86
|
+
{ with-restarts(foo -> 42):
|
87
|
+
signal(#bar)
|
88
|
+
} bind: #bar -> restart(#foo)
|
89
|
+
|
90
|
+
|
91
|
+
doc"
|
92
|
+
Register handlers for various signals for the duration of \hl{x}'s \
|
93
|
+
execution.
|
94
|
+
|
95
|
+
The body of \hl{y} is similar to \hl{match}; \hl{\italic{pattern} -> \
|
96
|
+
\italic{body}}.
|
97
|
+
|
98
|
+
The result is the result of \hl{body}.
|
99
|
+
" spec {
|
100
|
+
y contents all? [x]: x match { `(~_ -> ~_) -> true, _ -> false }
|
101
|
+
=> any
|
102
|
+
} for {
|
103
|
+
macro(body bind(&y)):
|
104
|
+
names [a]:
|
105
|
+
callback = `([~a]: ~a match: ~*(y contents))
|
106
|
+
`(let(handlers = ^handlers + [~callback]):
|
107
|
+
~body rescue:
|
108
|
+
(e: StandardError) -> error(e))
|
109
|
+
} examples:
|
110
|
+
{ signal(#a) } bind: #a -> "got A!" print
|
111
|
+
{ signal(#b) } bind: #a -> "got A!" print
|
112
|
+
{ { signal(#a) } bind: #a -> "inner" print } bind: #a -> "outer" print
|
113
|
+
|
114
|
+
|
115
|
+
doc"
|
116
|
+
Register restarts available for the duration of \hl{body}'s execution.
|
117
|
+
|
118
|
+
The \hl{restarts} should be in the form of \
|
119
|
+
\hl{\italic{name}(*\italic{args}) -> \italic{body}}.
|
120
|
+
|
121
|
+
The result is the result of \hl{body}.
|
122
|
+
" spec {
|
123
|
+
=> any
|
124
|
+
} for {
|
125
|
+
macro(with-restarts(*restarts, &block)):
|
126
|
+
rs = restarts collect [`(~n -> ~e)]:
|
127
|
+
n match:
|
128
|
+
Atomy::AST::Variable ->
|
129
|
+
`(Restart new(#~n, { ~e }))
|
130
|
+
|
131
|
+
Atomy::AST::Send -> do:
|
132
|
+
name = Atomy::AST::Variable new(0, n method-name)
|
133
|
+
`(Restart new(#~name, [~*(n arguments)] { ~e }))
|
134
|
+
|
135
|
+
body =
|
136
|
+
restarts reduce(
|
137
|
+
`{ ~block rescue: (e: StandardError) -> error(e) }
|
138
|
+
) [x, `(~name -> ~_)]:
|
139
|
+
`{ catch(#~catcher(name), &~x) }
|
140
|
+
|
141
|
+
`(let(restarts = [~*rs] + ^restarts, &~body))
|
142
|
+
} examples:
|
143
|
+
{ with-restarts(x -> 1, y -> 2):
|
144
|
+
signal(#a)
|
145
|
+
} bind: #a -> restart(#x)
|
146
|
+
|
147
|
+
{ with-restarts(x -> 1, y -> 2):
|
148
|
+
signal(#a)
|
149
|
+
} bind: #a -> restart(#y)
|
150
|
+
|
151
|
+
{ with-restarts(x(a) -> a * 7):
|
152
|
+
signal(#a)
|
153
|
+
} bind: #a -> restart(#x, 6)
|
154
|
+
|
155
|
+
|
156
|
+
section("Signalling"):
|
157
|
+
doc"
|
158
|
+
Signal a value through all bound handlers, nearest-first, stopping when \
|
159
|
+
a restart is invoked.
|
160
|
+
" spec {
|
161
|
+
=> nil
|
162
|
+
} for {
|
163
|
+
signal(c) := do:
|
164
|
+
^handlers reverse-each [callback]:
|
165
|
+
callback [c]
|
166
|
+
|
167
|
+
nil
|
168
|
+
} examples:
|
169
|
+
signal(#foo)
|
170
|
+
{ signal(#foo) } bind: #foo -> "got foo" print
|
171
|
+
|
172
|
+
|
173
|
+
doc"
|
174
|
+
Like \hl{signal}, except that if no restart is invoked, the current \
|
175
|
+
\hl{^debugger} is started.
|
176
|
+
|
177
|
+
If the given value is not an \hl{Error}, it is wrapped in a \
|
178
|
+
\hl{SimpleError}. If the value is a Ruby \hl{Exception}, it is wrapped \
|
179
|
+
in an \hl{ExceptionError}.
|
180
|
+
" spec {
|
181
|
+
=> _
|
182
|
+
} for {
|
183
|
+
error(x) := do:
|
184
|
+
e =
|
185
|
+
x match:
|
186
|
+
Exception ->
|
187
|
+
ExceptionError new(x) tap [err]:
|
188
|
+
err backtrace = x locations
|
189
|
+
|
190
|
+
Error ->
|
191
|
+
x tap [err]:
|
192
|
+
err backtrace = Rubinius::VM backtrace(0)
|
193
|
+
|
194
|
+
_ ->
|
195
|
+
SimpleError new(x) tap [err]:
|
196
|
+
err backtrace = Rubinius::VM backtrace(0)
|
197
|
+
|
198
|
+
signal(e)
|
199
|
+
|
200
|
+
with-output-to(^error-port):
|
201
|
+
^debugger run(e)
|
202
|
+
} examples:
|
203
|
+
error("Oh no!")
|
204
|
+
{ error("Oh no!") } bind: Error -> "INCOMING" print
|
205
|
+
|
206
|
+
|
207
|
+
doc"
|
208
|
+
Like \hl{signal}, except that if no restart is invoked, the warning is \
|
209
|
+
printed to \hl{^error-port}.
|
210
|
+
|
211
|
+
If the given value is not a \hl{Warning}, it is wrapped in a \
|
212
|
+
\hl{SimpleWarning}. Warning messages can be muffled by binding for \
|
213
|
+
\hl{Warning} and invoking the \hl{#muffle-warning} restart.
|
214
|
+
" spec {
|
215
|
+
=> nil
|
216
|
+
} for {
|
217
|
+
warning(x) :=
|
218
|
+
with-restarts(muffle-warning -> nil):
|
219
|
+
w =
|
220
|
+
x match:
|
221
|
+
Warning ->
|
222
|
+
x tap [wrn]:
|
223
|
+
wrn backtrace = Rubinius::VM backtrace(0)
|
224
|
+
|
225
|
+
_ ->
|
226
|
+
SimpleWarning new(x) tap [wrn]:
|
227
|
+
wrn backtrace = Rubinius::VM backtrace(0)
|
228
|
+
|
229
|
+
signal(w)
|
230
|
+
|
231
|
+
with-output-to(^error-port):
|
232
|
+
(w name + ": " + w message) print
|
233
|
+
|
234
|
+
nil
|
235
|
+
} examples:
|
236
|
+
warning("Suspicious!")
|
237
|
+
{ warning("Quiet, you!") } bind: Warning -> restart(#muffle-warning)
|
238
|
+
|
239
|
+
|
240
|
+
section("Debuggers"):
|
241
|
+
doc"
|
242
|
+
The default debugger. This will show the condition name, its message, \
|
243
|
+
and let the user pick from the available restarts.
|
244
|
+
" for:
|
245
|
+
class(DefaultDebugger):
|
246
|
+
class(<< self):
|
247
|
+
define(show-error-banner(e)):
|
248
|
+
("-" * 78) print
|
249
|
+
(e name + ": " + e message) each-line [l]:
|
250
|
+
("*** " + l) display
|
251
|
+
|
252
|
+
"\n" display
|
253
|
+
|
254
|
+
define(show-options-for(e)):
|
255
|
+
when(^restarts empty?):
|
256
|
+
exit(1)
|
257
|
+
|
258
|
+
"\n" display
|
259
|
+
"restarts:" print
|
260
|
+
^restarts each-with-index [r, i]:
|
261
|
+
(" :" + i to-s + " -> " + r name to-s) print
|
262
|
+
|
263
|
+
define(run(e)):
|
264
|
+
show-error-banner(e)
|
265
|
+
|
266
|
+
show-options-for(e)
|
267
|
+
|
268
|
+
"!> " display
|
269
|
+
^restarts [gets to-i] invoke
|
270
|
+
|
271
|
+
doc"
|
272
|
+
The current debugger. \hl{run} will be called with the condition as an \
|
273
|
+
argument.
|
274
|
+
" spec {
|
275
|
+
respond-to?(#run)
|
276
|
+
} for:
|
277
|
+
dynamic(debugger, DefaultDebugger)
|