rouge-lang 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +11 -0
- data/.gitignore +20 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +119 -0
- data/Rakefile +8 -0
- data/bin/rouge +2 -0
- data/lib/boot.rg +402 -0
- data/lib/rouge.rb +56 -0
- data/lib/rouge/atom.rb +25 -0
- data/lib/rouge/builtins.rb +596 -0
- data/lib/rouge/compiler.rb +108 -0
- data/lib/rouge/context.rb +235 -0
- data/lib/rouge/metadata.rb +19 -0
- data/lib/rouge/namespace.rb +125 -0
- data/lib/rouge/printer.rb +78 -0
- data/lib/rouge/reader.rb +433 -0
- data/lib/rouge/repl.rb +82 -0
- data/lib/rouge/seq.rb +221 -0
- data/lib/rouge/symbol.rb +77 -0
- data/lib/rouge/var.rb +69 -0
- data/lib/rouge/version.rb +7 -0
- data/lib/rouge/wrappers.rb +27 -0
- data/misc/TODO +45 -0
- data/misc/vimrc +1 -0
- data/rouge-lang.gemspec +28 -0
- data/spec/atom_spec.rb +39 -0
- data/spec/builtins_spec.rb +708 -0
- data/spec/compiler_spec.rb +137 -0
- data/spec/context_spec.rb +293 -0
- data/spec/core_spec.rg +123 -0
- data/spec/metadata_spec.rb +59 -0
- data/spec/namespace_spec.rb +125 -0
- data/spec/printer_spec.rb +191 -0
- data/spec/reader_spec.rb +422 -0
- data/spec/rouge_spec.rb +66 -0
- data/spec/seq_spec.rb +202 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/symbol_spec.rb +35 -0
- data/spec/var_spec.rb +61 -0
- data/spec/wrappers_spec.rb +51 -0
- metadata +216 -0
data/lib/rouge.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
if RUBY_VERSION < "1.9"
|
4
|
+
STDERR.puts "Rouge will probably not run on anything less than Ruby 1.9."
|
5
|
+
end
|
6
|
+
|
7
|
+
module Rouge; end
|
8
|
+
|
9
|
+
start = Time.now
|
10
|
+
Rouge.define_singleton_method :start, lambda {start}
|
11
|
+
|
12
|
+
class << Rouge
|
13
|
+
require 'rouge/version'
|
14
|
+
require 'rouge/wrappers'
|
15
|
+
require 'rouge/symbol'
|
16
|
+
require 'rouge/seq'
|
17
|
+
require 'rouge/reader'
|
18
|
+
require 'rouge/printer'
|
19
|
+
require 'rouge/context'
|
20
|
+
require 'rouge/repl'
|
21
|
+
|
22
|
+
def print(form, out)
|
23
|
+
Rouge::Printer.print form, out
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](ns)
|
27
|
+
Rouge::Namespace[ns]
|
28
|
+
end
|
29
|
+
|
30
|
+
def boot!
|
31
|
+
return if @booted
|
32
|
+
@booted = true
|
33
|
+
|
34
|
+
core = Rouge[:"rouge.core"]
|
35
|
+
core.refer Rouge[:"rouge.builtin"]
|
36
|
+
|
37
|
+
user = Rouge[:user]
|
38
|
+
user.refer Rouge[:"rouge.builtin"]
|
39
|
+
user.refer Rouge[:"rouge.core"]
|
40
|
+
user.refer Rouge[:ruby]
|
41
|
+
|
42
|
+
Rouge::Context.new(user).readeval(
|
43
|
+
File.read(Rouge.relative_to_lib('boot.rg')))
|
44
|
+
end
|
45
|
+
|
46
|
+
def repl(argv)
|
47
|
+
boot!
|
48
|
+
Rouge::REPL.repl(argv)
|
49
|
+
end
|
50
|
+
|
51
|
+
def relative_to_lib name
|
52
|
+
File.join(File.dirname(File.absolute_path(__FILE__)), name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# vim: set sw=2 et cc=80:
|
data/lib/rouge/atom.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Rouge::Atom
|
4
|
+
def initialize(value)
|
5
|
+
@value = value
|
6
|
+
end
|
7
|
+
|
8
|
+
def ==(var)
|
9
|
+
var.object_id == object_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def deref
|
13
|
+
@value
|
14
|
+
end
|
15
|
+
|
16
|
+
def swap! f, *args
|
17
|
+
@value = f.call(@value, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset! v
|
21
|
+
@value = v
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,596 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rouge::Builtins
|
4
|
+
require 'rouge/compiler'
|
5
|
+
|
6
|
+
SYMBOLS = {
|
7
|
+
:nil => nil,
|
8
|
+
:true => true,
|
9
|
+
:false => false,
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
class << Rouge::Builtins
|
14
|
+
def let(context, bindings, *body)
|
15
|
+
context = Rouge::Context.new context
|
16
|
+
bindings.to_a.each_slice(2) do |k, v|
|
17
|
+
destructure(context, k, v).each do |sk, sv|
|
18
|
+
context.set_here sk.name, sv
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self.do(context, *body)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _compile_let(ns, lexicals, bindings, *body)
|
25
|
+
lexicals = lexicals.dup
|
26
|
+
|
27
|
+
bindings = bindings.to_a.each_slice(2).flat_map do |k, v|
|
28
|
+
v = Rouge::Compiler.compile(ns, lexicals, v)
|
29
|
+
_compile_let_find_lexicals(lexicals, k)
|
30
|
+
[k, v]
|
31
|
+
end
|
32
|
+
|
33
|
+
[Rouge::Symbol[:let],
|
34
|
+
bindings,
|
35
|
+
*Rouge::Compiler.compile(ns, lexicals, body)]
|
36
|
+
end
|
37
|
+
|
38
|
+
def _compile_let_find_lexicals(lexicals, form)
|
39
|
+
if form.is_a?(Rouge::Symbol)
|
40
|
+
lexicals << form.name
|
41
|
+
elsif form.is_a?(Hash) and form.keys == [:keys]
|
42
|
+
form.values[0].each do |n|
|
43
|
+
lexicals << n.name
|
44
|
+
end
|
45
|
+
elsif form.is_a?(Hash)
|
46
|
+
form.keys.each do |p|
|
47
|
+
_compile_let_find_lexicals(lexicals, p)
|
48
|
+
end
|
49
|
+
elsif form.is_a?(Array)
|
50
|
+
form = form.dup
|
51
|
+
while form.length > 0
|
52
|
+
p = form.shift
|
53
|
+
|
54
|
+
next if p == :as
|
55
|
+
next if p == Rouge::Symbol[:&]
|
56
|
+
next if p == Rouge::Symbol[:|]
|
57
|
+
|
58
|
+
_compile_let_find_lexicals(lexicals, p)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise ArgumentError, "unknown LHS of LET expression: #{form.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def context(context)
|
66
|
+
context
|
67
|
+
end
|
68
|
+
|
69
|
+
def quote(context, form)
|
70
|
+
form
|
71
|
+
end
|
72
|
+
|
73
|
+
def _compile_quote(ns, lexicals, form)
|
74
|
+
[Rouge::Symbol[:quote], form]
|
75
|
+
end
|
76
|
+
|
77
|
+
def fn(context, *args)
|
78
|
+
if args[0].is_a? Rouge::Symbol
|
79
|
+
name = args.shift.to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
argv, *body = args
|
83
|
+
|
84
|
+
if argv[-2] == Rouge::Symbol[:&]
|
85
|
+
rest = argv[-1]
|
86
|
+
argv = argv[0...-2]
|
87
|
+
elsif argv[-4] == Rouge::Symbol[:&] and argv[-2] == Rouge::Symbol[:|]
|
88
|
+
rest = argv[-3]
|
89
|
+
argv = argv[0...-4] + argv[-2..-1]
|
90
|
+
else
|
91
|
+
rest = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
if argv[-2] == Rouge::Symbol[:|]
|
95
|
+
block = argv[-1]
|
96
|
+
argv = argv[0...-2]
|
97
|
+
else
|
98
|
+
block = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
fn = lambda {|*inner_args, &blockgiven|
|
102
|
+
if !rest ? (inner_args.length != argv.length) :
|
103
|
+
(inner_args.length < argv.length)
|
104
|
+
begin
|
105
|
+
raise ArgumentError,
|
106
|
+
"wrong number of arguments " \
|
107
|
+
"(#{inner_args.length} for #{argv.length})"
|
108
|
+
rescue ArgumentError => e
|
109
|
+
# orig = e.backtrace.pop
|
110
|
+
# e.backtrace.unshift "(rouge):?:FN call (#{name || "<anonymous>"})"
|
111
|
+
# e.backtrace.unshift orig
|
112
|
+
raise e
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
inner_context = Rouge::Context.new(context)
|
117
|
+
|
118
|
+
argv.each.with_index do |arg, i|
|
119
|
+
inner_context.set_here(arg.name, inner_args[i])
|
120
|
+
end
|
121
|
+
|
122
|
+
if rest
|
123
|
+
inner_context.set_here(rest.name,
|
124
|
+
Rouge::Seq::Cons[*inner_args[argv.length..-1]])
|
125
|
+
end
|
126
|
+
|
127
|
+
if block
|
128
|
+
inner_context.set_here(block.name, blockgiven)
|
129
|
+
end
|
130
|
+
|
131
|
+
begin
|
132
|
+
self.do(inner_context, *body)
|
133
|
+
rescue => e
|
134
|
+
# e.backtrace.unshift "(rouge):?: in #{name || "<anonymous>"}"
|
135
|
+
raise e
|
136
|
+
end
|
137
|
+
}
|
138
|
+
|
139
|
+
if name
|
140
|
+
fn.define_singleton_method(:to_s) { name }
|
141
|
+
end
|
142
|
+
|
143
|
+
fn
|
144
|
+
end
|
145
|
+
|
146
|
+
def _compile_fn(ns, lexicals, *args)
|
147
|
+
if args[0].is_a? Rouge::Symbol
|
148
|
+
name = args.shift
|
149
|
+
end
|
150
|
+
|
151
|
+
argv, *body = args
|
152
|
+
|
153
|
+
original_argv = argv
|
154
|
+
|
155
|
+
if argv[-2] == Rouge::Symbol[:&]
|
156
|
+
rest = argv[-1]
|
157
|
+
argv = argv[0...-2]
|
158
|
+
elsif argv[-4] == Rouge::Symbol[:&] and argv[-2] == Rouge::Symbol[:|]
|
159
|
+
rest = argv[-3]
|
160
|
+
argv = argv[0...-4] + argv[-2..-1]
|
161
|
+
else
|
162
|
+
rest = nil
|
163
|
+
end
|
164
|
+
|
165
|
+
if argv[-2] == Rouge::Symbol[:|]
|
166
|
+
block = argv[-1]
|
167
|
+
argv = argv[0...-2]
|
168
|
+
else
|
169
|
+
block = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
lexicals = lexicals.dup
|
173
|
+
argv.each do |arg|
|
174
|
+
lexicals << arg.name
|
175
|
+
end
|
176
|
+
lexicals << rest.name if rest
|
177
|
+
lexicals << block.name if block
|
178
|
+
|
179
|
+
[Rouge::Symbol[:fn],
|
180
|
+
*(name ? [name] : []),
|
181
|
+
original_argv,
|
182
|
+
*Rouge::Compiler.compile(ns, lexicals, body)]
|
183
|
+
end
|
184
|
+
|
185
|
+
def def(context, name, *form)
|
186
|
+
if name.ns != nil
|
187
|
+
raise ArgumentError, "cannot def qualified var"
|
188
|
+
end
|
189
|
+
|
190
|
+
case form.length
|
191
|
+
when 0
|
192
|
+
context.ns.intern name.name
|
193
|
+
when 1
|
194
|
+
context.ns.set_here name.name, context.eval(form[0])
|
195
|
+
else
|
196
|
+
raise ArgumentError, "def called with too many forms #{form.inspect}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def _compile_def(ns, lexicals, name, *form)
|
201
|
+
if name.ns != nil
|
202
|
+
raise ArgumentError, "cannot def qualified var"
|
203
|
+
end
|
204
|
+
|
205
|
+
lexicals = lexicals.dup
|
206
|
+
lexicals << name.name
|
207
|
+
|
208
|
+
[Rouge::Symbol[:def],
|
209
|
+
name,
|
210
|
+
*Rouge::Compiler.compile(ns, lexicals, form)]
|
211
|
+
end
|
212
|
+
|
213
|
+
def if(context, test, if_true, if_false=nil)
|
214
|
+
# Note that we rely on Ruby's sense of truthiness. (only false and nil are
|
215
|
+
# falsey)
|
216
|
+
if context.eval(test)
|
217
|
+
context.eval if_true
|
218
|
+
else
|
219
|
+
context.eval if_false
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def do(context, *forms)
|
224
|
+
r = nil
|
225
|
+
|
226
|
+
while forms.length > 0
|
227
|
+
begin
|
228
|
+
form = Rouge::Compiler.compile(
|
229
|
+
context.ns,
|
230
|
+
Set[*context.lexical_keys],
|
231
|
+
forms.shift)
|
232
|
+
|
233
|
+
r = context.eval(form)
|
234
|
+
rescue Rouge::Context::ChangeContextException => cce
|
235
|
+
context = cce.context
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
r
|
240
|
+
end
|
241
|
+
|
242
|
+
def _compile_do(ns, lexicals, *forms)
|
243
|
+
[Rouge::Symbol[:do], *forms]
|
244
|
+
end
|
245
|
+
|
246
|
+
def ns(context, name, *args)
|
247
|
+
ns = Rouge[name.name]
|
248
|
+
ns.refer Rouge[:"rouge.builtin"]
|
249
|
+
|
250
|
+
args.each do |arg|
|
251
|
+
kind, *params = arg.to_a
|
252
|
+
|
253
|
+
case kind
|
254
|
+
when :use
|
255
|
+
params.each do |use|
|
256
|
+
ns.refer Rouge[use.name]
|
257
|
+
end
|
258
|
+
when :require
|
259
|
+
params.each do |param|
|
260
|
+
if param.is_a? Rouge::Symbol
|
261
|
+
Kernel.require param.name.to_s
|
262
|
+
elsif param.is_a? Array and
|
263
|
+
param.length == 3 and
|
264
|
+
param[0].is_a? Rouge::Symbol and
|
265
|
+
param[1] == :as and
|
266
|
+
param[2].is_a? Rouge::Symbol
|
267
|
+
unless Rouge::Namespace.exists? param[0].name
|
268
|
+
context.readeval(File.read("#{param[0].name}.rg"))
|
269
|
+
end
|
270
|
+
Rouge::Namespace[param[2].name] = Rouge[param[0].name]
|
271
|
+
end
|
272
|
+
end
|
273
|
+
else
|
274
|
+
raise "TODO bad arg in ns: #{kind}"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context = Rouge::Context.new ns
|
279
|
+
raise Rouge::Context::ChangeContextException, context
|
280
|
+
end
|
281
|
+
|
282
|
+
def _compile_ns(ns, lexicals, name, *args)
|
283
|
+
[Rouge::Symbol[:ns], name, *args]
|
284
|
+
end
|
285
|
+
|
286
|
+
def defmacro(context, name, *parts)
|
287
|
+
if name.ns
|
288
|
+
raise ArgumentError, "cannot defmacro fully qualified var"
|
289
|
+
end
|
290
|
+
|
291
|
+
if parts[0].is_a? Array
|
292
|
+
args, *body = parts
|
293
|
+
macro = Rouge::Macro[
|
294
|
+
context.eval(Rouge::Seq::Cons[Rouge::Symbol[:fn], args, *body])]
|
295
|
+
elsif parts.all? {|part| part.is_a? Rouge::Seq::Cons}
|
296
|
+
arities = {}
|
297
|
+
|
298
|
+
parts.each do |cons|
|
299
|
+
args, *body = cons.to_a
|
300
|
+
|
301
|
+
if !args.is_a? Array
|
302
|
+
raise ArgumentError,
|
303
|
+
"bad multi-form defmacro component #{args.inspect}"
|
304
|
+
end
|
305
|
+
|
306
|
+
if args.index(Rouge::Symbol[:&])
|
307
|
+
arity = -1
|
308
|
+
else
|
309
|
+
arity = args.length
|
310
|
+
end
|
311
|
+
|
312
|
+
if arities[arity]
|
313
|
+
raise ArgumentError, "seen same arity twice"
|
314
|
+
end
|
315
|
+
|
316
|
+
arities[arity] =
|
317
|
+
context.eval(Rouge::Seq::Cons[Rouge::Symbol[:fn], args, *body])
|
318
|
+
end
|
319
|
+
|
320
|
+
macro = Rouge::Macro[
|
321
|
+
lambda {|*inner_args, &blockgiven|
|
322
|
+
if arities[inner_args.length]
|
323
|
+
arities[inner_args.length].call(*inner_args, &blockgiven)
|
324
|
+
elsif arities[-1]
|
325
|
+
arities[-1].call(*inner_args, &blockgiven)
|
326
|
+
else
|
327
|
+
raise ArgumentError, "no matching arity in macro"
|
328
|
+
end
|
329
|
+
}]
|
330
|
+
else
|
331
|
+
raise ArgumentError, "neither single-form defmacro nor multi-form"
|
332
|
+
end
|
333
|
+
|
334
|
+
macro.define_singleton_method(:to_s) { :"#{context.ns.name}/#{name.name}" }
|
335
|
+
|
336
|
+
context.ns.set_here name.name, macro
|
337
|
+
end
|
338
|
+
|
339
|
+
def _compile_defmacro(ns, lexicals, name, *parts)
|
340
|
+
if name.ns
|
341
|
+
raise ArgumentError, "cannot defmacro fully qualified var"
|
342
|
+
end
|
343
|
+
|
344
|
+
if parts[0].is_a? Array
|
345
|
+
args, *body = parts
|
346
|
+
[Rouge::Symbol[:defmacro],
|
347
|
+
name,
|
348
|
+
*_compile_fn(ns, lexicals, args, *body)[1..-1]]
|
349
|
+
elsif parts.all? {|part| part.is_a? Rouge::Seq::Cons}
|
350
|
+
[Rouge::Symbol[:defmacro],
|
351
|
+
name,
|
352
|
+
*parts.map do |cons|
|
353
|
+
args, *body = cons.to_a
|
354
|
+
|
355
|
+
if !args.is_a? Array
|
356
|
+
raise ArgumentError,
|
357
|
+
"bad multi-form defmacro component #{args.inspect}"
|
358
|
+
end
|
359
|
+
|
360
|
+
Rouge::Seq::Cons[*_compile_fn(ns, lexicals, args, *body)[1..-1]]
|
361
|
+
end]
|
362
|
+
else
|
363
|
+
raise ArgumentError, "neither single-form defmacro nor multi-form"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def apply(context, fun, *args)
|
368
|
+
args =
|
369
|
+
args[0..-2].map {|f| context.eval f} +
|
370
|
+
context.eval(args[-1]).to_a
|
371
|
+
# This is a terrible hack.
|
372
|
+
context.eval(Rouge::Seq::Cons[
|
373
|
+
fun,
|
374
|
+
*args.map {|a| Rouge::Seq::Cons[Rouge::Symbol[:quote], a]}])
|
375
|
+
end
|
376
|
+
|
377
|
+
def var(context, f)
|
378
|
+
# HACK: just so it'll be found when fully qualified.
|
379
|
+
f
|
380
|
+
end
|
381
|
+
|
382
|
+
def _compile_var(ns, lexicals, symbol)
|
383
|
+
if symbol.ns
|
384
|
+
[Rouge::Symbol[:quote], Rouge[symbol.ns][symbol.name]]
|
385
|
+
else
|
386
|
+
[Rouge::Symbol[:quote], ns[symbol.name]]
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def throw(context, throwable)
|
391
|
+
exception = context.eval(throwable)
|
392
|
+
begin
|
393
|
+
raise exception
|
394
|
+
rescue Exception => e
|
395
|
+
# TODO
|
396
|
+
#e.backtrace.unshift "(rouge):?:throw"
|
397
|
+
raise e
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def try(context, *body)
|
402
|
+
return unless body.length > 0
|
403
|
+
|
404
|
+
form = body[-1]
|
405
|
+
if form.is_a?(Rouge::Seq::Cons) and
|
406
|
+
form[0].is_a? Rouge::Symbol and
|
407
|
+
form[0].name == :finally
|
408
|
+
finally = form[1..-1].freeze
|
409
|
+
body.pop
|
410
|
+
end
|
411
|
+
|
412
|
+
catches = {}
|
413
|
+
while body.length > 0
|
414
|
+
form = body[-1]
|
415
|
+
if !form.is_a?(Rouge::Seq::Cons) or
|
416
|
+
!form[0].is_a? Rouge::Symbol or
|
417
|
+
form[0].name != :catch
|
418
|
+
break
|
419
|
+
end
|
420
|
+
|
421
|
+
body.pop
|
422
|
+
catches[context.eval(form[1])] =
|
423
|
+
{:bind => form[2],
|
424
|
+
:body => form[3..-1].freeze}
|
425
|
+
end
|
426
|
+
|
427
|
+
r =
|
428
|
+
begin
|
429
|
+
self.do(context, *body)
|
430
|
+
rescue Exception => e
|
431
|
+
catches.each do |klass, caught|
|
432
|
+
if klass === e
|
433
|
+
subcontext = Rouge::Context.new context
|
434
|
+
subcontext.set_here caught[:bind].name, e
|
435
|
+
r = self.do(subcontext, *caught[:body])
|
436
|
+
self.do(context, *finally) if finally
|
437
|
+
return r
|
438
|
+
end
|
439
|
+
end
|
440
|
+
self.do(context, *finally) if finally
|
441
|
+
raise e
|
442
|
+
end
|
443
|
+
|
444
|
+
self.do(context, *finally) if finally
|
445
|
+
r
|
446
|
+
end
|
447
|
+
|
448
|
+
def _compile_try(ns, lexicals, *body)
|
449
|
+
return [Rouge::Symbol[:try]] unless body.length > 0
|
450
|
+
|
451
|
+
form = body[-1]
|
452
|
+
if form.is_a?(Rouge::Seq::Cons) and
|
453
|
+
form[0].is_a? Rouge::Symbol and
|
454
|
+
form[0].name == :finally
|
455
|
+
finally = form[1..-1].freeze
|
456
|
+
body.pop
|
457
|
+
end
|
458
|
+
|
459
|
+
catches = []
|
460
|
+
while body.length > 0
|
461
|
+
form = body[-1]
|
462
|
+
if !form.is_a?(Rouge::Seq::Cons) or
|
463
|
+
!form[0].is_a? Rouge::Symbol or
|
464
|
+
form[0].name != :catch
|
465
|
+
break
|
466
|
+
end
|
467
|
+
|
468
|
+
body.pop
|
469
|
+
catches <<
|
470
|
+
{:class => form[1],
|
471
|
+
:bind => form[2],
|
472
|
+
:body => form[3..-1].freeze}
|
473
|
+
end
|
474
|
+
|
475
|
+
form =
|
476
|
+
[Rouge::Symbol[:try],
|
477
|
+
*Rouge::Compiler.compile(ns, lexicals, body),
|
478
|
+
*catches.reverse.map {|c|
|
479
|
+
if !c[:bind].is_a?(Rouge::Symbol) or c[:bind].ns
|
480
|
+
raise ArgumentError, "bad catch binding #{c[:bind]}"
|
481
|
+
end
|
482
|
+
|
483
|
+
bind_lexicals = lexicals.dup << c[:bind].name
|
484
|
+
Rouge::Seq::Cons[Rouge::Symbol[:catch],
|
485
|
+
Rouge::Compiler.compile(ns, lexicals, c[:class]),
|
486
|
+
c[:bind],
|
487
|
+
*c[:body].map {|f|
|
488
|
+
Rouge::Compiler.compile(ns, bind_lexicals, f)
|
489
|
+
}]
|
490
|
+
},
|
491
|
+
*(finally ? [Rouge::Seq::Cons[Rouge::Symbol[:finally],
|
492
|
+
*finally.map {|f|
|
493
|
+
Rouge::Compiler.compile(ns, lexicals, f)
|
494
|
+
}]] : [])]
|
495
|
+
end
|
496
|
+
|
497
|
+
def destructure(context, parameters, values, evalled=false, r={})
|
498
|
+
# TODO: can probably move this elsewhere as a regular function.
|
499
|
+
|
500
|
+
if parameters.is_a?(Rouge::Symbol)
|
501
|
+
values = context.eval(values) if !evalled
|
502
|
+
r[parameters] = values
|
503
|
+
return r
|
504
|
+
end
|
505
|
+
|
506
|
+
if parameters.is_a?(Hash) and parameters.keys == [:keys]
|
507
|
+
keys = parameters.values[0]
|
508
|
+
context.eval(values).select do |k,v|
|
509
|
+
keys.include?(Rouge::Symbol[k])
|
510
|
+
end.each {|k,v| r[Rouge::Symbol[k]] = v}
|
511
|
+
return r
|
512
|
+
end
|
513
|
+
|
514
|
+
if parameters.is_a?(Hash)
|
515
|
+
values = context.eval(values) if !evalled
|
516
|
+
parameters.each do |local, foreign|
|
517
|
+
destructure(context, local, values[foreign], true, r)
|
518
|
+
end
|
519
|
+
return r
|
520
|
+
end
|
521
|
+
|
522
|
+
if !parameters.is_a?(Array) and !evalled
|
523
|
+
raise ArgumentError, "unknown destructure parameter list"
|
524
|
+
end
|
525
|
+
|
526
|
+
if !evalled and values.is_a? Array
|
527
|
+
if values[-2] == Rouge::Symbol[:|]
|
528
|
+
block = context.eval(values[-1])
|
529
|
+
block_supplied = true
|
530
|
+
|
531
|
+
values = values[0...-2]
|
532
|
+
end
|
533
|
+
|
534
|
+
if values[-2] == Rouge::Symbol[:&]
|
535
|
+
values =
|
536
|
+
values[0...-2].map {|v| context.eval(v)} +
|
537
|
+
context.eval(values[-1]).to_a
|
538
|
+
else
|
539
|
+
values = values.map {|v| context.eval(v)}
|
540
|
+
end
|
541
|
+
else
|
542
|
+
values = context.eval(values)
|
543
|
+
end
|
544
|
+
|
545
|
+
values = Rouge::Seq.seq(values)
|
546
|
+
original_values = values
|
547
|
+
|
548
|
+
parameters = parameters.dup
|
549
|
+
while parameters.length > 0
|
550
|
+
p = parameters.shift
|
551
|
+
|
552
|
+
if p == Rouge::Symbol[:&]
|
553
|
+
r[parameters.shift] = values
|
554
|
+
values = Rouge::Seq::Empty
|
555
|
+
next
|
556
|
+
end
|
557
|
+
|
558
|
+
if p == Rouge::Symbol[:|]
|
559
|
+
if not block_supplied
|
560
|
+
raise ArgumentError, "no block supplied"
|
561
|
+
end
|
562
|
+
|
563
|
+
r[parameters.shift] = block
|
564
|
+
next
|
565
|
+
end
|
566
|
+
|
567
|
+
if p == :as
|
568
|
+
r[parameters.shift] = Rouge::Seq.seq(original_values)
|
569
|
+
values = Rouge::Seq::Empty
|
570
|
+
next
|
571
|
+
end
|
572
|
+
|
573
|
+
if p.is_a? Array
|
574
|
+
destructure(context, p, Rouge::Seq.seq(values.first), true, r)
|
575
|
+
else
|
576
|
+
if p.ns
|
577
|
+
raise Rouge::Context::BadBindingError,
|
578
|
+
"cannot let qualified name in DESTRUCTURE"
|
579
|
+
end
|
580
|
+
r[p] = values ? values.first : nil
|
581
|
+
end
|
582
|
+
|
583
|
+
values = values ? values.next : nil
|
584
|
+
end
|
585
|
+
|
586
|
+
r
|
587
|
+
end
|
588
|
+
|
589
|
+
def _compile_destructure(ns, lexicals, parameters, values)
|
590
|
+
[Rouge::Symbol[:destructure],
|
591
|
+
parameters,
|
592
|
+
Rouge::Compiler.compile(ns, lexicals, values)]
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
# vim: set sw=2 et cc=80:
|