diff --git a/test/ruby/sentgen.rb b/test/ruby/sentgen.rb index 29388e91d5..94cffdad7a 100644 --- a/test/ruby/sentgen.rb +++ b/test/ruby/sentgen.rb @@ -1,10 +1,202 @@ # sentence generator class SentGen + def SentGen.each_tree(syntax, sym, limit, &b) + SentGen.new(syntax).each_tree(sym, limit, &b) + end + + def SentGen.each_string(syntax, sym, limit, &b) + SentGen.new(syntax).each_string(sym, limit, &b) + end + def initialize(syntax) @syntax = syntax end + def self.expand_syntax(syntax) + syntax = remove_underivable_rules(syntax) + syntax = expand_justempty_rules(syntax) + syntax = remove_emptyable_rules(syntax) + syntax = expand_channel_rules(syntax) + + syntax = expand_noalt_rules(syntax) + syntax = reorder_rules(syntax) + end + + def self.remove_underivable_rules(syntax) + deribable_syms = {} + changed = true + while changed + changed = false + syntax.each {|sym, rules| + next if deribable_syms[sym] + rules.each {|rhs| + if rhs.all? {|e| String === e || deribable_syms[e] } + deribable_syms[sym] = true + changed = true + break + end + } + } + end + result = {} + syntax.each {|sym, rules| + next if !deribable_syms[sym] + rules2 = [] + rules.each {|rhs| + rules2 << rhs if rhs.all? {|e| String === e || deribable_syms[e] } + } + result[sym] = rules2.uniq + } + result + end + + def self.expand_justempty_rules(syntax) + justempty_syms = {} + changed = true + while changed + changed = false + syntax.each {|sym, rules| + next if justempty_syms[sym] + if rules.all? {|rhs| rhs.all? {|e| justempty_syms[e] } } + justempty_syms[sym] = true + changed = true + end + } + end + result = {} + syntax.each {|sym, rules| + result[sym] = rules.map {|rhs| rhs.reject {|e| justempty_syms[e] } }.uniq + } + result + end + + def self.expand_emptyable_syms(rhs, emptyable_syms) + if rhs.empty? + elsif rhs.length == 1 + if emptyable_syms[rhs[0]] + yield rhs + yield [] + else + yield rhs + end + else + butfirst = rhs[1..-1] + if emptyable_syms[rhs[0]] + expand_emptyable_syms(butfirst, emptyable_syms) {|rhs2| + yield [rhs[0]] + rhs2 + yield rhs2 + } + else + expand_emptyable_syms(butfirst, emptyable_syms) {|rhs2| + yield [rhs[0]] + rhs2 + } + end + end + end + + def self.remove_emptyable_rules(syntax) + emptyable_syms = {} + changed = true + while changed + changed = false + syntax.each {|sym, rules| + next if emptyable_syms[sym] + rules.each {|rhs| + if rhs.all? {|e| emptyable_syms[e] } + emptyable_syms[sym] = true + changed = true + break + end + } + } + end + result = {} + syntax.each {|sym, rules| + rules2 = [] + rules.each {|rhs| + expand_emptyable_syms(rhs, emptyable_syms) {|rhs2| + rules2 << rhs2 + } + } + result[sym] = rules2.uniq + } + result + end + + def self.expand_channel_rules(syntax) + channel_rules = {} + syntax.each {|sym, rules| + channel_rules[sym] = {sym=>true} + rules.each {|rhs| + if rhs.length == 1 && Symbol === rhs[0] + channel_rules[sym][rhs[0]] = true + end + } + } + changed = true + while changed + changed = false + channel_rules.each {|sym, set| + n1 = set.size + set.keys.each {|s| + set.update(channel_rules[s]) + } + n2 = set.size + changed = true if n1 < n2 + } + end + result = {} + syntax.each {|sym, rules| + rules2 = [] + channel_rules[sym].each_key {|s| + syntax[s].each {|rhs| + unless rhs.length == 1 && Symbol === rhs[0] + rules2 << rhs + end + } + } + result[sym] = rules2.uniq + } + result + end + + def self.expand_noalt_rules(syntax) + noalt_syms = {} + syntax.each {|sym, rules| + if rules.length == 1 + noalt_syms[sym] = true + end + } + result = {} + syntax.each {|sym, rules| + rules2 = [] + rules.each {|rhs| + rhs2 = [] + rhs.each {|e| + if noalt_syms[e] + rhs2.concat syntax[e][0] + else + rhs2 << e + end + } + rules2 << rhs2 + } + result[sym] = rules2.uniq + } + result + end + + def self.reorder_rules(syntax) + result = {} + syntax.each {|sym, rules| + result[sym] = rules.sort_by {|rhs| + [rhs.find_all {|e| Symbol === e }.length, rhs.length] + } + } + result + end + def each_tree(sym, limit) generate_from_sym(sym, limit) {|_, tree| yield tree @@ -25,6 +217,7 @@ class SentGen yield limit, sym else rules = @syntax[sym] + raise "undefined rule: #{sym}" if !rules rules.each {|rhs| if rhs.length == 1 || rules.length == 1 limit1 = limit diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb index 1a07575f94..33c0be027c 100644 --- a/test/ruby/test_assignment.rb +++ b/test/ruby/test_assignment.rb @@ -665,7 +665,8 @@ class TestAssignmentGen < Test::Unit::TestCase end def test_assignment - SentGen.new(Syntax).each_tree(:xassign, 3) {|assign| + syntax = SentGen.expand_syntax(Syntax) + SentGen.each_tree(syntax, :xassign, 3) {|assign| assign[0] = rename_var(assign[0]) sent = [assign].join('') bruby = do_assign(assign).to_a.sort