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,45 +0,0 @@
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
@@ -1,147 +0,0 @@
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
@@ -1,66 +0,0 @@
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
@@ -1,64 +0,0 @@
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
@@ -1,12 +0,0 @@
1
- module Apricot
2
- class Compiler
3
- class BytecodePrinter < Rubinius::Compiler::Printer
4
- def run
5
- puts @input.decode
6
-
7
- @output = @input
8
- run_next
9
- end
10
- end
11
- end
12
- end
@@ -1,60 +0,0 @@
1
- module Apricot
2
- class Compiler
3
- class Generator < Rubinius::Compiler::Stage
4
- stage :apricot_bytecode
5
- next_stage Rubinius::Compiler::Encoder
6
-
7
- def initialize(compiler, last)
8
- super
9
- compiler.generator = self
10
- end
11
-
12
- def run
13
- @output = Apricot::Generator.new
14
- @input.bytecode @output
15
- @output.close
16
- run_next
17
- end
18
- end
19
-
20
- class Parser < Rubinius::Compiler::Stage
21
- def initialize(compiler, last)
22
- super
23
- compiler.parser = self
24
- end
25
-
26
- def run
27
- @output = parse
28
- run_next
29
- end
30
- end
31
-
32
- class FileParser < Parser
33
- stage :apricot_file
34
- next_stage Generator
35
-
36
- def input(file)
37
- @file = file
38
- end
39
-
40
- def parse
41
- Apricot::Parser.parse_file(@file)
42
- end
43
- end
44
-
45
- class StringParser < Parser
46
- stage :apricot_string
47
- next_stage Generator
48
-
49
- def input(code, file = "(none)", line = 1)
50
- @input = code
51
- @file = file
52
- @line = line
53
- end
54
-
55
- def parse
56
- Apricot::Parser.parse_string(@input, @file, @line)
57
- end
58
- end
59
- end
60
- end
@@ -1,312 +0,0 @@
1
- describe Apricot::Parser do
2
- def parse(s)
3
- @ast = described_class.parse_string(s, "(spec)")
4
- @first = @ast.elements.first
5
- @ast.elements
6
- end
7
-
8
- def parse_one(s, klass)
9
- parse(s).length.should == 1
10
- @first.should be_a(klass)
11
- @first
12
- end
13
-
14
- def expect_syntax_error(s)
15
- expect { parse(s) }.to raise_error(Apricot::SyntaxError)
16
- end
17
-
18
- it 'parses nothing' do
19
- parse('').should be_empty
20
- end
21
-
22
- it 'skips whitespace' do
23
- parse(" \n\t,").should be_empty
24
- end
25
-
26
- it 'skips comments' do
27
- parse('; example').should be_empty
28
- parse('#!/usr/bin/env apricot').should be_empty
29
- end
30
-
31
- it 'discards commented forms' do
32
- parse('#_form').should be_empty
33
- end
34
-
35
- it 'parses identifiers' do
36
- parse_one('example', AST::Identifier)
37
- @first.name.should == :example
38
- end
39
-
40
- it 'parses pipe identifiers' do
41
- parse_one('#|example|', AST::Identifier).name.should == :example
42
- parse_one('#|foo bar|', AST::Identifier).name.should == :"foo bar"
43
- parse_one('#|foo\nbar|', AST::Identifier).name.should == :"foo\nbar"
44
- parse_one('#|foo\|bar|', AST::Identifier).name.should == :"foo|bar"
45
- parse_one('#|foo"bar|', AST::Identifier).name.should == :'foo"bar'
46
- end
47
-
48
- it 'parses constants' do
49
- parse_one('Example', AST::Identifier)
50
- @first.constant?.should be_true
51
- @first.const_names.should == [:Example]
52
- end
53
-
54
- it 'parses invalid constants as identifiers' do
55
- parse_one('Fo$o', AST::Identifier)
56
- @first.constant?.should be_false
57
- end
58
-
59
- it 'parses scoped constants' do
60
- parse_one('Foo::Bar::Baz', AST::Identifier)
61
- @first.constant?.should be_true
62
- @first.const_names.should == [:Foo, :Bar, :Baz]
63
- end
64
-
65
- it 'parses invalid scoped constants as identifiers' do
66
- parse_one('Foo::', AST::Identifier)
67
- @first.constant?.should be_false
68
- parse_one('Foo:', AST::Identifier)
69
- @first.constant?.should be_false
70
- parse_one('Foo::a', AST::Identifier)
71
- @first.constant?.should be_false
72
- parse_one('Foo::::Bar', AST::Identifier)
73
- @first.constant?.should be_false
74
- end
75
-
76
- it 'parses true, false, nil, and self' do
77
- parse('true false nil self').length.should == 4
78
- @ast[0].should be_a(AST::Literal)
79
- @ast[0].value.should == :true
80
- @ast[1].should be_a(AST::Literal)
81
- @ast[1].value.should == :false
82
- @ast[2].should be_a(AST::Literal)
83
- @ast[2].value.should == :nil
84
- @ast[3].should be_a(AST::Identifier)
85
- @ast[3].name.should == :self
86
- end
87
-
88
- it 'parses fixnums' do
89
- parse_one('123', AST::Literal)
90
- @first.value.should == 123
91
- end
92
-
93
- it 'parses bignums' do
94
- parse_one('12345678901234567890', AST::BignumLiteral)
95
- @first.value.should == 12345678901234567890
96
- end
97
-
98
- it 'parses radix integers' do
99
- parse_one('2r10', AST::Literal)
100
- @first.value.should == 2
101
- end
102
-
103
- it 'parses floats' do
104
- parse_one('1.23', AST::FloatLiteral)
105
- @first.value.should == 1.23
106
- end
107
-
108
- it 'parses rationals' do
109
- parse_one('12/34', AST::RationalLiteral)
110
- @first.numerator.should == 12
111
- @first.denominator.should == 34
112
- end
113
-
114
- it 'does not parse invalid numbers' do
115
- expect_syntax_error '12abc'
116
- end
117
-
118
- it 'parses empty strings' do
119
- parse_one('""', AST::StringLiteral)
120
- @first.value.should == ''
121
- end
122
-
123
- it 'parses strings' do
124
- parse_one('"Hello, world!"', AST::StringLiteral)
125
- @first.value.should == 'Hello, world!'
126
- end
127
-
128
- it 'parses multiline strings' do
129
- parse_one(%{"This is\na test"}, AST::StringLiteral)
130
- @first.value.should == "This is\na test"
131
- end
132
-
133
- it 'does not parse unfinished strings' do
134
- expect_syntax_error '"'
135
- end
136
-
137
- it 'parses strings with character escapes' do
138
- parse_one('"\\a\\b\\t\\n\\v\\f\\r\\e\\"\\\\"', AST::StringLiteral)
139
- @first.value.should == "\a\b\t\n\v\f\r\e\"\\"
140
- end
141
-
142
- it 'parses strings with octal escapes' do
143
- parse_one('"\\1\\01\\001"', AST::StringLiteral)
144
- @first.value.should == "\001\001\001"
145
- end
146
-
147
- it 'parses strings with hex escapes' do
148
- parse_one('"\\x1\\x01"', AST::StringLiteral)
149
- @first.value.should == "\001\001"
150
- end
151
-
152
- it 'does not parse strings with invalid hex escapes' do
153
- expect_syntax_error '"\\x"'
154
- end
155
-
156
- it 'stops parsing hex/octal escapes in strings at non-hex/octal digits' do
157
- parse_one('"\xAZ\082"', AST::StringLiteral)
158
- @first.value.should == "\x0AZ\00082"
159
- end
160
-
161
- it 'parses regexes' do
162
- parse_one('#r!!', AST::RegexLiteral).pattern.should == ''
163
- parse_one('#r!egex!', AST::RegexLiteral).pattern.should == 'egex'
164
- parse_one('#r(egex)', AST::RegexLiteral).pattern.should == 'egex'
165
- parse_one('#r[egex]', AST::RegexLiteral).pattern.should == 'egex'
166
- parse_one('#r{egex}', AST::RegexLiteral).pattern.should == 'egex'
167
- parse_one('#r<egex>', AST::RegexLiteral).pattern.should == 'egex'
168
- parse_one('#r!\!!', AST::RegexLiteral).pattern.should == '!'
169
- parse_one('#r!foo\bar!', AST::RegexLiteral).pattern.should == 'foo\bar'
170
- parse_one('#r!\\\\!', AST::RegexLiteral).pattern.should == "\\\\"
171
- end
172
-
173
- it 'parses regexes with trailing options' do
174
- parse_one('#r//i', AST::RegexLiteral)
175
- @first.options.should == Regexp::IGNORECASE
176
- parse_one('#r/foo/x', AST::RegexLiteral)
177
- @first.options.should == Regexp::EXTENDED
178
- parse_one('#r//im', AST::RegexLiteral)
179
- @first.options.should == Regexp::IGNORECASE | Regexp::MULTILINE
180
- end
181
-
182
- it 'does not parse regexes with unknown trailing options' do
183
- expect_syntax_error '#r/foo/asdf'
184
- end
185
-
186
- it 'parses symbols' do
187
- parse_one(':example', AST::SymbolLiteral)
188
- @first.value.should == :example
189
- end
190
-
191
- it 'parses quoted symbols' do
192
- parse_one(':"\x01()"', AST::SymbolLiteral)
193
- @first.value.should == :"\x01()"
194
- end
195
-
196
- it 'does not parse unfinished quoted symbols' do
197
- expect_syntax_error ':"'
198
- end
199
-
200
- it 'does not parse empty symbols' do
201
- expect_syntax_error ':'
202
- end
203
-
204
- it 'does parse empty quoted symbols' do
205
- parse_one(':""', AST::SymbolLiteral)
206
- @first.value.should == :""
207
- end
208
-
209
- it 'parses empty lists' do
210
- parse_one('()', AST::List)
211
- @first.elements.should be_empty
212
- end
213
-
214
- it 'parses lists' do
215
- parse_one('(1 two)', AST::List)
216
- @first[0].should be_a(AST::Literal)
217
- @first[1].should be_a(AST::Identifier)
218
- end
219
-
220
- it 'parses empty arrays' do
221
- parse_one('[]', AST::ArrayLiteral)
222
- @first.elements.should be_empty
223
- end
224
-
225
- it 'parses arrays' do
226
- parse_one('[1 two]', AST::ArrayLiteral)
227
- @first[0].should be_a(AST::Literal)
228
- @first[1].should be_a(AST::Identifier)
229
- end
230
-
231
- it 'parses empty hashes' do
232
- parse_one('{}', AST::HashLiteral)
233
- @first.elements.should be_empty
234
- end
235
-
236
- it 'parses hashes' do
237
- parse_one('{:example 1}', AST::HashLiteral)
238
- @first[0].should be_a(AST::SymbolLiteral)
239
- @first[1].should be_a(AST::Literal)
240
- end
241
-
242
- it 'does not parse invalid hashes' do
243
- expect_syntax_error '{:foo 1 :bar}'
244
- end
245
-
246
- it 'parses empty sets' do
247
- parse_one('#{}', AST::SetLiteral)
248
- @first.elements.should be_empty
249
- end
250
-
251
- it 'parses sets' do
252
- parse_one('#{1 two}', AST::SetLiteral)
253
- @first[0].should be_a(AST::Literal)
254
- @first[1].should be_a(AST::Identifier)
255
- end
256
-
257
- it 'parses multiple forms' do
258
- parse('foo bar').length.should == 2
259
- @ast[0].should be_a(AST::Identifier)
260
- @ast[1].should be_a(AST::Identifier)
261
- end
262
-
263
- it 'parses quoted forms' do
264
- parse_one("'test", AST::List)
265
- @first.elements.length.should == 2
266
- @first[0].should be_a(AST::Identifier)
267
- @first[0].name.should == :quote
268
- @first[1].should be_a(AST::Identifier)
269
- @first[1].name.should == :test
270
- end
271
-
272
- it 'parses syntax quoted forms' do
273
- begin
274
- old_gensym = Apricot.instance_variable_get :@gensym
275
- Apricot.instance_variable_set :@gensym, 41
276
-
277
- parse_one("`(foo ~bar ~@baz quux#)", AST::List)
278
- @first.should == AST::Node.from_value(
279
- List[Identifier.intern(:concat),
280
- List[Identifier.intern(:list),
281
- List[Identifier.intern(:quote),
282
- Identifier.intern(:foo)]],
283
- List[Identifier.intern(:list),
284
- Identifier.intern(:bar)],
285
- Identifier.intern(:baz),
286
- List[Identifier.intern(:list),
287
- List[Identifier.intern(:quote),
288
- Identifier.intern(:'quux#__42')]]])
289
- ensure
290
- Apricot.instance_variable_set :@gensym, old_gensym
291
- end
292
- end
293
-
294
- it 'parses #() shorthand' do
295
- Apricot.stub(:gensym).and_return(*:a..:z)
296
-
297
- parse("#()").should == parse("(fn [] ())")
298
- parse("#(foo)").should == parse("(fn [] (foo))")
299
- parse("#(%)").should == parse("(fn [a] (a))")
300
- parse("#(% %2)").should == parse("(fn [b c] (b c))")
301
- parse("#(%1 %2)").should == parse("(fn [d e] (d e))")
302
- parse("#(%2)").should == parse("(fn [g f] (f))")
303
- parse("#(%&)").should == parse("(fn [& h] (h))")
304
- parse("#(% %&)").should == parse("(fn [i & j] (i j))")
305
-
306
- expect_syntax_error("#(%0)")
307
- expect_syntax_error("#(%-1)")
308
- expect_syntax_error("#(%x)")
309
- expect_syntax_error("#(%1.1)")
310
- expect_syntax_error("#(%1asdf)")
311
- end
312
- end