rouge-lang 0.0.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/.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:
|