apricot 0.0.1

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