atomy 0.1.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.
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