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/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)
|