Class: Heist::Runtime::Macro::Matches
- Inherits:
-
Object
- Object
- Heist::Runtime::Macro::Matches
- Defined in:
- lib/heist/runtime/callable/macro/matches.rb
Overview
Matches
instances, with help from the Tree
class, are data structures that represent the way in which syntactic expressions match patterns found in Macro
rules. They provide an API for storing and retrieving such data, with the aim of removing some clutter from the macro parsing and expansion routines.
At a pure Ruby level, a Matches
is a wrapper around a hash that maps pattern variables to Tree
objects, which themselves are wrappers around nested arrays that represent how repeated pattern matches are grouped together. For example, given the pattern
(do ([variable init step ...] ...)
(test expression ...)
command ...)
and the expression
(do ([x 6 (- x 1)]
[acc 1])
((zero? x) acc)
(display x) (newline)
(set! acc (* acc x)))
the Matches
object would contain the following:
@data = {
"variable" => [ x,
acc
],
"init" => [ 6,
1
],
"step" => [ [ (- x 1)
],
[]
],
"test" => (zero? x),
"expression" => [ acc
],
"command" => [ (display x),
(newline),
(set! acc (* acc x))
]
}
Breaking this down, we see test
is not followed by an ellipsis in the pattern, and can thus only consume one item from the input expression. So, its match data is a single Expression
. variable
, init
, command
and expression
are all followed by a single ellipsis (variable
and init
appear in a list that is followed by an ellipsis), so can consume several values each; their match data are arrays of expressions.
step
, on the other hand, is followed by two ellipses: it itself is followed by an ellipsis, and step ...
appears inside a list that is also followed by an ellipsis. If a pattern is followed by more than one ellipsis, the match data it generates is a tree of nested arrays that describe how the expressions are grouped. Here, we see that the expression [variable init step ...]
appears twice in the input, so step
‘s root match element is an array of two elements. But, step
does not match any data in the second appearance ([acc 1]
), so the second element of this array is empty. The first element is an array containing the single match from the first appearance ((- x 1)
in the expression [x 6 (- x 1)]
).
Matches
tries to hide many of these details so the macro routines can read and write to this data structure in the simplest possible terms.
Instance Method Summary collapse
-
#descend!(names, depth) ⇒ Object
Tells the
Matches
object that the given pattern variables (the arraynames
) have encountered a trailing ellipsis at the given repetition depth. -
#expand!(template, depth) ⇒ Object
Takes a
template
Expression
, a repetitiondepth
and a block, and calls the blockn
times, wheren
is the number of matches for the pattern variables in the template at the given depth and the current iteration point in the tree. -
#get(name) ⇒ Object
Retrieves an expression from the
Matches
under the givenname
. -
#has?(name) ⇒ Boolean
Returns
true
iff the receiver has a pattern variable namedname
. -
#initialize(pattern, formals) ⇒ Matches
constructor
A
Matches
is initialized using aCons
representing a macro pattern, and an array of formal keywords supplied by theMacro
. -
#put(name, value) ⇒ Object
Writes an expression to the
Matches
object under the variablename
.
Constructor Details
#initialize(pattern, formals) ⇒ Matches
A Matches
is initialized using a Cons
representing a macro pattern, and an array of formal keywords supplied by the Macro
. Keywords do not need to store matches so they are ignored here.
82 83 84 85 86 |
# File 'lib/heist/runtime/callable/macro/matches.rb', line 82 def initialize(pattern, formals) @data = {} names = Macro.pattern_vars(pattern, formals) names.each { |name| @data[name] = Tree.new(name) } end |
Instance Method Details
#descend!(names, depth) ⇒ Object
Tells the Matches
object that the given pattern variables (the array names
) have encountered a trailing ellipsis at the given repetition depth. This allows Matches
to group repeated patterns correctly.
91 92 93 94 95 |
# File 'lib/heist/runtime/callable/macro/matches.rb', line 91 def descend!(names, depth) @data.each do |name, set| set.descend!(depth) if names.include?(name) end end |
#expand!(template, depth) ⇒ Object
Takes a template
Expression
, a repetition depth
and a block, and calls the block n
times, where n
is the number of matches for the pattern variables in the template at the given depth and the current iteration point in the tree. After each block call, the Matches
object moves the pointer for all the applicable pattern variables along one place at the given depth – see iterate!
and Tree#shift!
.
123 124 125 126 |
# File 'lib/heist/runtime/callable/macro/matches.rb', line 123 def (template, depth) names = Macro.pattern_vars(template) size(names, depth).times { yield() and iterate!(names, depth) } end |
#get(name) ⇒ Object
Retrieves an expression from the Matches
under the given name
. The receiver deals with pulling the expression from the right point in the tree; see the expand!
, iterate!
and Tree#read
and Tree#shift!
methods.
113 114 115 |
# File 'lib/heist/runtime/callable/macro/matches.rb', line 113 def get(name) @data[name.to_s].read end |
#has?(name) ⇒ Boolean
Returns true
iff the receiver has a pattern variable named name
.
105 106 107 |
# File 'lib/heist/runtime/callable/macro/matches.rb', line 105 def has?(name) @data.has_key?(name.to_s) end |
#put(name, value) ⇒ Object
Writes an expression to the Matches
object under the variable name
. The receiver deals with storing it in the correct repetition group.
99 100 101 102 |
# File 'lib/heist/runtime/callable/macro/matches.rb', line 99 def put(name, value) name = name.to_s @data[name] << value if has?(name) end |