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,108 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Rouge::Compiler
|
5
|
+
class Resolved
|
6
|
+
def initialize(res)
|
7
|
+
@res = res
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :res
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.compile(ns, lexicals, form)
|
14
|
+
case form
|
15
|
+
when Rouge::Symbol
|
16
|
+
name = form.name
|
17
|
+
is_new = (name[-1] == ?. and name.length > 1)
|
18
|
+
name = name[0..-2].to_sym if is_new
|
19
|
+
|
20
|
+
if !form.ns and
|
21
|
+
(lexicals.include?(name) or
|
22
|
+
(name[0] == ?. and name.length > 1) or
|
23
|
+
[:|, :&].include?(name))
|
24
|
+
# TODO: cache found ns/var/context or no. of context parents.
|
25
|
+
form
|
26
|
+
else
|
27
|
+
resolved = form.ns ? Rouge[form.ns] : ns
|
28
|
+
|
29
|
+
lookups = form.name_parts
|
30
|
+
resolved = resolved[lookups[0]]
|
31
|
+
i, count = 1, lookups.length
|
32
|
+
|
33
|
+
while i < count
|
34
|
+
resolved = resolved.deref if resolved.is_a?(Rouge::Var)
|
35
|
+
resolved = resolved.const_get(lookups[i])
|
36
|
+
i += 1
|
37
|
+
end
|
38
|
+
|
39
|
+
if is_new
|
40
|
+
klass = resolved
|
41
|
+
klass = klass.deref if klass.is_a?(Rouge::Var)
|
42
|
+
resolved = klass.method(:new)
|
43
|
+
end
|
44
|
+
|
45
|
+
Resolved.new resolved
|
46
|
+
end
|
47
|
+
when Array
|
48
|
+
form.map {|f| compile(ns, lexicals, f)}
|
49
|
+
when Hash
|
50
|
+
Hash[form.map {|k, v| [compile(ns, lexicals, k),
|
51
|
+
compile(ns, lexicals, v)]}]
|
52
|
+
when Rouge::Seq::ISeq
|
53
|
+
to_a = form.to_a
|
54
|
+
if to_a.empty?
|
55
|
+
return Rouge::Seq::Empty
|
56
|
+
end
|
57
|
+
|
58
|
+
head, *tail = to_a
|
59
|
+
|
60
|
+
if head.is_a?(Rouge::Symbol) and
|
61
|
+
(head.ns.nil? or head.ns == :"rouge.builtin") and
|
62
|
+
Rouge::Builtins.respond_to?("_compile_#{head.name}")
|
63
|
+
Rouge::Seq::Cons[*
|
64
|
+
Rouge::Builtins.send(
|
65
|
+
"_compile_#{head.name}",
|
66
|
+
ns, lexicals, *tail)]
|
67
|
+
else
|
68
|
+
head = compile(ns, lexicals, head)
|
69
|
+
|
70
|
+
# XXX ↓↓↓ This is insane ↓↓↓
|
71
|
+
if head.is_a?(Resolved) and
|
72
|
+
head.res.is_a?(Rouge::Var) and
|
73
|
+
head.res.deref.is_a?(Rouge::Macro)
|
74
|
+
# TODO: backtrace_fix
|
75
|
+
compile(ns, lexicals, head.res.deref.inner.call(*tail))
|
76
|
+
else
|
77
|
+
# Regular function call!
|
78
|
+
if tail.include? Rouge::Symbol[:|]
|
79
|
+
index = tail.index Rouge::Symbol[:|]
|
80
|
+
if tail.length == index + 2
|
81
|
+
# Function.
|
82
|
+
block = compile(ns, lexicals, tail[index + 1])
|
83
|
+
else
|
84
|
+
# Inline block.
|
85
|
+
block = compile(
|
86
|
+
ns, lexicals,
|
87
|
+
Rouge::Seq::Cons[Rouge::Symbol[:fn],
|
88
|
+
*tail[index + 1..-1]])
|
89
|
+
end
|
90
|
+
tail = tail[0...index]
|
91
|
+
else
|
92
|
+
block = nil
|
93
|
+
end
|
94
|
+
Rouge::Seq::Cons[
|
95
|
+
head,
|
96
|
+
*tail.map {|f| compile(ns, lexicals, f)},
|
97
|
+
*(block ? [Rouge::Symbol[:|],
|
98
|
+
block]
|
99
|
+
: [])]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
else
|
103
|
+
form
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rouge/wrappers'
|
3
|
+
require 'rouge/namespace'
|
4
|
+
|
5
|
+
class Rouge::Context
|
6
|
+
class BindingNotFoundError < StandardError; end
|
7
|
+
class BadBindingError < StandardError; end
|
8
|
+
class ChangeContextException < Exception
|
9
|
+
def initialize(context); @context = context; end
|
10
|
+
attr_reader :context
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(parent_or_ns)
|
14
|
+
case parent_or_ns
|
15
|
+
when Rouge::Namespace
|
16
|
+
@ns = parent_or_ns
|
17
|
+
when Rouge::Context
|
18
|
+
@parent = parent_or_ns
|
19
|
+
@ns = @parent.ns
|
20
|
+
end
|
21
|
+
@table = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
if @table.include? key
|
26
|
+
@table[key]
|
27
|
+
elsif @parent
|
28
|
+
@parent[key]
|
29
|
+
elsif @ns
|
30
|
+
@ns[key]
|
31
|
+
else
|
32
|
+
raise BindingNotFoundError, key
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_here(key, value)
|
37
|
+
if Rouge::Symbol[key].ns != nil
|
38
|
+
raise BadBindingError, "cannot bind #{key.inspect}"
|
39
|
+
end
|
40
|
+
|
41
|
+
@table[key] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_lexical(key, value)
|
45
|
+
if @table.include? key
|
46
|
+
@table[key] = value
|
47
|
+
elsif @parent
|
48
|
+
@parent.set_lexical key, value
|
49
|
+
else
|
50
|
+
raise BindingNotFoundError,
|
51
|
+
"setting #{key} to #{value.inspect}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def lexical_keys
|
56
|
+
@table.keys + (@parent ? @parent.lexical_keys : [])
|
57
|
+
end
|
58
|
+
|
59
|
+
# This readeval post-processes the backtrace. Accordingly, it should only
|
60
|
+
# be called by consumers, and never by Rouge internally itself, lest it
|
61
|
+
# catches an exception and processes the backtrace too early.
|
62
|
+
def readeval(input)
|
63
|
+
reader = Rouge::Reader.new(ns, input)
|
64
|
+
context = self
|
65
|
+
r = nil
|
66
|
+
|
67
|
+
while true
|
68
|
+
begin
|
69
|
+
form = reader.lex
|
70
|
+
rescue Rouge::Reader::EndOfDataError
|
71
|
+
return r
|
72
|
+
end
|
73
|
+
|
74
|
+
form = Rouge::Compiler.compile(context.ns, Set[*lexical_keys], form)
|
75
|
+
|
76
|
+
begin
|
77
|
+
r = context.eval(form)
|
78
|
+
rescue ChangeContextException => cce
|
79
|
+
context = cce.context
|
80
|
+
reader.ns = context.ns
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue Exception => e
|
84
|
+
# Remove Rouge-related lines unless the exception originated in Rouge.
|
85
|
+
# root = File.dirname(File.dirname(__FILE__))
|
86
|
+
# e.backtrace.map! {|line|
|
87
|
+
# line.scan(root).length > 0 ? nil : line
|
88
|
+
# }.compact! unless e.backtrace[0].scan(root).length > 0
|
89
|
+
raise e
|
90
|
+
end
|
91
|
+
|
92
|
+
# Internal use only -- doesn't post-process backtrace.
|
93
|
+
def eval(form)
|
94
|
+
case form
|
95
|
+
when Rouge::Compiler::Resolved
|
96
|
+
result = form.res
|
97
|
+
if result.is_a?(Rouge::Var)
|
98
|
+
result.deref
|
99
|
+
else
|
100
|
+
result
|
101
|
+
end
|
102
|
+
when Rouge::Symbol
|
103
|
+
eval_symbol form
|
104
|
+
when Rouge::Seq::Cons
|
105
|
+
eval_cons form
|
106
|
+
when Hash
|
107
|
+
Hash[form.map {|k,v| [eval(k), eval(v)]}].freeze
|
108
|
+
when Array
|
109
|
+
form.map {|f| eval(f)}.freeze
|
110
|
+
else
|
111
|
+
form
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# +symbol+ should be a Rouge::Symbol.
|
116
|
+
def locate(symbol)
|
117
|
+
if !symbol.is_a?(Rouge::Symbol)
|
118
|
+
raise ArgumentError, "locate not called with R::S"
|
119
|
+
elsif symbol.ns
|
120
|
+
raise ArgumentError, "locate called with NS'd R::S #{symbol}"
|
121
|
+
end
|
122
|
+
|
123
|
+
if symbol.name_s[-1] == ?. and symbol.name_s.length > 1
|
124
|
+
lambda {|*args, &block|
|
125
|
+
self[symbol.name_s[0..-2].intern].new(*args, &block)
|
126
|
+
}
|
127
|
+
else
|
128
|
+
self[symbol.name]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_reader :ns
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def eval_symbol(form)
|
137
|
+
if !form.ns and form.name_s[0] == ?. and form.name_s.length > 1
|
138
|
+
lambda {|receiver, *args, &block|
|
139
|
+
receiver.send(form.name_s[1..-1], *args, &block)
|
140
|
+
}
|
141
|
+
else
|
142
|
+
result = locate form
|
143
|
+
if result.is_a?(Rouge::Var)
|
144
|
+
result.deref
|
145
|
+
else
|
146
|
+
result
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def eval_cons(form)
|
152
|
+
fun = eval form[0]
|
153
|
+
|
154
|
+
case fun
|
155
|
+
when Rouge::Builtin
|
156
|
+
backtrace_fix("(rouge):?:builtin: ", form) do
|
157
|
+
fun.inner.call self, *form.to_a[1..-1]
|
158
|
+
end
|
159
|
+
else
|
160
|
+
args = form.to_a[1..-1]
|
161
|
+
|
162
|
+
if args.include? Rouge::Symbol[:|]
|
163
|
+
index = args.index Rouge::Symbol[:|]
|
164
|
+
if args.length == index + 2
|
165
|
+
# Function.
|
166
|
+
block = eval args[index + 1]
|
167
|
+
else
|
168
|
+
# Inline block.
|
169
|
+
block = eval(Rouge::Seq::Cons[
|
170
|
+
Rouge::Symbol[:fn],
|
171
|
+
args[index + 1],
|
172
|
+
*args[index + 2..-1]
|
173
|
+
])
|
174
|
+
end
|
175
|
+
args = args[0...index]
|
176
|
+
else
|
177
|
+
block = nil
|
178
|
+
end
|
179
|
+
|
180
|
+
args = args.map {|f| eval(f)}
|
181
|
+
|
182
|
+
backtrace_fix("(rouge):?:lambda: ", form) do
|
183
|
+
eval_call(fun, args, block)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def eval_call(fun, args, block)
|
189
|
+
num_args = args.length
|
190
|
+
case fun
|
191
|
+
when Symbol
|
192
|
+
if num_args == 1 || num_args == 2
|
193
|
+
default = args[1]
|
194
|
+
if args[0].is_a? Hash
|
195
|
+
args[0].fetch(fun) { default }
|
196
|
+
else
|
197
|
+
default
|
198
|
+
end
|
199
|
+
else
|
200
|
+
raise ArgumentError,
|
201
|
+
"Wrong number of args (#{num_args}) passed to ruby/Symbol :#{fun}"
|
202
|
+
end
|
203
|
+
when Hash
|
204
|
+
if num_args == 1 || num_args == 2
|
205
|
+
default = args[1]
|
206
|
+
fun.fetch(args[0]) { default }
|
207
|
+
else
|
208
|
+
raise ArgumentError,
|
209
|
+
"Wrong number of args (#{num_args}) passed to ruby/Hash"
|
210
|
+
end
|
211
|
+
else
|
212
|
+
fun.call(*args, &block)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def backtrace_fix(name, form, &block)
|
217
|
+
begin
|
218
|
+
block.call
|
219
|
+
rescue Exception => e
|
220
|
+
# target = block.source_location.join(':')
|
221
|
+
# changed = 0
|
222
|
+
# $!.backtrace.map! {|line|
|
223
|
+
# if line.scan("#{target}:").size > 0 and changed == 0
|
224
|
+
# changed += 1
|
225
|
+
# Rouge.print(form, name.dup)
|
226
|
+
# else
|
227
|
+
# line
|
228
|
+
# end
|
229
|
+
# }
|
230
|
+
raise e
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rouge::Metadata
|
4
|
+
class InvalidMetadataError < StandardError; end
|
5
|
+
|
6
|
+
def meta
|
7
|
+
@meta
|
8
|
+
end
|
9
|
+
|
10
|
+
def meta= m
|
11
|
+
if m != nil and m.class != Hash
|
12
|
+
raise InvalidMetadataError, "bad metadata: #{m.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
@meta = m
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# vim: set sw=2 et cc=80:
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rouge/context'
|
3
|
+
require 'rouge/builtins'
|
4
|
+
require 'rouge/var'
|
5
|
+
require 'rouge/atom'
|
6
|
+
|
7
|
+
class Rouge::Namespace
|
8
|
+
@namespaces = {}
|
9
|
+
|
10
|
+
class VarNotFoundError < StandardError; end
|
11
|
+
class RecursiveNamespaceError < StandardError; end
|
12
|
+
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
raise ArgumentError, "bad ns name" unless @name.is_a? Symbol
|
16
|
+
@table = {}
|
17
|
+
@refers = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"#<Rouge::NS #{@name.inspect}, " \
|
22
|
+
"refers #{@refers.map(&:inspect).join(", ")}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def refer(ns)
|
26
|
+
if ns.name == @name
|
27
|
+
raise RecursiveNamespaceError, "#@name will not refer #{ns.name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
@refers << ns if not @refers.include? ns
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](key)
|
34
|
+
if @table.include? key
|
35
|
+
return @table[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
@refers.each do |ns|
|
39
|
+
begin
|
40
|
+
return ns[key]
|
41
|
+
rescue VarNotFoundError
|
42
|
+
# no-op
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
raise VarNotFoundError, key
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_here(key, value)
|
50
|
+
@table[key] = Rouge::Var.new(@name, key, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def intern(key)
|
54
|
+
@table[key] ||= Rouge::Var.new(@name, key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def read(input)
|
58
|
+
Rouge::Reader.new(self, input).lex
|
59
|
+
end
|
60
|
+
|
61
|
+
def clear
|
62
|
+
@table = {}
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
attr_reader :name, :refers
|
67
|
+
end
|
68
|
+
|
69
|
+
class << Rouge::Namespace
|
70
|
+
def exists?(ns)
|
71
|
+
@namespaces.include? ns
|
72
|
+
end
|
73
|
+
|
74
|
+
def [](ns)
|
75
|
+
r = @namespaces[ns]
|
76
|
+
return r if r
|
77
|
+
|
78
|
+
self[ns] = new(ns)
|
79
|
+
@namespaces[ns] = new(ns)
|
80
|
+
end
|
81
|
+
|
82
|
+
def []=(ns, value)
|
83
|
+
@namespaces[ns] = value
|
84
|
+
end
|
85
|
+
|
86
|
+
def destroy(ns)
|
87
|
+
@namespaces.delete ns
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Rouge::Namespace::Ruby
|
92
|
+
@@cache = {}
|
93
|
+
|
94
|
+
def [](name)
|
95
|
+
return @@cache[name] if @@cache.include? name
|
96
|
+
if name =~ /^\$/
|
97
|
+
@@cache[name] = Rouge::Var.new(:ruby, name, eval(name.to_s))
|
98
|
+
else
|
99
|
+
@@cache[name] = Rouge::Var.new(:ruby, name, Kernel.const_get(name))
|
100
|
+
end
|
101
|
+
rescue NameError
|
102
|
+
raise Rouge::Namespace::VarNotFoundError, name
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_here(name, value)
|
106
|
+
@@cache[name] = Rouge::Var.new(:ruby, name, value)
|
107
|
+
Kernel.const_set name, value
|
108
|
+
end
|
109
|
+
|
110
|
+
def name
|
111
|
+
:ruby
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
ns = Rouge::Namespace[:"rouge.builtin"]
|
116
|
+
Rouge::Builtins.methods(false).reject {|s| s =~ /^_compile_/}.each do |m|
|
117
|
+
ns.set_here m, Rouge::Builtin[Rouge::Builtins.method(m)]
|
118
|
+
end
|
119
|
+
Rouge::Builtins::SYMBOLS.each do |name, val|
|
120
|
+
ns.set_here name, val
|
121
|
+
end
|
122
|
+
|
123
|
+
Rouge::Namespace[:ruby] = Rouge::Namespace::Ruby.new
|
124
|
+
|
125
|
+
# vim: set sw=2 et cc=80:
|