falafel 0.0.2.1 → 0.0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cfg.rb +8 -8
- data/lib/cfg_helper/chaining_free.rb +69 -0
- data/lib/cfg_helper/chomsky_nf.rb +155 -0
- data/lib/cfg_helper/chomsky_nf_simplifier.rb +117 -0
- data/lib/cfg_helper/cyk.rb +100 -0
- data/lib/cfg_helper/epsilon_free.rb +98 -0
- metadata +6 -6
- data/lib/chaining_free.rb +0 -67
- data/lib/chomsky_nf.rb +0 -153
- data/lib/chomsky_nf_simplifier.rb +0 -115
- data/lib/cyk.rb +0 -98
- data/lib/epsilon_free.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 754e7aa62a91e0f16ed875b859d8e208379df55509569dbd5d5a38e93b874fbe
|
4
|
+
data.tar.gz: bcbb64b7d4627514c2e3560b80e15ccc3f0dbf721448b96247188f778d8f897c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fda2266343ea3c9c919898f8ebd0381329189cf6eb08f3e281ba20e30e950d6d1bac77356ff91263c7377e77c808c1b0cb224b705dda1f3970b67b294242d9f
|
7
|
+
data.tar.gz: b9e9c7b8552c38d9a3f09143e68ac4cb78b5527899052a79ec975e1b6a08b45cef97158c3bc785f9f05bb1267943c38b7fc37a8575ea86ed409bd72a4f5317d8
|
data/lib/cfg.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'cyk'
|
4
|
-
require_relative 'chomsky_nf'
|
5
|
-
require_relative 'epsilon_free'
|
6
|
-
require_relative 'chaining_free'
|
3
|
+
require_relative 'cfg_helper/cyk'
|
4
|
+
require_relative 'cfg_helper/chomsky_nf'
|
5
|
+
require_relative 'cfg_helper/epsilon_free'
|
6
|
+
require_relative 'cfg_helper/chaining_free'
|
7
7
|
|
8
8
|
# Samll CFG impletation
|
9
9
|
# Generate words and check if words are the @lang
|
@@ -35,26 +35,26 @@ class CFG
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def epsilon_free(custom_rule)
|
38
|
-
e_free = EpsilonFree.new
|
38
|
+
e_free = CFGHelper::EpsilonFree.new
|
39
39
|
@rules_ef_res = e_free.run custom_rule || @rules
|
40
40
|
@rules_ef = e_free.rebuild_rules @rules, @rules_ef_res
|
41
41
|
end
|
42
42
|
|
43
43
|
def chaining_free
|
44
|
-
c_free = ChainingFree.new
|
44
|
+
c_free = CFGHelper::ChainingFree.new
|
45
45
|
@rules_cf_res = c_free.run @rules, @vars
|
46
46
|
@rules_cf = c_free.rebuild_rules @rules_cf_res
|
47
47
|
end
|
48
48
|
|
49
49
|
def chomsky_nf(custom_rule)
|
50
|
-
chomsky = ChomskyNF.new
|
50
|
+
chomsky = CFGHelper::ChomskyNF.new
|
51
51
|
rules = chomsky.run custom_rule || @rules, @alphabet
|
52
52
|
|
53
53
|
@chomsky_nf_rules = chomsky.simplify rules
|
54
54
|
end
|
55
55
|
|
56
56
|
def cyk_run(word)
|
57
|
-
cyk = CYK.new word, @chomsky_nf_rules
|
57
|
+
cyk = CFGHelper::CYK.new word, @chomsky_nf_rules
|
58
58
|
|
59
59
|
cyk.run
|
60
60
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CFGHelper
|
4
|
+
# remove chaining rules from CFG
|
5
|
+
class ChainingFree
|
6
|
+
def run(rules, vars)
|
7
|
+
@rules = rules
|
8
|
+
@vars = vars
|
9
|
+
c_r = _chaining_relation
|
10
|
+
|
11
|
+
# build transivity relation from _chaining_relation(K) as A
|
12
|
+
# remove _chaining_relation from rules
|
13
|
+
_rebuild_rules + c_r.map { |r| _transitivity_relation r }.reduce(:concat) - c_r
|
14
|
+
end
|
15
|
+
|
16
|
+
def rebuild_rules(current_rules)
|
17
|
+
res = {}
|
18
|
+
|
19
|
+
@vars.each { |v| res[v] = [] }
|
20
|
+
|
21
|
+
current_rules.each do |l, r|
|
22
|
+
res[l] << r.chars unless res[l].include? r.chars
|
23
|
+
end
|
24
|
+
|
25
|
+
res
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _chaining_relation
|
31
|
+
rules = _rebuild_rules
|
32
|
+
|
33
|
+
rules & @vars.product(@vars)
|
34
|
+
end
|
35
|
+
|
36
|
+
def _rebuild_rules
|
37
|
+
result = []
|
38
|
+
@rules.each do |var, rule|
|
39
|
+
rule.map do |r|
|
40
|
+
result << [var, r.join]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
def _transitivity_relation(pair)
|
48
|
+
transitive_relation = [pair]
|
49
|
+
|
50
|
+
new_pairs = []
|
51
|
+
|
52
|
+
_rebuild_rules.each do |a, b|
|
53
|
+
next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
|
54
|
+
|
55
|
+
new_pairs << [pair[0], b]
|
56
|
+
end
|
57
|
+
|
58
|
+
transitive_relation.concat new_pairs
|
59
|
+
end
|
60
|
+
|
61
|
+
def _new_pairs(rules)
|
62
|
+
rules.each do |a, b|
|
63
|
+
next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
|
64
|
+
|
65
|
+
new_pairs << [pair[0], b]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'chomsky_nf_simplifier'
|
4
|
+
|
5
|
+
module CFGHelper
|
6
|
+
# convert to Chomsky Normal Form
|
7
|
+
class ChomskyNF
|
8
|
+
def run(rules, alphabet)
|
9
|
+
new_rules = {}
|
10
|
+
@rules = rules
|
11
|
+
@alphabet = alphabet
|
12
|
+
|
13
|
+
# add cases like (A, a), (B, b)
|
14
|
+
_add_single_vars new_rules
|
15
|
+
|
16
|
+
# change rules like (X, aX) to (X, AX)
|
17
|
+
_handle_simple_rule new_rules
|
18
|
+
|
19
|
+
_handle_all_rule new_rules
|
20
|
+
end
|
21
|
+
|
22
|
+
def simplify(rules)
|
23
|
+
simplifier = CFGHelper::ChomskyNFSimplifier.new
|
24
|
+
|
25
|
+
simplifier.run rules, @alphabet
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _add_single_vars(new_rules)
|
31
|
+
@rules.values.reduce(:concat).each do |rule|
|
32
|
+
rule.select { |var| var == var.downcase }.each do |r|
|
33
|
+
new_rules[r.upcase] = r
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def _handle_simple_rule(new_rules)
|
39
|
+
@rules.each do |var, rule|
|
40
|
+
new_rules[var] = []
|
41
|
+
rule.each do |r|
|
42
|
+
next if r.empty?
|
43
|
+
|
44
|
+
new_rules[var] << r.map(&:upcase)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def _handle_all_rule(new_rules)
|
50
|
+
new_rules_buffer = []
|
51
|
+
|
52
|
+
new_rules.each do |var, rules|
|
53
|
+
next if _is_character? rules
|
54
|
+
|
55
|
+
new_rules_buffer << _new_rules_buffer(rules, var)
|
56
|
+
end
|
57
|
+
|
58
|
+
new_rules.merge! new_rules_buffer.reduce(&:merge)
|
59
|
+
end
|
60
|
+
|
61
|
+
def _is_character?(value)
|
62
|
+
value.is_a?(String) && value.length == 1
|
63
|
+
end
|
64
|
+
|
65
|
+
def _new_rules_buffer(rules, var)
|
66
|
+
helper_vars = _helper_vars rules
|
67
|
+
helper_vars.merge! @alphabet.each_with_object({}) { |ele, meme| meme[ele.capitalize] = ele.capitalize }
|
68
|
+
|
69
|
+
_new_rules rules, var, helper_vars
|
70
|
+
end
|
71
|
+
|
72
|
+
def _check_simplify(rule, holder, current_var)
|
73
|
+
return false unless rule.size == 1
|
74
|
+
|
75
|
+
holder[current_var] << rule.last unless holder[current_var].include? rule
|
76
|
+
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def _new_rules(rules, var, helper_vars)
|
81
|
+
holder = {}
|
82
|
+
|
83
|
+
rules.each do |rule|
|
84
|
+
rest = ''
|
85
|
+
rule.each_with_index do |head, index|
|
86
|
+
current_var = index.zero? ? var : rest
|
87
|
+
holder[current_var] ||= []
|
88
|
+
|
89
|
+
is_simple = _check_simplify rule, holder, current_var
|
90
|
+
|
91
|
+
break if is_simple
|
92
|
+
|
93
|
+
rest = _chomsky_nf_vars index, rule, helper_vars
|
94
|
+
|
95
|
+
nf_rest = "#{head}#{rest}"
|
96
|
+
|
97
|
+
if index + 2 == rule.size
|
98
|
+
if rest.nil?
|
99
|
+
rest = rule.last
|
100
|
+
nf_rest += rest
|
101
|
+
end
|
102
|
+
|
103
|
+
holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
|
104
|
+
|
105
|
+
break
|
106
|
+
else
|
107
|
+
holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
holder
|
113
|
+
end
|
114
|
+
|
115
|
+
def _chomsky_nf_vars(index, rule, helper_vars)
|
116
|
+
rule_size = rule.size
|
117
|
+
rest = rule[(index + 1)..rule_size].join
|
118
|
+
|
119
|
+
helper_vars.key(rest)
|
120
|
+
end
|
121
|
+
|
122
|
+
def _helper_vars(rules)
|
123
|
+
helper_vars = {}
|
124
|
+
|
125
|
+
rules.each do |rule|
|
126
|
+
rule.each_with_index do |_, index|
|
127
|
+
letter, rest = _chomsky_nf_helper_vars index, rule, helper_vars.keys
|
128
|
+
|
129
|
+
break if index + 2 == rule.size
|
130
|
+
|
131
|
+
next if helper_vars.values.include? rest
|
132
|
+
|
133
|
+
helper_vars[letter] = rest unless helper_vars.key?(letter) && rest.size > 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
helper_vars
|
138
|
+
end
|
139
|
+
|
140
|
+
def _chomsky_nf_helper_vars(index, rule, helper_vars)
|
141
|
+
rule_size = rule.size
|
142
|
+
letter = helper_vars.empty? ? _find_letter(index) : helper_vars.sort!.last.succ
|
143
|
+
rest = rule[(index + 1)..rule_size].join
|
144
|
+
|
145
|
+
[letter, rest]
|
146
|
+
end
|
147
|
+
|
148
|
+
def _find_letter(n)
|
149
|
+
result = 'H'
|
150
|
+
n.times { result = result.succ }
|
151
|
+
|
152
|
+
result
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CFGHelper
|
4
|
+
# simplify chomsky normal form
|
5
|
+
class ChomskyNFSimplifier
|
6
|
+
def run(new_rules, alphabet)
|
7
|
+
vars_with_one_letter = _vars_with_one_letter new_rules, alphabet
|
8
|
+
|
9
|
+
return new_rules if vars_with_one_letter.empty?
|
10
|
+
|
11
|
+
extendible_rules = _find_extendible_rules new_rules, vars_with_one_letter.keys
|
12
|
+
|
13
|
+
return new_rules if extendible_rules.empty?
|
14
|
+
|
15
|
+
missing_rules = _missing_rules extendible_rules, vars_with_one_letter
|
16
|
+
|
17
|
+
_add_missing_rules missing_rules, new_rules
|
18
|
+
|
19
|
+
new_rules
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def _vars_with_one_letter(new_rules, alphabet)
|
25
|
+
vars = {}
|
26
|
+
|
27
|
+
new_rules.each do |key, values|
|
28
|
+
# skip simple rules like (A, a), (B, b),...
|
29
|
+
next if values.size == 1
|
30
|
+
|
31
|
+
letters = values.select { |val| _is_letter val, alphabet }.uniq
|
32
|
+
vars[key] = letters
|
33
|
+
end
|
34
|
+
|
35
|
+
vars
|
36
|
+
end
|
37
|
+
|
38
|
+
def _is_letter(val, alphabet)
|
39
|
+
val.size == 1 && alphabet.include?(val.downcase)
|
40
|
+
end
|
41
|
+
|
42
|
+
def _find_extendible_rules(new_rules, rules_keys)
|
43
|
+
extendible = {}
|
44
|
+
|
45
|
+
filterd_rules = new_rules.reject { |_, rules| rules.is_a?(String) }
|
46
|
+
|
47
|
+
filterd_rules.each do |letter, rules|
|
48
|
+
rules_keys.each do |rules_key|
|
49
|
+
rules.each do |rule|
|
50
|
+
next unless rule.include?(rules_key)
|
51
|
+
|
52
|
+
extendible[letter] ||= []
|
53
|
+
extendible[letter] << rule
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
extendible
|
59
|
+
end
|
60
|
+
|
61
|
+
def _missing_rules(extendible_rules, vars)
|
62
|
+
res = {}
|
63
|
+
|
64
|
+
extendible_rules.each do |var_rule, nfs|
|
65
|
+
nfs.each do |nf|
|
66
|
+
vars.each do |match_char, replacement_chars|
|
67
|
+
replacement_chars.each do |rc|
|
68
|
+
res[var_rule] ||= []
|
69
|
+
res[var_rule] << _combinations(nf, rc, match_char)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
res[var_rule] = res[var_rule]&.flatten&.uniq
|
74
|
+
|
75
|
+
_check_special_case nf, res, var_rule, vars
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
res
|
80
|
+
end
|
81
|
+
|
82
|
+
def _combinations(original_string, replacement_char, match_char)
|
83
|
+
combinations = []
|
84
|
+
|
85
|
+
0.upto(1) do |i|
|
86
|
+
0.upto(1) do |j|
|
87
|
+
new_rule = original_string.dup
|
88
|
+
new_rule[0] = replacement_char if i == 1 && original_string[0] == match_char
|
89
|
+
new_rule[1] = replacement_char if j == 1 && original_string[1] == match_char
|
90
|
+
combinations << new_rule
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
combinations.uniq
|
95
|
+
end
|
96
|
+
|
97
|
+
def _add_missing_rules(missing_rules, new_rules)
|
98
|
+
missing_rules.each do |key, val|
|
99
|
+
next if val.nil?
|
100
|
+
|
101
|
+
new_rules[key].concat(val).uniq!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def _check_special_case(nf, res, var_rule, vars)
|
106
|
+
vars.each do |match_char, combi|
|
107
|
+
next unless nf == match_char * 2
|
108
|
+
|
109
|
+
missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
|
110
|
+
|
111
|
+
res[var_rule].concat(missing_rules)
|
112
|
+
end
|
113
|
+
|
114
|
+
res
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CFGHelper
|
4
|
+
# Implemention of cyk algo.
|
5
|
+
class CYK
|
6
|
+
attr_reader :matrix, :is_in_l
|
7
|
+
|
8
|
+
def initialize(word, custom_rule)
|
9
|
+
@word = word
|
10
|
+
@rules = custom_rule.select { |_, val| val.is_a?(Array) }
|
11
|
+
@helper_vars = @rules.values.flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
_cyk_fill_diagonal
|
16
|
+
_cyk_fill_matrix
|
17
|
+
@is_in_l = _is_in_l?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def _cyk_fill_diagonal
|
23
|
+
wrd_lng = @word.length
|
24
|
+
@matrix = wrd_lng.times.map { wrd_lng.times.map { [] } }
|
25
|
+
|
26
|
+
(0..wrd_lng - 1).each do |index|
|
27
|
+
@matrix[index][index] = @word[index].upcase
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def _cyk_fill_matrix
|
32
|
+
@matrix.size.times do |limiter|
|
33
|
+
(0...@matrix.length - limiter).each do |i|
|
34
|
+
j = i + limiter
|
35
|
+
|
36
|
+
next if i == j
|
37
|
+
|
38
|
+
helper_var = _helper_var i, j
|
39
|
+
matrix_ele_info = _matrix_ele_info i, j
|
40
|
+
@matrix[i][j] = _add_to_matrix helper_var, matrix_ele_info
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def _helper_var(row, col)
|
46
|
+
res = '0'
|
47
|
+
|
48
|
+
(row..col - 1).step(1) do |h|
|
49
|
+
p_ = @matrix[row][h]
|
50
|
+
q_ = @matrix[h + 1][col]
|
51
|
+
res = "#{p_}#{q_}"
|
52
|
+
|
53
|
+
break if @helper_vars.include? res
|
54
|
+
end
|
55
|
+
|
56
|
+
res
|
57
|
+
end
|
58
|
+
|
59
|
+
def _matrix_ele_info(i, j)
|
60
|
+
res = {}
|
61
|
+
|
62
|
+
res['i'] = i
|
63
|
+
res['j'] = j
|
64
|
+
res['row_c'] = @matrix.count
|
65
|
+
|
66
|
+
res
|
67
|
+
end
|
68
|
+
|
69
|
+
def _add_to_matrix(helper_var, matrix_ele_info)
|
70
|
+
matrix_ele = '0'
|
71
|
+
return if helper_var == matrix_ele
|
72
|
+
|
73
|
+
keys = []
|
74
|
+
|
75
|
+
@rules.each do |key, vals|
|
76
|
+
vals.each do |val|
|
77
|
+
keys << key if val == helper_var
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
_matrix_ele keys, matrix_ele_info
|
82
|
+
end
|
83
|
+
|
84
|
+
def _matrix_ele(keys, matrix_ele_info)
|
85
|
+
return 'S' if keys.include?('S') && _is_last_element?(matrix_ele_info)
|
86
|
+
|
87
|
+
return keys.last if keys.size == 1
|
88
|
+
|
89
|
+
keys.reject { |key| key == 'S' }.last || '0'
|
90
|
+
end
|
91
|
+
|
92
|
+
def _is_last_element?(matrix_ele_info)
|
93
|
+
matrix_ele_info['i'].zero? && matrix_ele_info['j'] == matrix_ele_info['row_c'] - 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def _is_in_l?
|
97
|
+
@matrix.first.last == 'S'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CFGHelper
|
4
|
+
# used to clear a CFG from the epsilon rules
|
5
|
+
class EpsilonFree
|
6
|
+
def run(rules)
|
7
|
+
epsilon = _epsilon rules
|
8
|
+
rules_ef = {}
|
9
|
+
|
10
|
+
return if epsilon.empty?
|
11
|
+
|
12
|
+
epsilon.each do |e_class|
|
13
|
+
cleared_rules = _cleared_rules rules[e_class]
|
14
|
+
epsilon_free_word = cleared_rules.map(&:join)
|
15
|
+
grammer = _grammer_epsilon_free epsilon_free_word, e_class
|
16
|
+
rules_ef[e_class] = grammer
|
17
|
+
end
|
18
|
+
|
19
|
+
rules_ef
|
20
|
+
end
|
21
|
+
|
22
|
+
def rebuild_rules(current_rules, rules_epsilon_free)
|
23
|
+
missing_vars = current_rules.keys - rules_epsilon_free.keys
|
24
|
+
|
25
|
+
res = {}
|
26
|
+
rules_epsilon_free.each do |key, rules|
|
27
|
+
res[key] = []
|
28
|
+
rules.each do |rule|
|
29
|
+
res[key] << rule.chars
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
missing_vars.each { |v| res[v] = current_rules[v] }
|
34
|
+
|
35
|
+
res
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def _epsilon(rules)
|
41
|
+
res = rules.map do |var, rule|
|
42
|
+
next unless rule.include? []
|
43
|
+
|
44
|
+
var
|
45
|
+
end
|
46
|
+
res.reject(&:nil?)
|
47
|
+
end
|
48
|
+
|
49
|
+
def _cleared_rules(rules)
|
50
|
+
rules.reject(&:empty?)
|
51
|
+
end
|
52
|
+
|
53
|
+
def _grammer_epsilon_free(epsilon_free_word, e_class)
|
54
|
+
rules_epsilon_location = _new_rules_epsilon_location epsilon_free_word, e_class
|
55
|
+
|
56
|
+
res = []
|
57
|
+
rules_epsilon_location.map do |rule, possibilities|
|
58
|
+
possibilities.each do |possibility|
|
59
|
+
res << _remove_e_class(possibility, rule.split(''))
|
60
|
+
end
|
61
|
+
|
62
|
+
res << epsilon_free_word.delete(e_class) if possibilities.size == 1
|
63
|
+
end
|
64
|
+
|
65
|
+
res.uniq.reject(&:nil?)
|
66
|
+
end
|
67
|
+
|
68
|
+
def _new_rules_epsilon_location(s_rule, e_class)
|
69
|
+
res = {}
|
70
|
+
s_rule.each do |rule|
|
71
|
+
s_index = []
|
72
|
+
rule.each_char.with_index { |char, index| s_index << index if char == e_class }
|
73
|
+
res[rule] = _power_set(s_index)
|
74
|
+
end
|
75
|
+
|
76
|
+
res
|
77
|
+
end
|
78
|
+
|
79
|
+
def _power_set(set)
|
80
|
+
if set.empty?
|
81
|
+
[[]]
|
82
|
+
else
|
83
|
+
element = set[0]
|
84
|
+
subsets = _power_set set[1..]
|
85
|
+
subsets + subsets.map { |subset| [element] + subset }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def _remove_e_class(possibility, cleared_rules)
|
90
|
+
return cleared_rules.join '' if possibility.empty?
|
91
|
+
|
92
|
+
new_rule = cleared_rules.join ''
|
93
|
+
possibility.reverse.each { |index| new_rule.slice! index }
|
94
|
+
|
95
|
+
new_rule
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falafel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.2.
|
4
|
+
version: 0.0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abed Alkedda
|
@@ -17,12 +17,12 @@ extensions: []
|
|
17
17
|
extra_rdoc_files: []
|
18
18
|
files:
|
19
19
|
- lib/cfg.rb
|
20
|
-
- lib/chaining_free.rb
|
21
|
-
- lib/chomsky_nf.rb
|
22
|
-
- lib/chomsky_nf_simplifier.rb
|
23
|
-
- lib/cyk.rb
|
20
|
+
- lib/cfg_helper/chaining_free.rb
|
21
|
+
- lib/cfg_helper/chomsky_nf.rb
|
22
|
+
- lib/cfg_helper/chomsky_nf_simplifier.rb
|
23
|
+
- lib/cfg_helper/cyk.rb
|
24
|
+
- lib/cfg_helper/epsilon_free.rb
|
24
25
|
- lib/dfa.rb
|
25
|
-
- lib/epsilon_free.rb
|
26
26
|
- lib/falafel.rb
|
27
27
|
- lib/pump.rb
|
28
28
|
- lib/rx.rb
|
data/lib/chaining_free.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# remove chaining rules from CFG
|
4
|
-
class ChainingFree
|
5
|
-
def run(rules, vars)
|
6
|
-
@rules = rules
|
7
|
-
@vars = vars
|
8
|
-
c_r = _chaining_relation
|
9
|
-
|
10
|
-
# build transivity relation from _chaining_relation(K) as A
|
11
|
-
# remove _chaining_relation from rules
|
12
|
-
_rebuild_rules + c_r.map { |r| _transitivity_relation r }.reduce(:concat) - c_r
|
13
|
-
end
|
14
|
-
|
15
|
-
def rebuild_rules(current_rules)
|
16
|
-
res = {}
|
17
|
-
|
18
|
-
@vars.each { |v| res[v] = [] }
|
19
|
-
|
20
|
-
current_rules.each do |l, r|
|
21
|
-
res[l] << r.chars unless res[l].include? r.chars
|
22
|
-
end
|
23
|
-
|
24
|
-
res
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def _chaining_relation
|
30
|
-
rules = _rebuild_rules
|
31
|
-
|
32
|
-
rules & @vars.product(@vars)
|
33
|
-
end
|
34
|
-
|
35
|
-
def _rebuild_rules
|
36
|
-
result = []
|
37
|
-
@rules.each do |var, rule|
|
38
|
-
rule.map do |r|
|
39
|
-
result << [var, r.join]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
result
|
44
|
-
end
|
45
|
-
|
46
|
-
def _transitivity_relation(pair)
|
47
|
-
transitive_relation = [pair]
|
48
|
-
|
49
|
-
new_pairs = []
|
50
|
-
|
51
|
-
_rebuild_rules.each do |a, b|
|
52
|
-
next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
|
53
|
-
|
54
|
-
new_pairs << [pair[0], b]
|
55
|
-
end
|
56
|
-
|
57
|
-
transitive_relation.concat new_pairs
|
58
|
-
end
|
59
|
-
|
60
|
-
def _new_pairs(rules)
|
61
|
-
rules.each do |a, b|
|
62
|
-
next unless pair[1] == a && !transitive_relation.include?([pair[0], b])
|
63
|
-
|
64
|
-
new_pairs << [pair[0], b]
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
data/lib/chomsky_nf.rb
DELETED
@@ -1,153 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'chomsky_nf_simplifier'
|
4
|
-
|
5
|
-
# convert to Chomsky Normal Form
|
6
|
-
class ChomskyNF
|
7
|
-
def run(rules, alphabet)
|
8
|
-
new_rules = {}
|
9
|
-
@rules = rules
|
10
|
-
@alphabet = alphabet
|
11
|
-
|
12
|
-
# add cases like (A, a), (B, b)
|
13
|
-
_add_single_vars new_rules
|
14
|
-
|
15
|
-
# change rules like (X, aX) to (X, AX)
|
16
|
-
_handle_simple_rule new_rules
|
17
|
-
|
18
|
-
_handle_all_rule new_rules
|
19
|
-
end
|
20
|
-
|
21
|
-
def simplify(rules)
|
22
|
-
simplifier = ChomskyNFSimplifier.new
|
23
|
-
|
24
|
-
simplifier.run rules, @alphabet
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def _add_single_vars(new_rules)
|
30
|
-
@rules.values.reduce(:concat).each do |rule|
|
31
|
-
rule.select { |var| var == var.downcase }.each do |r|
|
32
|
-
new_rules[r.upcase] = r
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def _handle_simple_rule(new_rules)
|
38
|
-
@rules.each do |var, rule|
|
39
|
-
new_rules[var] = []
|
40
|
-
rule.each do |r|
|
41
|
-
next if r.empty?
|
42
|
-
|
43
|
-
new_rules[var] << r.map(&:upcase)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def _handle_all_rule(new_rules)
|
49
|
-
new_rules_buffer = []
|
50
|
-
|
51
|
-
new_rules.each do |var, rules|
|
52
|
-
next if _is_character? rules
|
53
|
-
|
54
|
-
new_rules_buffer << _new_rules_buffer(rules, var)
|
55
|
-
end
|
56
|
-
|
57
|
-
new_rules.merge! new_rules_buffer.reduce(&:merge)
|
58
|
-
end
|
59
|
-
|
60
|
-
def _is_character?(value)
|
61
|
-
value.is_a?(String) && value.length == 1
|
62
|
-
end
|
63
|
-
|
64
|
-
def _new_rules_buffer(rules, var)
|
65
|
-
helper_vars = _helper_vars rules
|
66
|
-
helper_vars.merge! @alphabet.each_with_object({}) { |ele, meme| meme[ele.capitalize] = ele.capitalize }
|
67
|
-
|
68
|
-
_new_rules rules, var, helper_vars
|
69
|
-
end
|
70
|
-
|
71
|
-
def _check_simplify(rule, holder, current_var)
|
72
|
-
return false unless rule.size == 1
|
73
|
-
|
74
|
-
holder[current_var] << rule.last unless holder[current_var].include? rule
|
75
|
-
|
76
|
-
true
|
77
|
-
end
|
78
|
-
|
79
|
-
def _new_rules(rules, var, helper_vars)
|
80
|
-
holder = {}
|
81
|
-
|
82
|
-
rules.each do |rule|
|
83
|
-
rest = ''
|
84
|
-
rule.each_with_index do |head, index|
|
85
|
-
current_var = index.zero? ? var : rest
|
86
|
-
holder[current_var] ||= []
|
87
|
-
|
88
|
-
is_simple = _check_simplify rule, holder, current_var
|
89
|
-
|
90
|
-
break if is_simple
|
91
|
-
|
92
|
-
rest = _chomsky_nf_vars index, rule, helper_vars
|
93
|
-
|
94
|
-
nf_rest = "#{head}#{rest}"
|
95
|
-
|
96
|
-
if index + 2 == rule.size
|
97
|
-
if rest.nil?
|
98
|
-
rest = rule.last
|
99
|
-
nf_rest += rest
|
100
|
-
end
|
101
|
-
|
102
|
-
holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
|
103
|
-
|
104
|
-
break
|
105
|
-
else
|
106
|
-
holder[current_var] << nf_rest unless holder[current_var].include? nf_rest
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
holder
|
112
|
-
end
|
113
|
-
|
114
|
-
def _chomsky_nf_vars(index, rule, helper_vars)
|
115
|
-
rule_size = rule.size
|
116
|
-
rest = rule[(index + 1)..rule_size].join
|
117
|
-
|
118
|
-
helper_vars.key(rest)
|
119
|
-
end
|
120
|
-
|
121
|
-
def _helper_vars(rules)
|
122
|
-
helper_vars = {}
|
123
|
-
|
124
|
-
rules.each do |rule|
|
125
|
-
rule.each_with_index do |_, index|
|
126
|
-
letter, rest = _chomsky_nf_helper_vars index, rule, helper_vars.keys
|
127
|
-
|
128
|
-
break if index + 2 == rule.size
|
129
|
-
|
130
|
-
next if helper_vars.values.include? rest
|
131
|
-
|
132
|
-
helper_vars[letter] = rest unless helper_vars.key?(letter) && rest.size > 1
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
helper_vars
|
137
|
-
end
|
138
|
-
|
139
|
-
def _chomsky_nf_helper_vars(index, rule, helper_vars)
|
140
|
-
rule_size = rule.size
|
141
|
-
letter = helper_vars.empty? ? _find_letter(index) : helper_vars.sort!.last.succ
|
142
|
-
rest = rule[(index + 1)..rule_size].join
|
143
|
-
|
144
|
-
[letter, rest]
|
145
|
-
end
|
146
|
-
|
147
|
-
def _find_letter(n)
|
148
|
-
result = 'H'
|
149
|
-
n.times { result = result.succ }
|
150
|
-
|
151
|
-
result
|
152
|
-
end
|
153
|
-
end
|
@@ -1,115 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# simplify chomsky normal form
|
4
|
-
class ChomskyNFSimplifier
|
5
|
-
def run(new_rules, alphabet)
|
6
|
-
vars_with_one_letter = _vars_with_one_letter new_rules, alphabet
|
7
|
-
|
8
|
-
return new_rules if vars_with_one_letter.empty?
|
9
|
-
|
10
|
-
extendible_rules = _find_extendible_rules new_rules, vars_with_one_letter.keys
|
11
|
-
|
12
|
-
return new_rules if extendible_rules.empty?
|
13
|
-
|
14
|
-
missing_rules = _missing_rules extendible_rules, vars_with_one_letter
|
15
|
-
|
16
|
-
_add_missing_rules missing_rules, new_rules
|
17
|
-
|
18
|
-
new_rules
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def _vars_with_one_letter(new_rules, alphabet)
|
24
|
-
vars = {}
|
25
|
-
|
26
|
-
new_rules.each do |key, values|
|
27
|
-
# skip simple rules like (A, a), (B, b),...
|
28
|
-
next if values.size == 1
|
29
|
-
|
30
|
-
letters = values.select { |val| _is_letter val, alphabet }.uniq
|
31
|
-
vars[key] = letters
|
32
|
-
end
|
33
|
-
|
34
|
-
vars
|
35
|
-
end
|
36
|
-
|
37
|
-
def _is_letter(val, alphabet)
|
38
|
-
val.size == 1 && alphabet.include?(val.downcase)
|
39
|
-
end
|
40
|
-
|
41
|
-
def _find_extendible_rules(new_rules, rules_keys)
|
42
|
-
extendible = {}
|
43
|
-
|
44
|
-
filterd_rules = new_rules.reject { |_, rules| rules.is_a?(String) }
|
45
|
-
|
46
|
-
filterd_rules.each do |letter, rules|
|
47
|
-
rules_keys.each do |rules_key|
|
48
|
-
rules.each do |rule|
|
49
|
-
next unless rule.include?(rules_key)
|
50
|
-
|
51
|
-
extendible[letter] ||= []
|
52
|
-
extendible[letter] << rule
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
extendible
|
58
|
-
end
|
59
|
-
|
60
|
-
def _missing_rules(extendible_rules, vars)
|
61
|
-
res = {}
|
62
|
-
|
63
|
-
extendible_rules.each do |var_rule, nfs|
|
64
|
-
nfs.each do |nf|
|
65
|
-
vars.each do |match_char, replacement_chars|
|
66
|
-
replacement_chars.each do |rc|
|
67
|
-
res[var_rule] ||= []
|
68
|
-
res[var_rule] << _combinations(nf, rc, match_char)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
res[var_rule] = res[var_rule]&.flatten&.uniq
|
73
|
-
|
74
|
-
_check_special_case nf, res, var_rule, vars
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
res
|
79
|
-
end
|
80
|
-
|
81
|
-
def _combinations(original_string, replacement_char, match_char)
|
82
|
-
combinations = []
|
83
|
-
|
84
|
-
0.upto(1) do |i|
|
85
|
-
0.upto(1) do |j|
|
86
|
-
new_rule = original_string.dup
|
87
|
-
new_rule[0] = replacement_char if i == 1 && original_string[0] == match_char
|
88
|
-
new_rule[1] = replacement_char if j == 1 && original_string[1] == match_char
|
89
|
-
combinations << new_rule
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
combinations.uniq
|
94
|
-
end
|
95
|
-
|
96
|
-
def _add_missing_rules(missing_rules, new_rules)
|
97
|
-
missing_rules.each do |key, val|
|
98
|
-
next if val.nil?
|
99
|
-
|
100
|
-
new_rules[key].concat(val).uniq!
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def _check_special_case(nf, res, var_rule, vars)
|
105
|
-
vars.each do |match_char, combi|
|
106
|
-
next unless nf == match_char * 2
|
107
|
-
|
108
|
-
missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
|
109
|
-
|
110
|
-
res[var_rule].concat(missing_rules)
|
111
|
-
end
|
112
|
-
|
113
|
-
res
|
114
|
-
end
|
115
|
-
end
|
data/lib/cyk.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Implemention of cyk algo.
|
4
|
-
class CYK
|
5
|
-
attr_reader :matrix, :is_in_l
|
6
|
-
|
7
|
-
def initialize(word, custom_rule)
|
8
|
-
@word = word
|
9
|
-
@rules = custom_rule.select { |_, val| val.is_a?(Array) }
|
10
|
-
@helper_vars = @rules.values.flatten
|
11
|
-
end
|
12
|
-
|
13
|
-
def run
|
14
|
-
_cyk_fill_diagonal
|
15
|
-
_cyk_fill_matrix
|
16
|
-
@is_in_l = _is_in_l?
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def _cyk_fill_diagonal
|
22
|
-
wrd_lng = @word.length
|
23
|
-
@matrix = wrd_lng.times.map { wrd_lng.times.map { [] } }
|
24
|
-
|
25
|
-
(0..wrd_lng - 1).each do |index|
|
26
|
-
@matrix[index][index] = @word[index].upcase
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def _cyk_fill_matrix
|
31
|
-
@matrix.size.times do |limiter|
|
32
|
-
(0...@matrix.length - limiter).each do |i|
|
33
|
-
j = i + limiter
|
34
|
-
|
35
|
-
next if i == j
|
36
|
-
|
37
|
-
helper_var = _helper_var i, j
|
38
|
-
matrix_ele_info = _matrix_ele_info i, j
|
39
|
-
@matrix[i][j] = _add_to_matrix helper_var, matrix_ele_info
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def _helper_var(row, col)
|
45
|
-
res = '0'
|
46
|
-
|
47
|
-
(row..col - 1).step(1) do |h|
|
48
|
-
p_ = @matrix[row][h]
|
49
|
-
q_ = @matrix[h + 1][col]
|
50
|
-
res = "#{p_}#{q_}"
|
51
|
-
|
52
|
-
break if @helper_vars.include? res
|
53
|
-
end
|
54
|
-
|
55
|
-
res
|
56
|
-
end
|
57
|
-
|
58
|
-
def _matrix_ele_info(i, j)
|
59
|
-
res = {}
|
60
|
-
|
61
|
-
res['i'] = i
|
62
|
-
res['j'] = j
|
63
|
-
res['row_c'] = @matrix.count
|
64
|
-
|
65
|
-
res
|
66
|
-
end
|
67
|
-
|
68
|
-
def _add_to_matrix(helper_var, matrix_ele_info)
|
69
|
-
matrix_ele = '0'
|
70
|
-
return if helper_var == matrix_ele
|
71
|
-
|
72
|
-
keys = []
|
73
|
-
|
74
|
-
@rules.each do |key, vals|
|
75
|
-
vals.each do |val|
|
76
|
-
keys << key if val == helper_var
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
_matrix_ele keys, matrix_ele_info
|
81
|
-
end
|
82
|
-
|
83
|
-
def _matrix_ele(keys, matrix_ele_info)
|
84
|
-
return 'S' if keys.include?('S') && _is_last_element?(matrix_ele_info)
|
85
|
-
|
86
|
-
return keys.last if keys.size == 1
|
87
|
-
|
88
|
-
keys.reject { |key| key == 'S' }.last || '0'
|
89
|
-
end
|
90
|
-
|
91
|
-
def _is_last_element?(matrix_ele_info)
|
92
|
-
matrix_ele_info['i'].zero? && matrix_ele_info['j'] == matrix_ele_info['row_c'] - 1
|
93
|
-
end
|
94
|
-
|
95
|
-
def _is_in_l?
|
96
|
-
@matrix.first.last == 'S'
|
97
|
-
end
|
98
|
-
end
|
data/lib/epsilon_free.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# used to clear a CFG from the epsilon rules
|
4
|
-
class EpsilonFree
|
5
|
-
def run(rules)
|
6
|
-
epsilon = _epsilon rules
|
7
|
-
rules_ef = {}
|
8
|
-
|
9
|
-
return if epsilon.empty?
|
10
|
-
|
11
|
-
epsilon.each do |e_class|
|
12
|
-
cleared_rules = _cleared_rules rules[e_class]
|
13
|
-
epsilon_free_word = cleared_rules.map(&:join)
|
14
|
-
grammer = _grammer_epsilon_free epsilon_free_word, e_class
|
15
|
-
rules_ef[e_class] = grammer
|
16
|
-
end
|
17
|
-
|
18
|
-
rules_ef
|
19
|
-
end
|
20
|
-
|
21
|
-
def rebuild_rules(current_rules, rules_epsilon_free)
|
22
|
-
missing_vars = current_rules.keys - rules_epsilon_free.keys
|
23
|
-
|
24
|
-
res = {}
|
25
|
-
rules_epsilon_free.each do |key, rules|
|
26
|
-
res[key] = []
|
27
|
-
rules.each do |rule|
|
28
|
-
res[key] << rule.chars
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
missing_vars.each { |v| res[v] = current_rules[v] }
|
33
|
-
|
34
|
-
res
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def _epsilon(rules)
|
40
|
-
res = rules.map do |var, rule|
|
41
|
-
next unless rule.include? []
|
42
|
-
|
43
|
-
var
|
44
|
-
end
|
45
|
-
res.reject(&:nil?)
|
46
|
-
end
|
47
|
-
|
48
|
-
def _cleared_rules(rules)
|
49
|
-
rules.reject(&:empty?)
|
50
|
-
end
|
51
|
-
|
52
|
-
def _grammer_epsilon_free(epsilon_free_word, e_class)
|
53
|
-
rules_epsilon_location = _new_rules_epsilon_location epsilon_free_word, e_class
|
54
|
-
|
55
|
-
res = []
|
56
|
-
rules_epsilon_location.map do |rule, possibilities|
|
57
|
-
possibilities.each do |possibility|
|
58
|
-
res << _remove_e_class(possibility, rule.split(''))
|
59
|
-
end
|
60
|
-
|
61
|
-
res << epsilon_free_word.delete(e_class) if possibilities.size == 1
|
62
|
-
end
|
63
|
-
|
64
|
-
res.uniq.reject(&:nil?)
|
65
|
-
end
|
66
|
-
|
67
|
-
def _new_rules_epsilon_location(s_rule, e_class)
|
68
|
-
res = {}
|
69
|
-
s_rule.each do |rule|
|
70
|
-
s_index = []
|
71
|
-
rule.each_char.with_index { |char, index| s_index << index if char == e_class }
|
72
|
-
res[rule] = _power_set(s_index)
|
73
|
-
end
|
74
|
-
|
75
|
-
res
|
76
|
-
end
|
77
|
-
|
78
|
-
def _power_set(set)
|
79
|
-
if set.empty?
|
80
|
-
[[]]
|
81
|
-
else
|
82
|
-
element = set[0]
|
83
|
-
subsets = _power_set set[1..]
|
84
|
-
subsets + subsets.map { |subset| [element] + subset }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def _remove_e_class(possibility, cleared_rules)
|
89
|
-
return cleared_rules.join '' if possibility.empty?
|
90
|
-
|
91
|
-
new_rule = cleared_rules.join ''
|
92
|
-
possibility.reverse.each { |index| new_rule.slice! index }
|
93
|
-
|
94
|
-
new_rule
|
95
|
-
end
|
96
|
-
end
|