apricot 0.0.1 → 0.0.2

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +1 -0
  5. data/Gemfile.lock +229 -11
  6. data/README.md +46 -29
  7. data/Rakefile +1 -1
  8. data/apricot.gemspec +7 -3
  9. data/benchmarks/factorial.rb +51 -0
  10. data/benchmarks/interpolate.rb +20 -0
  11. data/bin/apricot +5 -23
  12. data/examples/bot.apr +1 -4
  13. data/examples/cinch-bot.apr +3 -3
  14. data/examples/sinatra.apr +9 -0
  15. data/kernel/core.apr +124 -75
  16. data/kernel/repl.apr +37 -0
  17. data/lib/apricot.rb +7 -26
  18. data/lib/apricot/boot.rb +24 -0
  19. data/lib/apricot/code_loader.rb +108 -0
  20. data/lib/apricot/compiler.rb +265 -32
  21. data/lib/apricot/generator.rb +10 -3
  22. data/lib/apricot/identifier.rb +25 -10
  23. data/lib/apricot/list.rb +28 -41
  24. data/lib/apricot/macroexpand.rb +14 -8
  25. data/lib/apricot/misc.rb +2 -1
  26. data/lib/apricot/namespace.rb +20 -3
  27. data/lib/apricot/{parser.rb → reader.rb} +221 -194
  28. data/lib/apricot/repl.rb +67 -24
  29. data/lib/apricot/ruby_ext.rb +27 -16
  30. data/lib/apricot/scopes.rb +159 -0
  31. data/lib/apricot/seq.rb +43 -1
  32. data/lib/apricot/special_forms.rb +16 -695
  33. data/lib/apricot/special_forms/def.rb +32 -0
  34. data/lib/apricot/special_forms/do.rb +23 -0
  35. data/lib/apricot/special_forms/dot.rb +112 -0
  36. data/lib/apricot/special_forms/fn.rb +342 -0
  37. data/lib/apricot/special_forms/if.rb +31 -0
  38. data/lib/apricot/special_forms/let.rb +8 -0
  39. data/lib/apricot/special_forms/loop.rb +10 -0
  40. data/lib/apricot/special_forms/quote.rb +9 -0
  41. data/lib/apricot/special_forms/recur.rb +26 -0
  42. data/lib/apricot/special_forms/try.rb +146 -0
  43. data/lib/apricot/variables.rb +65 -0
  44. data/lib/apricot/version.rb +1 -1
  45. data/spec/compiler_spec.rb +53 -450
  46. data/spec/fn_spec.rb +206 -0
  47. data/spec/list_spec.rb +1 -1
  48. data/spec/reader_spec.rb +349 -0
  49. data/spec/spec_helper.rb +40 -4
  50. data/spec/special_forms_spec.rb +203 -0
  51. metadata +99 -133
  52. data/lib/apricot/ast.rb +0 -3
  53. data/lib/apricot/ast/identifier.rb +0 -111
  54. data/lib/apricot/ast/list.rb +0 -99
  55. data/lib/apricot/ast/literals.rb +0 -240
  56. data/lib/apricot/ast/node.rb +0 -45
  57. data/lib/apricot/ast/scopes.rb +0 -147
  58. data/lib/apricot/ast/toplevel.rb +0 -66
  59. data/lib/apricot/ast/variables.rb +0 -64
  60. data/lib/apricot/printers.rb +0 -12
  61. data/lib/apricot/stages.rb +0 -60
  62. data/spec/parser_spec.rb +0 -312
@@ -1,3 +0,0 @@
1
- %w[node literals identifier list scopes variables toplevel].each do |r|
2
- require "apricot/ast/#{r}"
3
- end
@@ -1,111 +0,0 @@
1
- module Apricot
2
- module AST
3
- class Identifier < Node
4
- def initialize(line, name)
5
- super(line)
6
- @id = Apricot::Identifier.intern(name)
7
- end
8
-
9
- def reference(g)
10
- @reference ||= if name == :self
11
- SelfReference.new
12
- elsif qualified?
13
- NamespaceReference.new(unqualified_name, ns)
14
- else
15
- g.scope.find_var(name)
16
- end
17
- end
18
-
19
- def name
20
- @id.name
21
- end
22
-
23
- def constant?
24
- @id.constant?
25
- end
26
-
27
- def qualified?
28
- @id.qualified?
29
- end
30
-
31
- def ns
32
- @id.ns
33
- end
34
-
35
- def unqualified_name
36
- @id.unqualified_name
37
- end
38
-
39
- def const_names
40
- @id.const_names
41
- end
42
-
43
- def bytecode(g)
44
- pos(g)
45
-
46
- if constant?
47
- g.push_const const_names.first
48
- const_names.drop(1).each {|n| g.find_const n }
49
- else
50
- reference(g).bytecode(g)
51
- end
52
- end
53
-
54
- # called by (def <identifier> <value>)
55
- def assign_bytecode(g, value)
56
- if constant?
57
- if const_names.length == 1
58
- g.push_scope
59
- else
60
- g.push_const const_names[0]
61
- const_names[1..-2].each {|n| g.find_const n }
62
- end
63
-
64
- g.push_literal const_names.last
65
- value.bytecode(g)
66
- g.send :const_set, 2
67
- else
68
- g.compile_error "Can't change the value of self" if name == :self
69
-
70
- g.push_const :Apricot
71
- g.send :current_namespace, 0
72
- g.push_literal name
73
- value.bytecode(g)
74
- g.send :set_var, 2
75
- end
76
- end
77
-
78
- def quote_bytecode(g)
79
- pos(g)
80
-
81
- g.push_const :Apricot
82
- g.find_const :Identifier
83
- g.push_literal name
84
- g.send :intern, 1
85
- end
86
-
87
- def meta(g)
88
- ref = reference(g)
89
- ref.is_a?(NamespaceReference) && ref.meta
90
- end
91
-
92
- def namespace_fn?(g)
93
- ref = reference(g)
94
- ref.is_a?(NamespaceReference) && ref.fn?
95
- end
96
-
97
- def module_method?(g)
98
- ref = reference(g)
99
- ref.is_a?(NamespaceReference) && ref.method?
100
- end
101
-
102
- def to_value
103
- @id
104
- end
105
-
106
- def node_equal?(other)
107
- self.name == other.name
108
- end
109
- end
110
- end
111
- end
@@ -1,99 +0,0 @@
1
- module Apricot::AST
2
- class List < Node
3
- attr_reader :elements
4
-
5
- def initialize(line, elements)
6
- super(line)
7
- @elements = elements
8
- end
9
-
10
- def bytecode(g, macroexpand = true)
11
- pos(g)
12
-
13
- if @elements.empty?
14
- quote_bytecode(g)
15
- return
16
- end
17
-
18
- callee = @elements.first
19
- args = @elements.drop(1)
20
-
21
- # Handle special forms such as def, let, fn, quote, etc
22
- if callee.is_a?(Identifier) && special = Apricot::SpecialForm[callee.name]
23
- special.bytecode(g, args)
24
- return
25
- end
26
-
27
- if macroexpand
28
- form = Node.from_value(Apricot.macroexpand(self.to_value), line)
29
-
30
- # Avoid recursing and macroexpanding again if expansion returns a list
31
- if form.is_a?(List)
32
- form.bytecode(g, false)
33
- else
34
- form.bytecode(g)
35
- end
36
- return
37
- end
38
-
39
- # Handle (foo ...) and (Foo/bar ...) calls
40
- if callee.is_a?(Identifier)
41
- meta = callee.meta(g)
42
-
43
- # Handle inlinable function calls
44
- if meta && meta[:inline] && (!meta[:'inline-arities'] ||
45
- meta[:'inline-arities'].apricot_call(args.length))
46
- # Apply the inliner macro to the arguments and compile the result.
47
- inliner = meta[:inline]
48
- args.map!(&:to_value)
49
-
50
- begin
51
- inlined_form = inliner.apricot_call(*args)
52
- rescue => e
53
- g.compile_error "Inliner macro for '#{callee.name}' raised an exception:\n #{e}"
54
- end
55
-
56
- Node.from_value(inlined_form, line).bytecode(g)
57
- return
58
- elsif callee.namespace_fn?(g) || callee.module_method?(g)
59
- ns_id = Apricot::Identifier.intern(callee.ns.name)
60
- g.push_const ns_id.const_names.first
61
- ns_id.const_names.drop(1).each {|n| g.find_const(n) }
62
-
63
- args.each {|arg| arg.bytecode(g) }
64
- g.send callee.unqualified_name, args.length
65
- return
66
- end
67
- end
68
-
69
- # Handle everything else
70
- callee.bytecode(g)
71
- args.each {|arg| arg.bytecode(g) }
72
- g.send :apricot_call, args.length
73
- end
74
-
75
- def quote_bytecode(g)
76
- g.push_const :Apricot
77
- g.find_const :List
78
-
79
- if @elements.empty?
80
- g.find_const :EmptyList
81
- else
82
- @elements.each {|e| e.quote_bytecode(g) }
83
- g.send :[], @elements.length
84
- end
85
- end
86
-
87
- def to_value
88
- Apricot::List[*@elements.map(&:to_value)]
89
- end
90
-
91
- def node_equal?(other)
92
- self.elements == other.elements
93
- end
94
-
95
- def [](*i)
96
- @elements[*i]
97
- end
98
- end
99
- end
@@ -1,240 +0,0 @@
1
- module Apricot::AST
2
- class BasicLiteral < Node
3
- def quote_bytecode(g)
4
- bytecode(g)
5
- end
6
- end
7
-
8
- class Literal < BasicLiteral
9
- attr_reader :value
10
-
11
- def initialize(line, value)
12
- super(line)
13
- @value = value
14
- end
15
-
16
- def bytecode(g)
17
- pos(g)
18
- g.push @value
19
- end
20
-
21
- def to_value
22
- case @value
23
- when :true then true
24
- when :false then false
25
- when :nil then nil
26
- else @value
27
- end
28
- end
29
-
30
- def node_equal?(other)
31
- self.value == other.value
32
- end
33
- end
34
-
35
- def self.new_integer(line, value)
36
- case value
37
- when Bignum
38
- BignumLiteral.new(line, value)
39
- when Fixnum
40
- Literal.new(line, value)
41
- else
42
- raise TypeError, "#{value} is not an integer"
43
- end
44
- end
45
-
46
- class BignumLiteral < Literal
47
- def bytecode(g)
48
- pos(g)
49
- g.push_unique_literal @value
50
- end
51
- end
52
-
53
- class FloatLiteral < Literal
54
- def bytecode(g)
55
- pos(g)
56
- g.push_unique_literal @value
57
- end
58
- end
59
-
60
- class SymbolLiteral < Literal
61
- def bytecode(g)
62
- pos(g)
63
- g.push_literal @value
64
- end
65
- end
66
-
67
- class StringLiteral < Literal
68
- def bytecode(g)
69
- pos(g)
70
- g.push_literal @value
71
- g.string_dup # Duplicate string to avoid mutating the literal
72
- end
73
- end
74
-
75
- class RationalLiteral < BasicLiteral
76
- attr_reader :numerator, :denominator
77
-
78
- def initialize(line, numerator, denominator)
79
- super(line)
80
- @numerator = numerator
81
- @denominator = denominator
82
- end
83
-
84
- def bytecode(g)
85
- pos(g)
86
-
87
- # A rational literal should only be converted to a Rational the first
88
- # time it is encountered. We push a literal nil here, and then overwrite
89
- # the literal value with the created Rational if it is nil, i.e. the
90
- # first time only. Subsequent encounters will use the previously created
91
- # Rational. This idea was copied from Rubinius::AST::RegexLiteral.
92
- idx = g.add_literal(nil)
93
- g.push_literal_at idx
94
- g.dup
95
- g.is_nil
96
-
97
- lbl = g.new_label
98
- g.gif lbl
99
- g.pop
100
- g.push_self
101
- g.push @numerator
102
- g.push @denominator
103
- g.send :Rational, 2, true
104
- g.set_literal idx
105
- lbl.set!
106
- end
107
-
108
- def to_value
109
- Rational(@numerator, @denominator)
110
- end
111
-
112
- def node_equal?(other)
113
- self.numerator == other.numerator && self.denominator == other.denominator
114
- end
115
- end
116
-
117
- class RegexLiteral < BasicLiteral
118
- attr_accessor :pattern, :options
119
-
120
- def initialize(line, pattern, options)
121
- super(line)
122
- @pattern = pattern
123
- @options = options
124
- end
125
-
126
- def bytecode(g)
127
- pos(g)
128
-
129
- idx = g.add_literal(nil)
130
- g.push_literal_at idx
131
- g.dup
132
- g.is_nil
133
-
134
- lbl = g.new_label
135
- g.gif lbl
136
- g.pop
137
- g.push_const :Regexp
138
- g.push_literal @pattern
139
- g.push @options
140
- g.send :new, 2
141
- g.set_literal idx
142
- lbl.set!
143
- end
144
-
145
- def to_value
146
- Regexp.new(@pattern, @options)
147
- end
148
-
149
- def node_equal?(other)
150
- self.pattern == other.pattern && self.options == other.options
151
- end
152
- end
153
-
154
- class CollectionLiteral < Node
155
- attr_reader :elements
156
-
157
- def initialize(line, elements)
158
- super(line)
159
- @elements = elements
160
- end
161
-
162
- def quote_bytecode(g)
163
- bytecode(g, true)
164
- end
165
-
166
- def node_equal?(other)
167
- self.elements == other.elements
168
- end
169
-
170
- def [](*i)
171
- @elements[*i]
172
- end
173
- end
174
-
175
- class ArrayLiteral < CollectionLiteral
176
- def bytecode(g, quoted = false)
177
- pos(g)
178
-
179
- @elements.each {|e| quoted ? e.quote_bytecode(g) : e.bytecode(g) }
180
- g.make_array @elements.length
181
- end
182
-
183
- def to_value
184
- @elements.map(&:to_value)
185
- end
186
- end
187
-
188
- class HashLiteral < CollectionLiteral
189
- def bytecode(g, quoted = false)
190
- pos(g)
191
-
192
- # Create a new Hash
193
- g.push_const :Hash
194
- g.push(@elements.length / 2)
195
- g.send :new_from_literal, 1
196
-
197
- # Add keys and values
198
- @elements.each_slice(2) do |k, v|
199
- g.dup # The Hash
200
-
201
- if quoted
202
- k.quote_bytecode(g)
203
- v.quote_bytecode(g)
204
- else
205
- k.bytecode(g)
206
- v.bytecode(g)
207
- end
208
-
209
- g.send :[]=, 2
210
- g.pop # []= leaves v on the stack
211
- end
212
- end
213
-
214
- def to_value
215
- h = {}
216
- @elements.each_slice(2) {|k,v| h[k.to_value] = v.to_value }
217
- h
218
- end
219
- end
220
-
221
- class SetLiteral < CollectionLiteral
222
- def bytecode(g, quoted = false)
223
- pos(g)
224
-
225
- g.push_const :Set
226
- g.send :new, 0 # TODO: Inline this new?
227
-
228
- @elements.each do |e|
229
- quoted ? e.quote_bytecode(g) : e.bytecode(g)
230
- g.send :add, 1
231
- end
232
- end
233
-
234
- def to_value
235
- s = Set.new
236
- @elements.each {|e| s << e.to_value }
237
- s
238
- end
239
- end
240
- end