furnace 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,37 +1,71 @@
1
1
  module Furnace::AST
2
- class Matcher
3
- SpecialAny = MatcherSpecial.new(:any)
4
- SpecialSubset = MatcherSpecial.define(:subset)
2
+ class MatcherError < StandardError; end
5
3
 
4
+ class Matcher
6
5
  def initialize(&block)
7
6
  @pattern = self.class.class_exec(&block)
8
7
  end
9
8
 
10
- def match(object)
11
- genmatch(object.to_astlet, @pattern)
9
+ def match(object, captures={})
10
+ if genmatch(object.to_astlet, @pattern, captures)
11
+ captures
12
+ else
13
+ nil
14
+ end
12
15
  end
13
16
 
14
- def find_one(collection)
17
+ def find_one(collection, initial_captures={})
15
18
  collection.find do |elem|
16
- result = match elem
17
- yield elem, result if block_given? && result
18
- result
19
+ result = match elem, initial_captures.dup
20
+
21
+ if block_given? && result
22
+ yield elem, result
23
+ else
24
+ result
25
+ end
26
+ end
27
+ end
28
+
29
+ def find_one!(collection, initial_captures={})
30
+ found = nil
31
+
32
+ collection.each do |elem|
33
+ result = match elem, initial_captures.dup
34
+
35
+ if result
36
+ raise MatcherError, "already matched" if found
37
+
38
+ found = elem
39
+ yield elem, result if block_given?
40
+ end
19
41
  end
42
+
43
+ raise MatcherError, "no match found" unless found
44
+
45
+ found
20
46
  end
21
47
 
22
- def find_all(collection)
48
+ def find_all(collection, initial_captures={})
23
49
  collection.select do |elem|
24
- result = match elem
50
+ result = match elem, initial_captures.dup
25
51
  yield elem, result if block_given? && result
26
52
  result
27
53
  end
28
54
  end
29
55
 
56
+ SpecialAny = MatcherSpecial.new(:any)
57
+ SpecialSkip = MatcherSpecial.new(:skip)
58
+ SpecialSubset = MatcherSpecial.define(:subset)
59
+
30
60
  class << self
31
61
  def any
32
62
  SpecialAny
33
63
  end
34
64
 
65
+ def skip
66
+ SpecialSkip
67
+ end
68
+
35
69
  def subset
36
70
  SpecialSubset
37
71
  end
@@ -39,56 +73,76 @@ module Furnace::AST
39
73
  def capture(name)
40
74
  MatcherSpecial.new(:capture, name)
41
75
  end
76
+
77
+ def backref(name)
78
+ MatcherSpecial.new(:backref, name)
79
+ end
42
80
  end
43
81
 
44
82
  protected
45
83
 
46
- def submatch(array, pattern)
84
+ def submatch(array, pattern, captures)
47
85
  matches = true
86
+ nested_captures = captures.dup
48
87
 
49
- pattern.each_with_index do |subpattern, index|
50
- if array[index].nil?
51
- return false
52
- end
88
+ pattern.each_with_index do |nested_pattern, index|
89
+ return false if index > array.length
53
90
 
54
- case subpattern
91
+ case nested_pattern
92
+ when Array
93
+ matches &&= genmatch(array[index], nested_pattern, nested_captures)
55
94
  when SpecialAny
56
95
  # it matches
57
- when MatcherSpecial
58
- if subpattern.type == :subset
59
- all_submatches = true
60
-
61
- subpattern.params.each do |pattern_case|
62
- submatches = false
63
- array[index..-1].each do |subset_elem|
64
- submatches ||= genmatch(subset_elem, pattern_case)
65
- break if submatches
96
+ when SpecialSkip
97
+ # it matches all remaining elements
98
+ break
99
+ when MatcherSpecial.kind(:capture)
100
+ # it matches and captures
101
+ nested_captures[nested_pattern.param] = array[index]
102
+ when MatcherSpecial.kind(:backref)
103
+ matches &&= (nested_captures[nested_pattern.param] == array[index])
104
+ when MatcherSpecial.kind(:subset)
105
+ all_submatches = true
106
+
107
+ workset = Set.new array[index..-1]
108
+
109
+ nested_pattern.param.each do |subset_pattern|
110
+ sub_matches = false
111
+
112
+ workset.each do |subset_elem|
113
+ sub_matches ||= genmatch(subset_elem, subset_pattern, nested_captures)
114
+
115
+ if sub_matches
116
+ workset.delete subset_elem
117
+ break
66
118
  end
67
-
68
- all_submatches &&= submatches
69
- break unless all_submatches
70
119
  end
71
120
 
72
- matches &&= all_submatches
121
+ all_submatches &&= sub_matches
122
+ break unless all_submatches
73
123
  end
74
- when Array
75
- matches &&= genmatch(array[index], subpattern)
124
+
125
+ matches &&= all_submatches
76
126
  else
77
- matches &&= array[index] == subpattern
127
+ matches &&= (array[index] == nested_pattern)
78
128
  end
79
129
 
80
130
  break unless matches
81
131
  end
82
132
 
133
+ captures.replace(nested_captures) if matches
134
+
83
135
  matches
84
136
  end
85
137
 
86
- def genmatch(astlet, pattern)
87
- # if astlet.respond_to? :to_sexp
88
- # puts "match #{astlet.to_sexp} of #{pattern}"
89
- # else
90
- # puts "match #{astlet} of #{pattern}"
91
- # end
138
+ def genmatch(astlet, pattern, captures)
139
+ if $DEBUG
140
+ if astlet.respond_to? :to_sexp
141
+ puts "match #{astlet.to_sexp} of #{pattern}"
142
+ else
143
+ puts "match #{astlet} of #{pattern}"
144
+ end
145
+ end
92
146
 
93
147
  if pattern.first.is_a?(Symbol)
94
148
  # Match an astlet
@@ -96,7 +150,7 @@ module Furnace::AST
96
150
 
97
151
  if astlet.is_a? Node
98
152
  if astlet.type == type
99
- submatch(astlet.children, rest)
153
+ submatch(astlet.children, rest, captures)
100
154
  else
101
155
  false
102
156
  end
@@ -106,7 +160,7 @@ module Furnace::AST
106
160
  else
107
161
  # Match an array
108
162
  if astlet.is_a? Array
109
- submatch(astlet, pattern)
163
+ submatch(astlet, pattern, captures)
110
164
  else
111
165
  false
112
166
  end
@@ -1,13 +1,20 @@
1
1
  module Furnace::AST
2
2
  class MatcherSpecial
3
- attr_reader :type, :params
3
+ attr_reader :type, :param
4
4
 
5
- def initialize(type, params=nil)
6
- @type, @params = type, params
5
+ def initialize(type, param=nil)
6
+ @type, @param = type, param
7
7
  end
8
8
 
9
- def self.define(type)
10
- lambda { |*args| new(type, args) }
9
+ class << self
10
+ def define(type)
11
+ lambda { |*args| new(type, args) }
12
+ end
13
+
14
+ def kind(type)
15
+ @kind_lambdas ||= {}
16
+ @kind_lambdas[type] ||= lambda { |m| m.is_a?(MatcherSpecial) && m.type == type }
17
+ end
11
18
  end
12
19
  end
13
20
  end
@@ -42,6 +42,16 @@ module Furnace::AST
42
42
  node
43
43
  end
44
44
 
45
+ def ==(other)
46
+ if other.respond_to? :to_astlet
47
+ other = other.to_astlet
48
+ other.type == self.type &&
49
+ other.children == self.children
50
+ else
51
+ false
52
+ end
53
+ end
54
+
45
55
  def index
46
56
  parent.children.find_index(self)
47
57
  end
@@ -1,3 +1,3 @@
1
1
  module Furnace
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: furnace
3
3
  version: !ruby/object:Gem::Version
4
- hash: 148493004
4
+ hash: 246344429
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Peter Zotov
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-26 00:00:00 Z
18
+ date: 2012-01-28 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: Furnace is a static code analysis framework for dynamic languages, aimed at efficient type and behavior inference.