atomy 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/COPYING +30 -0
  2. data/README.md +1 -0
  3. data/bin/atomy +134 -0
  4. data/kernel/block.ay +30 -0
  5. data/kernel/boot.ay +10 -0
  6. data/kernel/comparison.ay +61 -0
  7. data/kernel/concurrency.ay +84 -0
  8. data/kernel/condition.ay +277 -0
  9. data/kernel/control-flow.ay +222 -0
  10. data/kernel/cosmetics.ay +3 -0
  11. data/kernel/data-delta.ay +105 -0
  12. data/kernel/data.ay +56 -0
  13. data/kernel/define.ay +93 -0
  14. data/kernel/doc.ay +453 -0
  15. data/kernel/documentation.ay +135 -0
  16. data/kernel/dynamic.ay +42 -0
  17. data/kernel/errors.ay +6 -0
  18. data/kernel/format.ay +13 -0
  19. data/kernel/format/data.ay +89 -0
  20. data/kernel/format/formatter.ay +345 -0
  21. data/kernel/format/parser.ay +13 -0
  22. data/kernel/hashes.ay +39 -0
  23. data/kernel/io.ay +244 -0
  24. data/kernel/namespaces.ay +63 -0
  25. data/kernel/node.ay +48 -0
  26. data/kernel/operators.ay +28 -0
  27. data/kernel/particles.ay +53 -0
  28. data/kernel/patterns.ay +256 -0
  29. data/kernel/precision.ay +148 -0
  30. data/kernel/pretty.ay +283 -0
  31. data/kernel/repl.ay +140 -0
  32. data/kernel/therie.ay +204 -0
  33. data/lib/ast/binary_send.rb +44 -0
  34. data/lib/ast/block.rb +268 -0
  35. data/lib/ast/constant.rb +88 -0
  36. data/lib/ast/internal/assign.rb +19 -0
  37. data/lib/ast/internal/block_pass.rb +21 -0
  38. data/lib/ast/internal/catch.rb +247 -0
  39. data/lib/ast/internal/class.rb +30 -0
  40. data/lib/ast/internal/class_variable.rb +23 -0
  41. data/lib/ast/internal/define.rb +174 -0
  42. data/lib/ast/internal/ensure.rb +135 -0
  43. data/lib/ast/internal/file.rb +14 -0
  44. data/lib/ast/internal/global_variable.rb +20 -0
  45. data/lib/ast/internal/if_then_else.rb +24 -0
  46. data/lib/ast/internal/instance_variable.rb +17 -0
  47. data/lib/ast/internal/let_macro.rb +35 -0
  48. data/lib/ast/internal/macro_quote.rb +23 -0
  49. data/lib/ast/internal/match.rb +53 -0
  50. data/lib/ast/internal/module.rb +30 -0
  51. data/lib/ast/internal/pattern.rb +17 -0
  52. data/lib/ast/internal/return.rb +29 -0
  53. data/lib/ast/internal/set.rb +19 -0
  54. data/lib/ast/internal/singleton_class.rb +18 -0
  55. data/lib/ast/internal/splat.rb +14 -0
  56. data/lib/ast/internal/when.rb +24 -0
  57. data/lib/ast/list.rb +25 -0
  58. data/lib/ast/macro.rb +37 -0
  59. data/lib/ast/node.rb +599 -0
  60. data/lib/ast/operator.rb +21 -0
  61. data/lib/ast/particle.rb +13 -0
  62. data/lib/ast/primitive.rb +20 -0
  63. data/lib/ast/quasi_quote.rb +20 -0
  64. data/lib/ast/quote.rb +13 -0
  65. data/lib/ast/send.rb +104 -0
  66. data/lib/ast/splice.rb +32 -0
  67. data/lib/ast/string.rb +23 -0
  68. data/lib/ast/unary.rb +44 -0
  69. data/lib/ast/unquote.rb +45 -0
  70. data/lib/ast/variable.rb +64 -0
  71. data/lib/atomy.kpeg.rb +3995 -0
  72. data/lib/code_loader.rb +137 -0
  73. data/lib/compiler/compiler.rb +155 -0
  74. data/lib/compiler/stages.rb +81 -0
  75. data/lib/formatter.kpeg.rb +1394 -0
  76. data/lib/macros.rb +317 -0
  77. data/lib/method.rb +261 -0
  78. data/lib/namespace.rb +236 -0
  79. data/lib/parser.rb +28 -0
  80. data/lib/patterns.rb +276 -0
  81. data/lib/patterns/any.rb +21 -0
  82. data/lib/patterns/attribute.rb +59 -0
  83. data/lib/patterns/block_pass.rb +54 -0
  84. data/lib/patterns/constant.rb +33 -0
  85. data/lib/patterns/default.rb +44 -0
  86. data/lib/patterns/head_tail.rb +63 -0
  87. data/lib/patterns/list.rb +77 -0
  88. data/lib/patterns/match.rb +45 -0
  89. data/lib/patterns/named.rb +55 -0
  90. data/lib/patterns/named_class.rb +46 -0
  91. data/lib/patterns/named_global.rb +46 -0
  92. data/lib/patterns/named_instance.rb +46 -0
  93. data/lib/patterns/particle.rb +29 -0
  94. data/lib/patterns/quasi_quote.rb +184 -0
  95. data/lib/patterns/quote.rb +33 -0
  96. data/lib/patterns/singleton_class.rb +31 -0
  97. data/lib/patterns/splat.rb +57 -0
  98. data/lib/util.rb +37 -0
  99. metadata +164 -0
data/lib/macros.rb ADDED
@@ -0,0 +1,317 @@
1
+ class MethodFail < ArgumentError
2
+ def initialize(mn)
3
+ @method_name = mn
4
+ end
5
+
6
+ def message
7
+ "method #{@method_name.to_s} did not understand " +
8
+ "its arguments (non-exhaustive patterns)"
9
+ end
10
+ end
11
+
12
+ module Atomy
13
+ OPERATORS = {}
14
+ STATE = {}
15
+
16
+ module Macro
17
+ def self.set_op_info(ops, assoc, prec)
18
+ ops.each do |o|
19
+ info = OPERATORS[o] ||= {}
20
+ info[:assoc] = assoc
21
+ info[:prec] = prec
22
+ end
23
+ end
24
+
25
+ class Environment
26
+ @@salt = 0
27
+ @@macros = {}
28
+ @@let = Hash.new { |h, k| h[k] = [] }
29
+ @@quoters = {}
30
+ @@line = 0
31
+
32
+ class << self
33
+ attr_accessor :quoters
34
+
35
+ def macros
36
+ @@macros
37
+ end
38
+
39
+ def let
40
+ @@let
41
+ end
42
+
43
+ def line
44
+ @@line
45
+ end
46
+
47
+ def line=(x)
48
+ @@line = x
49
+ end
50
+
51
+ def quoter(name, &blk)
52
+ @@quoters[name] = blk
53
+ end
54
+
55
+ def quote(name, contents, flags)
56
+ if a = @@quoters[name.to_sym]
57
+ a.call(contents, flags)
58
+ else
59
+ raise "unknown quoter #{name.inspect}"
60
+ end
61
+ end
62
+
63
+ def names(num = 0, &block)
64
+ num = block.arity if block
65
+
66
+ as = []
67
+ num.times do
68
+ as << Atomy::AST::Variable.new(0, "s:" + @@salt.to_s)
69
+ @@salt += 1
70
+ end
71
+
72
+ if block
73
+ block.call(*as)
74
+ else
75
+ as
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def self.register(name, args, body, let = false)
82
+ ns = Atomy::Namespace.get(Thread.current[:atomy_define_in])
83
+ meth = ns ? Atomy.namespaced(ns.name, name) : name
84
+ meth = (intern meth).to_sym
85
+
86
+ if let && Environment.respond_to?(meth)
87
+ Environment.let[name] << Environment.method(meth)
88
+ end
89
+
90
+ methods = Environment.macros
91
+ method = [[Patterns::Any.new, args], body.resolve]
92
+ if ms = methods[meth]
93
+ Atomy.insert_method(method, ms)
94
+ else
95
+ methods[meth] = [method]
96
+ end
97
+
98
+ Atomy.add_method(
99
+ Environment.singleton_class,
100
+ meth,
101
+ methods[meth],
102
+ nil,
103
+ :public,
104
+ true
105
+ )
106
+
107
+ meth
108
+ end
109
+
110
+ def self.intern(name)
111
+ "atomy-macro:" + name
112
+ end
113
+
114
+ # take a node and return its expansion
115
+ def self.expand(node)
116
+ name = node.method_name
117
+
118
+ return node unless name
119
+
120
+ methods = []
121
+ if name && ns = Atomy::Namespace.get
122
+ ([ns.name] + ns.using).each do |n|
123
+ methods << intern(Atomy.namespaced(n, name)).to_sym
124
+ end
125
+ end
126
+
127
+ methods << intern(name).to_sym
128
+
129
+ expanded = nil
130
+ methods.each do |meth|
131
+ next unless Environment.respond_to?(meth)
132
+
133
+ expanded = expand_node(node, meth)
134
+ break if expanded
135
+ end
136
+
137
+ expanded || node
138
+ end
139
+
140
+ def self.expand_node(node, meth)
141
+ Environment.line ||= node.line
142
+
143
+ begin
144
+ case node
145
+ when AST::BinarySend
146
+ expand_res Environment.send(
147
+ meth,
148
+ nil,
149
+ node.lhs,
150
+ node.rhs
151
+ )
152
+ when AST::Send
153
+ if node.arguments.last.kind_of?(AST::Unary) && \
154
+ node.arguments.last.operator == "&"
155
+ block = AST::BlockPass.new(node.line, node.arguments.pop.receiver)
156
+ else
157
+ block = node.block
158
+ end
159
+
160
+ expand_res Environment.send(
161
+ meth,
162
+ block,
163
+ node.receiver,
164
+ *node.arguments
165
+ )
166
+ when AST::Unary
167
+ expand_res Environment.send(
168
+ meth,
169
+ nil,
170
+ node.receiver
171
+ )
172
+ when AST::Variable
173
+ expand_res Environment.send(
174
+ meth
175
+ )
176
+ else
177
+ # just stopping
178
+ nil
179
+ end
180
+ rescue MethodFail, ArgumentError => e
181
+ # expand normally if the macro doesn't seem to be a match
182
+ raise unless e.instance_variable_get("@method_name") == meth
183
+ nil
184
+ ensure
185
+ Environment.line = nil
186
+ end
187
+ end
188
+
189
+ # helper method for #expand
190
+ def self.expand_res(node)
191
+ expand(node.to_node)
192
+ end
193
+
194
+ # @!x
195
+ # to:
196
+ # @`(!~x)
197
+ #
198
+ # @!?x
199
+ # to:
200
+ # @(`!?~x)
201
+ def self.unary_chain(n)
202
+ d = n.dup
203
+ x = d
204
+ while x.kind_of?(Atomy::AST::Unary)
205
+ if x.receiver.kind_of?(Atomy::AST::Unary)
206
+ y = x.receiver.dup
207
+ x.receiver = y
208
+ x = y
209
+ else
210
+ unless x.receiver.kind_of?(Atomy::AST::Primitive)
211
+ x.receiver = Atomy::AST::Unquote.new(
212
+ x.receiver.line,
213
+ x.receiver
214
+ )
215
+ end
216
+ break
217
+ end
218
+ end
219
+
220
+ Atomy::AST::QuasiQuote.new(d.line, d)
221
+ end
222
+
223
+ # x(a) y(b)
224
+ # to:
225
+ # `(x(~a)) y(b)
226
+ #
227
+ # x(a) y(b) z(c)
228
+ # to:
229
+ # `(x(~a) y(~b)) z(c)
230
+ #
231
+ # x(&a) b(c) should bind the proc-arg
232
+ def self.send_chain(n)
233
+ return n if n.block
234
+
235
+ d = n.dup
236
+ x = d
237
+ while x.kind_of?(Atomy::AST::Send)
238
+ as = []
239
+ x.arguments.each do |a|
240
+ if a.kind_of?(Atomy::AST::Unary) && a.operator == "&"
241
+ x.block = Atomy::AST::Unquote.new(
242
+ a.line,
243
+ a.receiver
244
+ )
245
+ else
246
+ as << Atomy::AST::Unquote.new(
247
+ a.line,
248
+ a
249
+ )
250
+ end
251
+ end
252
+
253
+ x.arguments = as
254
+
255
+ if x.receiver.kind_of?(Atomy::AST::Send) && !x.receiver.block
256
+ y = x.receiver.dup
257
+ x.receiver = y
258
+ x = y
259
+ else
260
+ unless x.receiver.kind_of?(Atomy::AST::Primitive)
261
+ x.receiver = Atomy::AST::Unquote.new(
262
+ x.receiver.line,
263
+ x.receiver
264
+ )
265
+ end
266
+ break
267
+ end
268
+ end
269
+
270
+ Atomy::AST::QuasiQuote.new(d.line, d)
271
+ end
272
+
273
+ def self.macro_pattern(n)
274
+ if n.kind_of?(Atomy::AST::Send) && !n.block
275
+ n = send_chain(n)
276
+ end
277
+
278
+ if n.kind_of?(Atomy::AST::Unary) && n.operator != "&" && n.operator != "*"
279
+ n = unary_chain(n)
280
+ end
281
+
282
+ # TODO: this is too powerful. `foo = 'Object` pattern breaks.
283
+ n = n.recursively do |sub|
284
+ case sub
285
+ when Atomy::AST::Constant
286
+ Atomy::AST::ScopedConstant.new(
287
+ sub.line,
288
+ Atomy::AST::ScopedConstant.new(
289
+ sub.line,
290
+ Atomy::AST::Constant.new(
291
+ sub.line,
292
+ "Atomy"
293
+ ),
294
+ "AST"
295
+ ),
296
+ sub.identifier
297
+ )
298
+ else
299
+ sub
300
+ end
301
+ end
302
+
303
+ case n
304
+ when Atomy::AST::Primitive
305
+ if n.value == :self
306
+ Atomy::Patterns::Quote.new(
307
+ Atomy::AST::Primitive.new(n.line, :self)
308
+ )
309
+ else
310
+ n.to_pattern
311
+ end
312
+ else
313
+ n.to_pattern
314
+ end
315
+ end
316
+ end
317
+ end
data/lib/method.rb ADDED
@@ -0,0 +1,261 @@
1
+ module Atomy
2
+ def self.segments(args)
3
+ req = []
4
+ dfs = []
5
+ spl = nil
6
+ blk = nil
7
+ args.each do |a|
8
+ case a
9
+ when Patterns::BlockPass
10
+ blk = a
11
+ when Patterns::Splat
12
+ spl = a
13
+ when Patterns::Default
14
+ dfs << a
15
+ else
16
+ req << a
17
+ end
18
+ end
19
+ [req, dfs, spl, blk]
20
+ end
21
+
22
+ def self.match_self?(pat)
23
+ case pat
24
+ when Patterns::Match
25
+ pat.value != :self
26
+ when Patterns::Constant
27
+ false
28
+ when Patterns::Named
29
+ match_self?(pat.pattern)
30
+ else
31
+ true
32
+ end
33
+ end
34
+
35
+ def self.build_method(name, branches, is_macro = false, file = :dynamic, line = 1)
36
+ g = Rubinius::Generator.new
37
+ g.name = name.to_sym
38
+ g.file = file.to_sym
39
+ g.set_line Integer(line)
40
+
41
+ done = g.new_label
42
+ mismatch = g.new_label
43
+
44
+ g.push_state Rubinius::AST::ClosedScope.new(line)
45
+
46
+ g.state.push_name name
47
+
48
+ block_offset = is_macro ? 1 : 0
49
+
50
+ total = 0
51
+ min_reqs = nil
52
+ reqs = 0
53
+ defs = 0
54
+ names = []
55
+ splatted = false
56
+
57
+ resolved = branches.collect do |pats, meth|
58
+ segs = segments(pats[1])
59
+
60
+ min_reqs ||= segs[0].size
61
+ min_reqs = [min_reqs, segs[0].size].min
62
+ reqs = [reqs, segs[0].size].max
63
+ defs = [defs, segs[1].size].max
64
+ total = [reqs + defs, total].max
65
+
66
+ names += pats[0].local_names
67
+ pats[1].each do |p|
68
+ names += p.local_names
69
+ end
70
+
71
+ splatted = true if segs[2]
72
+
73
+ [pats[0], segs, meth]
74
+ end
75
+
76
+ names.uniq!
77
+
78
+ if splatted
79
+ g.splat_index = block_offset + reqs + defs
80
+ end
81
+
82
+ total += block_offset
83
+
84
+ total.times do |n|
85
+ names.unshift("arg:" + n.to_s)
86
+ end
87
+
88
+ locals = {}
89
+ names.each do |n|
90
+ locals[n] = g.state.scope.new_local(n).reference
91
+ end
92
+
93
+ g.local_names = names
94
+ g.total_args = total
95
+ g.required_args = min_reqs
96
+ g.local_count = total + g.local_names.size
97
+
98
+ g.push_self
99
+ resolved.each do |recv, (reqs, defs, splat, block), meth|
100
+ skip = g.new_label
101
+ argmis = g.new_label
102
+
103
+ if reqs.size > min_reqs
104
+ g.passed_arg((reqs.size + block_offset) - 1)
105
+ g.gif skip
106
+ end
107
+
108
+ if match_self?(recv)
109
+ g.dup
110
+ recv.matches?(g)
111
+ g.gif skip
112
+ end
113
+
114
+ if splat
115
+ g.push_local(g.splat_index)
116
+ splat.pattern.deconstruct(g)
117
+ end
118
+
119
+ if recv.bindings > 0
120
+ g.dup
121
+ recv.deconstruct(g, locals)
122
+ end
123
+
124
+ if block
125
+ if is_macro
126
+ g.push_local(0)
127
+ block.pattern.deconstruct(g, locals)
128
+ else
129
+ g.push_block_arg
130
+ block.deconstruct(g, locals)
131
+ end
132
+ end
133
+
134
+ reqs.each_with_index do |a, i|
135
+ g.push_local(i + block_offset)
136
+
137
+ if a.bindings > 0
138
+ g.dup
139
+ a.matches?(g)
140
+ g.gif argmis
141
+ a.deconstruct(g, locals)
142
+ else
143
+ a.matches?(g)
144
+ g.gif skip
145
+ end
146
+ end
147
+
148
+ defs.each_with_index do |d, i|
149
+ passed = g.new_label
150
+ decons = g.new_label
151
+
152
+ num = reqs.size + i + block_offset
153
+ g.passed_arg num
154
+ g.git passed
155
+
156
+ d.default.compile(g)
157
+ g.set_local num
158
+ g.goto decons
159
+
160
+ passed.set!
161
+ g.push_local num
162
+
163
+ decons.set!
164
+ d.deconstruct(g)
165
+ end
166
+
167
+ meth.compile(g)
168
+ g.goto done
169
+
170
+ argmis.set!
171
+ g.pop
172
+
173
+ skip.set!
174
+ end
175
+
176
+ g.invoke_primitive :vm_check_super_callable, 0
177
+ g.gif mismatch
178
+
179
+ g.push_block
180
+ if g.state.super?
181
+ g.zsuper g.state.super.name
182
+ else
183
+ g.zsuper nil
184
+ end
185
+ g.goto done
186
+
187
+ mismatch.set!
188
+ g.push_self
189
+ g.push_cpath_top
190
+ g.find_const :MethodFail
191
+ g.push_literal name
192
+ g.send :new, 1
193
+ g.allow_private
194
+ g.send :raise, 1
195
+
196
+ done.set!
197
+ g.state.pop_name
198
+ g.ret
199
+ g.close
200
+ g.pop_state
201
+ g.use_detected
202
+ g.encode
203
+
204
+ g.package Rubinius::CompiledMethod
205
+ end
206
+
207
+ def self.add_method(target, name, branches, static_scope, visibility = :public, is_macro = false)
208
+ cm = build_method(name, branches, is_macro)
209
+
210
+ unless static_scope
211
+ static_scope = Rubinius::StaticScope.new(
212
+ self,
213
+ Rubinius::StaticScope.new(Object)
214
+ )
215
+ end
216
+
217
+ cm.scope = static_scope
218
+
219
+ Rubinius.add_method name, cm, target, visibility
220
+ end
221
+
222
+ def self.compare_heads(xs, ys)
223
+ return 1 if xs.size > ys.size
224
+ return -1 if xs.size < ys.size
225
+
226
+ xs.zip(ys) do |x, y|
227
+ cmp = x <=> y
228
+ return cmp unless cmp == 0
229
+ end
230
+
231
+ 0
232
+ end
233
+
234
+ def self.equivalent?(xs, ys)
235
+ xs.zip(ys) do |x, y|
236
+ return false unless x =~ y
237
+ end
238
+
239
+ true
240
+ end
241
+
242
+ def self.insert_method(new, branches)
243
+ (nr, na), nb = new
244
+ if nr.respond_to?(:<=>)
245
+ branches.each_with_index do |branch, i|
246
+ (r, a), b = branch
247
+ case compare_heads([nr] + na, [r] + a)
248
+ when 1
249
+ return branches.insert(i, new)
250
+ when 0
251
+ if equivalent?([nr] + na, [r] + a)
252
+ branches[i] = new
253
+ return branches
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ branches << new
260
+ end
261
+ end