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/kernel/repl.ay ADDED
@@ -0,0 +1,140 @@
1
+ namespace(atomy)
2
+
3
+ title"The REPL"
4
+
5
+ require("readline")
6
+
7
+ doc"
8
+ The basics of a REPL - reading, evaluating, and printing in a loop. More \
9
+ flexibility is provided by various signals. See \hl{repl} for a fancier \
10
+ REPL, which builds upon this, and \hl{ReplDebugger}.
11
+
12
+ When showing the prompt, \hl{#prompt} is signaled with a \hl{#use-prompt} \
13
+ restart available. Invoke this restart and pass along a string to \
14
+ override the prompt, which defaults to \code{>>}.
15
+
16
+ Input preceded by a colon (\code{:}) and followed by an alphanumeric \
17
+ character is assumed to be a \italic{special command}. These are not \
18
+ evaluated, and are signaled as \hl{#special(\italic{text})}.
19
+
20
+ When the user sends \code{EOF} (Ctrl+D) or an interrupt (Ctrl+C), \
21
+ \hl{#quit} is signaled.
22
+
23
+ When the user enters code, \hl{#input(\italic{text})} is signaled. The code\
24
+ is evaluated with two restarts registered: \code{#retry} for re-attempting \
25
+ evaluation, and \code{#abort}, for canceling the evaluation. After the code\
26
+ is evaluated, \hl{#evaluated(\italic{result})} is signaled.
27
+
28
+ \hl{#loop} is signaled before the loop starts over again (i.e., after the \
29
+ input is handled).
30
+ " spec {
31
+ bnd is-a?(Binding)
32
+ } for:
33
+ basic-repl(bnd = TOPLEVEL_BINDING) :=
34
+ loop:
35
+ prompt =
36
+ with-restarts(use-prompt(p) -> p):
37
+ signal(#prompt)
38
+ ">> "
39
+
40
+ in =
41
+ { Readline readline(prompt)
42
+ } rescue:
43
+ Interrupt -> do { signal(#quit), "" }
44
+
45
+ in match:
46
+ nil -> signal(#quit)
47
+
48
+ "" -> #ok
49
+
50
+ String ? (=~ r"^:[[:alnum:]]") ->
51
+ signal(#special(in [1 .. -1]))
52
+
53
+ source -> do:
54
+ signal(#input(source))
55
+
56
+ try = {
57
+ with-restarts(retry -> try [], abort -> #ok):
58
+ res = Atomy::Compiler evaluate(source, bnd)
59
+ signal(#evaluated(res))
60
+ }
61
+
62
+ try []
63
+
64
+ signal(#loop)
65
+
66
+
67
+ doc"
68
+ An interactive debugger REPL for handling errors. This will list the \
69
+ results along with a number, allow the user to continue evaluating code, \
70
+ and once they enter a \italic{special command} in the form of \
71
+ \code{:\italic{number}}, the specified restart will be invoked.
72
+ " for:
73
+ class(ReplDebugger):
74
+ class(<< self):
75
+ show-backtrace(e) := do:
76
+ Rubinius::Backtrace backtrace(e backtrace) _/show print
77
+ debug(e)
78
+
79
+ run(e) := do:
80
+ DefaultDebugger show-error-banner(e)
81
+
82
+ with-restarts(backtrace -> show-backtrace(e)):
83
+ debug(e)
84
+
85
+ debug(e) := do:
86
+ DefaultDebugger show-options-for(e)
87
+
88
+ { basic-repl } bind:
89
+ #prompt -> restart(#use-prompt, "[!]> ")
90
+
91
+ #special(n ? (=~ r"\d+")) ->
92
+ ^restarts [n to-i] invoke
93
+
94
+ #quit -> exit(1)
95
+
96
+ dynamic(debugger, ReplDebugger)
97
+
98
+ doc"
99
+ A more feature-filled REPL, providing persistent history and setting \
100
+ \hl{^debugger} to \hl{ReplDebugger}.
101
+
102
+ History will be managed and appended to a file specified by \hl{history} \
103
+ upon termination.
104
+ " spec {
105
+ history is-a?(String)
106
+ bnd is-a?(Binding)
107
+ } for:
108
+ repl(history = nil, bnd = TOPLEVEL_BINDING) := do:
109
+ when(history && File exists?(history)):
110
+ File open(history, "r") [f]:
111
+ f readlines each [l]:
112
+ Readline::HISTORY << l strip
113
+
114
+ SANE_HISTORY = []
115
+
116
+ { let(debugger = ReplDebugger):
117
+ frame = 0
118
+
119
+ { let(atomy/pretty/colored? = true):
120
+ basic-repl(bnd)
121
+ } bind:
122
+ #prompt ->
123
+ restart(#use-prompt, "[" + frame to-s + "]> ")
124
+
125
+ #loop ->
126
+ (frame += 1)
127
+
128
+ #quit -> exit(0)
129
+
130
+ #input(str) -> (SANE_HISTORY << str)
131
+
132
+ #special("h") ->
133
+ ":h\thelp" print
134
+
135
+ #evaluated(r) ->
136
+ (" => " + r show) print
137
+ } ensuring:
138
+ when(history):
139
+ File open(history, "a") [f]:
140
+ f puts(*SANE_HISTORY)
data/kernel/therie.ay ADDED
@@ -0,0 +1,204 @@
1
+ namespace(therie)
2
+
3
+ title"Testing with Therie"
4
+
5
+ doc"
6
+ Therie is a small and simple behavioral-style testing suite that comes with \
7
+ Atomy. To use it, you should use the \hl{therie} namespace, which provides \
8
+ the following methods.
9
+
10
+ \atomy{
11
+ use(therie)
12
+ }
13
+ "
14
+
15
+ dynamic(nesting-level, 0)
16
+
17
+ doc"
18
+ A trivial container of \hl{passed} and \hl{failed} counts, with accessors \
19
+ for both.
20
+ " for:
21
+ class(Stats):
22
+ attr-accessor(#passed, #failed)
23
+
24
+ export:
25
+ initialize := do:
26
+ @passed = 0
27
+ @failed = 0
28
+
29
+ inspect := f"Stats(@passed = %d, @failed = %d)" [@passed, @failed]
30
+
31
+ dynamic(stats, nil)
32
+
33
+
34
+ section("Structure"):
35
+ doc"\evaluate{use(therie)}"
36
+
37
+ doc"
38
+ Run \hl{tests} and keep track of how many passed and how many failed, \
39
+ printing the stats at the end and returning them.
40
+ " spec {
41
+ => Stats
42
+ } for {
43
+ theorize(&tests) :=
44
+ let(stats = Stats new):
45
+ tests call
46
+ "\n" display
47
+
48
+ ^stats onto:
49
+ f"total of %d tests (%s passed, %s failed)" [
50
+ failed + passed
51
+ passed to-s colored(#green)
52
+ if(failed == 0)
53
+ then: "0"
54
+ else: failed to-s colored(#red)
55
+ ] display
56
+
57
+ "\n" display
58
+
59
+ ^stats
60
+ } examples:
61
+ theorize:
62
+ describe("foo"):
63
+ it("does x"): true should-be(false)
64
+ it("does x"): true should-be(true)
65
+
66
+
67
+ doc"
68
+ Logically group together a set of behavior.
69
+
70
+ Prints out \hl{what}, with each test in \hl{body} indented afterward.
71
+ " for {
72
+ describe(what, &body) := do:
73
+ f"- %s" [what] indented print
74
+
75
+ let(nesting-level = ^nesting-level + 1):
76
+ body call
77
+
78
+ nil
79
+ } examples:
80
+ describe("foo"):
81
+ it("does x"): true should-be(false)
82
+ it("does x"): true should-be(true)
83
+
84
+
85
+ doc"
86
+ Describe some behavior that the tests in \hl{body} will demonstrate.
87
+ " for {
88
+ it(description, &tests) := do:
89
+ { tests call
90
+ f"✓ %s" [description] indented colored(#green) print
91
+ } rescue {
92
+ e -> do:
93
+ when(^stats):
94
+ ^stats failed += 1
95
+
96
+ f"✗ %s" [description] indented colored(#red) print
97
+ f" ` %s: %s" [
98
+ e class name
99
+ e message
100
+ ] indented colored(#yellow) print
101
+
102
+ e backtrace first(5) each [l]:
103
+ f" %s" [l colored(#cyan)] indented print
104
+ } else:
105
+ when(^stats):
106
+ ^stats passed += 1
107
+
108
+ nil
109
+ } examples:
110
+ it("adds correctly"): (2 + 2) should-be(4)
111
+ it("adds correctly"): (1 + 2) should-be(4)
112
+
113
+
114
+ section("Tests"):
115
+ doc"\evaluate{use(therie)}"
116
+
117
+ doc"
118
+ Test that \hl{predicate} is satisified by \hl{o} by evaluating it with \
119
+ \hl{o} as \hl{self}.
120
+ " for {
121
+ o should(&check) :=
122
+ unless(o onto(&check)):
123
+ raise(f"assertion failed for %v" [o])
124
+ } examples:
125
+ (2 + 2) should: even?
126
+ (2 + 2) should: odd?
127
+
128
+
129
+ doc"
130
+ Test for \hl{x == y}.
131
+ " for {
132
+ x should-be(y) :=
133
+ unless(x == y):
134
+ raise(f"expected %v, got %v" [y, x])
135
+ } examples:
136
+ (2 + 2) should-be(4)
137
+ (1 + 2) should-be(4)
138
+
139
+
140
+ doc"
141
+ Test that executing \hl{x} will raise an exception of class \hl{y}.
142
+ " spec {
143
+ x respond-to?(#call)
144
+ y is-a?(Class)
145
+ } for {
146
+ x should-raise(y) :=
147
+ x rescue {
148
+ e ->
149
+ unless(e kind-of?(y)):
150
+ raise(f"expected exception %s, got: %s" [y, e])
151
+ } else:
152
+ raise("#should-raise - no exception raised")
153
+ } examples:
154
+ { abc } should-raise(NoMethodError)
155
+ { #ok } should-raise(NoMethodError)
156
+
157
+
158
+ doc"
159
+ Test that executing \hl{x} will signal an error of class \hl{y}.
160
+ " spec {
161
+ x respond-to?(#call)
162
+ y is-a?(Class)
163
+ } for {
164
+ x should-error(y) :=
165
+ { with-restarts(errored -> nil):
166
+ x call
167
+ raise("#should-error - no error signaled")
168
+ } bind {
169
+ (e: ExceptionError) ->
170
+ raise(e exception)
171
+
172
+ (e: Error) -> do:
173
+ when(e kind-of?(y)):
174
+ restart(#errored)
175
+
176
+ raise(f"expected error %s, got: %s" [y, e message])
177
+ }
178
+ } examples:
179
+ { error(#foo) } should-error(SimpleError)
180
+ { #ok } should-error(SimpleError)
181
+
182
+
183
+ -- helpers
184
+ String indented := " " * (^nesting-level * 2) + self
185
+
186
+ String colored(color) := do:
187
+ -- only makes sense to colorize if we're outputting to a terminal
188
+ unless(^output-port tty?):
189
+ return(self)
190
+
191
+ codes =
192
+ [ #black
193
+ #red
194
+ #green
195
+ #yellow
196
+ #blue
197
+ #magenta
198
+ #cyan
199
+ #white
200
+ ] zip((0 .. 7) to-a)
201
+
202
+ hash = Hash [codes]
203
+
204
+ "\e[9" + hash [color] to-s + "m" + self + "\e[0m"
@@ -0,0 +1,44 @@
1
+ module Atomy
2
+ module AST
3
+ class BinarySend < Node
4
+ children :lhs, :rhs
5
+ attributes :operator
6
+ slots [:private, "false"], :namespace?
7
+ generate
8
+
9
+ alias :method_name :operator
10
+
11
+ def register_macro(body, let = false)
12
+ Atomy::Macro.register(
13
+ @operator,
14
+ [@lhs, @rhs].collect do |n|
15
+ Atomy::Macro.macro_pattern n
16
+ end,
17
+ body,
18
+ let
19
+ )
20
+ end
21
+
22
+ def message_name
23
+ Atomy.namespaced(@namespace, @operator)
24
+ end
25
+
26
+ def prepare
27
+ resolve.expand
28
+ end
29
+
30
+ def bytecode(g)
31
+ pos(g)
32
+ @lhs.compile(g)
33
+ g.push_literal message_name.to_sym unless @namespace == "_"
34
+ @rhs.compile(g)
35
+ if @namespace == "_"
36
+ g.send @operator.to_sym, 1
37
+ else
38
+ g.send :atomy_send, 2
39
+ #g.call_custom method_name.to_sym, 1
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/ast/block.rb ADDED
@@ -0,0 +1,268 @@
1
+ module Atomy
2
+ module AST
3
+ class Block < Rubinius::AST::Iter
4
+ include NodeLike
5
+ extend SentientNode
6
+
7
+ children [:contents], [:arguments]
8
+ generate
9
+
10
+ def block_arguments
11
+ BlockArguments.new @arguments
12
+ end
13
+
14
+ def block_body
15
+ BlockBody.new @line, @contents
16
+ end
17
+
18
+ def body
19
+ InlinedBody.new @line, @contents
20
+ end
21
+
22
+ alias :caller :body
23
+
24
+ def bytecode(g)
25
+ pos(g)
26
+
27
+ state = g.state
28
+ state.scope.nest_scope self
29
+
30
+ blk = new_block_generator g, block_arguments
31
+
32
+ blk.push_state self
33
+ blk.state.push_super state.super
34
+ blk.state.push_eval state.eval
35
+
36
+ blk.state.push_name blk.name
37
+
38
+ # Push line info down.
39
+ pos(blk)
40
+
41
+ block_arguments.bytecode(blk)
42
+
43
+ blk.state.push_block
44
+ blk.push_modifiers
45
+ blk.break = nil
46
+ blk.next = nil
47
+ blk.redo = blk.new_label
48
+ blk.redo.set!
49
+
50
+ block_body.compile(blk)
51
+
52
+ blk.pop_modifiers
53
+ blk.state.pop_block
54
+ blk.ret
55
+ blk.close
56
+ blk.pop_state
57
+
58
+ blk.splat_index = block_arguments.splat_index
59
+ blk.local_count = local_count
60
+ blk.local_names = local_names
61
+
62
+ g.create_block blk
63
+
64
+ g.push_cpath_top
65
+ g.find_const :Proc
66
+ g.swap
67
+ g.send :__from_block__, 1
68
+ end
69
+
70
+ def as_message(send)
71
+ case send.receiver
72
+ when Send
73
+ if send.receiver.method_name == "[]"
74
+ dup.tap do |b|
75
+ b.arguments = send.receiver.arguments
76
+ end.as_message(send.receiver)
77
+ else
78
+ send.receiver.dup.tap do |s|
79
+ s.block = self
80
+ end
81
+ end
82
+ when Variable, Unquote,
83
+ Constant, ScopedConstant, ToplevelConstant
84
+ send.receiver = send.receiver.to_send
85
+ as_message(send)
86
+ when List
87
+ dup.tap do |b|
88
+ b.arguments = send.receiver.elements
89
+ end
90
+ else
91
+ unless send.method_name
92
+ raise "unknown receiver for block: #{send.receiver.inspect}"
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ class InlinedBody < Node
99
+ children [:expressions]
100
+ generate
101
+
102
+ attr_accessor :parent
103
+
104
+ def variables
105
+ @variables ||= {}
106
+ end
107
+
108
+ def local_count
109
+ @parent.local_names
110
+ end
111
+
112
+ def local_names
113
+ @parent.local_names
114
+ end
115
+
116
+ def allocate_slot
117
+ @parent.allocate_slot
118
+ end
119
+
120
+ def nest_scope(scope)
121
+ scope.parent = self
122
+ end
123
+
124
+ def search_local(name)
125
+ if variable = variables[name]
126
+ variable.nested_reference
127
+ else
128
+ @parent.search_local(name)
129
+ end
130
+ end
131
+
132
+ def pseudo_local(name)
133
+ if variable = variables[name]
134
+ variable.nested_reference
135
+ elsif reference = @parent.search_local(name)
136
+ reference.depth += 1
137
+ reference
138
+ end
139
+ end
140
+
141
+ def new_local(name)
142
+ variables[name] =
143
+ @parent.new_local(name + ":" + @parent.allocate_slot.to_s)
144
+ end
145
+
146
+ def new_nested_local(name)
147
+ @parent.new_local(name).nested_reference
148
+ end
149
+
150
+ def empty?
151
+ @expressions.empty?
152
+ end
153
+
154
+ def setup(g)
155
+ g.state.scope.nest_scope self
156
+
157
+ blk = g.state.block?
158
+ ens = g.state.ensure?
159
+ res = g.state.rescue?
160
+ lop = g.state.loop?
161
+ msn = g.state.masgn?
162
+
163
+ g.push_state self
164
+
165
+ g.state.push_block if blk
166
+ g.state.push_ensure if ens
167
+ g.state.push_rescue(res) if res
168
+ g.state.push_loop if lop
169
+ g.state.push_masgn if msn
170
+ end
171
+
172
+ def reset(g)
173
+ g.pop_state
174
+ end
175
+
176
+ def bytecode(g)
177
+ pos(g)
178
+
179
+ setup(g)
180
+
181
+ g.push_nil if empty?
182
+
183
+ @expressions.each_with_index do |node,idx|
184
+ g.pop unless idx == 0
185
+ node.compile(g)
186
+ end
187
+
188
+ reset(g)
189
+ end
190
+ end
191
+
192
+ class BlockArguments
193
+ attr_reader :arguments
194
+
195
+ def initialize(args)
196
+ @arguments = args.collect(&:to_pattern)
197
+ end
198
+
199
+ def bytecode(g)
200
+ return if @arguments.empty?
201
+
202
+ if @arguments.last.kind_of?(Patterns::BlockPass)
203
+ g.push_block_arg
204
+ @arguments.pop.deconstruct(g)
205
+ end
206
+
207
+ g.cast_for_splat_block_arg
208
+ @arguments.each do |a|
209
+ if a.kind_of?(Patterns::Splat)
210
+ a.pattern.deconstruct(g)
211
+ return
212
+ else
213
+ g.shift_array
214
+ a.match(g)
215
+ end
216
+ end
217
+ g.pop
218
+ end
219
+
220
+ def local_names
221
+ @arguments.collect { |a| a.local_names }.flatten
222
+ end
223
+
224
+ def size
225
+ @arguments.size
226
+ end
227
+
228
+ def locals
229
+ local_names.size
230
+ end
231
+
232
+ def required_args
233
+ size
234
+ end
235
+
236
+ def total_args
237
+ size
238
+ end
239
+
240
+ def splat_index
241
+ @arguments.each do |a,i|
242
+ return i if a.kind_of?(Patterns::Splat)
243
+ end
244
+ nil
245
+ end
246
+ end
247
+
248
+ class BlockBody < Node
249
+ children [:expressions]
250
+ generate
251
+
252
+ def empty?
253
+ @expressions.empty?
254
+ end
255
+
256
+ def bytecode(g)
257
+ pos(g)
258
+
259
+ g.push_nil if empty?
260
+
261
+ @expressions.each_with_index do |node,idx|
262
+ g.pop unless idx == 0
263
+ node.compile(g)
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end