falafel 0.0.0.1 → 0.0.1.1
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 +96 -0
- data/lib/chaining_free.rb +67 -0
- data/lib/chomsky_nf.rb +153 -0
- data/lib/chomsky_nf_simplifier.rb +110 -0
- data/lib/cyk.rb +74 -0
- data/lib/dfa.rb +16 -13
- data/lib/epsilon_free.rb +96 -0
- data/lib/falafel.rb +12 -2
- data/lib/rx.rb +29 -19
- metadata +14 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 796b681163a3d9d57b47cadcb95f1d5e2e9af1e97a0b841b2e31ddc9c725769b
|
4
|
+
data.tar.gz: 3811e2bc49acfee5aa9a4e4f27569df022fb68c2ddafbafa10b43a7c92afe4d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96bf15557a50f7a81c8a71d200a0876f7fc445d64d017d3c2011fa088457d61e05327e8ea39c7147d349049a8e8b3811a815c92e04f414109c4505782a8c6613
|
7
|
+
data.tar.gz: 45573ad63969221aed699f9412d45409c4f72a26c1ec3e83e3bcb4c5fba03c6fb9bdae80d61b3c0a046e4c25a66a00e7c64c00a1841142de675e7eaa45158876
|
data/lib/cfg.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'cyk'
|
4
|
+
require_relative 'chomsky_nf'
|
5
|
+
require_relative 'epsilon_free'
|
6
|
+
require_relative 'chaining_free'
|
7
|
+
|
8
|
+
# Samll CFG impletation
|
9
|
+
# Generate words and check if words are the @lang
|
10
|
+
# generates random words
|
11
|
+
class CFG
|
12
|
+
attr_accessor :start_var, :rules
|
13
|
+
attr_reader :rules_ef, :rules_cf, :rnd_words, :reachables,
|
14
|
+
:rules_ef_res, :rules_cf_res, :chomsky_nf_rules,
|
15
|
+
:cyk_matrix, :is_in_l
|
16
|
+
|
17
|
+
def initialize(alphabet, vars, start_var, rules)
|
18
|
+
@start_var = start_var
|
19
|
+
@rules = _rules rules
|
20
|
+
@rnd_words = []
|
21
|
+
@reachables = []
|
22
|
+
@vars = vars
|
23
|
+
@alphabet = alphabet
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_words(count)
|
27
|
+
@rnd_words = []
|
28
|
+
|
29
|
+
loop do
|
30
|
+
word = _expand @start_var
|
31
|
+
|
32
|
+
@rnd_words << word unless @rnd_words.include? word
|
33
|
+
|
34
|
+
break if @rnd_words.size == count
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def var_reachable?
|
39
|
+
@rules[@start_var].each do |rule|
|
40
|
+
@vars.each { |var| @reachables << var if rule.include? var }
|
41
|
+
end
|
42
|
+
@reachables.uniq!
|
43
|
+
end
|
44
|
+
|
45
|
+
def var_productive?
|
46
|
+
''
|
47
|
+
end
|
48
|
+
|
49
|
+
def var_reduced?
|
50
|
+
''
|
51
|
+
end
|
52
|
+
|
53
|
+
def epsilon_free
|
54
|
+
e_free = EpsilonFree.new
|
55
|
+
@rules_ef_res = e_free.run @rules
|
56
|
+
@rules_ef = e_free.rebuild_rules @rules, @rules_ef_res
|
57
|
+
end
|
58
|
+
|
59
|
+
def chaining_free
|
60
|
+
c_free = ChainingFree.new
|
61
|
+
@rules_cf_res = c_free.run @rules, @vars
|
62
|
+
@rules_cf = c_free.rebuild_rules @rules_cf_res
|
63
|
+
end
|
64
|
+
|
65
|
+
def chomsky_nf(custom_rule)
|
66
|
+
chomsky = ChomskyNF.new
|
67
|
+
rules = chomsky.run custom_rule || @rules, @alphabet
|
68
|
+
|
69
|
+
@chomsky_nf_rules = chomsky.simplify rules
|
70
|
+
end
|
71
|
+
|
72
|
+
def cyk_run(word)
|
73
|
+
cyk = CYK.new word, @chomsky_nf_rules
|
74
|
+
|
75
|
+
cyk.run
|
76
|
+
|
77
|
+
@cyk_matrix = cyk.matrix
|
78
|
+
@is_in_l = cyk.is_in_l
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def _rules(rules)
|
84
|
+
rules.each_value { |k| k.each_with_index { |item, index| k[index] = item[0].is_a?(String) ? item[0].split('') : item } }
|
85
|
+
end
|
86
|
+
|
87
|
+
def _expand(symbol)
|
88
|
+
production = @rules[symbol]
|
89
|
+
|
90
|
+
return symbol if production.nil?
|
91
|
+
|
92
|
+
rhs = production.sample # pick up a random element from array
|
93
|
+
|
94
|
+
rhs.map { |s| _expand(s) }.join
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,67 @@
|
|
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
ADDED
@@ -0,0 +1,153 @@
|
|
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
|
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
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# simplify chomsky normal form
|
4
|
+
class ChomskyNFSimplifier
|
5
|
+
def run(new_rules, alphabet)
|
6
|
+
var_has_one_letter = _var_has_one_letter new_rules, alphabet
|
7
|
+
|
8
|
+
return new_rules if var_has_one_letter.empty?
|
9
|
+
|
10
|
+
rules = _find_modify_rules new_rules, var_has_one_letter
|
11
|
+
|
12
|
+
return new_rules if rules.empty?
|
13
|
+
|
14
|
+
missing_rules = _generate_missing_rules rules, var_has_one_letter
|
15
|
+
|
16
|
+
_add_missing_rules missing_rules, new_rules
|
17
|
+
|
18
|
+
new_rules
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def _var_has_one_letter(new_rules, alphabet)
|
24
|
+
vars = {}
|
25
|
+
|
26
|
+
new_rules.each do |key, values|
|
27
|
+
next if values.size == 1
|
28
|
+
|
29
|
+
values.each do |val|
|
30
|
+
next unless val.size == 1 && alphabet.include?(val.downcase)
|
31
|
+
|
32
|
+
vars[key] ||= []
|
33
|
+
vars[key] << val
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
vars
|
38
|
+
end
|
39
|
+
|
40
|
+
def _find_modify_rules(new_rules, vars)
|
41
|
+
modify = {}
|
42
|
+
|
43
|
+
new_rules.each do |key, val|
|
44
|
+
next if val.is_a?(String)
|
45
|
+
|
46
|
+
val.each do |v|
|
47
|
+
vars.each_key do |k|
|
48
|
+
next unless v.include?(k)
|
49
|
+
|
50
|
+
res = val.select { |x| x.include? k }
|
51
|
+
|
52
|
+
modify[key] ||= []
|
53
|
+
modify[key] << res
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
modify
|
59
|
+
end
|
60
|
+
|
61
|
+
def _generate_missing_rules(rules, vars)
|
62
|
+
res = {}
|
63
|
+
|
64
|
+
rules.each do |var_rule, nfs|
|
65
|
+
nfs.flatten.each do |nf|
|
66
|
+
vars.each do |match_char, replacement_chars|
|
67
|
+
replacement_chars.each do |rc|
|
68
|
+
new_rules = _combinations nf, rc, match_char
|
69
|
+
res[var_rule] ||= []
|
70
|
+
res[var_rule] << new_rules
|
71
|
+
end
|
72
|
+
end
|
73
|
+
_check_special_case nf, res, var_rule, vars
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
res
|
78
|
+
end
|
79
|
+
|
80
|
+
def _combinations(original_string, replacement_char, match_char)
|
81
|
+
combinations = []
|
82
|
+
|
83
|
+
0.upto(1) do |i|
|
84
|
+
0.upto(1) do |j|
|
85
|
+
new_rule = original_string.dup
|
86
|
+
new_rule[0] = replacement_char if i == 1 && original_string[0] == match_char
|
87
|
+
new_rule[1] = replacement_char if j == 1 && original_string[1] == match_char
|
88
|
+
combinations << new_rule
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
combinations.uniq
|
93
|
+
end
|
94
|
+
|
95
|
+
def _add_missing_rules(missing_rules, new_rules)
|
96
|
+
missing_rules.each { |key, val| new_rules[key] = val.flatten.uniq! }
|
97
|
+
end
|
98
|
+
|
99
|
+
def _check_special_case(nf, res, var_rule, vars)
|
100
|
+
vars.each do |match_char, combi|
|
101
|
+
next unless nf == match_char * 2
|
102
|
+
|
103
|
+
missing_rules = combi.map { |lft| combi.map { |rgt| "#{lft}#{rgt}" } }.flatten
|
104
|
+
|
105
|
+
res[var_rule] << missing_rules
|
106
|
+
end
|
107
|
+
|
108
|
+
res
|
109
|
+
end
|
110
|
+
end
|
data/lib/cyk.rb
ADDED
@@ -0,0 +1,74 @@
|
|
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?
|
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
|
+
|
39
|
+
@matrix[i][j] = _add_to_matrix helper_var
|
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 _add_to_matrix(rule)
|
59
|
+
res = '0'
|
60
|
+
return if rule == res
|
61
|
+
|
62
|
+
@rules.each do |key, vals|
|
63
|
+
vals.each do |val|
|
64
|
+
res = key if val == rule
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
res
|
69
|
+
end
|
70
|
+
|
71
|
+
def _is_in_l?
|
72
|
+
@is_in_l = @matrix.first.last == 'S'
|
73
|
+
end
|
74
|
+
end
|
data/lib/dfa.rb
CHANGED
@@ -53,26 +53,29 @@ class DFA < Falafel
|
|
53
53
|
(finals & states.keys).each_with_object({}) { |elt, memo| memo[elt] = states[elt] }
|
54
54
|
end
|
55
55
|
|
56
|
-
def _r(finals, states,
|
57
|
-
r
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
56
|
+
def _r(finals, states, steps)
|
57
|
+
r = []
|
58
|
+
last_r = steps["R#{steps.size - 1}"]
|
59
|
+
|
60
|
+
last_r.each do |pair|
|
61
|
+
@delta_star.each_key do |c|
|
62
|
+
pair_frst = pair[0]
|
63
|
+
pair_lst = pair[1]
|
64
|
+
|
65
|
+
ri_set = _ri_set states, c, states.key(pair_frst), states.key(pair_lst)
|
66
|
+
|
67
|
+
break unless last_r.include? ri_set
|
68
|
+
|
69
|
+
r << [pair_frst, pair_lst] unless r.include? [pair_frst, pair_lst]
|
66
70
|
end
|
67
71
|
end
|
68
|
-
r.uniq!
|
69
72
|
|
70
73
|
r
|
71
74
|
end
|
72
75
|
|
73
76
|
def _steps_builder(finals, states, steps)
|
74
77
|
0.upto(1_000) do |l|
|
75
|
-
r = _r finals, states, steps
|
78
|
+
r = _r finals, states, steps
|
76
79
|
steps["R#{l + 1}"] = r
|
77
80
|
|
78
81
|
return steps if steps["R#{l}"] == r
|
@@ -95,6 +98,6 @@ class DFA < Falafel
|
|
95
98
|
lft = states[(_image [p], @delta_star[c])[0]]
|
96
99
|
rgt = states[(_image [q], @delta_star[c])[0]]
|
97
100
|
|
98
|
-
[
|
101
|
+
[lft, rgt]
|
99
102
|
end
|
100
103
|
end
|
data/lib/epsilon_free.rb
ADDED
@@ -0,0 +1,96 @@
|
|
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
|
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
|
data/lib/falafel.rb
CHANGED
@@ -2,9 +2,13 @@
|
|
2
2
|
|
3
3
|
require_relative 'rx'
|
4
4
|
|
5
|
-
#
|
5
|
+
# NFA to DFA
|
6
6
|
# DFA to REG
|
7
7
|
# Minimize DFA
|
8
|
+
# Remove epsilons from CFG
|
9
|
+
# Remove chaining from CFG
|
10
|
+
# Find a chomsky normal form fro CFG
|
11
|
+
# Apply CYK alogrithm on CFG and solve word problem
|
8
12
|
class Falafel
|
9
13
|
def self.new
|
10
14
|
instance = allocate
|
@@ -86,7 +90,7 @@ class Falafel
|
|
86
90
|
end
|
87
91
|
|
88
92
|
def nfa_to_reg
|
89
|
-
RX.new { |rx| rx.build @states, @delta_star }
|
93
|
+
RX.new { |rx| rx.build @states, @delta_star, @start, @finals }
|
90
94
|
end
|
91
95
|
|
92
96
|
def dfa_to_min(automat)
|
@@ -106,6 +110,12 @@ class Falafel
|
|
106
110
|
Pump.new lang: nil, word: nil, n: 2, k: 20
|
107
111
|
end
|
108
112
|
|
113
|
+
def cfg(alphabet, vars_set, start_var, rules)
|
114
|
+
require_relative 'cfg'
|
115
|
+
|
116
|
+
CFG.new alphabet, vars_set, start_var, rules
|
117
|
+
end
|
118
|
+
|
109
119
|
private
|
110
120
|
|
111
121
|
# alle bilder eine menge m unter r
|
data/lib/rx.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# Regex string builder
|
4
4
|
class RX
|
5
|
+
attr_reader :final_reg
|
6
|
+
|
5
7
|
def self.new
|
6
8
|
instance = allocate
|
7
9
|
yield instance
|
@@ -9,31 +11,52 @@ class RX
|
|
9
11
|
instance
|
10
12
|
end
|
11
13
|
|
12
|
-
def build(states, delta_star)
|
14
|
+
def build(states, delta_star, start, finals)
|
13
15
|
@states = states
|
14
16
|
@delta_star = delta_star
|
17
|
+
@start = start
|
18
|
+
@finals = finals
|
15
19
|
@empty = "\u2205"
|
16
20
|
_to_reg
|
17
21
|
end
|
18
22
|
|
23
|
+
def print_matrix
|
24
|
+
@steps.each do |key, value|
|
25
|
+
puts key
|
26
|
+
value.transpose.each do |column|
|
27
|
+
column.each do |element|
|
28
|
+
printf("\t|%-#{value.map { |c| c.max_by(&:length).length }.max}s", element)
|
29
|
+
end
|
30
|
+
puts '|'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
_final_reg
|
34
|
+
end
|
35
|
+
|
36
|
+
def _final_reg
|
37
|
+
start = @start[0] - 1
|
38
|
+
final = @finals[0] - 1
|
39
|
+
lang = @steps["l#{@steps.size - 1}"]
|
40
|
+
@final_reg = lang[start][final]
|
41
|
+
end
|
42
|
+
|
19
43
|
private
|
20
44
|
|
21
45
|
def _to_reg
|
22
46
|
l0 = _l0
|
23
|
-
steps = {}
|
24
|
-
steps['l0'] = l0
|
47
|
+
@steps = {}
|
48
|
+
@steps['l0'] = l0
|
25
49
|
|
26
50
|
@states.size.times do |h|
|
27
51
|
l = []
|
28
52
|
@states.each_with_index do |_, p|
|
29
53
|
l[p] = []
|
30
54
|
@states.each_with_index do |_, q|
|
31
|
-
l[p][q] = _l steps["l#{h}"], p, q, steps.size - 1
|
55
|
+
l[p][q] = _l @steps["l#{h}"], p, q, @steps.size - 1
|
32
56
|
end
|
33
57
|
end
|
34
|
-
steps["l#{steps.size}"] = l
|
58
|
+
@steps["l#{@steps.size}"] = l
|
35
59
|
end
|
36
|
-
_print_matrix steps
|
37
60
|
end
|
38
61
|
|
39
62
|
def _l0
|
@@ -138,17 +161,4 @@ class RX
|
|
138
161
|
|
139
162
|
rgt
|
140
163
|
end
|
141
|
-
|
142
|
-
def _print_matrix(steps)
|
143
|
-
steps.each do |key, value|
|
144
|
-
puts key
|
145
|
-
max_width = value.map { |column| column.max_by(&:length).length }.max
|
146
|
-
value.transpose.each do |column|
|
147
|
-
column.each do |element|
|
148
|
-
printf("\t|%-#{max_width}s", element)
|
149
|
-
end
|
150
|
-
puts '|'
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
164
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falafel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Abed
|
7
|
+
- Abed Alkedda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -16,12 +16,19 @@ executables: []
|
|
16
16
|
extensions: []
|
17
17
|
extra_rdoc_files: []
|
18
18
|
files:
|
19
|
+
- lib/cfg.rb
|
20
|
+
- lib/chaining_free.rb
|
21
|
+
- lib/chomsky_nf.rb
|
22
|
+
- lib/chomsky_nf_simplifier.rb
|
23
|
+
- lib/cyk.rb
|
19
24
|
- lib/dfa.rb
|
25
|
+
- lib/epsilon_free.rb
|
20
26
|
- lib/falafel.rb
|
21
27
|
- lib/pump.rb
|
22
28
|
- lib/rx.rb
|
23
|
-
homepage:
|
24
|
-
licenses:
|
29
|
+
homepage: https://github.com/AbedAlkedda/AFS
|
30
|
+
licenses:
|
31
|
+
- Ruby
|
25
32
|
metadata: {}
|
26
33
|
post_install_message:
|
27
34
|
rdoc_options: []
|
@@ -31,7 +38,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
38
|
requirements:
|
32
39
|
- - ">="
|
33
40
|
- !ruby/object:Gem::Version
|
34
|
-
version: 2.
|
41
|
+
version: 2.7.0
|
35
42
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
43
|
requirements:
|
37
44
|
- - ">="
|
@@ -41,5 +48,5 @@ requirements: []
|
|
41
48
|
rubygems_version: 3.3.26
|
42
49
|
signing_key:
|
43
50
|
specification_version: 4
|
44
|
-
summary: Falafel is a gem to help you understand
|
51
|
+
summary: Falafel is a gem to help you understand Automata better, hopefully
|
45
52
|
test_files: []
|