apricot 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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