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,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