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
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rouge/wrappers'
|
3
|
+
|
4
|
+
module Rouge::Printer
|
5
|
+
class UnknownFormError < StandardError; end
|
6
|
+
|
7
|
+
def self.print(form, out)
|
8
|
+
case form
|
9
|
+
when Integer
|
10
|
+
out << form.to_s
|
11
|
+
when Rouge::Symbol
|
12
|
+
if form.ns_s
|
13
|
+
out << form.ns_s
|
14
|
+
out << "/"
|
15
|
+
end
|
16
|
+
out << form.name_s
|
17
|
+
when Symbol
|
18
|
+
out << form.inspect
|
19
|
+
when String
|
20
|
+
out << form.inspect
|
21
|
+
when Array
|
22
|
+
out << "["
|
23
|
+
form.each.with_index do |e, i|
|
24
|
+
out << " " unless i.zero?
|
25
|
+
print(e, out)
|
26
|
+
end
|
27
|
+
out << "]"
|
28
|
+
when Rouge::Seq::Empty
|
29
|
+
out << "()"
|
30
|
+
when Rouge::Seq::Cons
|
31
|
+
if form.length == 2 and form[0] == Rouge::Symbol[:quote]
|
32
|
+
out << "'"
|
33
|
+
print(form[1], out)
|
34
|
+
elsif form.length == 2 and form[0] == Rouge::Symbol[:var]
|
35
|
+
out << "#'"
|
36
|
+
print(form[1], out)
|
37
|
+
else
|
38
|
+
out << "("
|
39
|
+
form.each.with_index do |e, i|
|
40
|
+
out << " " unless i.zero?
|
41
|
+
print(e, out)
|
42
|
+
end
|
43
|
+
out << ")"
|
44
|
+
end
|
45
|
+
when Rouge::Var
|
46
|
+
out << "#'#{form.ns}/#{form.name}"
|
47
|
+
when Hash
|
48
|
+
out << "{"
|
49
|
+
form.each.with_index do |kv,i|
|
50
|
+
out << ", " unless i.zero?
|
51
|
+
print(kv[0], out)
|
52
|
+
out << " "
|
53
|
+
print(kv[1], out)
|
54
|
+
end
|
55
|
+
out << "}"
|
56
|
+
when NilClass
|
57
|
+
out << "nil"
|
58
|
+
when TrueClass
|
59
|
+
out << "true"
|
60
|
+
when FalseClass
|
61
|
+
out << "false"
|
62
|
+
when Class, Module
|
63
|
+
if form.name
|
64
|
+
out << "ruby/#{form.name.split('::').join('.')}"
|
65
|
+
else
|
66
|
+
out << form.inspect
|
67
|
+
end
|
68
|
+
when Rouge::Builtin
|
69
|
+
out << "rouge.builtin/#{form.inner.name}"
|
70
|
+
when Regexp
|
71
|
+
out << "#\"#{form.source}\""
|
72
|
+
else
|
73
|
+
out << form.inspect
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# vim: set sw=2 et cc=80:
|
data/lib/rouge/reader.rb
ADDED
@@ -0,0 +1,433 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rouge/wrappers'
|
3
|
+
|
4
|
+
class Rouge::Reader
|
5
|
+
class UnexpectedCharacterError < StandardError; end
|
6
|
+
class EndOfDataError < StandardError; end
|
7
|
+
|
8
|
+
attr_accessor :ns
|
9
|
+
|
10
|
+
@@gensym_counter = 0
|
11
|
+
|
12
|
+
def initialize(ns, input)
|
13
|
+
@ns = ns
|
14
|
+
@src = input
|
15
|
+
@n = 0
|
16
|
+
@gensyms = []
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def lex
|
21
|
+
r =
|
22
|
+
case peek
|
23
|
+
when MAYBE_NUMBER
|
24
|
+
number
|
25
|
+
when /:/
|
26
|
+
keyword
|
27
|
+
when /"/
|
28
|
+
string
|
29
|
+
when /\(/
|
30
|
+
Rouge::Seq::Cons[*list(')')]
|
31
|
+
when /\[/
|
32
|
+
list ']'
|
33
|
+
when /#/
|
34
|
+
dispatch
|
35
|
+
when SYMBOL
|
36
|
+
# SYMBOL after \[ and #, because it includes both
|
37
|
+
symbol_or_number
|
38
|
+
when /{/
|
39
|
+
map
|
40
|
+
when /'/
|
41
|
+
quotation
|
42
|
+
when /`/
|
43
|
+
syntaxquotation
|
44
|
+
when /~/
|
45
|
+
dequotation
|
46
|
+
when /\^/
|
47
|
+
metadata
|
48
|
+
when /@/
|
49
|
+
deref
|
50
|
+
when nil
|
51
|
+
reader_raise EndOfDataError, "in #lex"
|
52
|
+
else
|
53
|
+
reader_raise UnexpectedCharacterError, "#{peek.inspect} in #lex"
|
54
|
+
end
|
55
|
+
|
56
|
+
r
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def number
|
62
|
+
read_number(slurp(MAYBE_NUMBER))
|
63
|
+
end
|
64
|
+
|
65
|
+
def keyword
|
66
|
+
begin
|
67
|
+
slurp(/:"/)
|
68
|
+
@n -= 1
|
69
|
+
s = string
|
70
|
+
s.intern
|
71
|
+
rescue UnexpectedCharacterError
|
72
|
+
slurp(/^:[a-zA-Z0-9\-_!\?\*\/]+/)[1..-1].intern
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def string
|
77
|
+
s = ""
|
78
|
+
t = consume
|
79
|
+
while true
|
80
|
+
c = @src[@n]
|
81
|
+
|
82
|
+
if c.nil?
|
83
|
+
reader_raise EndOfDataError, "in string, got: #{s}"
|
84
|
+
end
|
85
|
+
|
86
|
+
@n += 1
|
87
|
+
|
88
|
+
if c == t
|
89
|
+
break
|
90
|
+
end
|
91
|
+
|
92
|
+
if c == ?\\
|
93
|
+
c = consume
|
94
|
+
|
95
|
+
case c
|
96
|
+
when nil
|
97
|
+
reader_raise EndOfDataError, "in escaped string, got: #{s}"
|
98
|
+
when /[abefnrstv]/
|
99
|
+
c = {?a => ?\a,
|
100
|
+
?b => ?\b,
|
101
|
+
?e => ?\e,
|
102
|
+
?f => ?\f,
|
103
|
+
?n => ?\n,
|
104
|
+
?r => ?\r,
|
105
|
+
?s => ?\s,
|
106
|
+
?t => ?\t,
|
107
|
+
?v => ?\v}[c]
|
108
|
+
else
|
109
|
+
# Just leave it be.
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
s += c
|
114
|
+
end
|
115
|
+
s.freeze
|
116
|
+
end
|
117
|
+
|
118
|
+
def list(ending)
|
119
|
+
consume
|
120
|
+
r = []
|
121
|
+
|
122
|
+
while true
|
123
|
+
if peek == ending
|
124
|
+
break
|
125
|
+
end
|
126
|
+
r << lex
|
127
|
+
end
|
128
|
+
|
129
|
+
consume
|
130
|
+
r.freeze
|
131
|
+
end
|
132
|
+
|
133
|
+
def symbol_or_number
|
134
|
+
s = slurp(SYMBOL)
|
135
|
+
if (s[0] == ?- or s[0] == ?+) and s[1..-1] =~ NUMBER
|
136
|
+
read_number(s)
|
137
|
+
else
|
138
|
+
Rouge::Symbol[s.intern]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def map
|
143
|
+
consume
|
144
|
+
r = {}
|
145
|
+
|
146
|
+
while true
|
147
|
+
if peek == '}'
|
148
|
+
break
|
149
|
+
end
|
150
|
+
k = lex
|
151
|
+
v = lex
|
152
|
+
r[k] = v
|
153
|
+
end
|
154
|
+
|
155
|
+
consume
|
156
|
+
r.freeze
|
157
|
+
end
|
158
|
+
|
159
|
+
def quotation
|
160
|
+
consume
|
161
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote], lex]
|
162
|
+
end
|
163
|
+
|
164
|
+
def syntaxquotation
|
165
|
+
consume
|
166
|
+
@gensyms.unshift(@@gensym_counter += 1)
|
167
|
+
r = dequote lex
|
168
|
+
@gensyms.shift
|
169
|
+
r
|
170
|
+
end
|
171
|
+
|
172
|
+
def dequotation
|
173
|
+
consume
|
174
|
+
if peek == ?@
|
175
|
+
consume
|
176
|
+
Rouge::Splice[lex].freeze
|
177
|
+
else
|
178
|
+
Rouge::Dequote[lex].freeze
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def dequote form
|
183
|
+
case form
|
184
|
+
when Rouge::Seq::Cons, Array
|
185
|
+
rest = []
|
186
|
+
group = []
|
187
|
+
form.each do |f|
|
188
|
+
if f.is_a? Rouge::Splice
|
189
|
+
if group.length > 0
|
190
|
+
rest << Rouge::Seq::Cons[Rouge::Symbol[:list], *group]
|
191
|
+
group = []
|
192
|
+
end
|
193
|
+
rest << f.inner
|
194
|
+
else
|
195
|
+
group << dequote(f)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
if group.length > 0
|
200
|
+
rest << Rouge::Seq::Cons[Rouge::Symbol[:list], *group]
|
201
|
+
end
|
202
|
+
|
203
|
+
r =
|
204
|
+
if rest.length == 1
|
205
|
+
rest[0]
|
206
|
+
else
|
207
|
+
Rouge::Seq::Cons[Rouge::Symbol[:concat], *rest]
|
208
|
+
end
|
209
|
+
|
210
|
+
if form.is_a?(Array)
|
211
|
+
Rouge::Seq::Cons[Rouge::Symbol[:apply],
|
212
|
+
Rouge::Symbol[:vector],
|
213
|
+
r]
|
214
|
+
elsif rest.length > 1
|
215
|
+
Rouge::Seq::Cons[Rouge::Symbol[:seq], r]
|
216
|
+
else
|
217
|
+
r
|
218
|
+
end
|
219
|
+
when Hash
|
220
|
+
Hash[form.map {|k,v| [dequote(k), dequote(v)]}]
|
221
|
+
when Rouge::Dequote
|
222
|
+
form.inner
|
223
|
+
when Rouge::Symbol
|
224
|
+
if form.ns.nil? and form.name_s =~ /(\#)$/
|
225
|
+
Rouge::Seq::Cons[
|
226
|
+
Rouge::Symbol[:quote],
|
227
|
+
Rouge::Symbol[
|
228
|
+
("#{form.name.to_s.gsub(/(\#)$/, '')}__" \
|
229
|
+
"#{@gensyms[0]}__auto__").intern]]
|
230
|
+
elsif form.ns or form.name_s =~ /^\./ or %w(& |).include? form.name_s
|
231
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote], form]
|
232
|
+
elsif form.ns.nil?
|
233
|
+
begin
|
234
|
+
var = @ns[form.name]
|
235
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
236
|
+
Rouge::Symbol[var.name]]
|
237
|
+
rescue Rouge::Namespace::VarNotFoundError
|
238
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote],
|
239
|
+
Rouge::Symbol[:"#{@ns.name}/#{form.name}"]]
|
240
|
+
end
|
241
|
+
else
|
242
|
+
raise "impossible, right?" # XXX: be bothered to ensure this is so
|
243
|
+
end
|
244
|
+
else
|
245
|
+
Rouge::Seq::Cons[Rouge::Symbol[:quote], form]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def regexp
|
250
|
+
s = ""
|
251
|
+
t = '"'
|
252
|
+
while true
|
253
|
+
c = @src[@n]
|
254
|
+
|
255
|
+
if c.nil?
|
256
|
+
reader_raise EndOfDataError, "in regexp, got: #{s}"
|
257
|
+
end
|
258
|
+
|
259
|
+
@n += 1
|
260
|
+
|
261
|
+
if c == t
|
262
|
+
break
|
263
|
+
end
|
264
|
+
|
265
|
+
if c == ?\\
|
266
|
+
c = "\\"
|
267
|
+
if peek == ?"
|
268
|
+
c << consume
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
s << c
|
273
|
+
end
|
274
|
+
|
275
|
+
Regexp.new(s).freeze
|
276
|
+
end
|
277
|
+
|
278
|
+
def dispatch
|
279
|
+
consume
|
280
|
+
case peek
|
281
|
+
when '('
|
282
|
+
body, count = dispatch_rewrite_fn(lex, 0)
|
283
|
+
Rouge::Seq::Cons[
|
284
|
+
Rouge::Symbol[:fn],
|
285
|
+
(1..count).map {|n| Rouge::Symbol[:"%#{n}"]}.freeze,
|
286
|
+
body]
|
287
|
+
when "'"
|
288
|
+
consume
|
289
|
+
Rouge::Seq::Cons[Rouge::Symbol[:var], lex]
|
290
|
+
when "_"
|
291
|
+
consume
|
292
|
+
lex
|
293
|
+
lex
|
294
|
+
when '"'
|
295
|
+
consume
|
296
|
+
regexp
|
297
|
+
else
|
298
|
+
reader_raise UnexpectedCharacterError, "#{peek.inspect} in #dispatch"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def dispatch_rewrite_fn form, count
|
303
|
+
case form
|
304
|
+
when Rouge::Seq::Cons, Array
|
305
|
+
mapped = form.map do |e|
|
306
|
+
e, count = dispatch_rewrite_fn(e, count)
|
307
|
+
e
|
308
|
+
end.freeze
|
309
|
+
|
310
|
+
if form.is_a?(Rouge::Seq::Cons)
|
311
|
+
[Rouge::Seq::Cons[*mapped], count]
|
312
|
+
else
|
313
|
+
[mapped, count]
|
314
|
+
end
|
315
|
+
when Rouge::Symbol
|
316
|
+
if form.name == :"%"
|
317
|
+
[Rouge::Symbol[:"%1"], [1, count].max]
|
318
|
+
elsif form.name.to_s =~ /^%(\d+)$/
|
319
|
+
[form, [$1.to_i, count].max]
|
320
|
+
else
|
321
|
+
[form, count]
|
322
|
+
end
|
323
|
+
else
|
324
|
+
[form, count]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def metadata
|
329
|
+
consume
|
330
|
+
meta = lex
|
331
|
+
attach = lex
|
332
|
+
|
333
|
+
if not attach.class < Rouge::Metadata
|
334
|
+
reader_raise ArgumentError,
|
335
|
+
"metadata can only be applied to classes mixing in Rouge::Metadata"
|
336
|
+
end
|
337
|
+
|
338
|
+
meta =
|
339
|
+
case meta
|
340
|
+
when Symbol
|
341
|
+
{meta => true}
|
342
|
+
when String
|
343
|
+
{:tag => meta}
|
344
|
+
else
|
345
|
+
meta
|
346
|
+
end
|
347
|
+
|
348
|
+
extant = attach.meta
|
349
|
+
if extant.nil?
|
350
|
+
attach.meta = meta
|
351
|
+
else
|
352
|
+
attach.meta = extant.merge(meta)
|
353
|
+
end
|
354
|
+
|
355
|
+
attach
|
356
|
+
end
|
357
|
+
|
358
|
+
def deref
|
359
|
+
consume
|
360
|
+
Rouge::Seq::Cons[Rouge::Symbol[:"rouge.core/deref"], lex]
|
361
|
+
end
|
362
|
+
|
363
|
+
def slurp re
|
364
|
+
@src[@n..-1] =~ re
|
365
|
+
reader_raise UnexpectedCharacterError, "#{@src[@n]} in #slurp #{re}" if !$&
|
366
|
+
@n += $&.length
|
367
|
+
$&
|
368
|
+
end
|
369
|
+
|
370
|
+
def peek
|
371
|
+
while @src[@n] =~ /[\s,;]/
|
372
|
+
if $& == ";"
|
373
|
+
while @src[@n] =~ /[^\n]/
|
374
|
+
@n += 1
|
375
|
+
end
|
376
|
+
else
|
377
|
+
@n += 1
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
@src[@n]
|
382
|
+
end
|
383
|
+
|
384
|
+
def consume
|
385
|
+
c = peek
|
386
|
+
@n += 1
|
387
|
+
c
|
388
|
+
end
|
389
|
+
|
390
|
+
def reader_raise ex, m
|
391
|
+
around =
|
392
|
+
"#{@src[[@n - 3, 0].max...[@n, 0].max]}" +
|
393
|
+
"#{@src[@n]}" +
|
394
|
+
"#{(@src[@n + 1..@n + 3] || "").gsub(/\n.*$/, '')}"
|
395
|
+
|
396
|
+
line = @src[0...@n].count("\n") + 1
|
397
|
+
char = @src[0...@n].reverse.index("\n") || 0 + 1
|
398
|
+
|
399
|
+
raise ex,
|
400
|
+
"around: #{around}\n" +
|
401
|
+
" ^\n" +
|
402
|
+
"line #{line} char #{char}: #{m}"
|
403
|
+
end
|
404
|
+
|
405
|
+
def read_number s
|
406
|
+
if NUMBER.match s
|
407
|
+
eval s
|
408
|
+
else
|
409
|
+
reader_raise UnexpectedCharacterError, "#{s} in #read_number"
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Loose expression for a possible numeric literal.
|
414
|
+
MAYBE_NUMBER = /^[+-]?\d[\da-fA-FxX\._+-]*/
|
415
|
+
|
416
|
+
# Ruby integer.
|
417
|
+
INT = /\d+(?:_\d+)*/
|
418
|
+
|
419
|
+
# Strict expression for a numeric literal.
|
420
|
+
NUMBER = /
|
421
|
+
^[+-]?
|
422
|
+
(?:
|
423
|
+
(?:0[xX][\da-fA-F]+) (?# Hexadecimal integer)
|
424
|
+
| (?:0[bB][01]+) (?# Binary integer)
|
425
|
+
| (?:0\d+) (?# Octal integer)
|
426
|
+
| (?:#{INT}(?:(?:\.#{INT})?(?:[eE][+-]?#{INT})?)?) (?# Integers and floats)
|
427
|
+
)\z
|
428
|
+
/ox
|
429
|
+
|
430
|
+
SYMBOL = /^(\.\[\])|([a-zA-Z0-9\-_!&\?\*\/\.\+\|=%$<>#]+)/
|
431
|
+
end
|
432
|
+
|
433
|
+
# vim: set sw=2 et cc=80:
|