skeem 0.0.23 → 0.0.24
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +11 -6
- data/lib/skeem/datum_dsl.rb +139 -0
- data/lib/skeem/element_visitor.rb +91 -0
- data/lib/skeem/grammar.rb +31 -8
- data/lib/skeem/interpreter.rb +1 -1
- data/lib/skeem/primitive/primitive_builder.rb +47 -37
- data/lib/skeem/runtime.rb +28 -4
- data/lib/skeem/s_expr_builder.rb +64 -25
- data/lib/skeem/s_expr_nodes.rb +81 -327
- data/lib/skeem/skm_compound_datum.rb +118 -0
- data/lib/skeem/skm_element.rb +85 -0
- data/lib/skeem/skm_expression.rb +7 -0
- data/lib/skeem/skm_simple_datum.rb +132 -0
- data/lib/skeem/skm_unary_expression.rb +107 -0
- data/lib/skeem/standard/base.skm +3 -3
- data/lib/skeem/tokenizer.rb +8 -1
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/datum_dsl_spec.rb +191 -0
- data/spec/skeem/element_visitor_spec.rb +170 -0
- data/spec/skeem/interpreter_spec.rb +126 -36
- data/spec/skeem/primitive/primitive_builder_spec.rb +48 -36
- data/spec/skeem/runtime_spec.rb +28 -2
- data/spec/skeem/s_expr_nodes_spec.rb +15 -277
- data/spec/skeem/skm_compound_datum_spec.rb +132 -0
- data/spec/skeem/skm_element_spec.rb +50 -0
- data/spec/skeem/skm_simple_datum_spec.rb +233 -0
- data/spec/skeem/skm_unary_expression_spec.rb +201 -0
- data/spec/skeem/tokenizer_spec.rb +7 -35
- metadata +21 -3
- data/lib/skeem/convertible.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1f6bf0ae4407a85eb404c02c2497964744c26b2
|
4
|
+
data.tar.gz: f66450bf98cb129a7e40499bf589ef6454760c12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f07d638db941e45c54717ba99addfdb627ba9c76ace292905bb3fa3687888a42a633f51cc9dc6975d058f6995782e56f9a7439cc8e0a0086c1a4ba93be84040a
|
7
|
+
data.tar.gz: 03b924927a3cf1e08780de16743403ab17ca68646e17f1f1b36bcfc58f3478cf403d09c2cf0626306f83dc60e6b06c4a9df57502500ca246361bbcc641833bc7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## [0.0.24] - 2018-11-08
|
2
|
+
Many internal refactoring, augmented spec files base, initial quasiquotation implementation.
|
3
|
+
|
4
|
+
### Added
|
5
|
+
- File `primitive_builder.rb` implementation of standard: `string->length`
|
6
|
+
- File `datum_dsl.rb` to implement an internal DSL for building literal data elements.
|
7
|
+
- Files `tokenizer.rb`, `grammar.rb`, `class SExprBuilder` Added support for quasiquotation: (quasiquote foo) or `foo and unquoting
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- File `README.md` udpated to reflect currently implemented features.
|
11
|
+
|
1
12
|
## [0.0.23] - 2018-10-24
|
2
13
|
### Added
|
3
14
|
- File `primitive_builder.rb` implementation of: standard `or`, `string->symbol`, `number->string`, `string-append` procedures.
|
data/README.md
CHANGED
@@ -40,11 +40,6 @@ Here are a few pointers for the Scheme programming language:
|
|
40
40
|
- [The Scheme Programming Language, 4th Edition](https://www.scheme.com/tspl4/) by Kent Dybvig. An complete, introductory textbook on Scheme based on the older R5RS standard.
|
41
41
|
- [Teach Yourself Scheme in Fixnum Days](http://ds26gte.github.io/tyscheme/index.html) by Dorai Sitaram
|
42
42
|
|
43
|
-
## Other similar Ruby projects
|
44
|
-
__Skeem__ isn't the sole implementation of the Scheme language in Ruby.
|
45
|
-
Here are a few other ones:
|
46
|
-
- [Heist gem](https://rubygems.org/gems/heist) -- Probably one of best Scheme implementation in Ruby. Really worth a try. Alas, the [project](https://github.com/jcoglan/heist) seems to be dormant for several years.
|
47
|
-
|
48
43
|
## Usage
|
49
44
|
|
50
45
|
### Example 1 (Variable definition)
|
@@ -120,6 +115,7 @@ Here are a few other ones:
|
|
120
115
|
- Of the number hierarchy:
|
121
116
|
`real` (e.g. 2.718, 6.671e-11),
|
122
117
|
`integer` (42, -3)
|
118
|
+
- Lists (quoted) : '(1 two "three")
|
123
119
|
- Strings: `"Hello, world."`
|
124
120
|
- Identifiers (symbols): `really-cool-procedure`
|
125
121
|
- Vectors: `#(1 2 "three")`
|
@@ -127,6 +123,7 @@ Here are a few other ones:
|
|
127
123
|
### Scheme Expressions
|
128
124
|
- Constant literals
|
129
125
|
- Quotations
|
126
|
+
- Quasiquotation (without unquote-splicing)
|
130
127
|
- Variable references
|
131
128
|
- Procedure calls
|
132
129
|
- Lambda expressions
|
@@ -173,7 +170,7 @@ This section lists the implemented standard procedures
|
|
173
170
|
* `list?`, `null?`, `list`, `length`
|
174
171
|
|
175
172
|
#### String procedures
|
176
|
-
* `string?`, `string-append`, `string->symbol`,
|
173
|
+
* `string?`, `string-append`, `string-length`, `string->symbol`,
|
177
174
|
|
178
175
|
#### Symbol procedures
|
179
176
|
* `symbol?`
|
@@ -193,6 +190,14 @@ Roadmap:
|
|
193
190
|
- Extend the language in order to support [Minikanren](https://github.com/TheReasonedSchemer2ndEd/CodeFromTheReasonedSchemer2ndEd)
|
194
191
|
- Make it pass all examples from the [Reasoned Schemer](https://mitpress.mit.edu/books/reasoned-schemer-second-edition) book.
|
195
192
|
|
193
|
+
## Other Scheme implementation in Ruby
|
194
|
+
__Skeem__ isn't the sole implementation of the Scheme language in Ruby.
|
195
|
+
Here are a few other ones:
|
196
|
+
- [Heist gem](https://rubygems.org/gems/heist) -- Probably one of best Scheme implementation in Ruby. Really worth a try. Alas, the [project](https://github.com/jcoglan/heist) seems to be dormant for several years.
|
197
|
+
- [Schemerald gem](https://rubygems.org/gems/schemerald). The last commit for the [project](https://github.com/vntzy/schemerald) is October 2017.
|
198
|
+
|
199
|
+
- [rubic gem](https://rubygems.org/gems/rubic). The last commit for the [project](https://github.com/notozeki/rubic) is June 2015.
|
200
|
+
|
196
201
|
## Contributing
|
197
202
|
|
198
203
|
Bug reports and pull requests are welcome on GitHub at https://github.com/famished-tiger/Skeem.
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require_relative 'skm_simple_datum'
|
2
|
+
require_relative 'skm_compound_datum'
|
3
|
+
|
4
|
+
module Skeem
|
5
|
+
# Mixin module that provides factory methods that ease the conversion of
|
6
|
+
# Ruby literals into SkmSimpleDatum or SkmCompoundDatum objects.
|
7
|
+
module DatumDSL
|
8
|
+
def boolean(aBoolean)
|
9
|
+
return aBoolean if aBoolean.kind_of?(SkmBoolean)
|
10
|
+
|
11
|
+
result = case aBoolean
|
12
|
+
when TrueClass, FalseClass
|
13
|
+
SkmBoolean.create(aBoolean)
|
14
|
+
when /^#t(?:rue)?|true$/
|
15
|
+
SkmBoolean.create(true)
|
16
|
+
when /^#f(?:alse)?|false$/
|
17
|
+
SkmBoolean.create(false)
|
18
|
+
else
|
19
|
+
raise StandardError, aBoolean.inspect
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def integer(aLiteral)
|
24
|
+
return aLiteral if aLiteral.kind_of?(SkmInteger)
|
25
|
+
|
26
|
+
result = case aLiteral
|
27
|
+
when Integer
|
28
|
+
SkmInteger.create(aLiteral)
|
29
|
+
when /^[+-]?\d+$/
|
30
|
+
SkmInteger.create(aLiteral.to_i)
|
31
|
+
else
|
32
|
+
raise StandardError, aLiteral.inspect
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def real(aLiteral)
|
37
|
+
return aLiteral if aLiteral.kind_of?(SkmReal)
|
38
|
+
|
39
|
+
result = case aLiteral
|
40
|
+
when Numeric
|
41
|
+
SkmReal.create(aLiteral)
|
42
|
+
when /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
|
43
|
+
SkmReal.create(aLiteral.to_f)
|
44
|
+
else
|
45
|
+
raise StandardError, aLiteral.inspect
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def string(aLiteral)
|
50
|
+
return aLiteral if aLiteral.kind_of?(SkmString)
|
51
|
+
|
52
|
+
result = case aLiteral
|
53
|
+
when String
|
54
|
+
SkmString.create(aLiteral)
|
55
|
+
else
|
56
|
+
SkmString.create(aLiteral.to_s)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def identifier(aLiteral)
|
61
|
+
return aLiteral if aLiteral.kind_of?(SkmIdentifier)
|
62
|
+
|
63
|
+
result = case aLiteral
|
64
|
+
when String
|
65
|
+
SkmIdentifier.create(aLiteral)
|
66
|
+
when SkmString
|
67
|
+
SkmIdentifier.create(aLiteral.value)
|
68
|
+
else
|
69
|
+
raise StandardError, aLiteral.inspect
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias symbol identifier
|
74
|
+
|
75
|
+
def list(aLiteral)
|
76
|
+
result = case aLiteral
|
77
|
+
when Array
|
78
|
+
SkmList.new(to_datum(aLiteral))
|
79
|
+
when SkmList
|
80
|
+
SkmList.new(to_datum(aLiteral.members))
|
81
|
+
else
|
82
|
+
SkmList.new([to_datum(aLiteral)])
|
83
|
+
end
|
84
|
+
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
def vector(aLiteral)
|
89
|
+
result = case aLiteral
|
90
|
+
when Array
|
91
|
+
SkmVector.new(to_datum(aLiteral))
|
92
|
+
when SkmVector
|
93
|
+
SkmVector.new(to_datum(aLiteral.members))
|
94
|
+
else
|
95
|
+
SkmVector.new([to_datum(aLiteral)])
|
96
|
+
end
|
97
|
+
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_datum(aLiteral)
|
102
|
+
return aLiteral if aLiteral.kind_of?(SkmSimpleDatum)
|
103
|
+
return list(aLiteral.members) if aLiteral.kind_of?(SkmList)
|
104
|
+
return vector(aLiteral.members) if aLiteral.kind_of?(SkmVector)
|
105
|
+
|
106
|
+
result = case aLiteral
|
107
|
+
when Array
|
108
|
+
aLiteral.map { |elem| to_datum(elem) }
|
109
|
+
when Integer
|
110
|
+
SkmInteger.create(aLiteral)
|
111
|
+
when Float
|
112
|
+
SkmReal.create(aLiteral)
|
113
|
+
when TrueClass, FalseClass
|
114
|
+
SkmBoolean.create(aLiteral)
|
115
|
+
when String
|
116
|
+
parse_literal(aLiteral)
|
117
|
+
else
|
118
|
+
raise StandardError, aLiteral.inspect
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def parse_literal(aLiteral)
|
125
|
+
if aLiteral =~ /^#t(?:rue)?|true$/
|
126
|
+
boolean(aLiteral)
|
127
|
+
elsif aLiteral =~ /^#f(?:alse)?|false$/
|
128
|
+
boolean(aLiteral)
|
129
|
+
elsif aLiteral =~ /^[+-]?\d+$/
|
130
|
+
integer(aLiteral)
|
131
|
+
elsif aLiteral =~ /^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?$/
|
132
|
+
real(aLiteral)
|
133
|
+
else
|
134
|
+
string(aLiteral)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end # module
|
139
|
+
end # module
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Skeem
|
2
|
+
class SkmElementVisitor
|
3
|
+
# Link to the root element to visit
|
4
|
+
attr_reader(:root)
|
5
|
+
|
6
|
+
# List of objects that subscribed to the visit event notification.
|
7
|
+
attr_reader(:subscribers)
|
8
|
+
|
9
|
+
attr_reader(:runtime)
|
10
|
+
|
11
|
+
# Build a visitor for the given root.
|
12
|
+
# @param aRoot [SkmElement] the parse tree to visit.
|
13
|
+
def initialize(aRoot)
|
14
|
+
raise StandardError if aRoot.nil?
|
15
|
+
@root = aRoot
|
16
|
+
@subscribers = []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Add a subscriber for the visit event notifications.
|
20
|
+
# @param aSubscriber [Object]
|
21
|
+
def subscribe(aSubscriber)
|
22
|
+
subscribers << aSubscriber
|
23
|
+
end
|
24
|
+
|
25
|
+
# Remove the given object from the subscription list.
|
26
|
+
# The object won't be notified of visit events.
|
27
|
+
# @param aSubscriber [Object]
|
28
|
+
def unsubscribe(aSubscriber)
|
29
|
+
subscribers.delete_if { |entry| entry == aSubscriber }
|
30
|
+
end
|
31
|
+
|
32
|
+
# The signal to begin the visit of the root.
|
33
|
+
def start(aRuntime)
|
34
|
+
@runtime = aRuntime
|
35
|
+
root.accept(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Visit event. The visitor is visiting the
|
39
|
+
# given simple datum object.
|
40
|
+
# @param aTerminal [SkmTerminal] the terminal to visit.
|
41
|
+
def visit_simple_datum(aSimpleDatum)
|
42
|
+
broadcast(:before_simple_datum, aSimpleDatum)
|
43
|
+
broadcast(:after_simple_datum, aSimpleDatum)
|
44
|
+
end
|
45
|
+
|
46
|
+
def visit_compound_datum(aCompoundDatum)
|
47
|
+
broadcast(:before_compound_datum, aCompoundDatum)
|
48
|
+
traverse_children(aCompoundDatum)
|
49
|
+
broadcast(:after_compound_datum, aCompoundDatum)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
=begin
|
55
|
+
# Visit event. The visitor is about to visit the given non terminal node.
|
56
|
+
# @param aNonTerminalNode [NonTerminalNode] the node to visit.
|
57
|
+
def visit_nonterminal(aNonTerminalNode)
|
58
|
+
if @traversal == :post_order
|
59
|
+
broadcast(:before_non_terminal, aNonTerminalNode)
|
60
|
+
traverse_subnodes(aNonTerminalNode)
|
61
|
+
else
|
62
|
+
traverse_subnodes(aNonTerminalNode)
|
63
|
+
broadcast(:before_non_terminal, aNonTerminalNode)
|
64
|
+
end
|
65
|
+
broadcast(:after_non_terminal, aNonTerminalNode)
|
66
|
+
end
|
67
|
+
=end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def traverse_children(aParent)
|
72
|
+
children = aParent.children
|
73
|
+
broadcast(:before_children, aParent, children)
|
74
|
+
|
75
|
+
# Let's proceed with the visit of children
|
76
|
+
children.each { |a_child| a_child.accept(self) }
|
77
|
+
|
78
|
+
broadcast(:after_children, aParent, children)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Send a notification to all subscribers.
|
82
|
+
# @param msg [Symbol] event to notify
|
83
|
+
# @param args [Array] arguments of the notification.
|
84
|
+
def broadcast(msg, *args)
|
85
|
+
subscribers.each do |subscr|
|
86
|
+
next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
|
87
|
+
subscr.send(msg, runtime, *args)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end # class
|
91
|
+
end # module
|
data/lib/skeem/grammar.rb
CHANGED
@@ -9,8 +9,8 @@ module Skeem
|
|
9
9
|
# Names of grammar elements are based on the R7RS documentation
|
10
10
|
builder = Rley::Syntax::GrammarBuilder.new do
|
11
11
|
# Delimiters, separators...
|
12
|
-
add_terminals('APOSTROPHE'
|
13
|
-
add_terminals('LPAREN', 'RPAREN')
|
12
|
+
add_terminals('APOSTROPHE', 'COMMA', 'COMMA_AT_SIGN')
|
13
|
+
add_terminals('GRAVE_ACCENT', 'LPAREN', 'RPAREN')
|
14
14
|
add_terminals('PERIOD')
|
15
15
|
add_terminals('VECTOR_BEGIN')
|
16
16
|
|
@@ -20,7 +20,8 @@ module Skeem
|
|
20
20
|
|
21
21
|
# Keywords...
|
22
22
|
add_terminals('DEFINE', 'IF', 'LAMBDA')
|
23
|
-
add_terminals('QUOTE')
|
23
|
+
add_terminals('QUOTE', 'QUASIQUOTE', 'UNQUOTE')
|
24
|
+
add_terminals('UNQUOTE-SPLICING')
|
24
25
|
|
25
26
|
rule('program' => 'cmd_or_def_plus').as 'main'
|
26
27
|
rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
|
@@ -29,15 +30,16 @@ module Skeem
|
|
29
30
|
rule 'cmd_or_def' => 'definition'
|
30
31
|
rule 'command' => 'expression'
|
31
32
|
rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN').as 'definition'
|
32
|
-
rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
|
33
|
+
rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
|
33
34
|
rule('expression' => 'IDENTIFIER').as 'variable_reference'
|
34
35
|
rule 'expression' => 'literal'
|
35
36
|
rule 'expression' => 'procedure_call'
|
36
37
|
rule 'expression' => 'lambda_expression'
|
37
38
|
rule 'expression' => 'conditional'
|
39
|
+
rule 'expression' => 'derived_expression'
|
38
40
|
rule 'literal' => 'quotation'
|
39
41
|
rule 'literal' => 'self-evaluating'
|
40
|
-
rule('quotation' => 'APOSTROPHE datum').as '
|
42
|
+
rule('quotation' => 'APOSTROPHE datum').as 'quotation_short'
|
41
43
|
rule('quotation' => 'LPAREN QUOTE datum RPAREN').as 'quotation'
|
42
44
|
rule 'self-evaluating' => 'BOOLEAN'
|
43
45
|
rule 'self-evaluating' => 'number'
|
@@ -47,7 +49,7 @@ module Skeem
|
|
47
49
|
rule 'datum' => 'compound_datum'
|
48
50
|
rule 'simple_datum' => 'BOOLEAN'
|
49
51
|
rule 'simple_datum' => 'number'
|
50
|
-
rule 'simple_datum' => 'STRING_LIT'
|
52
|
+
rule 'simple_datum' => 'STRING_LIT'
|
51
53
|
rule 'simple_datum' => 'symbol'
|
52
54
|
rule 'compound_datum' => 'list'
|
53
55
|
rule 'compound_datum' => 'vector'
|
@@ -55,7 +57,7 @@ module Skeem
|
|
55
57
|
rule 'list' => 'LPAREN datum_plus PERIOD datum RPAREN'
|
56
58
|
rule('vector' => 'VECTOR_BEGIN datum_star RPAREN').as 'vector'
|
57
59
|
rule('datum_plus' => 'datum_plus datum').as 'multiple_datums'
|
58
|
-
rule('datum_plus' => 'datum').as 'last_datum'
|
60
|
+
rule('datum_plus' => 'datum').as 'last_datum'
|
59
61
|
rule('datum_star' => 'datum_star datum').as 'datum_star'
|
60
62
|
rule('datum_star' => []).as 'no_datum_yet'
|
61
63
|
rule 'symbol' => 'IDENTIFIER'
|
@@ -66,7 +68,7 @@ module Skeem
|
|
66
68
|
rule 'operator' => 'expression'
|
67
69
|
rule 'operand' => 'expression'
|
68
70
|
rule('def_formals' => 'identifier_star').as 'def_formals'
|
69
|
-
rule('def_formals' => 'identifier_star PERIOD IDENTIFIER').as 'pair_formals'
|
71
|
+
rule('def_formals' => 'identifier_star PERIOD IDENTIFIER').as 'pair_formals'
|
70
72
|
rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
|
71
73
|
rule('formals' => 'LPAREN identifier_star RPAREN').as 'fixed_arity_formals'
|
72
74
|
rule('formals' => 'IDENTIFIER').as 'variadic_formals'
|
@@ -88,6 +90,27 @@ module Skeem
|
|
88
90
|
rule 'alternate' => []
|
89
91
|
rule 'number' => 'INTEGER'
|
90
92
|
rule 'number' => 'REAL'
|
93
|
+
rule 'derived_expression' => 'quasiquotation'
|
94
|
+
rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
|
95
|
+
rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
|
96
|
+
rule 'qq_template' => 'simple_datum'
|
97
|
+
rule 'qq_template' => 'list_qq_template'
|
98
|
+
rule 'qq_template' => 'vector_qq_template'
|
99
|
+
rule 'qq_template' => 'unquotation'
|
100
|
+
rule('list_qq_template' => 'LPAREN qq_template_or_splice_star RPAREN').as 'list_qq'
|
101
|
+
rule 'list_qq_template' => 'LPAREN qq_template_or_splice_plus PERIOD qq_template RPAREN'
|
102
|
+
rule 'list_qq_template' => 'GRAVE_ACCENT qq_template'
|
103
|
+
rule('vector_qq_template' => 'VECTOR_BEGIN qq_template_or_splice_star RPAREN').as 'vector_qq'
|
104
|
+
rule('unquotation' => 'COMMA qq_template').as 'unquotation_short'
|
105
|
+
rule 'unquotation' => 'LPAREN UNQUOTE qq_template RPAREN'
|
106
|
+
rule('qq_template_or_splice_star' => 'qq_template_or_splice_star qq_template_or_splice').as 'multiple_template_splice'
|
107
|
+
rule('qq_template_or_splice_star' => []).as 'no_template_splice_yet'
|
108
|
+
rule 'qq_template_or_splice_plus' => 'qq_template_or_splice_plus qq_template_or_splice'
|
109
|
+
rule 'qq_template_or_splice_plus' => 'qq_template_or_splice'
|
110
|
+
rule 'qq_template_or_splice' => 'qq_template'
|
111
|
+
rule 'qq_template_or_splice' => 'splicing_unquotation'
|
112
|
+
rule 'splicing_unquotation' => 'COMMA_AT_SIGN qq_template'
|
113
|
+
rule 'splicing_unquotation' => 'LPAREN UNQUOTE-SPLICING qq_template RPAREN'
|
91
114
|
end
|
92
115
|
|
93
116
|
# And now build the grammar and make it accessible via a global constant
|