Class: Heist::Runtime::Macro::Matches

Inherits:
Object
  • Object
show all
Defined in:
lib/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

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/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/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/runtime/callable/macro/matches.rb', line 123

def expand!(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/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.

Returns:

  • (Boolean)


105
106
107
# File 'lib/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/runtime/callable/macro/matches.rb', line 99

def put(name, value)
  name = name.to_s
  @data[name] << value if has?(name)
end