apricot 0.0.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 (49) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +26 -0
  7. data/README.md +90 -0
  8. data/Rakefile +9 -0
  9. data/apricot.gemspec +22 -0
  10. data/bin/apricot +58 -0
  11. data/examples/bot.apr +23 -0
  12. data/examples/cinch-bot.apr +12 -0
  13. data/examples/hanoi.apr +10 -0
  14. data/examples/hello.apr +1 -0
  15. data/examples/plot.apr +28 -0
  16. data/examples/quine.apr +1 -0
  17. data/kernel/core.apr +928 -0
  18. data/lib/apricot/ast/identifier.rb +111 -0
  19. data/lib/apricot/ast/list.rb +99 -0
  20. data/lib/apricot/ast/literals.rb +240 -0
  21. data/lib/apricot/ast/node.rb +45 -0
  22. data/lib/apricot/ast/scopes.rb +147 -0
  23. data/lib/apricot/ast/toplevel.rb +66 -0
  24. data/lib/apricot/ast/variables.rb +64 -0
  25. data/lib/apricot/ast.rb +3 -0
  26. data/lib/apricot/compiler.rb +55 -0
  27. data/lib/apricot/cons.rb +27 -0
  28. data/lib/apricot/errors.rb +38 -0
  29. data/lib/apricot/generator.rb +15 -0
  30. data/lib/apricot/identifier.rb +91 -0
  31. data/lib/apricot/list.rb +96 -0
  32. data/lib/apricot/macroexpand.rb +47 -0
  33. data/lib/apricot/misc.rb +11 -0
  34. data/lib/apricot/namespace.rb +59 -0
  35. data/lib/apricot/parser.rb +541 -0
  36. data/lib/apricot/printers.rb +12 -0
  37. data/lib/apricot/repl.rb +254 -0
  38. data/lib/apricot/ruby_ext.rb +254 -0
  39. data/lib/apricot/seq.rb +44 -0
  40. data/lib/apricot/special_forms.rb +735 -0
  41. data/lib/apricot/stages.rb +60 -0
  42. data/lib/apricot/version.rb +3 -0
  43. data/lib/apricot.rb +30 -0
  44. data/spec/compiler_spec.rb +499 -0
  45. data/spec/identifier_spec.rb +58 -0
  46. data/spec/list_spec.rb +96 -0
  47. data/spec/parser_spec.rb +312 -0
  48. data/spec/spec_helper.rb +10 -0
  49. metadata +188 -0
@@ -0,0 +1,111 @@
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
@@ -0,0 +1,99 @@
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
@@ -0,0 +1,240 @@
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
@@ -0,0 +1,45 @@
1
+ module Apricot
2
+ module AST
3
+ class Node
4
+ attr_reader :line
5
+
6
+ def initialize(line)
7
+ @line = line
8
+ end
9
+
10
+ def pos(g)
11
+ g.set_line(@line)
12
+ end
13
+
14
+ def ==(other)
15
+ return true if self.equal?(other)
16
+ return false unless self.class == other.class
17
+ self.node_equal?(other)
18
+ end
19
+
20
+ def self.from_value(val, line = 0)
21
+ case val
22
+ when true then Literal.new(line, :true)
23
+ when false then Literal.new(line, :false)
24
+ when nil then Literal.new(line, :nil)
25
+ when Integer then AST.new_integer(line, val)
26
+ when Symbol then SymbolLiteral.new(line, val)
27
+ when Float then FloatLiteral.new(line, val)
28
+ when String then StringLiteral.new(line, val)
29
+ when Rational then RationalLiteral.new(line, val.numerator, val.denominator)
30
+ when Regexp then RegexLiteral.new(line, val.source, val.options)
31
+ when Array then ArrayLiteral.new(line, val.map {|x| from_value x, line})
32
+ when Set then SetLiteral.new(line, val.map {|x| from_value x, line})
33
+ when Apricot::Identifier then Identifier.new(line, val.name)
34
+ when Apricot::Seq then List.new(line, val.map {|x| from_value x, line})
35
+ when Hash
36
+ elems = []
37
+ val.each_pair {|k,v| elems << from_value(k, line) << from_value(v, line) }
38
+ HashLiteral.new(line, elems)
39
+ else
40
+ raise TypeError, "No AST node for #{val} (#{val.class})"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,147 @@
1
+ module Apricot
2
+ module AST
3
+ # This is a scope with real local variable storage, i.e. it is part of a
4
+ # block of code like a fn or the top level program. Let scopes do not have
5
+ # storage and must ask for storage from one of these.
6
+ module StorageScope
7
+ def variable_names
8
+ @variable_names ||= []
9
+ end
10
+
11
+ def store_new_local(name)
12
+ slot = next_slot
13
+ variable_names << name
14
+ slot
15
+ end
16
+
17
+ def next_slot
18
+ variable_names.size
19
+ end
20
+
21
+ def local_count
22
+ variable_names.size
23
+ end
24
+
25
+ def local_names
26
+ variable_names
27
+ end
28
+ end
29
+
30
+ class Scope
31
+ attr_reader :parent, :variables
32
+ # The loop label stores the code location where a (recur) form should
33
+ # jump to. The secondary loop label is used in the case of recur in a fn
34
+ # overload with variadic arguments. If the array passed for the variadic
35
+ # arguments in the recur is empty, it should instead jump to the
36
+ # matching non-variadic overload, if applicable.
37
+ attr_accessor :loop_label, :secondary_loop_label
38
+
39
+ def initialize(parent)
40
+ @parent = parent
41
+ @variables = {}
42
+ @loop_label = nil
43
+ end
44
+ end
45
+
46
+ class FnScope < Scope
47
+ attr_reader :name, :self_reference
48
+
49
+ def initialize(parent, name)
50
+ super(parent)
51
+
52
+ if name
53
+ @name = name
54
+ name_slot = @parent.store_new_local(name)
55
+ @self_reference = LocalReference.new(name_slot, 1)
56
+ end
57
+ end
58
+
59
+ # An identifier or a nested scope is looking up a variable. If the
60
+ # variable is found here, return a reference to it. Otherwise look it up
61
+ # on the parent and increment its depth because it is beyond the bounds
62
+ # of the current block of code (fn).
63
+ def find_var(name, depth = 0)
64
+ return @self_reference if name == @name
65
+
66
+ @parent.find_var(name, depth + 1)
67
+ end
68
+
69
+ # A (recur) is looking for a recursion target (ie. a loop or a fn
70
+ # overload scope).
71
+ def find_recur_target
72
+ @parent.find_recur_target
73
+ end
74
+ end
75
+
76
+ class OverloadScope < Scope
77
+ include StorageScope
78
+
79
+ attr_accessor :splat
80
+ alias_method :splat?, :splat
81
+
82
+ attr_accessor :block_arg
83
+
84
+ def initialize(parent_fn)
85
+ super(parent_fn)
86
+ end
87
+
88
+ # An identifier or a nested scope is looking up a variable. If the
89
+ # variable is found here, return a reference to it. Otherwise look it up
90
+ # on the parent (a fn). Don't increase the depth, the lookup on the fn
91
+ # will do that, and if we do it twice then the generated
92
+ # push_local_depth instructions look up too many scopes.
93
+ def find_var(name, depth = 0)
94
+ if slot = @variables[name]
95
+ LocalReference.new(slot, depth)
96
+ else
97
+ @parent.find_var(name, depth)
98
+ end
99
+ end
100
+
101
+ # Create a new local on the current level.
102
+ def new_local(name)
103
+ name = name.name if name.is_a? Identifier
104
+ @variables[name] = store_new_local(name)
105
+ end
106
+
107
+ # A (recur) is looking for a recursion target. This, being a fn
108
+ # overload, is one.
109
+ def find_recur_target
110
+ self
111
+ end
112
+ end
113
+
114
+ # The let scope doesn't have real storage for locals. It stores its locals
115
+ # on the nearest enclosing real scope, which is any separate block of code
116
+ # such as a fn, defn, defmacro or the top level of the program.
117
+ class LetScope < Scope
118
+ # An identifier or a nested scope is looking up a variable.
119
+ def find_var(name, depth = 0)
120
+ if slot = @variables[name]
121
+ LocalReference.new(slot, depth)
122
+ else
123
+ @parent.find_var(name, depth)
124
+ end
125
+ end
126
+
127
+ # Create a new local on the current level, with storage on the nearest
128
+ # enclosing real scope.
129
+ def new_local(name)
130
+ name = name.name if name.is_a? Identifier
131
+ @variables[name] = @parent.store_new_local(name)
132
+ end
133
+
134
+ # A deeper let is asking for a new local slot. Pass it along to the
135
+ # parent so it eventually reaches a real scope.
136
+ def store_new_local(name)
137
+ @parent.store_new_local(name)
138
+ end
139
+
140
+ # A (recur) is looking for a recursion target. This is one only if it is
141
+ # a (loop) form.
142
+ def find_recur_target
143
+ loop_label ? self : @parent.find_recur_target
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,66 @@
1
+ module Apricot
2
+ module AST
3
+ class TopLevel < Node
4
+ include StorageScope
5
+
6
+ attr_reader :elements, :file
7
+
8
+ def initialize(elements, file, line = 1, evaluate = false)
9
+ @elements = elements
10
+ @file = file
11
+ @line = line
12
+ @evaluate = evaluate
13
+ end
14
+
15
+ def bytecode(g)
16
+ g.name = :__top_level__
17
+ g.file = @file.to_sym
18
+
19
+ g.scopes << self
20
+
21
+ pos(g)
22
+
23
+ if @elements.empty?
24
+ g.push_nil
25
+ else
26
+ @elements.each_with_index do |e, i|
27
+ g.pop unless i == 0
28
+ e.bytecode(g)
29
+ # We evaluate top level forms as we generate the bytecode for them
30
+ # so macros defined in a file can be used immediately after the
31
+ # definition.
32
+ Apricot::Compiler.eval_node(e, @file) if @evaluate
33
+ end
34
+ end
35
+
36
+ g.ret
37
+
38
+ g.scopes.pop
39
+
40
+ g.local_count = local_count
41
+ g.local_names = local_names
42
+ end
43
+
44
+ # A nested scope is looking up a variable. There are no local variables
45
+ # at the top level, so look up the variable on the current namespace.
46
+ def find_var(name, depth = nil)
47
+ # Ignore depth, it has no bearing on namespace lookups.
48
+ NamespaceReference.new(name)
49
+ end
50
+
51
+ def node_equal?(other)
52
+ self.file == other.file && self.elements == other.elements
53
+ end
54
+
55
+ def [](*i)
56
+ @elements[*i]
57
+ end
58
+
59
+ # A (recur) is looking for a recursion target. Since this is the top
60
+ # level, which has no parent, the lookup has failed.
61
+ def find_recur_target
62
+ nil
63
+ end
64
+ end
65
+ end
66
+ end