Class: Parslet::Pattern

Inherits:
Object
  • Object
show all
Defined in:
lib/parslet/pattern.rb

Overview

Matches trees against expressions. Trees are formed by arrays and hashes for expressing membership and sequence. The leafs of the tree are other classes.

A tree issued by the parslet library might look like this:

{ 
  :function_call => {
    :name => 'foobar', 
    :args => [1, 2, 3]
  }
}

A pattern that would match against this tree would be:

{ :function_call => { :name => simple(:name), :args => sequence(:args) }}

Note that Parslet::Pattern only matches at a given subtree; it wont try to match recursively. To do that, please use Parslet::Transform.

Defined Under Namespace

Classes: SequenceBind, SimpleBind, SubtreeBind

Instance Method Summary collapse

Constructor Details

#initialize(pattern) ⇒ Pattern

Returns a new instance of Pattern.



22
23
24
# File 'lib/parslet/pattern.rb', line 22

def initialize(pattern)
  @pattern = pattern
end

Instance Method Details

#element_match(tree, exp, bindings) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns true if the tree element given by tree matches the expression given by exp. This match must respect bindings already made in bindings. Note that bindings is carried along and modified.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/parslet/pattern.rb', line 49

def element_match(tree, exp, bindings) 
  # p [:elm, tree, exp]
  case [tree, exp].map { |e| e.class }
    when [Hash,Hash]
      return element_match_hash(tree, exp, bindings)
    when [Array,Array]
      return element_match_ary_single(tree, exp, bindings)
  else
    # If elements match exactly, then that is good enough in all cases
    return true if tree == exp
    
    # If exp is a bind variable: Check if the binding matches
    if exp.respond_to?(:can_bind?) && exp.can_bind?(tree)
      return element_match_binding(tree, exp, bindings)
    end
                
    # Otherwise: No match (we don't know anything about the element
    # combination)
    return false
  end
end

#element_match_ary_single(sequence, exp, bindings) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



89
90
91
92
93
94
# File 'lib/parslet/pattern.rb', line 89

def element_match_ary_single(sequence, exp, bindings)
  return false if sequence.size != exp.size
  
  return sequence.zip(exp).all? { |elt, subexp|
    element_match(elt, subexp, bindings) }
end

#element_match_binding(tree, exp, bindings) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/parslet/pattern.rb', line 73

def element_match_binding(tree, exp, bindings)
  var_name = exp.variable_name

  # TODO test for the hidden :_ feature.
  if var_name && bound_value = bindings[var_name]
    return bound_value == tree
  end
  
  # New binding: 
  bindings.store var_name, tree
  
  return true
end

#element_match_hash(tree, exp, bindings) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/parslet/pattern.rb', line 98

def element_match_hash(tree, exp, bindings)
  # Early failure when one hash is bigger than the other
  return false unless exp.size == tree.size
  
  # We iterate over expected pattern, since we demand that the keys that
  # are there should be in tree as well.
  exp.each do |expected_key, expected_value|
    return false unless tree.has_key? expected_key
    
    # Recurse into the value and stop early on failure
    value = tree[expected_key]
    return false unless element_match(value, expected_value, bindings)
  end
  
  return true
end

#match(subtree, bindings = nil) ⇒ Hash?

Decides if the given subtree matches this pattern. Returns the bindings made on a successful match or nil if the match fails. If you specify bindings to be a hash, the mappings in it will be treated like bindings made during an attempted match.

Pattern.new('a').match('a', :foo => 'bar') # => { :foo => 'bar' }

Parameters:

  • subtree (String, Hash, Array)

    poro subtree returned by a parse

  • bindings (Hash) (defaults to: nil)

    variable bindings to be verified

Returns:

  • (Hash, nil)

    On success: variable bindings that allow a match. On failure: nil



38
39
40
41
# File 'lib/parslet/pattern.rb', line 38

def match(subtree, bindings=nil)
  bindings = bindings && bindings.dup || Hash.new
  return bindings if element_match(subtree, @pattern, bindings)
end