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,64 @@
1
+ module Apricot
2
+ module AST
3
+ class LocalReference
4
+ attr_reader :slot, :depth
5
+
6
+ def initialize(slot, depth = 0)
7
+ @slot = slot
8
+ @depth = depth
9
+ end
10
+
11
+ def bytecode(g)
12
+ if @depth == 0
13
+ g.push_local @slot
14
+ else
15
+ g.push_local_depth @depth, @slot
16
+ end
17
+ end
18
+ end
19
+
20
+ class NamespaceReference
21
+ def initialize(name, ns = nil)
22
+ @name = name
23
+ @ns = ns || Apricot.current_namespace
24
+ end
25
+
26
+ def bytecode(g)
27
+ if @ns.is_a?(Namespace) && !@ns.vars.include?(@name)
28
+ g.compile_error "Unable to resolve name #{@name} in namespace #{@ns}"
29
+ end
30
+
31
+ ns_id = Apricot::Identifier.intern(@ns.name)
32
+ g.push_const ns_id.const_names.first
33
+ ns_id.const_names.drop(1).each {|n| g.find_const(n) }
34
+
35
+ g.push_literal @name
36
+
37
+ if @ns.is_a? Namespace
38
+ g.send :get_var, 1
39
+ else # @ns is a regular Ruby module
40
+ g.send :method, 1
41
+ end
42
+ end
43
+
44
+ def meta
45
+ @ns.is_a?(Namespace) && @ns.vars[@name] && @ns.vars[@name].apricot_meta
46
+ end
47
+
48
+ def fn?
49
+ @ns.is_a?(Namespace) && @ns.fns.include?(@name)
50
+ end
51
+
52
+ def method?
53
+ !@ns.is_a?(Namespace) && @ns.respond_to?(@name)
54
+ end
55
+ end
56
+
57
+ # For the 'self' identifier. Just like Ruby's 'self'.
58
+ class SelfReference
59
+ def bytecode(g)
60
+ g.push_self
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ %w[node literals identifier list scopes variables toplevel].each do |r|
2
+ require "apricot/ast/#{r}"
3
+ end
@@ -0,0 +1,55 @@
1
+ module Apricot
2
+ class Compiler < Rubinius::Compiler
3
+ def self.compile(file, output = nil, debug = false)
4
+ compiler = new :apricot_file, :compiled_file
5
+
6
+ compiler.parser.input file
7
+ compiler.packager.print(BytecodePrinter) if debug
8
+ compiler.writer.name = output || Rubinius::Compiler.compiled_name(file)
9
+
10
+ prepare_compiled_code compiler.run
11
+ end
12
+
13
+ def self.compile_string(code, file = nil, line = 1, debug = false)
14
+ compiler = new :apricot_string, :compiled_method
15
+
16
+ compiler.parser.input(code, file || "(none)", line)
17
+ compiler.packager.print(BytecodePrinter) if debug
18
+
19
+ prepare_compiled_code compiler.run
20
+ end
21
+
22
+ def self.compile_node(node, file = "(none)", line = 1, debug = false)
23
+ compiler = new :apricot_bytecode, :compiled_method
24
+
25
+ compiler.generator.input AST::TopLevel.new([node], file, line, false)
26
+ compiler.packager.print(BytecodePrinter) if debug
27
+
28
+ prepare_compiled_code compiler.run
29
+ end
30
+
31
+ def self.compile_form(node, file = "(none)", line = 1, debug = false)
32
+ compile_node(AST::Node.from_value(node, line), file, line, debug)
33
+ end
34
+
35
+ def self.prepare_compiled_code(cc)
36
+ cc.scope = Rubinius::ConstantScope.new(Object)
37
+ cc
38
+ end
39
+
40
+ def self.eval(code, file = "(eval)", line = 1, debug = false)
41
+ cc = compile_string(code, file, line, debug)
42
+ Rubinius.run_script cc
43
+ end
44
+
45
+ def self.eval_form(form, file = "(eval)", line = 1, debug = false)
46
+ cc = compile_form(form, file, line, debug)
47
+ Rubinius.run_script cc
48
+ end
49
+
50
+ def self.eval_node(node, file = "(eval)", line = 1, debug = false)
51
+ cc = compile_node(node, file, line, debug)
52
+ Rubinius.run_script cc
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ module Apricot
2
+ class Cons
3
+ include Seq
4
+
5
+ def initialize(head, tail)
6
+ @head = head
7
+ @tail = tail.to_seq
8
+ end
9
+
10
+ def first
11
+ @head
12
+ end
13
+
14
+ def next
15
+ if @tail
16
+ @tail
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ def each
23
+ yield first
24
+ @tail.each {|x| yield x }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ module Apricot
2
+ class SyntaxError < StandardError
3
+ attr_reader :filename, :line, :msg
4
+
5
+ def initialize(filename, line, msg, incomplete = false)
6
+ @filename = filename
7
+ @line = line
8
+ @msg = msg
9
+ @incomplete = incomplete
10
+ end
11
+
12
+ def incomplete?
13
+ @incomplete
14
+ end
15
+
16
+ def to_s
17
+ "#{@filename}:#{@line}: #{@msg}"
18
+ end
19
+ end
20
+
21
+ class CompileError < StandardError
22
+ attr_reader :filename, :line, :msg
23
+
24
+ def initialize(filename, line, msg)
25
+ @filename = filename
26
+ @line = line
27
+ @msg = msg
28
+ end
29
+
30
+ def to_s
31
+ "#{@filename}:#{@line}: #{@msg}"
32
+ end
33
+ end
34
+
35
+ def self.compile_error(file, line, msg)
36
+ raise CompileError.new(file, line, msg)
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ module Apricot
2
+ class Generator < Rubinius::Generator
3
+ def scopes
4
+ @scopes ||= []
5
+ end
6
+
7
+ def scope
8
+ @scopes.last
9
+ end
10
+
11
+ def compile_error(msg)
12
+ raise CompileError.new(file, line, msg)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,91 @@
1
+ module Apricot
2
+ class Identifier
3
+ attr_reader :name, :ns, :unqualified_name
4
+
5
+ @table = {}
6
+
7
+ def self.intern(name)
8
+ name = name.to_sym
9
+ @table[name] ||= new(name)
10
+ end
11
+
12
+ private_class_method :new
13
+
14
+ def initialize(name)
15
+ @name = name
16
+
17
+ if @name =~ /\A(?:[A-Z]\w*::)*[A-Z]\w*\z/
18
+ @constant = true
19
+ @const_names = @name.to_s.split('::').map(&:to_sym)
20
+ elsif @name =~ /\A(.+?)\/(.+)\z/
21
+ @qualified = true
22
+ ns_id = Identifier.intern($1)
23
+ raise 'namespace in identifier must be a constant' unless ns_id.constant?
24
+
25
+ @ns = ns_id.const_names.reduce(Object) do |mod, name|
26
+ mod.const_get(name)
27
+ end
28
+
29
+ @unqualified_name = $2.to_sym
30
+ else
31
+ @ns = Apricot.current_namespace
32
+ @unqualified_name = name
33
+ end
34
+ end
35
+
36
+ def qualified?
37
+ @qualified
38
+ end
39
+
40
+ def unqualified_name
41
+ @unqualified_name
42
+ end
43
+
44
+ def constant?
45
+ @constant
46
+ end
47
+
48
+ def const_names
49
+ raise "#{@name} is not a constant" unless constant?
50
+ @const_names
51
+ end
52
+
53
+ # Copying Identifiers is not allowed.
54
+ def initialize_copy(other)
55
+ raise TypeError, "copy of #{self.class} is not allowed"
56
+ end
57
+
58
+ private :initialize_copy
59
+
60
+ alias_method :==, :equal?
61
+ alias_method :eql?, :equal?
62
+
63
+ def hash
64
+ @name.hash
65
+ end
66
+
67
+ def inspect
68
+ case @name
69
+ when :true, :false, :nil, /\A(?:\+|-)?\d/
70
+ # Use arbitrary identifier syntax for identifiers that would otherwise
71
+ # be parsed as keywords or numbers
72
+ str = @name.to_s.gsub(/(\\.)|\|/) { $1 || '\|' }
73
+ "#|#{str}|"
74
+ when /\A#{Apricot::Parser::IDENTIFIER}+\z/
75
+ @name.to_s
76
+ else
77
+ str = @name.to_s.inspect[1..-2]
78
+ str.gsub!(/(\\.)|\|/) { $1 || '\|' }
79
+ "#|#{str}|"
80
+ end
81
+ end
82
+
83
+ def to_s
84
+ @name.to_s
85
+ end
86
+
87
+ def to_sym
88
+ @name
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,96 @@
1
+ module Apricot
2
+ # A linked list implementation representing (a b c) syntax in Apricot
3
+ class List
4
+ include Seq
5
+
6
+ def self.[](*args)
7
+ list = EmptyList
8
+ args.reverse_each do |arg|
9
+ list = list.cons(arg)
10
+ end
11
+ list
12
+ end
13
+
14
+ attr_reader :head, :tail
15
+
16
+ def initialize(head, tail)
17
+ @head = head
18
+ @tail = tail
19
+ end
20
+
21
+ def cons(x)
22
+ List.new(x, self)
23
+ end
24
+
25
+ def each
26
+ list = self
27
+ until list.empty?
28
+ yield list.head
29
+ list = list.tail
30
+ end
31
+ end
32
+
33
+ def ==(other)
34
+ return true if self.equal? other
35
+ return false unless other.is_a? List
36
+
37
+ list = self
38
+
39
+ until list.empty?
40
+ return false if other.empty? || list.head != other.head
41
+
42
+ list = list.tail
43
+ other = other.tail
44
+ end
45
+
46
+ other.empty?
47
+ end
48
+
49
+ alias_method :eql?, :==
50
+
51
+ def hash
52
+ hashes = map {|x| x.hash }
53
+ hashes.reduce(hashes.size) {|acc,hash| acc ^ hash }
54
+ end
55
+
56
+ def empty?
57
+ !@tail
58
+ end
59
+
60
+ def initialize_copy(other)
61
+ super
62
+ @tail = other.tail.dup if other.tail && !other.tail.empty?
63
+ end
64
+
65
+ private :initialize_copy
66
+
67
+ def to_list
68
+ self
69
+ end
70
+
71
+ def first
72
+ empty? ? nil : @head
73
+ end
74
+
75
+ def next
76
+ @tail.empty? ? nil : @tail
77
+ end
78
+
79
+ def to_seq
80
+ empty? ? nil : self
81
+ end
82
+
83
+ def inspect
84
+ return '()' if empty?
85
+
86
+ str = '('
87
+ each {|x| str << x.apricot_inspect << ' ' }
88
+ str.chop!
89
+ str << ')'
90
+ end
91
+
92
+ alias_method :to_s, :inspect
93
+
94
+ EmptyList = new(nil, nil)
95
+ end
96
+ end
@@ -0,0 +1,47 @@
1
+ module Apricot
2
+ def self.macroexpand(form)
3
+ ex = macroexpand_1(form)
4
+ ex.equal?(form) ? ex : macroexpand(ex)
5
+ end
6
+
7
+ def self.macroexpand_1(form)
8
+ return form unless form.is_a? List
9
+
10
+ callee = form.first
11
+ return form unless callee.is_a?(Identifier) && !callee.constant?
12
+
13
+ name = callee.name
14
+ name_s = name.to_s
15
+ args = form.tail
16
+
17
+ # Handle the (.method receiver args*) send expression form
18
+ if name.length > 1 && name_s != '..' && name_s.start_with?('.')
19
+ raise ArgumentError, "Too few arguments to send expression, expecting (.method receiver ...)" if args.empty?
20
+
21
+ dot = Identifier.intern(:'.')
22
+ method = Identifier.intern(name_s[1..-1])
23
+ return List[dot, args.first, method, *args.tail]
24
+ end
25
+
26
+ # Handle the (Class. args*) shorthand new form
27
+ if name.length > 1 && name_s != '..' && name_s.end_with?('.')
28
+ dot = Identifier.intern(:'.')
29
+ klass = Identifier.intern(name_s[0..-2])
30
+ new = Identifier.intern(:new)
31
+ return List[dot, klass, new, *args]
32
+ end
33
+
34
+ # Handle defined macros
35
+ if callee.ns.is_a?(Namespace) && callee.ns.vars.include?(callee.unqualified_name)
36
+ potential_macro = callee.ns.get_var(callee.unqualified_name)
37
+ meta = potential_macro.apricot_meta
38
+
39
+ if meta && meta[:macro]
40
+ return potential_macro.call(*args)
41
+ end
42
+ end
43
+
44
+ # Default case
45
+ form
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ # This file contains some things that are used in a bunch of places and don't
2
+ # fit anywhere in particular.
3
+
4
+ module Apricot
5
+ # TODO: Should this counter be thread-local?
6
+ @gensym = 0
7
+
8
+ def self.gensym(prefix = 'g')
9
+ :"#{prefix}__#{@gensym += 1}"
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Apricot
2
+ class Namespace < Module
3
+ def self.find_or_create(constant)
4
+ ns = constant.const_names.reduce(Object) do |mod, name|
5
+ if mod.const_defined? name
6
+ next_mod = mod.const_get name
7
+ raise TypeError, "#{mod}::#{name} (#{next_mod}) is not a Module" unless next_mod.is_a? Module
8
+ next_mod
9
+ else
10
+ mod.const_set(name, Namespace.new)
11
+ end
12
+ end
13
+
14
+ raise TypeError, "#{constant.name} is not a Namespace" unless ns.is_a? Namespace
15
+
16
+ ns
17
+ end
18
+
19
+ attr_reader :vars, :fns
20
+
21
+ def initialize
22
+ @vars = {}
23
+ @fns = Set[]
24
+ end
25
+
26
+ def set_var(name, val)
27
+ @vars[name] = val
28
+
29
+ val = val.to_proc if val.is_a? Method
30
+
31
+ if val.is_a?(Proc) && (@fns.include?(name) || !self.respond_to?(name))
32
+ @fns.add name
33
+ define_singleton_method(name, val)
34
+ elsif @fns.include?(name)
35
+ @fns.delete name
36
+ singleton_class.send(:undef_method, name)
37
+ end
38
+
39
+ val
40
+ end
41
+
42
+ def get_var(name)
43
+ # raise may be a function defined on the namespace so we need to
44
+ # explicitly call the Ruby raise method.
45
+ Kernel.raise NameError, "Undefined variable '#{name}' on #{self}" unless @vars.include? name
46
+ @vars[name]
47
+ end
48
+ end
49
+
50
+ class << self
51
+ def current_namespace
52
+ Core.get_var(:"*ns*")
53
+ end
54
+
55
+ def current_namespace=(ns)
56
+ Core.set_var(:"*ns*", ns)
57
+ end
58
+ end
59
+ end