rlsm 1.0.0 → 1.1.0
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.
- data/{README.txt → README} +10 -23
- data/Rakefile +79 -7
- data/ext/array/array_c_ext.c +137 -0
- data/ext/array/extconf.rb +2 -0
- data/ext/binop/binop_c_ext.c +57 -0
- data/ext/binop/extconf.rb +2 -0
- data/ext/monoid/extconf.rb +2 -0
- data/ext/monoid/monoid_c_ext.c +330 -0
- data/lib/rlsm.rb +10 -14
- data/lib/rlsm/binary_operation.rb +151 -0
- data/lib/rlsm/dfa.rb +418 -602
- data/lib/rlsm/helper.rb +12 -0
- data/lib/rlsm/monoid.rb +454 -694
- data/lib/rlsm/regexp.rb +125 -0
- data/lib/rlsm/regexp_parser.rb +450 -0
- data/test/helpers.rb +66 -0
- data/test/test_binop.rb +119 -0
- data/test/test_dfa.rb +435 -0
- data/test/test_monoid.rb +552 -0
- data/test/test_regexp.rb +440 -0
- metadata +109 -37
- data/History.txt +0 -6
- data/Manifest.txt +0 -18
- data/bin/smon +0 -39
- data/data/monoids.db +0 -0
- data/lib/database.rb +0 -95
- data/lib/monkey_patching/array_ext.rb +0 -50
- data/lib/rlsm/re.rb +0 -504
- data/lib/smon/base.rb +0 -284
- data/lib/smon/db.rb +0 -98
- data/lib/smon/dot.rb +0 -65
- data/lib/smon/latex.rb +0 -313
- data/lib/smon/smon.rb +0 -183
- data/stdarb.tex +0 -118
data/History.txt
DELETED
data/Manifest.txt
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
History.txt
|
2
|
-
Manifest.txt
|
3
|
-
README.txt
|
4
|
-
Rakefile
|
5
|
-
bin/smon
|
6
|
-
data/monoids.db
|
7
|
-
lib/database.rb
|
8
|
-
lib/monkey_patching/array_ext.rb
|
9
|
-
lib/rlsm.rb
|
10
|
-
lib/rlsm/dfa.rb
|
11
|
-
lib/rlsm/monoid.rb
|
12
|
-
lib/rlsm/re.rb
|
13
|
-
lib/smon/base.rb
|
14
|
-
lib/smon/db.rb
|
15
|
-
lib/smon/dot.rb
|
16
|
-
lib/smon/latex.rb
|
17
|
-
lib/smon/smon.rb
|
18
|
-
stdarb.tex
|
data/bin/smon
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'smon', 'smon')
|
3
|
-
require 'optparse'
|
4
|
-
|
5
|
-
Version = RLSM::VERSION
|
6
|
-
out = STDOUT
|
7
|
-
path = ""
|
8
|
-
|
9
|
-
opts = OptionParser.new do |op|
|
10
|
-
op.banner = <<USEAGE
|
11
|
-
Usage: smon [-oh] [FILE1 FILE2 ...]
|
12
|
-
Executes each FILE as smon script or if no file is given
|
13
|
-
starts in interactive mode.
|
14
|
-
|
15
|
-
USEAGE
|
16
|
-
op.separator("Options:")
|
17
|
-
|
18
|
-
op.on('-p','--path PATH', "Adds PATH to the loadpath for librarys.") do |p|
|
19
|
-
path = p
|
20
|
-
end
|
21
|
-
|
22
|
-
op.on('-o', '--out FILE', "Output is redirected to FILE.") do |f|
|
23
|
-
out = File.open(f, 'w')
|
24
|
-
end
|
25
|
-
|
26
|
-
op.on_tail('-h', '--help', "This help.") { puts op; exit }
|
27
|
-
op.on_tail('--version', "Shows version.") { puts op.ver }
|
28
|
-
end
|
29
|
-
|
30
|
-
begin
|
31
|
-
opts.parse! ARGV
|
32
|
-
|
33
|
-
SMON.new :files =>ARGV, :messenger => out, :path => path
|
34
|
-
rescue => e
|
35
|
-
STDERR.puts "Error: #{e.message}"
|
36
|
-
STDERR.puts
|
37
|
-
STDERR.puts opts
|
38
|
-
exit 1
|
39
|
-
end
|
data/data/monoids.db
DELETED
Binary file
|
data/lib/database.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'rlsm'))
|
2
|
-
|
3
|
-
require "rubygems"
|
4
|
-
require "sqlite3"
|
5
|
-
|
6
|
-
class RLSM::MonoidDB
|
7
|
-
@@db = SQLite3::Database.open(File.join(File.dirname(__FILE__), '..',
|
8
|
-
'data', 'monoids.db'))
|
9
|
-
|
10
|
-
Columns = [:binop, :m_order, :num_generators,
|
11
|
-
:num_idempotents, :num_left_nulls, :num_right_nulls,
|
12
|
-
:syntactic, :idempotent, :aperiodic,
|
13
|
-
:commutative, :is_group, :has_null,
|
14
|
-
:l_trivial, :r_trivial, :d_trivial]
|
15
|
-
|
16
|
-
def self.query(query, &block)
|
17
|
-
if block_given?
|
18
|
-
@@db.execute(query, &block)
|
19
|
-
else
|
20
|
-
@@db.execute(query)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.find(params = {}, &block)
|
25
|
-
if block_given?
|
26
|
-
query construct_query(params), &block
|
27
|
-
else
|
28
|
-
query construct_query(params)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.count(params = {})
|
33
|
-
q = construct_query(params).sub('T binop F',
|
34
|
-
"T count(*), total(syntactic) F")
|
35
|
-
query(q).first.map { |x| x.to_i }
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.statistic
|
39
|
-
res = @@db.execute2 <<SQL
|
40
|
-
SELECT
|
41
|
-
m_order AS 'Order',
|
42
|
-
count(*) AS 'Total',
|
43
|
-
total(syntactic) AS 'Syntactic',
|
44
|
-
total(is_group) AS 'Groups',
|
45
|
-
total(commutative) AS 'Commutative',
|
46
|
-
total(aperiodic) AS 'Aperiodic',
|
47
|
-
total(idempotent) AS 'Idempotent'
|
48
|
-
FROM monoids
|
49
|
-
GROUP BY m_order
|
50
|
-
ORDER BY 'Order' ASC;
|
51
|
-
SQL
|
52
|
-
|
53
|
-
desc = res.shift
|
54
|
-
res.map! { |row| row.map { |x| x.to_i } }
|
55
|
-
res.unshift desc
|
56
|
-
|
57
|
-
res
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
def self.construct_query(params)
|
62
|
-
limit = ""
|
63
|
-
if params[:limit]
|
64
|
-
limit = "\nLIMIT #{params[:limit]}"
|
65
|
-
params.delete :limit
|
66
|
-
if params[:offset]
|
67
|
-
limit += " OFFSET #{params[:offset]}"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
order_by = "\nORDER BY binop #{params[:ordering] || 'ASC'}"
|
72
|
-
|
73
|
-
params.delete :ordering
|
74
|
-
|
75
|
-
q = "SELECT binop FROM monoids"
|
76
|
-
|
77
|
-
where = Columns.inject([]) do |res,col|
|
78
|
-
if params[col]
|
79
|
-
if [:binop, :m_order].include?(col) or col.to_s =~ /^num_/
|
80
|
-
res << "#{col.to_s} = #{params[col]}"
|
81
|
-
else
|
82
|
-
res << "#{col.to_s} = 1"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
res
|
87
|
-
end.join(" AND ")
|
88
|
-
|
89
|
-
if where.length > 0
|
90
|
-
q += "\nWHERE " + where
|
91
|
-
end
|
92
|
-
|
93
|
-
q + order_by + limit + ";"
|
94
|
-
end
|
95
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module RLSMArrayExt # :nodoc:
|
2
|
-
#Returns all permutations of the array.
|
3
|
-
def permutations
|
4
|
-
return [self] if size < 2
|
5
|
-
perm = []
|
6
|
-
each { |e| (self - [e]).permutations.each { |p| perm << ([e] + p) } }
|
7
|
-
perm
|
8
|
-
end
|
9
|
-
|
10
|
-
#Returns the powerset of the array (interpreted as set).
|
11
|
-
def powerset
|
12
|
-
ret = self.inject([[]]) do |acc, x|
|
13
|
-
res = []
|
14
|
-
acc.each { |s| res << s; res << ([x]+s).sort }
|
15
|
-
res
|
16
|
-
end
|
17
|
-
|
18
|
-
ret.sort_lex
|
19
|
-
end
|
20
|
-
|
21
|
-
#Sorts an array of arrays more or less lexicographical.
|
22
|
-
def sort_lex
|
23
|
-
sort { |s1, s2| s1.size == s2.size ? s1 <=> s2 : s1.size <=> s2.size }
|
24
|
-
end
|
25
|
-
|
26
|
-
#Returns the cartesian product of self and the given arrays
|
27
|
-
def product(*args)
|
28
|
-
args.inject(self.map { |x| [x] }) do |res,arr|
|
29
|
-
new = []
|
30
|
-
arr.each do |x|
|
31
|
-
new += res.map { |tup| tup += [x] }
|
32
|
-
end
|
33
|
-
new
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
#Returns all unordered pairs.
|
38
|
-
def unordered_pairs
|
39
|
-
pairs = []
|
40
|
-
(0...size-1).each do |i|
|
41
|
-
pairs |= self[i+1..-1].map { |x| [self[i],x] }
|
42
|
-
end
|
43
|
-
|
44
|
-
pairs
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class Array # :nodoc:
|
49
|
-
include RLSMArrayExt
|
50
|
-
end
|
data/lib/rlsm/re.rb
DELETED
@@ -1,504 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rlsm'))
|
2
|
-
require 'enumerator'
|
3
|
-
|
4
|
-
class RLSM::RE
|
5
|
-
LeftBracket = '('
|
6
|
-
RightBracket = ')'
|
7
|
-
Star = '*'
|
8
|
-
Union = '|'
|
9
|
-
Lambda = '&'
|
10
|
-
Specials = [LeftBracket, RightBracket, Star, Union, Lambda]
|
11
|
-
|
12
|
-
def to_s
|
13
|
-
@pattern
|
14
|
-
end
|
15
|
-
|
16
|
-
def inspect
|
17
|
-
"<#{self.class}: #@pattern>"
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(pattern = '')
|
21
|
-
if pattern == ''
|
22
|
-
@empty_set = true
|
23
|
-
elsif pattern.scan(/./).all? { |x| Specials.include? x }
|
24
|
-
@pattern = Lambda
|
25
|
-
else
|
26
|
-
@pattern = pattern
|
27
|
-
end
|
28
|
-
|
29
|
-
unless @empty_set
|
30
|
-
validate_brackets_balanced
|
31
|
-
validate_form
|
32
|
-
preparse
|
33
|
-
parse
|
34
|
-
else
|
35
|
-
@pattern = ''
|
36
|
-
@parsed = { :first => [], :last => [], :follow => [], :null => false }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
#Returns the patttern of the regexp
|
41
|
-
attr_reader :pattern
|
42
|
-
|
43
|
-
#Returns the union of this and the other re as new re.
|
44
|
-
def +(other)
|
45
|
-
#One is the empty set?
|
46
|
-
return RLSM::RE.new(@pattern) if other.pattern == ''
|
47
|
-
return RLSM::RE.new(other.pattern) if @pattern == ''
|
48
|
-
|
49
|
-
RLSM::RE.new(LeftBracket + @pattern + RightBracket +
|
50
|
-
Union +
|
51
|
-
LeftBracket + other.pattern + RightBracket)
|
52
|
-
end
|
53
|
-
|
54
|
-
#Returns the catenation of this and other re.
|
55
|
-
def *(other)
|
56
|
-
#One is the empty set?
|
57
|
-
return RLSM::RE.new if other.pattern == '' or @pattern == ''
|
58
|
-
|
59
|
-
RLSM::RE.new(LeftBracket + @pattern + RightBracket +
|
60
|
-
LeftBracket + other.pattern + RightBracket)
|
61
|
-
end
|
62
|
-
|
63
|
-
#Returns the stared re.
|
64
|
-
def star
|
65
|
-
return RLSM::RE.new if @pattern == ''
|
66
|
-
RLSM::RE.new(LeftBracket + @pattern + RightBracket + Star)
|
67
|
-
end
|
68
|
-
|
69
|
-
#Alters the re in place to the star form. Returns the altered re.
|
70
|
-
def star!
|
71
|
-
unless @pattern == ''
|
72
|
-
@pattern = LeftBracket + @pattern + RightBracket + Star
|
73
|
-
parse
|
74
|
-
end
|
75
|
-
|
76
|
-
self
|
77
|
-
end
|
78
|
-
|
79
|
-
#Returns a minimal DFA which accepts the same language.
|
80
|
-
def to_dfa
|
81
|
-
if @empty_set
|
82
|
-
RLSM::DFA.new(:alphabet => [],:states => ['0'],:initial => '0',
|
83
|
-
:finals => [], :transitions => [])
|
84
|
-
else
|
85
|
-
add_initial_state
|
86
|
-
perform_subset_construction
|
87
|
-
RLSM::DFA.create(@dfa_hash).minimize!(:rename => :new)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
#Returns the syntactic monoid to this language.
|
92
|
-
def to_monoid
|
93
|
-
to_dfa.to_monoid
|
94
|
-
end
|
95
|
-
|
96
|
-
#Returns self.
|
97
|
-
def to_re
|
98
|
-
self
|
99
|
-
end
|
100
|
-
|
101
|
-
#Returns true if the res are equal
|
102
|
-
def ==(other)
|
103
|
-
return true if @pattern == other.pattern
|
104
|
-
|
105
|
-
to_dfa.isomorph_to?(other.to_dfa)
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
def add_initial_state
|
110
|
-
@parsed[:initial] = [-1]
|
111
|
-
@parsed[:follow] |= @parsed[:initial].product(@parsed[:first])
|
112
|
-
end
|
113
|
-
|
114
|
-
def perform_subset_construction
|
115
|
-
@dfa_hash = {:transitions => [], :finals => [], :initial => '0'}
|
116
|
-
@dfa_hash[:finals] << @parsed[:initial] if @parsed[:null]
|
117
|
-
alphabet = @iso.uniq
|
118
|
-
unmarked = [@parsed[:initial]]
|
119
|
-
marked = []
|
120
|
-
until unmarked.empty?
|
121
|
-
state = unmarked.shift
|
122
|
-
marked << state
|
123
|
-
alphabet.each do |char|
|
124
|
-
nstate = move(state, char)
|
125
|
-
unmarked << nstate unless (unmarked | marked).include? nstate
|
126
|
-
if @parsed[:last].any? { |x| nstate.include? x }
|
127
|
-
@dfa_hash[:finals] << nstate unless @dfa_hash[:finals].include? nstate
|
128
|
-
end
|
129
|
-
@dfa_hash[:transitions] << [char, state, nstate]
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
@dfa_hash[:finals].map! { |x| marked.index(x).to_s }
|
134
|
-
@dfa_hash[:transitions].map! { |c,x,y| [c,marked.index(x).to_s,
|
135
|
-
marked.index(y).to_s] }
|
136
|
-
end
|
137
|
-
|
138
|
-
def move(state,c)
|
139
|
-
state.map do |x|
|
140
|
-
@parsed[:follow].find_all { |y,z| y == x and @iso[z] == c }.map do |a|
|
141
|
-
a.last
|
142
|
-
end
|
143
|
-
end.flatten.uniq.sort
|
144
|
-
end
|
145
|
-
|
146
|
-
def parse
|
147
|
-
pat, @iso = transform_pattern_to_unique_identifiers
|
148
|
-
|
149
|
-
@parsed = parse_pattern(pat)
|
150
|
-
@pattern = @parsed[:pattern]
|
151
|
-
end
|
152
|
-
|
153
|
-
def parse_pattern(pat, parent = nil)
|
154
|
-
pat = remove_surrounding_brackets pat
|
155
|
-
pat = [Lambda] if pat.all? { |x| Specials.include? x }
|
156
|
-
|
157
|
-
case type_of pat
|
158
|
-
when :term : return parse_term(pat, parent)
|
159
|
-
when :star : return parse_star(pat, parent)
|
160
|
-
when :union : return parse_union(pat, parent)
|
161
|
-
when :cat : return parse_cat(pat, parent)
|
162
|
-
else
|
163
|
-
raise REException, "Unable to parse pattern: #{pat.join}"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def parse_children(childs, parent)
|
168
|
-
childs.map { |child| parse_pattern(child, parent) }
|
169
|
-
end
|
170
|
-
|
171
|
-
def recursive_split(child, type)
|
172
|
-
if type_of(child) == type
|
173
|
-
return self.send("split_#{type}".to_sym, child)
|
174
|
-
else
|
175
|
-
return [child]
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def parse_union(p, parent)
|
180
|
-
childs = parse_children(split_union(p), parent)
|
181
|
-
childs = simplify_union(childs, parent)
|
182
|
-
|
183
|
-
#If after simplification there is only one child left, the union isn't
|
184
|
-
#needed anymore.
|
185
|
-
return childs.first if childs.size == 1
|
186
|
-
|
187
|
-
childs = sort_union(childs)
|
188
|
-
|
189
|
-
construct_union_result_from childs
|
190
|
-
end
|
191
|
-
|
192
|
-
def split_union(p)
|
193
|
-
depth = 0
|
194
|
-
splitted = [[]]
|
195
|
-
p.each do |x|
|
196
|
-
depth += count(x)
|
197
|
-
if depth == 0 and x == Union
|
198
|
-
splitted << remove_surrounding_brackets(splitted.pop)
|
199
|
-
splitted << []
|
200
|
-
else
|
201
|
-
splitted.last << x
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
splitted.inject([]) { |res,x| res | recursive_split(x, :union) }
|
206
|
-
end
|
207
|
-
|
208
|
-
def simplify_union(childs, parent)
|
209
|
-
#Check if we need an empty word, not the case if
|
210
|
-
# - parent is a star
|
211
|
-
# - some nullable choices exists
|
212
|
-
if childs.any? { |x| x[:null] and x[:pattern] != Lambda } or parent == :star
|
213
|
-
childs = childs.reject { |x| x[:pattern] == Lambda }
|
214
|
-
end
|
215
|
-
|
216
|
-
#Simplify somthing like 'a|a' to 'a'
|
217
|
-
childs.inject([]) do |res,child|
|
218
|
-
res << child unless res.any? { |x| x[:pattern] == child[:pattern] }
|
219
|
-
res
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def sort_union(childs)
|
224
|
-
childs.sort do |x1,x2|
|
225
|
-
if x1[:pattern] == Lambda
|
226
|
-
-1
|
227
|
-
elsif x2[:pattern] == Lambda
|
228
|
-
1
|
229
|
-
else
|
230
|
-
x1[:pattern] <=> x2[:pattern]
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
def construct_union_result_from(childs)
|
236
|
-
res = {}
|
237
|
-
res[:type] = :union
|
238
|
-
|
239
|
-
res[:null] = childs.any? { |x| x[:null] }
|
240
|
-
res[:first] = childs.map { |x| x[:first] }.flatten
|
241
|
-
res[:last] = childs.map { |x| x[:last] }.flatten
|
242
|
-
res[:follow] = childs.inject([]) { |r,x| r | x[:follow] }
|
243
|
-
res[:pattern] = childs.map { |x| x[:pattern] }.join(Union)
|
244
|
-
|
245
|
-
res
|
246
|
-
end
|
247
|
-
|
248
|
-
def parse_cat(p, parent)
|
249
|
-
childs = parse_children(split_cat(p), parent)
|
250
|
-
|
251
|
-
childs = simplify_cat(childs, parent)
|
252
|
-
|
253
|
-
#If after simplification there is only one child left, the cat isn't
|
254
|
-
#needed anymore.
|
255
|
-
return childs.first if childs.size == 1
|
256
|
-
|
257
|
-
construct_cat_result_from childs
|
258
|
-
end
|
259
|
-
|
260
|
-
def split_cat(p)
|
261
|
-
splitted = [[]]
|
262
|
-
depth = 0
|
263
|
-
p.each_with_index do |x,i|
|
264
|
-
depth += count(x)
|
265
|
-
if depth == 1 and x == LeftBracket
|
266
|
-
splitted << [LeftBracket]
|
267
|
-
elsif depth == 0
|
268
|
-
if p[i+1] == Star
|
269
|
-
if x == RightBracket
|
270
|
-
splitted.last << RightBracket
|
271
|
-
splitted.last << Star
|
272
|
-
splitted << []
|
273
|
-
else
|
274
|
-
splitted << [x,Star]
|
275
|
-
splitted << []
|
276
|
-
end
|
277
|
-
else
|
278
|
-
splitted.last << x unless x == Star
|
279
|
-
if x == RightBracket
|
280
|
-
last = splitted.pop
|
281
|
-
splitted << remove_surrounding_brackets(last) unless last.empty?
|
282
|
-
splitted << []
|
283
|
-
end
|
284
|
-
end
|
285
|
-
else
|
286
|
-
splitted.last << x
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
splitted.inject([]) do |res,x|
|
291
|
-
unless x.empty? or x == [Lambda]
|
292
|
-
res | recursive_split(x, :cat)
|
293
|
-
else
|
294
|
-
res
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def simplify_cat(childs, parent)
|
300
|
-
#Simplify a*a* to a*
|
301
|
-
childs = childs.inject([]) do |res, child|
|
302
|
-
unless child[:type] == :star and
|
303
|
-
res.last and res.last[:type] == :star and
|
304
|
-
child[:pattern] == res.last[:pattern]
|
305
|
-
res << child
|
306
|
-
end
|
307
|
-
|
308
|
-
res
|
309
|
-
end
|
310
|
-
|
311
|
-
#Simplify (aa*)* to a*
|
312
|
-
if parent == :star and childs.size == 2
|
313
|
-
star_exp, other = childs.partition { |x| x[:type] == :star }
|
314
|
-
unless star_exp.empty? or other.empty?
|
315
|
-
p1 = remove_surrounding_brackets(star_exp.first[:pattern].
|
316
|
-
scan(/./)[0..-2])
|
317
|
-
p2 = remove_surrounding_brackets(other.first[:pattern].
|
318
|
-
scan(/./))
|
319
|
-
|
320
|
-
if p1 == p2
|
321
|
-
return other
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
childs
|
327
|
-
end
|
328
|
-
|
329
|
-
def construct_cat_result_from(childs)
|
330
|
-
childs.map! do |x|
|
331
|
-
if x[:type] == :union
|
332
|
-
x[:pattern] = LeftBracket + x[:pattern] + RightBracket
|
333
|
-
x
|
334
|
-
else
|
335
|
-
x
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
res = {}
|
340
|
-
res[:null] = childs.all? { |x| x[:null] }
|
341
|
-
|
342
|
-
childs.each do |x|
|
343
|
-
res[:first] = (res[:first] ||= []) | x[:first]
|
344
|
-
break unless x[:null]
|
345
|
-
end
|
346
|
-
|
347
|
-
childs.reverse.each do |x|
|
348
|
-
res[:last] = (res[:last] ||= []) | x[:last]
|
349
|
-
break unless x[:null]
|
350
|
-
end
|
351
|
-
|
352
|
-
res[:follow] = childs.inject([]) { |r,x| r | x[:follow] }
|
353
|
-
|
354
|
-
(1...childs.size).each do |i|
|
355
|
-
res[:follow] |= childs[i-1][:last].product(childs[i][:first])
|
356
|
-
j = i
|
357
|
-
while childs[j][:null] and (j < childs.size - 1)
|
358
|
-
res[:follow] |= childs[i-1][:last].product(childs[j+1][:first])
|
359
|
-
j += 1
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
res[:pattern] = childs.map { |x| x[:pattern] }.join
|
364
|
-
|
365
|
-
res
|
366
|
-
end
|
367
|
-
|
368
|
-
def parse_term(pat, parent)
|
369
|
-
pat = pat.reject { |x| x == Lambda }
|
370
|
-
|
371
|
-
res = {}
|
372
|
-
|
373
|
-
res[:first] = [pat.first].compact
|
374
|
-
res[:last] = [pat.last].compact
|
375
|
-
res[:follow] = pat.enum_cons(2).to_a
|
376
|
-
res[:null] = pat.empty?
|
377
|
-
res[:pattern] = if res[:null]
|
378
|
-
Lambda
|
379
|
-
else
|
380
|
-
pat.map { |i| @iso[i] }.join
|
381
|
-
end
|
382
|
-
res
|
383
|
-
end
|
384
|
-
|
385
|
-
def parse_star(pat, parent)
|
386
|
-
pat = remove_surrounding_brackets(pat[0..-2])
|
387
|
-
child = parse_pattern(pat, :star)
|
388
|
-
|
389
|
-
if child[:pattern] == Lambda or child[:type] == :star
|
390
|
-
return child
|
391
|
-
else
|
392
|
-
res = {}
|
393
|
-
res[:type] = :star
|
394
|
-
res[:null] = true
|
395
|
-
res[:first] = child[:first]
|
396
|
-
res[:last] = child[:last]
|
397
|
-
res[:follow] = (child[:follow] | child[:last].product(child[:first]))
|
398
|
-
res[:pattern] = if child[:pattern].size > 1
|
399
|
-
LeftBracket + child[:pattern] + RightBracket + Star
|
400
|
-
else
|
401
|
-
child[:pattern] + Star
|
402
|
-
end
|
403
|
-
|
404
|
-
return res
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
def remove_surrounding_brackets(pat)
|
409
|
-
pat = pat[1..-2] while type_of(pat) == :surr_brackets
|
410
|
-
pat
|
411
|
-
end
|
412
|
-
|
413
|
-
def type_of(p)
|
414
|
-
return :term unless p.any? { |x| (Specials - [Lambda]).include? x }
|
415
|
-
|
416
|
-
unnested_characters = []
|
417
|
-
depth = 0
|
418
|
-
p.each do |x|
|
419
|
-
if x == LeftBracket
|
420
|
-
unnested_characters << x if depth == 0
|
421
|
-
depth += 1
|
422
|
-
elsif x == RightBracket
|
423
|
-
depth -= 1
|
424
|
-
unnested_characters << x if depth == 0
|
425
|
-
else
|
426
|
-
unnested_characters << x if depth == 0
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
return :union if unnested_characters.include? Union
|
431
|
-
return :star if p.size == 2 and p.last == Star and !Specials.include? p[0]
|
432
|
-
return :star if unnested_characters == [LeftBracket, RightBracket, Star]
|
433
|
-
return :surr_brackets if unnested_characters == [LeftBracket, RightBracket]
|
434
|
-
:cat
|
435
|
-
end
|
436
|
-
|
437
|
-
def transform_pattern_to_unique_identifiers
|
438
|
-
pat = @pattern.scan(/./)
|
439
|
-
iso = []
|
440
|
-
for i in (0...pat.size)
|
441
|
-
next if Specials.include? pat[i]
|
442
|
-
iso << pat[i]
|
443
|
-
pat[i] = iso.size - 1
|
444
|
-
end
|
445
|
-
|
446
|
-
[pat, iso]
|
447
|
-
end
|
448
|
-
|
449
|
-
def preparse
|
450
|
-
substitute_empty_brackets
|
451
|
-
simplify_brackets_around_singeltons
|
452
|
-
simplify_lambdastar_to_lambda
|
453
|
-
simplify_implicit_empty_set_unions
|
454
|
-
squeeze_repeated_specials
|
455
|
-
end
|
456
|
-
|
457
|
-
def squeeze_repeated_specials
|
458
|
-
@pattern.squeeze! Star
|
459
|
-
@pattern.squeeze! Union
|
460
|
-
@pattern.squeeze! Lambda
|
461
|
-
end
|
462
|
-
|
463
|
-
def simplify_implicit_empty_set_unions
|
464
|
-
@pattern.gsub!(LeftBracket + Union, LeftBracket)
|
465
|
-
@pattern.gsub!(LeftBracket + Star + Union, LeftBracket)
|
466
|
-
@pattern.gsub!(Union + Star + Union, Union)
|
467
|
-
end
|
468
|
-
|
469
|
-
def simplify_lambdastar_to_lambda
|
470
|
-
str = Lambda + Star
|
471
|
-
@pattern.gsub!(str, Lambda) while @pattern.include? str
|
472
|
-
end
|
473
|
-
|
474
|
-
def substitute_empty_brackets
|
475
|
-
@pattern.gsub! LeftBracket + RightBracket, Lambda
|
476
|
-
end
|
477
|
-
|
478
|
-
def simplify_brackets_around_singeltons
|
479
|
-
re = Regexp.new("\\#{LeftBracket}(.)\\#{RightBracket}")
|
480
|
-
@pattern.gsub!(re, '\1') while @pattern =~ re
|
481
|
-
end
|
482
|
-
|
483
|
-
def validate_form
|
484
|
-
if @pattern =~ Regexp.new("\\#{LeftBracket}\\#{Star}[^#{Specials.join}]")
|
485
|
-
raise REException, "Not wellformed. Detected '#{Regexp.last_match(0)}'"
|
486
|
-
end
|
487
|
-
end
|
488
|
-
|
489
|
-
def validate_brackets_balanced
|
490
|
-
unless 0 == @pattern.scan(/./).inject(0) do |res,x|
|
491
|
-
res += count(x)
|
492
|
-
break if res < 0
|
493
|
-
res
|
494
|
-
end
|
495
|
-
raise REException, "Unbalanced parentheses in pattern!"
|
496
|
-
end
|
497
|
-
end
|
498
|
-
|
499
|
-
def count(x)
|
500
|
-
return 1 if x == LeftBracket
|
501
|
-
return -1 if x == RightBracket
|
502
|
-
0
|
503
|
-
end
|
504
|
-
end
|