summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-08-18 17:04:06 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-08-18 17:04:06 +0000
commit9e5d0d861f1f082c0c8632e62aa018972cc533bb (patch)
tree293b52c9757c1bff3793839abe49cce300424b94 /test
parent7017b63b81dd2ab35c6f33d9dd01357480b77b9a (diff)
sentence.rb documented.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13108 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'test')
-rw-r--r--test/ruby/sentence.rb290
-rw-r--r--test/ruby/test_assignment.rb2
-rw-r--r--test/ruby/test_yield.rb4
3 files changed, 281 insertions, 15 deletions
diff --git a/test/ruby/sentence.rb b/test/ruby/sentence.rb
index 3697fbea10..3eaaf9c11a 100644
--- a/test/ruby/sentence.rb
+++ b/test/ruby/sentence.rb
@@ -1,30 +1,204 @@
-# sentence
+# == sentence library
+#
+# = Features
+#
+# * syntax based sentences generation
+# * sentence operations such as substitution.
+#
+# = Example
+#
+# Some arithmetic expressions using "+", "-", "*" and "/" are generated as follows.
+#
+# require 'sentence'
+# Sentence.each({
+# :exp => [["num"],
+# [:exp, "+", :exp],
+# [:exp, "-", :exp],
+# [:exp, "*", :exp],
+# [:exp, "/", :exp]]
+# }, :exp, 2) {|sent| p sent }
+# #=>
+# #<Sentence: "num">
+# #<Sentence: ("num") "+" ("num")>
+# #<Sentence: ("num") "+" (("num") "+" ("num"))>
+# #<Sentence: ("num") "+" (("num") "-" ("num"))>
+# #<Sentence: ("num") "+" (("num") "*" ("num"))>
+# #<Sentence: ("num") "+" (("num") "/" ("num"))>
+# #<Sentence: (("num") "+" ("num")) "+" ("num")>
+# ...
+#
+# Sentence.each takes 3 arguments.
+# The first argument is the syntax for the expressions.
+# The second argument, :exp, is a generating nonterminal.
+# The third argument, 2, limits derivation to restrict results finitely.
+#
+# Some arithmetic expressions including parenthesis can be generated as follows.
+#
+# synax = {
+# :factor => [["n"],
+# ["(", :exp, ")"]],
+# :term => [[:factor],
+# [:term, "*", :factor],
+# [:term, "/", :factor]],
+# :exp => [[:term],
+# [:exp, "+", :term],
+# [:exp, "-", :term]]
+# }
+# Sentence.each(syntax, :exp, 2) {|sent| p sent }
+# #=>
+# #<Sentence: (("n"))>
+# #<Sentence: (("(" ((("n"))) ")"))>
+# #<Sentence: (("(" ((("(" ((("n"))) ")"))) ")"))>
+# #<Sentence: (("(" (((("n")) "*" ("n"))) ")"))>
+# #<Sentence: (("(" (((("n")) "/" ("n"))) ")"))>
+# #<Sentence: (("(" (((("n"))) "+" (("n"))) ")"))>
+# #<Sentence: (("(" (((("n"))) "-" (("n"))) ")"))>
+# #<Sentence: ((("n")) "*" ("n"))>
+# #<Sentence: ((("n")) "*" ("(" ((("n"))) ")"))>
+# ...
+#
+# Sentence#to_s can be used to concatenate strings
+# in a sentence:
+#
+# Sentence.each(syntax, :exp, 2) {|sent| p sent.to_s }
+# #=>
+# "n"
+# "(n)"
+# "((n))"
+# "(n*n)"
+# "(n/n)"
+# "(n+n)"
+# "(n-n)"
+# "n*n"
+# "n*(n)"
+# ...
+#
+# Sentence class represents a tree with string leaves.
+#
class Sentence
+ # _ary_ represents a tree.
+ # It should be a possibly nested array which contains strings.
+ #
+ # Note that _ary_ is not copied.
+ # Don't modify _ary_ after the sentence object is instantiated.
+ #
+ # Sentence.new(["a", "pen"])
+ # #<Sentence: "a" "pen">
+ #
+ # Sentence.new(["I", "have", ["a", "pen"]])
+ # #<Sentence: "I" "have" ("a" "pen")>
+ #
def initialize(ary)
@sent = ary
end
+ # returns a string which is concatenation of all strings.
+ # No separator is used.
+ #
+ # Sentence.new(["2", "+", "3"]).to_s
+ # "2+3"
+ #
+ # Sentence.new(["2", "+", ["3", "*", "5"]]).to_s
+ # "2+3*5"
+ #
def to_s
@sent.join('')
end
+ # returns a string which is concatenation of all strings separated by _sep_.
+ # If _sep_ is not given, single space is used.
+ #
+ # Sentence.new(["I", "have", ["a", "pen"]]).join
+ # "I have a pen"
+ #
+ # Sentence.new(["I", "have", ["a", "pen"]]).join("/")
+ # "I/have/a/pen"
+ #
+ # Sentence.new(["a", [], "b"]).join("/")
+ # "a/b"
+ #
+ def join(sep=' ')
+ @sent.flatten.join(sep)
+ end
+
+ # returns a tree as a nested array.
+ #
+ # Note that the result is not copied.
+ # Don't modify the result.
+ #
+ # Sentence.new([["foo", "bar"], "baz"]).to_a
+ # #=> [["foo", "bar"], "baz"]
+ #
def to_a
@sent
end
+ # returns <i>i</i>th element as a sentence or string.
+ #
+ # s = Sentence.new([["foo", "bar"], "baz"])
+ # s #=> #<Sentence: ("foo" "bar") "baz">
+ # s[0] #=> #<Sentence: "foo" "bar">
+ # s[1] #=> "baz"
+ #
def [](i)
- Sentence.new(@sent[i])
+ e = @sent[i]
+ e.respond_to?(:to_ary) ? Sentence.new(e) : e
+ end
+
+ # returns the number of top level elements.
+ #
+ # Sentence.new(%w[foo bar]).length
+ # #=> 2
+ #
+ # Sentence.new([%w[2 * 7], "+", %w[3 * 5]]).length
+ # #=> 3
+ #
+ def length
+ @sent.length
end
def inspect
- "#<#{self.class}: #{@sent.inspect}>"
+ "#<#{self.class}: #{inner_inspect(@sent, '')}>"
end
+ # :stopdoc:
+ def inner_inspect(ary, r)
+ first = true
+ ary.each {|obj|
+ r << ' ' if !first
+ first = false
+ if obj.respond_to? :to_ary
+ r << '('
+ inner_inspect(obj, r)
+ r << ')'
+ else
+ r << obj.inspect
+ end
+ }
+ r
+ end
+ # :startdoc:
+
+ # returns new sentence object which
+ # _target_ is substituted by the block.
+ #
+ # Sentence#subst invokes <tt>_target_ === _string_</tt> for each
+ # string in the sentence.
+ # The strings which === returns true are substituted by the block.
+ # The block is invoked with the substituting string.
+ #
+ # Sentence.new(%w[2 + 3]).subst("+") { "*" }
+ # #<Sentence: "2" "*" "3">
+ #
+ # Sentence.new(%w[2 + 3]).subst(/\A\d+\z/) {|s| ((s.to_i)*2).to_s }
+ # #=> #<Sentence: "4" "+" "6">
+ #
def subst(target, &b)
Sentence.new(subst_rec(@sent, target, &b))
end
+ # :stopdoc:
def subst_rec(obj, target, &b)
if obj.respond_to? :to_ary
a = []
@@ -36,19 +210,25 @@ class Sentence
obj
end
end
+ # :startdoc:
+ # find a subsentence and return it.
+ # The block is invoked for each subsentence in preorder manner.
+ # The first subsentence which the block returns true is returned.
+ #
+ # Sentence.new([%w[2 * 7], "+", %w[3 * 5]]).find_subtree {|s| s[1] == "*" }
+ # #=> #<Sentence: "2" "*" "7">
+ #
def find_subtree(&b)
- if r = find_subtree_rec(@sent, &b)
- Sentence.new(r)
- else
- nil
- end
+ find_subtree_rec(@sent, &b)
end
+ # :stopdoc:
def find_subtree_rec(obj, &b)
if obj.respond_to? :to_ary
- if b.call obj
- return obj
+ s = Sentence.new(obj)
+ if b.call s
+ return s
else
obj.each {|e|
r = find_subtree_rec(e, &b)
@@ -58,15 +238,33 @@ class Sentence
end
nil
end
+ # :startdoc:
+ # returns a new sentence object which expands according to the condition
+ # given by the block.
+ #
+ # The block is invoked for each subsentence.
+ # The subsentences which the block returns true are
+ # expanded into parent.
+ #
+ # s = Sentence.new([%w[2 * 7], "+", %w[3 * 5]])
+ # #=> #<Sentence: ("2" "*" "7") "+" ("3" "*" "5")>
+ #
+ # s.expand { true }
+ # #=> #<Sentence: "2" "*" "7" "+" "3" "*" "5">
+ #
+ # s.expand {|s| s[0] == "3" }
+ # #=> #<Sentence: (("2" "*" "7") "+" "3" "*" "5")>
+ #
def expand(&b)
Sentence.new(expand_rec(@sent, &b))
end
+ # :stopdoc:
def expand_rec(obj, r=[], &b)
- #puts "#{obj.inspect}\t\t#{r.inspect}"
if obj.respond_to? :to_ary
- if b.call obj
+ s = Sentence.new(obj)
+ if b.call s
obj.each {|o|
expand_rec(o, r, &b)
}
@@ -82,13 +280,80 @@ class Sentence
end
r
end
+ # :startdoc:
+ # Sentence.each generates sentences
+ # by deriving the start symbol _sym_ using _syntax_.
+ # The derivation is restricted by an positive integer _limit_ to
+ # avoid infinite generation.
+ #
+ # Sentence.each yields the block with a generated sentence.
+ #
+ # Sentence.each({
+ # :exp => [["n"],
+ # [:exp, "+", :exp],
+ # [:exp, "*", :exp]]
+ # }, :exp, 1) {|sent| p sent }
+ # #=>
+ # #<Sentence: "n">
+ # #<Sentence: ("n") "+" ("n")>
+ # #<Sentence: ("n") "*" ("n")>
+ #
+ # Sentence.each({
+ # :exp => [["n"],
+ # [:exp, "+", :exp],
+ # [:exp, "*", :exp]]
+ # }, :exp, 2) {|sent| p sent }
+ # #=>
+ # #<Sentence: "n">
+ # #<Sentence: ("n") "+" ("n")>
+ # #<Sentence: ("n") "+" (("n") "+" ("n"))>
+ # #<Sentence: ("n") "+" (("n") "*" ("n"))>
+ # #<Sentence: (("n") "+" ("n")) "+" ("n")>
+ # #<Sentence: (("n") "*" ("n")) "+" ("n")>
+ # #<Sentence: ("n") "*" ("n")>
+ # #<Sentence: ("n") "*" (("n") "+" ("n"))>
+ # #<Sentence: ("n") "*" (("n") "*" ("n"))>
+ # #<Sentence: (("n") "+" ("n")) "*" ("n")>
+ # #<Sentence: (("n") "*" ("n")) "*" ("n")>
+ #
def Sentence.each(syntax, sym, limit)
Gen.new(syntax).each_tree(sym, limit) {|tree|
yield Sentence.new(tree)
}
end
+ # Sentence.expand_syntax returns an expanded syntax:
+ # * no underivable rule
+ # * no rule which derives only to empty sequence indirectly
+ # * no rule which is derivable to empty and non-empty
+ # * no channel rule
+ #
+ # Note that the rules which can derive empty and non-empty
+ # sequences are modified to derive only non-empty sequences.
+ #
+ # Sentence.expand_syntax({
+ # :underivable => [[:underivable]],
+ # :just_empty1 => [[]],
+ # :just_empty2 => [[:just_empty1, :just_empty1]],
+ # :empty_or_not => [[], ["foo"]],
+ # :empty_or_not_2 => [[:empty_or_not, :empty_or_not]],
+ # :data => [["a", "b"], ["c", "d"]],
+ # :channel => [[:data]],
+ # })
+ # #=>
+ # {:channel=>[["a", "b"], ["c", "d"]],
+ # :data=>[["a", "b"], ["c", "d"]],
+ # :empty_or_not=>[["foo"]],
+ # :empty_or_not_2=>[[], ["foo"], ["foo", "foo"]],
+ # :just_empty1=>[],
+ # :just_empty2=>[]}
+ #
+ def Sentence.expand_syntax(syntax)
+ Sentence::Gen.expand_syntax(syntax)
+ end
+
+ # :stopdoc:
class Gen
def Gen.each_tree(syntax, sym, limit, &b)
Gen.new(syntax).each_tree(sym, limit, &b)
@@ -333,6 +598,7 @@ class Sentence
nil
end
end
+ # :startdoc:
end
diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb
index 3a1e014714..c6b42d1b36 100644
--- a/test/ruby/test_assignment.rb
+++ b/test/ruby/test_assignment.rb
@@ -658,7 +658,7 @@ class TestAssignmentGen < Test::Unit::TestCase
end
def test_assignment
- syntax = Sentence::Gen.expand_syntax(Syntax)
+ syntax = Sentence.expand_syntax(Syntax)
Sentence.each(syntax, :xassign, 3) {|assign|
assign, vars = rename_var(assign)
sent = assign.to_s
diff --git a/test/ruby/test_yield.rb b/test/ruby/test_yield.rb
index 8bc4972bd3..3ae6881b87 100644
--- a/test/ruby/test_yield.rb
+++ b/test/ruby/test_yield.rb
@@ -322,14 +322,14 @@ class TestRubyYieldGen < Test::Unit::TestCase
end
def test_yield
- syntax = Sentence::Gen.expand_syntax(Syntax)
+ syntax = Sentence.expand_syntax(Syntax)
Sentence.each(syntax, :test_proc, 4) {|t|
check_nofork(t)
}
end
def test_yield_lambda
- syntax = Sentence::Gen.expand_syntax(Syntax)
+ syntax = Sentence.expand_syntax(Syntax)
Sentence.each(syntax, :test_lambda, 4) {|t|
check_nofork(t, true)
}