summaryrefslogtreecommitdiff
path: root/sample
diff options
context:
space:
mode:
Diffstat (limited to 'sample')
-rw-r--r--sample/README43
-rw-r--r--sample/all-ruby-quine.rb24
-rw-r--r--sample/benchmark.rb19
-rw-r--r--sample/biorhythm.rb161
-rw-r--r--sample/cal.rb262
-rw-r--r--sample/cbreak.rb12
-rw-r--r--sample/cgi-session-pstore.rb11
-rw-r--r--sample/clnt.rb12
-rw-r--r--sample/coverage.rb62
-rw-r--r--sample/dbmtest.rb14
-rw-r--r--sample/delegate.rb31
-rw-r--r--sample/dir.rb9
-rw-r--r--sample/dualstack-fetch.rb48
-rw-r--r--sample/dualstack-httpd.rb54
-rw-r--r--sample/eval.rb21
-rw-r--r--sample/export.rb2
-rw-r--r--sample/exyacc.rb34
-rw-r--r--sample/fact.rb9
-rw-r--r--sample/fib.awk8
-rw-r--r--sample/fib.pl19
-rw-r--r--sample/fib.py2
-rw-r--r--sample/fib.scm6
-rw-r--r--sample/freq.rb13
-rw-r--r--sample/from.rb160
-rw-r--r--sample/fullpath.rb16
-rw-r--r--sample/getopts.test36
-rw-r--r--sample/goodfriday.rb48
-rw-r--r--sample/inf-ruby.el310
-rw-r--r--sample/iseq_loader.rb243
-rw-r--r--sample/list.rb20
-rw-r--r--sample/list2.rb2
-rw-r--r--sample/list3.rb4
-rw-r--r--sample/logger/app.rb46
-rw-r--r--sample/logger/log.rb27
-rw-r--r--sample/logger/shifting.rb26
-rwxr-xr-xsample/mine.rb176
-rw-r--r--sample/mkproto.rb30
-rw-r--r--sample/mpart.rb42
-rw-r--r--sample/mrshtest.rb14
-rw-r--r--sample/observ.rb11
-rw-r--r--sample/occur.pl8
-rw-r--r--sample/occur.rb14
-rw-r--r--sample/occur2.rb16
-rw-r--r--sample/open3.rb12
-rw-r--r--sample/openssl/c_rehash.rb173
-rw-r--r--sample/openssl/cert2text.rb26
-rw-r--r--sample/openssl/certstore.rb158
-rw-r--r--sample/openssl/cipher.rb54
-rw-r--r--sample/openssl/crlstore.rb122
-rw-r--r--sample/openssl/echo_cli.rb44
-rw-r--r--sample/openssl/echo_svr.rb65
-rw-r--r--sample/openssl/gen_csr.rb49
-rw-r--r--sample/openssl/smime_read.rb22
-rw-r--r--sample/openssl/smime_write.rb22
-rw-r--r--sample/openssl/wget.rb34
-rwxr-xr-xsample/optparse/opttest.rb125
-rwxr-xr-xsample/optparse/subcommand.rb19
-rw-r--r--sample/philos.rb9
-rw-r--r--sample/pi.rb2
-rw-r--r--sample/prism/find_calls.rb105
-rw-r--r--sample/prism/find_comments.rb100
-rw-r--r--sample/prism/locate_nodes.rb84
-rw-r--r--sample/prism/make_tags.rb302
-rw-r--r--sample/prism/multiplex_constants.rb138
-rw-r--r--sample/prism/relocate_constants.rb43
-rw-r--r--sample/prism/visit_nodes.rb63
-rw-r--r--sample/pstore.rb19
-rw-r--r--sample/pty/expect_sample.rb58
-rw-r--r--sample/pty/script.rb37
-rw-r--r--sample/pty/shl.rb93
-rw-r--r--sample/rbc.rb1015
-rw-r--r--sample/rcs.awk54
-rw-r--r--sample/rcs.rb30
-rw-r--r--sample/rdoc/markup/rdoc2latex.rb15
-rw-r--r--sample/rdoc/markup/sample.rb40
-rw-r--r--sample/regx.rb23
-rw-r--r--sample/rinda-ring.rb22
-rw-r--r--sample/ripper/ruby2html.rb116
-rw-r--r--sample/ripper/strip-comment.rb19
-rw-r--r--sample/ruby-mode.el676
-rw-r--r--sample/rubydb2x.el104
-rw-r--r--sample/rubydb3x.el104
-rw-r--r--sample/sieve.rb22
-rw-r--r--sample/simple-bench.rb140
-rw-r--r--sample/svr.rb14
-rw-r--r--sample/tempfile.rb8
-rw-r--r--sample/test.rb1066
-rw-r--r--sample/time.rb16
-rw-r--r--sample/timeout.rb42
-rw-r--r--sample/tkbiff.rb149
-rw-r--r--sample/tkbrowse.rb79
-rw-r--r--sample/tkdialog.rb62
-rw-r--r--sample/tkfrom.rb132
-rw-r--r--sample/tkhello.rb10
-rw-r--r--sample/tkline.rb45
-rw-r--r--sample/tktimer.rb50
-rw-r--r--sample/trick2013/README.md15
-rw-r--r--sample/trick2013/kinaba/authors.markdown3
-rw-r--r--sample/trick2013/kinaba/entry.rb1
-rw-r--r--sample/trick2013/kinaba/remarks.markdown37
-rw-r--r--sample/trick2013/mame/authors.markdown3
-rw-r--r--sample/trick2013/mame/entry.rb97
-rw-r--r--sample/trick2013/mame/remarks.markdown47
-rw-r--r--sample/trick2013/shinh/authors.markdown2
-rw-r--r--sample/trick2013/shinh/entry.rb10
-rw-r--r--sample/trick2013/shinh/remarks.markdown4
-rw-r--r--sample/trick2013/yhara/authors.markdown3
-rw-r--r--sample/trick2013/yhara/entry.rb28
-rw-r--r--sample/trick2013/yhara/remarks.en.markdown23
-rw-r--r--sample/trick2013/yhara/remarks.markdown24
-rw-r--r--sample/trick2015/README.md16
-rw-r--r--sample/trick2015/eregon/authors.markdown3
-rw-r--r--sample/trick2015/eregon/entry.rb16
-rw-r--r--sample/trick2015/eregon/remarks.markdown70
-rw-r--r--sample/trick2015/kinaba/authors.markdown4
-rw-r--r--sample/trick2015/kinaba/entry.rb150
-rw-r--r--sample/trick2015/kinaba/remarks.markdown85
-rw-r--r--sample/trick2015/ksk_1/authors.markdown3
-rw-r--r--sample/trick2015/ksk_1/entry.rb1
-rw-r--r--sample/trick2015/ksk_1/remarks.markdown120
-rw-r--r--sample/trick2015/ksk_2/abnormal.cnf6
-rw-r--r--sample/trick2015/ksk_2/authors.markdown3
-rw-r--r--sample/trick2015/ksk_2/entry.rb1
-rw-r--r--sample/trick2015/ksk_2/quinn.cnf21
-rw-r--r--sample/trick2015/ksk_2/remarks.markdown204
-rw-r--r--sample/trick2015/ksk_2/sample.cnf9
-rw-r--r--sample/trick2015/ksk_2/uf20-01.cnf99
-rw-r--r--sample/trick2015/ksk_2/unsat.cnf11
-rw-r--r--sample/trick2015/monae/authors.markdown1
-rw-r--r--sample/trick2015/monae/entry.rb26
-rw-r--r--sample/trick2015/monae/remarks.markdown25
-rw-r--r--sample/trick2018/01-kinaba/authors.markdown3
-rw-r--r--sample/trick2018/01-kinaba/entry.rb8
-rw-r--r--sample/trick2018/01-kinaba/remarks.markdown55
-rw-r--r--sample/trick2018/02-mame/authors.markdown3
-rw-r--r--sample/trick2018/02-mame/entry.rb15
-rw-r--r--sample/trick2018/02-mame/remarks.markdown16
-rw-r--r--sample/trick2018/03-tompng/Gemfile2
-rw-r--r--sample/trick2018/03-tompng/Gemfile.lock13
-rw-r--r--sample/trick2018/03-tompng/authors.markdown3
-rw-r--r--sample/trick2018/03-tompng/entry.rb31
-rw-r--r--sample/trick2018/03-tompng/output.txt44
-rw-r--r--sample/trick2018/03-tompng/remarks.markdown19
-rw-r--r--sample/trick2018/03-tompng/trick.pngbin0 -> 5661 bytes
-rw-r--r--sample/trick2018/04-colin/authors.markdown3
-rw-r--r--sample/trick2018/04-colin/entry.rb2
-rw-r--r--sample/trick2018/04-colin/remarks.markdown62
-rw-r--r--sample/trick2018/05-tompng/authors.markdown3
-rw-r--r--sample/trick2018/05-tompng/entry.rb41
-rw-r--r--sample/trick2018/05-tompng/preview_of_output.pngbin0 -> 66800 bytes
-rw-r--r--sample/trick2018/05-tompng/remarks.markdown31
-rw-r--r--sample/trick2018/README.md16
-rw-r--r--sample/trick2022/01-tompng/Gemfile2
-rw-r--r--sample/trick2022/01-tompng/Gemfile.lock13
-rw-r--r--sample/trick2022/01-tompng/authors.markdown3
-rw-r--r--sample/trick2022/01-tompng/entry.rb40
-rw-r--r--sample/trick2022/01-tompng/remarks.markdown51
-rw-r--r--sample/trick2022/02-tompng/authors.markdown3
-rw-r--r--sample/trick2022/02-tompng/entry.rb32
-rw-r--r--sample/trick2022/02-tompng/remarks.markdown32
-rw-r--r--sample/trick2022/03-mame/authors.markdown3
-rw-r--r--sample/trick2022/03-mame/entry.rb27
-rw-r--r--sample/trick2022/03-mame/remarks.markdown96
-rw-r--r--sample/trick2022/03-mame/test.txt13
-rw-r--r--sample/trick2022/README.md14
-rw-r--r--sample/trick2025/01-omoikane/authors.markdown5
-rw-r--r--sample/trick2025/01-omoikane/bf.rb81
-rw-r--r--sample/trick2025/01-omoikane/entry.rb32
-rw-r--r--sample/trick2025/01-omoikane/remarks.markdown71
-rw-r--r--sample/trick2025/01-omoikane/sample_input.txt35
-rw-r--r--sample/trick2025/01-omoikane/spoiler_rot13.txt470
-rw-r--r--sample/trick2025/02-mame/authors.markdown3
-rw-r--r--sample/trick2025/02-mame/entry.rb34
-rw-r--r--sample/trick2025/02-mame/remarks.markdown141
-rw-r--r--sample/trick2025/02-mame/sample.orig.rb8
-rw-r--r--sample/trick2025/02-mame/test.patch16
-rw-r--r--sample/trick2025/03-tompng/authors.markdown3
-rw-r--r--sample/trick2025/03-tompng/entry.rb74
-rw-r--r--sample/trick2025/03-tompng/remarks.markdown146
-rw-r--r--sample/trick2025/04-tompng/authors.markdown3
-rw-r--r--sample/trick2025/04-tompng/entry.rb36
-rw-r--r--sample/trick2025/04-tompng/remarks.markdown43
-rw-r--r--sample/trick2025/05-tompng/authors.markdown3
-rw-r--r--sample/trick2025/05-tompng/entry.rb118
-rw-r--r--sample/trick2025/05-tompng/remarks.markdown106
-rw-r--r--sample/trick2025/README.md16
-rw-r--r--sample/trojan.rb7
-rw-r--r--sample/tsvr.rb17
-rw-r--r--sample/uumerge.rb18
-rw-r--r--sample/weakref.rb9
190 files changed, 7055 insertions, 4466 deletions
diff --git a/sample/README b/sample/README
new file mode 100644
index 0000000000..b55234a947
--- /dev/null
+++ b/sample/README
@@ -0,0 +1,43 @@
+README this file
+biorhythm.rb biorhythm calculator
+cal.rb cal(1) clone
+cbreak.rb no echo done by ioctl
+clnt.rb socket client
+coverage.rb simple test code coverage tool
+dir.rb directory access
+dualstack-fetch.rb IPv6 demo
+dualstack-httpd.rb IPv6 demo
+eval.rb simple evaluator
+export.rb method access example
+exyacc.rb extrace BNF from yacc file
+fact.rb factorial calculator
+fib.awk Fibonacci number (AWK)
+fib.pl Fibonacci number (Perl)
+fib.py Fibonacci number (Python)
+fib.rb Fibonacci number (Ruby)
+fib.scm Fibonacci number (Scheme)
+from.rb scan mail spool
+fullpath.rb convert ls -lR to fullpath format
+less.rb front end for less
+list.rb stupid object sample
+list2.rb stupid object sample
+list3.rb stupid object sample
+mine.rb simple mine sweeper
+mkproto.rb extract prototype from C
+mpart.rb split file int multi part
+observ.rb observer design pattern sample
+occur.pl count word occurrence (Perl)
+occur.rb count word occurrence (Ruby)
+philos.rb famous dining philosophers
+pi.rb calculate PI
+rcs.awk random character stereogram (AWK)
+rcs.rb random character stereogram (Ruby)
+rcs.dat data for random character stereogram
+sieve.rb sieve of Eratosthenes
+svr.rb socket server
+test.rb test suite used by `make test'
+time.rb /usr/bin/time clone
+timeout.rb timeout test
+trojan.rb simple tool to find file that may be trojan horse.
+tsvr.rb socket server using thread
+uumerge.rb merge files and uudecode them
diff --git a/sample/all-ruby-quine.rb b/sample/all-ruby-quine.rb
new file mode 100644
index 0000000000..7686121468
--- /dev/null
+++ b/sample/all-ruby-quine.rb
@@ -0,0 +1,24 @@
+ eval($s=("t='eval($s=('+d=34.chr;s=3
+ 2.chr+$s*i=8;v=$VERSION||eval('begin;v=V
+ ERSION;rescue;v||RUBY_VERSION;end');f=('?'*8
+ +'A|'+'?'*20+'G?c'+'?'*15+'A@CXx@~@_`OpGxCxp@~pO
+ xS|O~G?c?q?xC`AP|q?x_|C_xC_xO@H@cG?G?qA|_|_`GCpOxC|H
+NFccqq@`_|OF@`?q?x_@x_x_`GB`O``O~G?C@qCxCxP@D@|G~C?pO|C?
+ pO|C?AP|A~HNN`ccxC|Q@L@B"+"GpGpc@p?x_`GB`???_@FO|OB@
+ xC|P`@?c?q?HPx@~@_`G@`????@L^`?q?x?xq@|_|O~GC`
+ xA~@_@GBD').unpack('c*');w=4+v.length*u=
+ 15;r=10.chr;j=0;while-24+w*u>i=1+i
+ ;x=i%w;x>0||t=t+d+'+'+r+d;k=
+ i/w%12>2&&x%u>3&&x%u+i
+ /w*11-34+('-._'.
+ index(c=v[
+ x/u,1]
+ )||c.hex +3)*99|
+ |0; k=f [k/6 ][k%
+ 6]; t=t +s[
+ k*j =k+ j,1
+ ]end;pr int (t+
+ d+' ).s pli
+ t.j oin [0,
+ 609 ])# Y.E. '+r)
+ ").split .join)#
diff --git a/sample/benchmark.rb b/sample/benchmark.rb
new file mode 100644
index 0000000000..de5d66f505
--- /dev/null
+++ b/sample/benchmark.rb
@@ -0,0 +1,19 @@
+require 'benchmark'
+
+include Benchmark
+
+n = ARGV[0].to_i.nonzero? || 50000
+puts %Q([#{n} times iterations of `a = "1"'])
+benchmark(CAPTION, 7, FORMAT) do |x|
+ x.report("for:") {for _ in 1..n; _ = "1"; end} # Benchmark.measure
+ x.report("times:") {n.times do ; _ = "1"; end}
+ x.report("upto:") {1.upto(n) do ; _ = "1"; end}
+end
+
+benchmark do
+ [
+ measure{for _ in 1..n; _ = "1"; end}, # Benchmark.measure
+ measure{n.times do ; _ = "1"; end},
+ measure{1.upto(n) do ; _ = "1"; end}
+ ]
+end
diff --git a/sample/biorhythm.rb b/sample/biorhythm.rb
index 50ad1f2ef7..f5d189014b 100644
--- a/sample/biorhythm.rb
+++ b/sample/biorhythm.rb
@@ -1,127 +1,112 @@
#!/usr/local/bin/ruby
#
-# biorhythm.rb -
+# biorhythm.rb -
# $Release Version: $
# $Revision$
-# $Date$
# by Yasuo OHBA(STAFS Development Room)
#
# --
#
-#
+#
#
-include Math
-require "date.rb"
-require "parsearg.rb"
+# probably based on:
+#
+# Newsgroups: comp.sources.misc,de.comp.sources.os9
+# From: fkk@stasys.sta.sub.org (Frank Kaefer)
+# Subject: v41i126: br - Biorhythm v3.0, Part01/01
+# Message-ID: <1994Feb1.070616.15982@sparky.sterling.com>
+# Sender: kent@sparky.sterling.com (Kent Landfield)
+# Organization: Sterling Software
+# Date: Tue, 1 Feb 1994 07:06:16 GMT
+#
+# Posting-number: Volume 41, Issue 126
+# Archive-name: br/part01
+# Environment: basic, dos, os9
-def usage()
- print "Usage:\n"
- print "biorhythm.rb [options]\n"
- print " options...\n"
- print " -D YYYYMMDD(birthday) : ٤ default ͤȤ. \n"
- print " --sdate | --date YYYYMMDD : system date ⤷ϻꤷդȤ.\n"
- print " --birthday YYYYMMDD : λ򤹤. \n"
- print " -v | -g : Values or Graph λ. \n"
- print " --days DAYS : ֤λ򤹤(Graph λΤͭ). \n"
- print " --help : help\n"
-end
-$USAGE = 'usage'
+include Math
+require "date"
+require "optparse"
+require "optparse/date"
-def printHeader(y, m, d, p, w)
+def print_header(y, m, d, p, w)
print "\n>>> Biorhythm <<<\n"
printf "The birthday %04d.%02d.%02d is a %s\n", y, m, d, w
printf "Age in days: [%d]\n\n", p
end
-def getPosition(z)
- pi = 3.14159265
- phys = (50.0 * (1.0 + sin((z / 23.0 - (z / 23)) * 360.0 * pi / 180.0))).to_i
- emot = (50.0 * (1.0 + sin((z / 28.0 - (z / 28)) * 360.0 * pi / 180.0))).to_i
- geist =(50.0 * (1.0 + sin((z / 33.0 - (z / 33)) * 360.0 * pi / 180.0))).to_i
+def get_position(z)
+ z = Integer(z)
+ phys = (50.0 * (1.0 + sin((z / 23.0 - (z / 23)) * 360.0 * PI / 180.0))).to_i
+ emot = (50.0 * (1.0 + sin((z / 28.0 - (z / 28)) * 360.0 * PI / 180.0))).to_i
+ geist =(50.0 * (1.0 + sin((z / 33.0 - (z / 33)) * 360.0 * PI / 180.0))).to_i
return phys, emot, geist
end
+def prompt(msg)
+ $stderr.print msg
+ return gets.chomp
+end
+
#
# main program
#
-parseArgs(0, nil, "vg", "D:", "sdate", "date:", "birthday:", "days:")
-
-if $OPT_D
- dd = Date.new(Time.now.strftime("%Y%m%d"))
- bd = Date.new($OPT_D)
- ausgabeart = "g"
-else
- if $OPT_birthday
- bd = Date.new($OPT_birthday)
- else
- printf(STDERR, "Birthday (YYYYMMDD) : ")
- if (si = STDIN.gets.chop) != ""
- bd = Date.new(si)
- end
- end
- if !bd
- printf STDERR, "BAD Input Birthday!!\n"
- exit()
- end
-
- if $OPT_sdate
- dd = Date.new(Time.now.strftime("%Y%m%d"))
- elsif $OPT_date
- dd = Date.new($OPT_date)
- else
- printf(STDERR, "Date [<RETURN> for Systemdate] (YYYYMMDD) : ")
- if (si = STDIN.gets.chop) != ""
- dd = Date.new(si)
- end
- end
- if !dd
- dd = Date.new(Time.now.strftime("%Y%m%d"))
- end
-
- if $OPT_v
- ausgabeart = "v"
- elsif $OPT_g
- ausgabeart = "g"
- else
- printf(STDERR, "Values for today or Graph (v/g) [default g] : ")
- ausgabeart = STDIN.gets.chop
+options = {
+ :graph => true,
+ :date => Date.today,
+ :days => 9,
+}
+ARGV.options do |opts|
+ opts.on("-b", "--birthday=DATE", Date, "specify your birthday"){|v|
+ options[:birthday] = v
+ }
+ opts.on("--date=DATE", Date, "specify date to show"){|v|
+ options[:date] = v
+ }
+ opts.on("-g", "--show-graph", TrueClass, "show graph (default)"){|v|
+ options[:graph] = v
+ }
+ opts.on("-v", "--show-values", TrueClass, "show values"){|v|
+ options[:graph] = !v
+ }
+ opts.on("--days=DAYS", Integer, "graph range (only in effect for graph)"){|v|
+ options[:days] = v - 1
+ }
+ opts.on_tail("-h", "--help", "show this message"){puts opts; exit}
+ begin
+ opts.parse!
+ rescue => ex
+ puts "Error: #{ex.message}"
+ puts opts
+ exit
end
end
-if (ausgabeart == "v")
- printHeader(bd.year, bd.month, bd.day, dd.period - bd.period, bd.name_of_week)
+
+bd = options[:birthday] || Date.parse(prompt("Your birthday (YYYYMMDD): "))
+dd = options[:date] || Date.today
+ausgabeart = options[:graph] ? "g" : "v"
+display_period = options[:days]
+
+if ausgabeart == "v"
+ print_header(bd.year, bd.month, bd.day, dd - bd, bd.strftime("%a"))
print "\n"
-
- phys, emot, geist = getPosition(dd.period - bd.period)
+
+ phys, emot, geist = get_position(dd - bd)
printf "Biorhythm: %04d.%02d.%02d\n", dd.year, dd.month, dd.day
printf "Physical: %d%%\n", phys
printf "Emotional: %d%%\n", emot
printf "Mental: %d%%\n", geist
print "\n"
else
- if $OPT_days
- display_period = $OPT_days.to_i
- elsif $OPT_D
- display_period = 9
- else
- printf(STDERR, "Graph for how many days [default 10] : ")
- display_period = STDIN.gets.chop
- if (display_period == "")
- display_period = 9
- else
- display_period = display_period.to_i - 1
- end
- end
-
- printHeader(bd.year, bd.month, bd.day, dd.period - bd.period, bd.name_of_week)
+ print_header(bd.year, bd.month, bd.day, dd - bd, bd.strftime("%a"))
print " P=physical, E=emotional, M=mental\n"
print " -------------------------+-------------------------\n"
print " Bad Condition | Good Condition\n"
print " -------------------------+-------------------------\n"
-
- for z in (dd.period - bd.period)..(dd.period - bd.period + display_period)
- phys, emot, geist = getPosition(z)
-
+
+ (dd - bd).step(dd - bd + display_period) do |z|
+ phys, emot, geist = get_position(z)
+
printf "%04d.%02d.%02d : ", dd.year, dd.month, dd.day
p = (phys / 2.0 + 0.5).to_i
e = (emot / 2.0 + 0.5).to_i
diff --git a/sample/cal.rb b/sample/cal.rb
index b0f5d88335..97f75bcf1c 100644
--- a/sample/cal.rb
+++ b/sample/cal.rb
@@ -1,118 +1,166 @@
-#! /usr/local/bin/ruby
-
-# cal.rb (bsd compatible version): Written by Tadayoshi Funaba 1998
-# $Id: bsdcal.rb,v 1.1 1998/06/01 12:53:01 tadf Exp $
-
-require 'date2'
-
-$tab =
-{
- 'cn' => true, # China
- 'de' => 2342032, # Germany (protestant states)
- 'dk' => 2342032, # Denmark
- 'es' => 2299161, # Spain
- 'fi' => 2361390, # Finland
- 'fr' => 2299227, # France
- 'gb' => 2361222, # United Kingdom
- 'gr' => 2423868, # Greece
- 'hu' => 2301004, # Hungary
- 'it' => 2299161, # Italy
- 'jp' => true, # Japan
- 'no' => 2342032, # Norway
- 'pl' => 2299161, # Poland
- 'pt' => 2299161, # Portugal
- 'ru' => 2421639, # Russia
- 'se' => 2361390, # Sweden
- 'us' => 2361222, # United States
- 'os' => false, # (old style)
- 'ns' => true # (new style)
-}
-
-$cc = 'gb'
-
-def usage
- $stderr.puts 'usage: cal [-c iso3166] [-jy] [[month] year]'
- exit 1
-end
+#! /usr/bin/env ruby
+
+# cal.rb: Written by Tadayoshi Funaba 1998-2004,2006,2008
+# $Id: cal.rb,v 2.11 2008-01-06 08:42:17+09 tadf Exp $
+
+require 'date'
+
+class Cal
-def cal(m, y, gs)
- for d in 1..31
- break if jd = Date.exist?(y, m, d, gs)
+ START =
+ {
+ 'cn' => Date::GREGORIAN, # China
+ 'de' => 2342032, # Germany (protestant states)
+ 'dk' => 2342032, # Denmark
+ 'es' => 2299161, # Spain
+ 'fi' => 2361390, # Finland
+ 'fr' => 2299227, # France
+ 'gb' => 2361222, # United Kingdom
+ 'gr' => 2423868, # Greece
+ 'hu' => 2301004, # Hungary
+ 'it' => 2299161, # Italy
+ 'jp' => Date::GREGORIAN, # Japan
+ 'no' => 2342032, # Norway
+ 'pl' => 2299161, # Poland
+ 'pt' => 2299161, # Portugal
+ 'ru' => 2421639, # Russia
+ 'se' => 2361390, # Sweden
+ 'us' => 2361222, # United States
+ 'os' => Date::JULIAN, # (old style)
+ 'ns' => Date::GREGORIAN # (new style)
+ }
+
+ DEFAULT_START = 'gb'
+
+ def initialize
+ opt_j; opt_m; opt_t; opt_y; opt_c
end
- fst = cur = Date.new(jd, gs)
- ti = Date::MONTHNAMES[m]
- ti << ' ' << y.to_s unless $yr
- mo = ti.center((($w + 1) * 7) - 1) << "\n"
- mo << ['S', 'M', 'Tu', 'W', 'Th', 'F', 'S'].
- collect{|x| x.rjust($w)}.join(' ') << "\n"
- mo << ' ' * (($w + 1) * fst.wday)
- while cur.mon == fst.mon
- mo << (if $jd then cur.yday else cur.mday end).to_s.rjust($w)
- mo << (if (cur += 1).wday != 0 then "\s" else "\n" end)
+
+ def opt_j(flag=false) @opt_j = flag end
+ def opt_m(flag=false) @opt_m = flag end
+ def opt_t(flag=false) @opt_t = flag end
+ def opt_y(flag=false) @opt_y = flag end
+
+ def opt_c(arg=DEFAULT_START) @start = START[arg] end
+
+ def set_params
+ @dw = if @opt_j then 3 else 2 end
+ @mw = (@dw + 1) * 7 - 1
+ @mn = if @opt_j then 2 else 3 end
+ @tw = (@mw + 2) * @mn - 2
+ @k = if @opt_m then 1 else 0 end
+ @da = if @opt_j then :yday else :mday end
end
- mo << "\n" * (6 - ((fst.wday + (cur - fst)) / 7))
- mo
-end
-def zip(xs)
- yr = ''
- until xs.empty?
- ln = (if $jd then l, r, *xs = xs; [l, r]
- else l, c, r, *xs = xs; [l, c, r] end).
- collect{|x| x.split(/\n/no)}
- 8.times do
- yr << ln.collect{|x|
- x.shift.ljust((($w + 1) * 7) - 1)}.join(' ') << "\n"
- end
+ def pict(y, m)
+ d = (1..31).detect{|x| Date.valid_date?(y, m, x, @start)}
+ fi = Date.new(y, m, d, @start)
+ fi -= (fi.jd - @k + 1) % 7
+
+ ve = (fi..fi + 6).collect{|cu|
+ %w(S M Tu W Th F S)[cu.wday]
+ }
+ ve += (fi..fi + 41).collect{|cu|
+ if cu.mon == m then cu.send(@da) end.to_s
+ }
+
+ ve = ve.collect{|e| e.rjust(@dw)}
+
+ gr = group(ve, 7)
+ gr = trans(gr) if @opt_t
+ ta = gr.collect{|xs| xs.join(' ')}
+
+ ca = %w(January February March April May June July
+ August September October November December)[m - 1]
+ ca = ca + ' ' + y.to_s if !@opt_y
+ ca = ca.center(@mw)
+
+ ta.unshift(ca)
+ end
+
+ def group(xs, n)
+ (0..xs.size / n - 1).collect{|i| xs[i * n, n]}
+ end
+
+ def trans(xs)
+ (0..xs[0].size - 1).collect{|i| xs.collect{|x| x[i]}}
+ end
+
+ def stack(xs)
+ if xs.empty? then [] else xs[0] + stack(xs[1..-1]) end
end
- yr
+
+ def block(xs, n)
+ stack(group(xs, n).collect{|ys| trans(ys).collect{|zs| zs.join(' ')}})
+ end
+
+ def unlines(xs)
+ xs.collect{|x| x + "\n"}.join
+ end
+
+ def monthly(y, m)
+ unlines(pict(y, m))
+ end
+
+ def addmon(y, m, n)
+ y, m = (y * 12 + (m - 1) + n).divmod(12)
+ return y, m + 1
+ end
+
+ def yearly(y)
+ y.to_s.center(@tw) + "\n\n" +
+ unlines(block((0..11).collect{|n| pict(*addmon(y, 1, n))}, @mn)) + "\n"
+ end
+
+ def print(y, m)
+ set_params
+ if @opt_y then yearly(y) else monthly(y, m) end
+ end
+
end
-while /^-(.*)$/no =~ $*[0]
- a = $1
- if /^c(.+)?$/no =~ a then
- if $1 then
- $cc = $1.downcase
- elsif $*.length >= 2 then
- $cc = $*[1].downcase
- $*.shift
- else
- usage
- end
- else
- a.scan(/./no) do |c|
- case c
- when 'j'; $jd = true
- when 'y'; $yr = true
- else usage
+if __FILE__ == $0
+
+ require 'getoptlong'
+
+ def usage
+ warn 'usage: cal [-c iso3166] [-jmty] [[month] year]'
+ exit 1
+ end
+
+ cal = Cal.new
+
+ begin
+ GetoptLong.new(['-c', GetoptLong::REQUIRED_ARGUMENT],
+ ['-j', GetoptLong::NO_ARGUMENT],
+ ['-m', GetoptLong::NO_ARGUMENT],
+ ['-t', GetoptLong::NO_ARGUMENT],
+ ['-y', GetoptLong::NO_ARGUMENT]).
+ each do |opt, arg|
+ case opt
+ when '-c'; cal.opt_c(arg) || raise
+ when '-j'; cal.opt_j(true)
+ when '-m'; cal.opt_m(true)
+ when '-t'; cal.opt_t(true)
+ when '-y'; cal.opt_y(true)
end
end
+ rescue
+ usage
end
- $*.shift
-end
-usage if (gs = $tab[$cc]).nil?
-case $*.length
-when 0
- td = Date.today
- m = td.mon
- y = td.year
-when 1
- y = $*[0].to_i
- $yr = true
-when 2
- m = $*[0].to_i
- y = $*[1].to_i
-else
- usage
-end
-usage unless m.nil? or (1..12) === m
-usage unless y >= -4712
-$w = if $jd then 3 else 2 end
-unless $yr then
- print cal(m, y, gs)
-else
- print y.to_s.center(((($w + 1) * 7) - 1) *
- (if $jd then 2 else 3 end) +
- (if $jd then 2 else 4 end)), "\n\n",
- zip((1..12).collect{|m| cal(m, y, gs)}), "\n"
+
+ y, m = ARGV.values_at(1, 0).compact.collect{|x| x.to_i}
+ cal.opt_y(true) if y && !m
+
+ to = Date.today
+ y ||= to.year
+ m ||= to.mon
+
+ usage unless m >= 1 && m <= 12
+ usage unless y >= -4712
+
+ print cal.print(y, m)
+
end
+
+# See Bird & Wadler's Introduction to functional programming 4.5.
diff --git a/sample/cbreak.rb b/sample/cbreak.rb
index cbb15d2f41..7f1385cce3 100644
--- a/sample/cbreak.rb
+++ b/sample/cbreak.rb
@@ -5,15 +5,15 @@ ECHO = 0x00000008
TIOCGETP = 0x40067408
TIOCSETP = 0x80067409
-def cbreak ()
- set_cbreak(TRUE)
+def cbreak
+ set_cbreak(true)
end
-def cooked ()
- set_cbreak(FALSE)
+def cooked
+ set_cbreak(false)
end
-def set_cbreak (on)
+def set_cbreak(on)
tty = "\0" * 256
STDIN.ioctl(TIOCGETP, tty)
ttys = tty.unpack("C4 S")
@@ -30,7 +30,7 @@ end
cbreak();
print("this is no-echo line: ");
-readline().print
+readline().display
cooked();
print("this is echo line: ");
readline()
diff --git a/sample/cgi-session-pstore.rb b/sample/cgi-session-pstore.rb
new file mode 100644
index 0000000000..ec8b4989d6
--- /dev/null
+++ b/sample/cgi-session-pstore.rb
@@ -0,0 +1,11 @@
+require 'cgi'
+require 'cgi/session/pstore'
+
+STDIN.reopen(IO::NULL)
+cgi = CGI.new
+session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::PStore)
+session['key'] = {'k' => 'v'}
+puts session['key'].class
+fail unless Hash === session['key']
+puts session['key'].inspect
+fail unless session['key'].inspect == '{"k"=>"v"}'
diff --git a/sample/clnt.rb b/sample/clnt.rb
index 7998379aa2..0f3d17bf19 100644
--- a/sample/clnt.rb
+++ b/sample/clnt.rb
@@ -3,15 +3,19 @@
require "socket"
-host=(if ARGV.length == 2; ARGV.shift; else "localhost"; end)
+if ARGV.length >= 2
+ host = ARGV.shift
+else
+ host = "localhost"
+end
print("Trying ", host, " ...")
STDOUT.flush
-s = TCPsocket.open(host, ARGV.shift)
+s = TCPSocket.open(host, ARGV.shift)
print(" done\n")
print("addr: ", s.addr.join(":"), "\n")
print("peer: ", s.peeraddr.join(":"), "\n")
-while gets()
- s.write($_)
+while line = gets()
+ s.write(line)
print(s.readline)
end
s.close
diff --git a/sample/coverage.rb b/sample/coverage.rb
new file mode 100644
index 0000000000..42ba89fd50
--- /dev/null
+++ b/sample/coverage.rb
@@ -0,0 +1,62 @@
+require "coverage.so"
+
+Coverage.start
+
+ext = ENV["COVERUBY_EXT"] || ".cov"
+accum = ENV["COVERUBY_ACCUM"]
+accum = !accum || accum == "" || !(%w(f n 0).include?(accum[0]))
+pwd = Dir.pwd
+
+at_exit do
+ exit_exc = $!
+ Dir.chdir(pwd) do
+ Coverage.result.each do |sfile, covs|
+ cfile = sfile + ext
+
+ writable = proc do |f|
+ File.writable?(f) || File.writable?(File.dirname(f))
+ end
+ unless writable[cfile]
+ cfile = cfile.gsub(File::PATH_SEPARATOR, "#")
+ next unless writable[cfile]
+ end
+
+ readlines = proc do |f|
+ File.read(f).force_encoding("ASCII-8BIT").lines.to_a
+ end
+
+ sources = (readlines[sfile] rescue [])
+
+ pcovs = []
+ if accum
+ pcovs = (readlines[cfile] rescue []).map.with_index do |line, idx|
+ if line[/^\s*(?:(#####)|(\d+)|-):\s*\d+:(.*)$/n]
+ cov, line = $1 ? 0 : ($2 ? $2.to_i : nil), $3
+ if !sources[idx] || sources[idx].chomp != line.chomp
+ warn("source file changed, ignoring: `#{ cfile }'")
+ break []
+ end
+ cov
+ else
+ p line
+ warn("coverage file corrupted, ignoring: #{ cfile }")
+ break []
+ end
+ end
+ unless pcovs.empty? || pcovs.size == covs.size
+ warn("coverage file changed, ignoring: `#{ cfile }'")
+ pcovs = []
+ end
+ end
+
+ File.open(cfile, "w") do |out|
+ covs.zip(sources, pcovs).each_with_index do |(cov, line, pcov), idx|
+ cov += pcov || 0 if cov
+ cov = (cov ? (cov == 0 ? "#####" : cov.to_s) : "-").rjust(9)
+ out.puts("%s:% 5d:%s" % [cov, idx + 1, line])
+ end
+ end
+ end
+ end
+ raise exit_exc if exit_exc
+end
diff --git a/sample/dbmtest.rb b/sample/dbmtest.rb
deleted file mode 100644
index c77cc2065b..0000000000
--- a/sample/dbmtest.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# ruby dbm acess
-require "dbm"
-
-d = DBM.open("test")
-keys = d.keys
-if keys.length > 0 then
- for k in keys; print k, "\n"; end
- for v in d.values; print v, "\n"; end
-else
- d['foobar'] = 'FB'
- d['baz'] = 'BZ'
- d['quux'] = 'QX'
-end
-
diff --git a/sample/delegate.rb b/sample/delegate.rb
new file mode 100644
index 0000000000..dc7ea2a0af
--- /dev/null
+++ b/sample/delegate.rb
@@ -0,0 +1,31 @@
+require 'delegate'
+
+class ExtArray < DelegateClass(Array)
+ def initialize()
+ super([])
+ end
+end
+
+ary = ExtArray.new
+p ary.class
+ary.push 25
+p ary
+ary.push 42
+ary.each {|x| p x}
+
+foo = Object.new
+def foo.test
+ 25
+end
+def foo.iter
+ yield self
+end
+def foo.error
+ raise 'this is OK'
+end
+foo2 = SimpleDelegator.new(foo)
+p foo2
+foo2.instance_eval{print "foo\n"}
+p foo.test == foo2.test # => true
+p foo2.iter{[55,true]} # => true
+foo2.error # raise error!
diff --git a/sample/dir.rb b/sample/dir.rb
index 2465c4d68e..81257cd917 100644
--- a/sample/dir.rb
+++ b/sample/dir.rb
@@ -1,10 +1,7 @@
# directory access
# list all files but .*/*~/*.o
-dirp = Dir.open(".")
-for f in dirp
- $_ = f
- unless (~/^\./ || ~/~$/ || ~/\.o/)
- print f, "\n"
+Dir.foreach(".") do |file|
+ unless file.start_with?('.') or file.end_with?('~', '.o')
+ puts file
end
end
-dirp.close
diff --git a/sample/dualstack-fetch.rb b/sample/dualstack-fetch.rb
new file mode 100644
index 0000000000..18d33cc45a
--- /dev/null
+++ b/sample/dualstack-fetch.rb
@@ -0,0 +1,48 @@
+# simple webpage fetcher
+
+# The code demonstrates how a multi-protocol client should be written.
+# TCPSocket is using getaddrinfo() internally, so there should be no problem.
+
+require "socket"
+
+if ARGV.size != 1
+ STDERR.print "requires URL\n"
+ exit
+end
+
+url = ARGV[0]
+if url !~ /^http:\/\/([^\/]+)(\/.*)$/
+ STDERR.print "only http with full hostname is supported\n"
+ exit
+end
+
+# split URL into host, port and path
+hostport = $1
+path = $2
+if (hostport =~ /^(.*):([0-9]+)$/)
+ host = $1
+ port = $2
+else
+ host = hostport
+ port = 80
+end
+if host =~ /^\[(.*)\]$/
+ host = $1
+end
+
+#STDERR.print "url=<#{ARGV[0]}>\n"
+#STDERR.print "host=<#{host}>\n"
+#STDERR.print "port=<#{port}>\n"
+#STDERR.print "path=<#{path}>\n"
+
+STDERR.print "conntecting to #{host} port #{port}\n"
+c = TCPSocket.new(host, port)
+dest = Socket.getnameinfo(c.getpeername,
+ Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)
+STDERR.print "conntected to #{dest[0]} port #{dest[1]}\n"
+c.print "GET #{path} HTTP/1.0\n"
+c.print "Host: #{host}\n"
+c.print "\n"
+while c.gets
+ print
+end
diff --git a/sample/dualstack-httpd.rb b/sample/dualstack-httpd.rb
new file mode 100644
index 0000000000..ab02e17aea
--- /dev/null
+++ b/sample/dualstack-httpd.rb
@@ -0,0 +1,54 @@
+# simple httpd
+
+# The code demonstrates how a multi-protocol daemon should be written.
+
+require "socket"
+
+port = 8888
+res = Socket.getaddrinfo(nil, port, nil, Socket::SOCK_STREAM, nil, Socket::AI_PASSIVE)
+sockpool = []
+names = []
+threads = []
+
+res.each do |i|
+ s = TCPServer.new(i[3], i[1])
+ n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ")
+ sockpool.push s
+ names.push n
+end
+
+(0 .. sockpool.size - 1).each do |i|
+ mysock = sockpool[i]
+ myname = names[i]
+ STDERR.print "socket #{mysock} started, address #{myname}\n"
+ threads[i] = Thread.start do # Thread.start cannot be used here!
+ ls = mysock # copy to dynamic variable
+ t = Thread.current
+ STDERR.print "socket #{myname} listener started, pid #{$$} thread #{t}\n"
+ while true
+ as = ls.accept
+ Thread.start do
+ STDERR.print "socket #{myname} accepted, thread ", Thread.current, "\n"
+ s = as # copy to dynamic variable
+ str = ''
+ while line = s.gets
+ break if line == "\r\n" or line == "\n"
+ str << line
+ end
+ STDERR.print "socket #{myname} got string\n"
+ s.write("HTTP/1.0 200 OK\n")
+ s.write("Content-type: text/plain\n\n")
+ s.write("this is test: my name is #{myname}, you sent:\n")
+ s.write("---start\n")
+ s.write(str)
+ s.write("---end\n")
+ s.close
+ STDERR.print "socket #{myname} processed, thread ", Thread.current, " terminating\n"
+ end
+ end
+ end
+end
+
+for t in threads
+ t.join
+end
diff --git a/sample/eval.rb b/sample/eval.rb
index 216bf8ca39..ed4b7c3de5 100644
--- a/sample/eval.rb
+++ b/sample/eval.rb
@@ -1,18 +1,18 @@
line = ''
-indent=0
-$stdout.sync = TRUE
+indent = 0
+$stdout.sync = true
print "ruby> "
-while TRUE
+loop do
l = gets
- unless l
- break if line == ''
+ if l.nil?
+ break if line.empty?
else
- line = line + l
+ line += l
if l =~ /,\s*$/
print "ruby| "
next
end
- if l =~ /^\s*(class|module|def|if|case|while|for|begin)\b[^_]/
+ if l =~ /^\s*(class|module|def|if|unless|case|while|until|for|begin)\b[^_]/
indent += 1
end
if l =~ /^\s*end\b[^_]/
@@ -31,11 +31,10 @@ while TRUE
end
begin
print eval(line).inspect, "\n"
- rescue
- $! = 'exception raised' unless $!
- print "ERR: ", $!, "\n"
+ rescue ScriptError, StandardError
+ printf "ERR: %s\n", $! || 'exception raised'
end
- break if not l
+ break if l.nil?
line = ''
print "ruby> "
end
diff --git a/sample/export.rb b/sample/export.rb
index 949e5b10bf..2ab2e93f2e 100644
--- a/sample/export.rb
+++ b/sample/export.rb
@@ -30,7 +30,7 @@ f.printf "%s\n", Foo
f.quux
-class Bar<Foo
+class Bar < Foo
def quux
super
baz()
diff --git a/sample/exyacc.rb b/sample/exyacc.rb
index 908ba6c501..cbcc18d58b 100644
--- a/sample/exyacc.rb
+++ b/sample/exyacc.rb
@@ -1,22 +1,20 @@
#! /usr/local/bin/ruby -Kn
# usage: exyacc.rb [yaccfiles]
-# this is coverted from exyacc.pl in the camel book
+# this is covered from exyacc.pl in the camel book
-$/ = nil
-
-while gets()
- sbeg = $_.index("\n%%") + 1
- send = $_.rindex("\n%%") + 1
- $_ = $_[sbeg, send-sbeg]
- sub!(/.*\n/, "")
- gsub!(/'\{'/, "'\001'")
- gsub!(/'}'/, "'\002'")
- gsub!('\*/', "\003\003")
- gsub!("/\\*[^\003]*\003\003", '')
- while gsub!(/\{[^{}]*}/, ''); end
- gsub!(/'\001'/, "'{'")
- gsub!(/'\002'/, "'}'")
- while gsub!(/^[ \t]*\n(\s)/, '\1'); end
- gsub!(/([:|])[ \t\n]+(\w)/, '\1 \2')
- print $_
+ARGF.each(nil) do |source|
+ sbeg = source.index("\n%%") + 1
+ send = source.rindex("\n%%") + 1
+ grammar = source[sbeg, send-sbeg]
+ grammar.sub!(/.*\n/, "")
+ grammar.gsub!(/'\{'/, "'\001'")
+ grammar.gsub!(/["']\}["']/, "'\002'")
+ grammar.gsub!(%r{\*/}, "\003\003")
+ grammar.gsub!(%r{/\*[^\003]*\003\003}, '')
+ while grammar.gsub!(/\{[^{}]*\}/, ''); end
+ grammar.gsub!(/'\001'/, "'{'")
+ grammar.gsub!(/'\002'/, "'}'")
+ while grammar.gsub!(/^[ \t]*\n(\s)/, '\1'); end
+ grammar.gsub!(/([:|])[ \t\n]+(\w)/, '\1 \2')
+ print grammar
end
diff --git a/sample/fact.rb b/sample/fact.rb
index 1462a6923a..9f6ca72ca7 100644
--- a/sample/fact.rb
+++ b/sample/fact.rb
@@ -1,10 +1,9 @@
def fact(n)
return 1 if n == 0
f = 1
- while n>0
- f *= n
- n -= 1
+ n.downto(1) do |i|
+ f *= i
end
- return f
+ f
end
-print fact(ARGV[0].to_i), "\n"
+puts fact(ARGV[0].to_i)
diff --git a/sample/fib.awk b/sample/fib.awk
index 7ebe8930f5..9589f97965 100644
--- a/sample/fib.awk
+++ b/sample/fib.awk
@@ -1,5 +1,5 @@
- function fib(n) {
- if ( n<2 ) return n; else return fib(n-2) + fib(n-1)
- }
+function fib(n) {
+ if ( n<2 ) return n; else return fib(n-2) + fib(n-1)
+}
- BEGIN { print fib(20); }
+BEGIN { print fib(20); }
diff --git a/sample/fib.pl b/sample/fib.pl
index c5593764aa..4c35eaae31 100644
--- a/sample/fib.pl
+++ b/sample/fib.pl
@@ -1,10 +1,11 @@
- sub fib {
- local($n)=@_;
- if( $n<2 ){
- return $n;
- } {
- return &fib($n-2)+&fib($n-1)
- }
- }
+sub fib {
+ my($n)=@_;
+ if ($n<2) {
+ return $n;
+ }
+ else {
+ return fib($n-2)+fib($n-1);
+ }
+}
- print &fib(20), "\n";
+print fib(20), "\n";
diff --git a/sample/fib.py b/sample/fib.py
index 8318021d24..90dc1e09ed 100644
--- a/sample/fib.py
+++ b/sample/fib.py
@@ -6,5 +6,5 @@ def fib(n):
else:
return fib(n-2)+fib(n-1)
-print fib(20)
+print(fib(20))
diff --git a/sample/fib.scm b/sample/fib.scm
index 5c2b86e656..d2dc770282 100644
--- a/sample/fib.scm
+++ b/sample/fib.scm
@@ -1,8 +1,8 @@
(define (fib n)
(if (< n 2)
- n
- (+ (fib (- n 2)) (fib (- n 1)))))
+ n
+ (+ (fib (- n 2)) (fib (- n 1)))))
(display (fib 20))
(newline)
-
+(quit)
diff --git a/sample/freq.rb b/sample/freq.rb
deleted file mode 100644
index 16bf1794be..0000000000
--- a/sample/freq.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# word occurrence listing
-# usege: ruby freq.rb file..
-freq = {}
-while gets
- while sub!(/\w+/, '')
- word = $&
- freq[word] = freq.fetch(word, 0)+1
- end
-end
-
-for word in freq.keys.sort
- printf("%s -- %d\n", word, freq[word])
-end
diff --git a/sample/from.rb b/sample/from.rb
index 98fc54799d..0e5a08de5f 100644
--- a/sample/from.rb
+++ b/sample/from.rb
@@ -1,97 +1,113 @@
#! /usr/local/bin/ruby
-require "parsedate"
+require "time"
require "kconv"
-require "mailread"
-
-include ParseDate
-include Kconv
class String
-
- def kconv(code = Kconv::EUC)
- Kconv.kconv(self, code, Kconv::AUTO)
- end
-
def kjust(len)
- len += 1
- me = self[0, len].ljust(len)
- if me =~ /.$/ and $&.size == 2
- me[-2, 2] = ' '
+ res = ''
+ rlen = 0
+ self.each_char do |char|
+ delta = char.bytesize > 1 ? 2 : 1
+ break if rlen + delta > len
+ rlen += delta
+ res += char
end
- me.chop!
+ res += ' ' * (len - rlen) if rlen < len
+ res
end
-
end
-if ARGV[0] == '-w'
- wait = TRUE
- ARGV.shift
+def fromout(date, from, subj)
+ return 0 if !date
+ y, m, d = Time.parse(date).to_a.reverse[4, 3] if date
+ y ||= 0; m ||= 0; d ||= 0
+ from ||= "sombody@somewhere"
+ from.delete!("\r\n")
+ from = from.kconv(Encoding.default_external).kjust(28)
+ subj ||= "(nil)"
+ subj.delete!("\r\n")
+ subj = subj.kconv(Encoding.default_external).kjust(40)
+ printf "%02d/%02d/%02d [%s] %s\n", y%100, m, d, from, subj
+ return 1
end
-if ARGV.length == 0
- file = ENV['MAIL']
- user = ENV['USER'] || ENV['USERNAME'] || ENV['LOGNAME']
-else
- file = user = ARGV[0]
- ARGV.clear
-end
+def get_mailfile(user)
+ file = user
+ unless user
+ file = ENV['MAIL']
+ user = ENV['USER'] || ENV['USERNAME'] || ENV['LOGNAME']
+ end
-if file == nil or !File.exist? file
- [ENV['SPOOLDIR'], '/usr/spool', '/var/spool', '/usr', '/var'].each do |m|
- if File.exist? f = "#{m}/mail/#{user}"
- file = f
- break
+ if file == nil or !File.exist?(file)
+ [ENV['SPOOLDIR'], '/usr/spool', '/var/spool', '/usr', '/var'].each do |m|
+ path = "#{m}/mail/#{user}"
+ if File.exist?(path)
+ file = path
+ break
+ end
end
end
+ file
end
-$outcount = 0;
-def fromout(date, from, subj)
- return if !date
- y = m = d = 0
- y, m, d = parsedate(date) if date
- if from
- from.gsub! /\n/, ""
- else
- from = "sombody@somewhere"
- end
- if subj
- subj.gsub! /\n/, ""
- else
- subj = "(nil)"
- end
- if ENV['LANG'] =~ /sjis/i
- lang = Kconv::SJIS
- else
- lang = Kconv::EUC
+def from_main
+ if ARGV[0] == '-w'
+ wait = true
+ ARGV.shift
end
- from = from.kconv(lang).kjust(28)
- subj = subj.kconv(lang).kjust(40)
- printf "%02d/%02d/%02d [%s] %s\n",y%100,m,d,from,subj
- $outcount += 1
-end
+ file = get_mailfile(ARGV[0])
+
+ outcount = 0
+ if File.exist?(file)
+ atime = File.atime(file)
+ mtime = File.mtime(file)
+ File.open(file, "r") do |f|
+ until f.eof?
+ header = {}
+ f.each_line do |line|
+ next if /^From / =~ line # skip From-line
+ break if /^$/ =~ line # end of header
+
+ if /^(?<attr>\S+?):\s*(?<value>.*)/ =~ line
+ attr.capitalize!
+ header[attr] = value
+ elsif attr
+ header[attr] += "\n" + line.lstrip
+ end
+ end
-if File.exist?(file)
- atime = File.atime(file)
- mtime = File.mtime(file)
- f = open(file, "r")
- begin
- until f.eof?
- mail = Mail.new(f)
- fromout mail.header['Date'],mail.header['From'],mail.header['Subject']
+ f.each_line do |line|
+ break if /^From / =~ line
+ end
+ outcount += fromout(header['Date'], header['From'], header['Subject'])
+ end
end
- ensure
- f.close
File.utime(atime, mtime, file)
end
+
+ if outcount == 0
+ print "You have no mail.\n"
+ sleep 2 if wait
+ elsif wait
+ system "stty cbreak -echo"
+ $stdin.getc
+ system "stty cooked echo"
+ end
end
-if $outcount == 0
- print "You have no mail.\n"
- sleep 2 if wait
-elsif wait
- system "stty cbreak -echo"
- getc()
- system "stty cooked echo"
+if __FILE__ == $0
+ from_main
end
+
+__END__
+
+=begin
+
+= from.rb
+
+== USAGE
+
+ruby from.rb [-w] [username_or_filename]
+
+=end
diff --git a/sample/fullpath.rb b/sample/fullpath.rb
index ce268e20b9..112ca58081 100644
--- a/sample/fullpath.rb
+++ b/sample/fullpath.rb
@@ -3,21 +3,21 @@
if ARGV[0] =~ /-p/
ARGV.shift
- path = ARGV.shift
+ path = ARGV.shift
end
if path == nil
path = ""
-elsif path !~ /\/$/
+elsif path !~ %r|/$|
path += "/"
end
-while gets()
- if /:$/
- path = $_.chop.chop + "/"
- elsif /^total/ || /^d/
- elsif /^(.*\d )(.+)$/
+while line = gets()
+ case line
+ when /:$/
+ path = line.chop.chop + "/"
+ when /^total/, /^d/
+ when /^(.*\d )(.+)$/
print($1, path, $2, "\n")
end
end
-
diff --git a/sample/getopts.test b/sample/getopts.test
deleted file mode 100644
index 2866bccea8..0000000000
--- a/sample/getopts.test
+++ /dev/null
@@ -1,36 +0,0 @@
-#! /usr/local/bin/ruby
-
-load("parsearg.rb")
-
-def usage()
- printf "Usage:\n"
- printf "%s -d [-x x] [-y y] [--geometry geom] [--version] [string ...]\n", $0
-end
-
-$USAGE = 'usage'
-parseArgs(0, "d&(x|y)", "dfg", "x:", "y:", "geometry:800x600", "version")
-if ($OPT_d)
- if $OPT_version
- printf "version 1.0\n"
- end
- if ($OPT_x)
- printf("x = %d\n", $OPT_x.to_i)
- end
- if ($OPT_y)
- printf("y = %d\n", $OPT_y.to_i)
- end
- if ($OPT_geometry)
- printf("geometry = %s\n", $OPT_geometry)
- end
- if $OPT_f
- printf "f = TRUE\n"
- end
- if $OPT_g
- printf "g = TRUE\n"
- end
-end
-
-while (ARGV.length != 0)
- print "other = ", ARGV[0], "\n"
- ARGV.shift
-end
diff --git a/sample/goodfriday.rb b/sample/goodfriday.rb
deleted file mode 100644
index f0027ec5dd..0000000000
--- a/sample/goodfriday.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-#! /usr/local/bin/ruby
-
-# goodfriday.rb: Written by Tadayoshi Funaba 1998
-# $Id: goodfriday.rb,v 1.1 1998/03/08 09:44:44 tadf Exp $
-
-require 'date2'
-
-def easter(y)
- g = (y % 19) + 1
- c = (y / 100) + 1
- x = (3 * c / 4) - 12
- z = ((8 * c + 5) / 25) - 5
- d = (5 * y / 4) - x - 10
- e = (11 * g + 20 + z - x) % 30
- e += 1 if e == 25 and g > 11 or e == 24
- n = 44 - e
- n += 30 if n < 21
- n = n + 7 - ((d + n) % 7)
- if n <= 31 then [y, 3, n] else [y, 4, n - 31] end
-end
-
-es = Date.new3(*easter(Time.now.year))
-[[-9*7, 'Septuagesima Sunday'],
- [-8*7, 'Sexagesima Sunday'],
- [-7*7, 'Quinquagesima Sunday (Shrove Sunday)'],
- [-48, 'Shrove Monday'],
- [-47, 'Shrove Tuesday'],
- [-46, 'Ash Wednesday'],
- [-6*7, 'Quadragesima Sunday'],
- [-3*7, 'Mothering Sunday'],
- [-2*7, 'Passion Sunday'],
- [-7, 'Palm Sunday'],
- [-3, 'Maunday Thursday'],
- [-2, 'Good Friday'],
- [-1, 'Easter Eve'],
- [0, 'Easter Day'],
- [1, 'Easter Monday'],
- [7, 'Low Sunday'],
- [5*7, 'Rogation Sunday'],
- [39, 'Ascension Day (Holy Thursday)'],
- [42, 'Sunday after Ascension Day'],
- [7*7, 'Pentecost (Whitsunday)'],
- [50, 'Whitmonday'],
- [8*7, 'Trinity Sunday'],
- [60, 'Corpus Christi (Thursday after Trinity)']].
-each do |xs|
- puts ((es + xs.shift).to_s + ' ' + xs.shift)
-end
diff --git a/sample/inf-ruby.el b/sample/inf-ruby.el
deleted file mode 100644
index 504aff2db7..0000000000
--- a/sample/inf-ruby.el
+++ /dev/null
@@ -1,310 +0,0 @@
-(J;;;(B (J-*-Emacs-Lisp-*-(B
-;;;
-;;; $Id: inf-ruby.el,v 1.4 1998/05/20 02:45:58 senda Exp $
-;;; $Author: senda $
-;;; $Date: 1998/05/20 02:45:58 $
-;;;
-;;; Inferior Ruby Mode - ruby process in a buffer.
-;;; adapted from cmuscheme.el
-;;;
-;;; Usage:
-;;;
-;;; (0) check ruby-program-name variable that can run your environment.
-;;;
-;;; (1) modify .emacs to use ruby-mode
-;;; for example :
-;;;
-;;; (autoload 'ruby-mode "ruby-mode"
-;;; "Mode for editing ruby source files")
-;;; (setq auto-mode-alist
-;;; (append '(("\\.rb$" . ruby-mode)) auto-mode-alist))
-;;; (setq interpreter-mode-alist (append '(("^#!.*ruby" . ruby-mode))
-;;; interpreter-mode-alist))
-;;;
-;;; (2) set to road inf-ruby and set inf-ruby key definition in ruby-mode.
-;;;
-;;; (autoload 'run-ruby "inf-ruby"
-;;; "Run an inferior Ruby process")
-;;; (autoload 'inf-ruby-keys "inf-ruby"
-;;; "Set local key defs for inf-ruby in ruby-mode")
-;;; (add-hook 'ruby-mode-hook
-;;; '(lambda ()
-;;; (inf-ruby-keys)
-;;; ))
-;;;
-;;; HISTORY
-;;; senda - 8 Apr 1998: Created.
-;;; $Log: inf-ruby.el,v $
-;;; Revision 1.4 1998/05/20 02:45:58 senda
-;;; default program to irb
-;;;
-;;; Revision 1.3 1998/04/10 04:11:30 senda
-;;; modification by Matsumoto san (1.1b9_09)
-;;; remove-in-string defined
-;;; global variable :
-;;; inferior-ruby-first-prompt-pattern
-;;; inferior-ruby-prompt-pattern
-;;; defined
-;;;
-;;; Revision 1.2 1998/04/09 07:53:42 senda
-;;; remove M-C-x in inferior-ruby-mode
-;;;
-;;; Revision 1.1 1998/04/09 07:28:36 senda
-;;; Initial revision
-;;;
-;;;
-
-(require 'comint)
-(require 'ruby-mode)
-
-;;
-;; you may change these variables
-;;
-;(defvar ruby-program-name "rbc --noreadline"
-; "*Program invoked by the run-ruby command")
-;
-;(defvar inferior-ruby-first-prompt-pattern "^rbc0> *"
-; "first prompt regex pattern of ruby interpreter.")
-;
-;(defvar inferior-ruby-prompt-pattern "^\\(rbc.[>*\"'] *\\)+"
-; "prompt regex pattern of ruby interpreter.")
-
-;;;; for irb
-(defvar ruby-program-name "irb --inf-ruby-mode"
- "*Program invoked by the run-ruby command")
-
-(defvar inferior-ruby-first-prompt-pattern "^irb(.*)[0-9:]+0> *"
- "first prompt regex pattern of ruby interpreter.")
-
-(defvar inferior-ruby-prompt-pattern "^\\(irb(.*)[0-9:]+[>*\"'] *\\)+"
- "prompt regex pattern of ruby interpreter.")
-
-;;
-;; mode variables
-;;
-(defvar inferior-ruby-mode-hook nil
- "*Hook for customising inferior-ruby mode.")
-(defvar inferior-ruby-mode-map nil
- "*Mode map for inferior-ruby-mode")
-
-(cond ((not inferior-ruby-mode-map)
- (setq inferior-ruby-mode-map
- (copy-keymap comint-mode-map))
-; (define-key inferior-ruby-mode-map "\M-\C-x" ;gnu convention
-; 'ruby-send-definition)
-; (define-key inferior-ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
- (define-key inferior-ruby-mode-map "\C-c\C-l" 'ruby-load-file)
-))
-
-(defun inf-ruby-keys ()
- "Set local key defs for inf-ruby in ruby-mode"
- (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition)
-; (define-key ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
- (define-key ruby-mode-map "\C-c\C-e" 'ruby-send-definition)
- (define-key ruby-mode-map "\C-c\M-e" 'ruby-send-definition-and-go)
- (define-key ruby-mode-map "\C-c\C-r" 'ruby-send-region)
- (define-key ruby-mode-map "\C-c\M-r" 'ruby-send-region-and-go)
- (define-key ruby-mode-map "\C-c\C-z" 'switch-to-ruby)
- (define-key ruby-mode-map "\C-c\C-l" 'ruby-load-file)
- (define-key ruby-mode-map "\C-c\C-s" 'run-ruby)
-)
-
-(defvar ruby-buffer nil "current ruby (actually irb) process buffer.")
-
-(defun inferior-ruby-mode ()
- "Major mode for interacting with an inferior ruby (irb) process.
-
-The following commands are available:
-\\{inferior-ruby-mode-map}
-
-A ruby process can be fired up with M-x run-ruby.
-
-Customisation: Entry to this mode runs the hooks on comint-mode-hook and
-inferior-ruby-mode-hook (in that order).
-
-You can send text to the inferior ruby process from other buffers containing
-Ruby source.
- switch-to-ruby switches the current buffer to the ruby process buffer.
- ruby-send-definition sends the current definition to the ruby process.
- ruby-send-region sends the current region to the ruby process.
-
- ruby-send-definition-and-go, ruby-send-region-and-go,
- switch to the ruby process buffer after sending their text.
-For information on running multiple processes in multiple buffers, see
-documentation for variable ruby-buffer.
-
-Commands:
-Return after the end of the process' output sends the text from the
- end of process to point.
-Return before the end of the process' output copies the sexp ending at point
- to the end of the process' output, and sends it.
-Delete converts tabs to spaces as it moves back.
-Tab indents for ruby; with argument, shifts rest
- of expression rigidly with the current line.
-C-M-q does Tab on each line starting within following expression.
-Paragraphs are separated only by blank lines. # start comments.
-If you accidentally suspend your process, use \\[comint-continue-subjob]
-to continue it."
- (interactive)
- (comint-mode)
- ;; Customise in inferior-ruby-mode-hook
- ;(setq comint-prompt-regexp "^[^>\n]*>+ *")
- (setq comint-prompt-regexp inferior-ruby-prompt-pattern)
- ;;(scheme-mode-variables)
- (ruby-mode-variables)
- (setq major-mode 'inferior-ruby-mode)
- (setq mode-name "Inferior Ruby")
- (setq mode-line-process '(":%s"))
- (use-local-map inferior-ruby-mode-map)
- (setq comint-input-filter (function ruby-input-filter))
- (setq comint-get-old-input (function ruby-get-old-input))
- (run-hooks 'inferior-ruby-mode-hook))
-
-(defvar inferior-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
- "*Input matching this regexp are not saved on the history list.
-Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")
-
-(defun ruby-input-filter (str)
- "Don't save anything matching inferior-ruby-filter-regexp"
- (not (string-match inferior-ruby-filter-regexp str)))
-
-;; adapted from replace-in-string in XEmacs (subr.el)
-(defun remove-in-string (str regexp)
- "Remove all matches in STR for REGEXP and returns the new string."
- (let ((rtn-str "") (start 0) match prev-start)
- (while (setq match (string-match regexp str start))
- (setq prev-start start
- start (match-end 0)
- rtn-str (concat rtn-str (substring str prev-start match))))
- (concat rtn-str (substring str start))))
-
-(defun ruby-get-old-input ()
- "Snarf the sexp ending at point"
- (save-excursion
- (let ((end (point)))
- (re-search-backward inferior-ruby-first-prompt-pattern)
- (remove-in-string (buffer-substring (point) end)
- inferior-ruby-prompt-pattern)
- )))
-
-(defun ruby-args-to-list (string)
- (let ((where (string-match "[ \t]" string)))
- (cond ((null where) (list string))
- ((not (= where 0))
- (cons (substring string 0 where)
- (ruby-args-to-list (substring string (+ 1 where)
- (length string)))))
- (t (let ((pos (string-match "[^ \t]" string)))
- (if (null pos)
- nil
- (ruby-args-to-list (substring string pos
- (length string)))))))))
-
-(defun run-ruby (cmd)
- "Run an inferior Ruby process, input and output via buffer *ruby*.
-If there is a process already running in `*ruby*', switch to that buffer.
-With argument, allows you to edit the command line (default is value
-of `ruby-program-name'). Runs the hooks `inferior-ruby-mode-hook'
-\(after the `comint-mode-hook' is run).
-\(Type \\[describe-mode] in the process buffer for a list of commands.)"
-
- (interactive (list (if current-prefix-arg
- (read-string "Run Ruby: " ruby-program-name)
- ruby-program-name)))
- (if (not (comint-check-proc "*ruby*"))
- (let ((cmdlist (ruby-args-to-list cmd)))
- (set-buffer (apply 'make-comint "ruby" (car cmdlist)
- nil (cdr cmdlist)))
- (inferior-ruby-mode)))
- (setq ruby-program-name cmd)
- (setq ruby-buffer "*ruby*")
- (pop-to-buffer "*ruby*"))
-
-(defun ruby-send-region (start end)
- "Send the current region to the inferior Ruby process."
- (interactive "r")
- (comint-send-region (ruby-proc) start end)
- (comint-send-string (ruby-proc) "\n"))
-
-(defun ruby-send-definition ()
- "Send the current definition to the inferior Ruby process."
- (interactive)
- (save-excursion
- (ruby-end-of-defun)
- (let ((end (point)))
- (ruby-beginning-of-defun)
- (ruby-send-region (point) end))))
-
-;(defun ruby-send-last-sexp ()
-; "Send the previous sexp to the inferior Ruby process."
-; (interactive)
-; (ruby-send-region (save-excursion (backward-sexp) (point)) (point)))
-
-(defun switch-to-ruby (eob-p)
- "Switch to the ruby process buffer.
-With argument, positions cursor at end of buffer."
- (interactive "P")
- (if (get-buffer ruby-buffer)
- (pop-to-buffer ruby-buffer)
- (error "No current process buffer. See variable ruby-buffer."))
- (cond (eob-p
- (push-mark)
- (goto-char (point-max)))))
-
-(defun ruby-send-region-and-go (start end)
- "Send the current region to the inferior Ruby process.
-Then switch to the process buffer."
- (interactive "r")
- (ruby-send-region start end)
- (switch-to-ruby t))
-
-(defun ruby-send-definition-and-go ()
- "Send the current definition to the inferior Ruby.
-Then switch to the process buffer."
- (interactive)
- (ruby-send-definition)
- (switch-to-ruby t))
-
-(defvar ruby-source-modes '(ruby-mode)
- "*Used to determine if a buffer contains Ruby source code.
-If it's loaded into a buffer that is in one of these major modes, it's
-considered a ruby source file by ruby-load-file.
-Used by these commands to determine defaults.")
-
-(defvar ruby-prev-l/c-dir/file nil
- "Caches the last (directory . file) pair.
-Caches the last pair used in the last ruby-load-file command.
-Used for determining the default in the
-next one.")
-
-(defun ruby-load-file (file-name)
- "Load a Ruby file into the inferior Ruby process."
- (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
- ruby-source-modes t)) ; T because LOAD
- ; needs an exact name
- (comint-check-source file-name) ; Check to see if buffer needs saved.
- (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name)
- (file-name-nondirectory file-name)))
- (comint-send-string (ruby-proc) (concat "(load \""
- file-name
- "\"\)\n")))
-
-(defun ruby-proc ()
- "Returns the current ruby process. See variable ruby-buffer."
- (let ((proc (get-buffer-process (if (eq major-mode 'inferior-ruby-mode)
- (current-buffer)
- ruby-buffer))))
- (or proc
- (error "No current process. See variable ruby-buffer"))))
-
-;;; Do the user's customisation...
-
-(defvar inf-ruby-load-hook nil
- "This hook is run when inf-ruby is loaded in.
-This is a good place to put keybindings.")
-
-(run-hooks 'inf-ruby-load-hook)
-
-(provide 'inf-ruby)
-
-;;; inf-ruby.el ends here
diff --git a/sample/iseq_loader.rb b/sample/iseq_loader.rb
new file mode 100644
index 0000000000..8c271405d6
--- /dev/null
+++ b/sample/iseq_loader.rb
@@ -0,0 +1,243 @@
+#
+# iseq_loader.rb - sample of compiler/loader for binary compiled file
+#
+# Usage as a compiler: ruby iseq_loader.rb [file or directory] ...
+#
+# It compiles and stores specified files.
+# If directories are specified, then compiles and stores all *.rb files.
+# (using Dir.glob)
+#
+# TODO: add remove option
+# TODO: add verify option
+#
+# Usage as a loader: simply require this file with the following setting.
+#
+# Setting with environment variables.
+#
+# * RUBY_ISEQ_LOADER_STORAGE to select storage type
+# * dbm: use dbm
+# * fs: [default] use file system. locate a compiled binary files in same
+# directory of scripts like Rubinius. foo.rb.yarb will be created for foo.rb.
+# * fs2: use file system. locate compiled file in specified directory.
+# * nothing: do nothing.
+#
+# * RUBY_ISEQ_LOADER_STORAGE_DIR to select directory
+# * default: ~/.ruby_binaries/
+#
+# * RUBY_ISEQ_LOADER_STORAGE_COMPILE_IF_NOT_COMPILED
+# * true: store compiled file if compiled data is not available.
+# * false: [default] do nothing if there is no compiled iseq data.
+
+class RubyVM::InstructionSequence
+ $ISEQ_LOADER_LOADED = 0
+ $ISEQ_LOADER_COMPILED = 0
+ $ISEQ_LOADER_IGNORED = 0
+ LAUNCHED_TIME = Time.now
+ COMPILE_FILE_ENABLE = false || true
+ COMPILE_VERBOSE = $VERBOSE || false # || true
+ COMPILE_DEBUG = ENV['RUBY_ISEQ_LOADER_DEBUG']
+ COMPILE_IF_NOT_COMPILED = ENV['RUBY_ISEQ_LOADER_STORAGE_COMPILE_IF_NOT_COMPILED'] == 'true'
+
+ at_exit{
+ STDERR.puts "[ISEQ_LOADER] #{Process.pid} time: #{Time.now - LAUNCHED_TIME}, " +
+ "loaded: #{$ISEQ_LOADER_LOADED}, " +
+ "compiled: #{$ISEQ_LOADER_COMPILED}, " +
+ "ignored: #{$ISEQ_LOADER_IGNORED}"
+ } if COMPILE_VERBOSE
+
+ unless cf_dir = ENV['RUBY_ISEQ_LOADER_STORAGE_DIR']
+ cf_dir = File.expand_path("~/.ruby_binaries")
+ unless File.exist?(cf_dir)
+ Dir.mkdir(cf_dir)
+ end
+ end
+ CF_PREFIX = "#{cf_dir}/cb."
+
+ class NullStorage
+ def load_iseq fname; end
+ def compile_and_save_isq fname; end
+ def unlink_compiled_iseq; end
+ end
+
+ class BasicStorage
+ def initialize
+ require 'digest/sha1'
+ end
+
+ def load_iseq fname
+ iseq_key = iseq_key_name(fname)
+ if compiled_iseq_exist?(fname, iseq_key) && compiled_iseq_is_younger?(fname, iseq_key)
+ $ISEQ_LOADER_LOADED += 1
+ STDERR.puts "[ISEQ_LOADER] #{Process.pid} load #{fname} from #{iseq_key}" if COMPILE_DEBUG
+ binary = read_compiled_iseq(fname, iseq_key)
+ iseq = RubyVM::InstructionSequence.load_from_binary(binary)
+ # p [extra_data(iseq.path), RubyVM::InstructionSequence.load_from_binary_extra_data(binary)]
+ # raise unless extra_data(iseq.path) == RubyVM::InstructionSequence.load_from_binary_extra_data(binary)
+ iseq
+ elsif COMPILE_IF_NOT_COMPILED
+ compile_and_save_iseq(fname, iseq_key)
+ else
+ $ISEQ_LOADER_IGNORED += 1
+ # p fname
+ nil
+ end
+ end
+
+ def extra_data fname
+ "SHA-1:#{::Digest::SHA1.file(fname).digest}"
+ end
+
+ def compile_and_save_iseq fname, iseq_key = iseq_key_name(fname)
+ $ISEQ_LOADER_COMPILED += 1
+ STDERR.puts "[RUBY_COMPILED_FILE] compile #{fname}" if COMPILE_DEBUG
+ iseq = RubyVM::InstructionSequence.compile_file(fname)
+
+ binary = iseq.to_binary(extra_data(fname))
+ write_compiled_iseq(fname, iseq_key, binary)
+ iseq
+ end
+
+ # def unlink_compiled_iseq; nil; end # should implement at sub classes
+
+ private
+
+ def iseq_key_name fname
+ fname
+ end
+
+ # should implement at sub classes
+ # def compiled_iseq_younger? fname, iseq_key; end
+ # def compiled_iseq_exist? fname, iseq_key; end
+ # def read_compiled_file fname, iseq_key; end
+ # def write_compiled_file fname, iseq_key, binary; end
+ end
+
+ class FSStorage < BasicStorage
+ def initialize
+ super
+ require 'fileutils'
+ @dir = CF_PREFIX + "files"
+ unless File.directory?(@dir)
+ FileUtils.mkdir_p(@dir)
+ end
+ end
+
+ def unlink_compiled_iseq
+ File.unlink(compile_file_path)
+ end
+
+ private
+
+ def iseq_key_name fname
+ "#{fname}.yarb" # same directory
+ end
+
+ def compiled_iseq_exist? fname, iseq_key
+ File.exist?(iseq_key)
+ end
+
+ def compiled_iseq_is_younger? fname, iseq_key
+ File.mtime(iseq_key) >= File.mtime(fname)
+ end
+
+ def read_compiled_iseq fname, iseq_key
+ File.open(iseq_key, 'rb'){|f| f.read}
+ end
+
+ def write_compiled_iseq fname, iseq_key, binary
+ File.open(iseq_key, 'wb'){|f| f.write(binary)}
+ end
+ end
+
+ class FS2Storage < FSStorage
+ def iseq_key_name fname
+ @dir + fname.gsub(/[^A-Za-z0-9\._-]/){|c| '%02x' % c.ord} # special directory
+ end
+ end
+
+ class DBMStorage < BasicStorage
+ def initialize
+ require 'dbm'
+ @db = DBM.open(CF_PREFIX+'db')
+ end
+
+ def unlink_compiled_iseq
+ @db.delete fname
+ end
+
+ private
+
+ def date_key_name fname
+ "date.#{fname}"
+ end
+
+ def iseq_key_name fname
+ "body.#{fname}"
+ end
+
+ def compiled_iseq_exist? fname, iseq_key
+ @db.has_key? iseq_key
+ end
+
+ def compiled_iseq_is_younger? fname, iseq_key
+ date_key = date_key_name(fname)
+ if @db.has_key? date_key
+ @db[date_key].to_i >= File.mtime(fname).to_i
+ end
+ end
+
+ def read_compiled_iseq fname, iseq_key
+ @db[iseq_key]
+ end
+
+ def write_compiled_iseq fname, iseq_key, binary
+ date_key = date_key_name(fname)
+ @db[iseq_key] = binary
+ @db[date_key] = Time.now.to_i
+ end
+ end
+
+ STORAGE = case ENV['RUBY_ISEQ_LOADER_STORAGE']
+ when 'dbm'
+ DBMStorage.new
+ when 'fs'
+ FSStorage.new
+ when 'fs2'
+ FS2Storage.new
+ when 'null'
+ NullStorage.new
+ else
+ FSStorage.new
+ end
+
+ STDERR.puts "[ISEQ_LOADER] use #{STORAGE.class} " if COMPILE_VERBOSE
+
+ def self.load_iseq fname
+ STORAGE.load_iseq(fname)
+ end
+
+ def self.compile_and_save_iseq fname
+ STORAGE.compile_and_save_iseq fname
+ end
+
+ def self.unlink_compiled_iseq fname
+ STORAGE.unlink_compiled_iseq fname
+ end
+end
+
+if __FILE__ == $0
+ ARGV.each{|path|
+ if File.directory?(path)
+ pattern = File.join(path, '**/*.rb')
+ Dir.glob(pattern){|file|
+ begin
+ RubyVM::InstructionSequence.compile_and_save_iseq(file)
+ rescue SyntaxError => e
+ STDERR.puts e
+ end
+ }
+ else
+ RubyVM::InstructionSequence.compile_and_save_iseq(path)
+ end
+ }
+end
diff --git a/sample/list.rb b/sample/list.rb
index 76035e67d6..7458ba0244 100644
--- a/sample/list.rb
+++ b/sample/list.rb
@@ -1,8 +1,8 @@
# Linked list example
class MyElem
- # ֥˼ưŪ˸ƤФ᥽å
+ # object initializer called from Class#new
def initialize(item)
- # @ѿϥ󥹥ѿ(פʤ)
+ # @variables are instance variable, no declaration needed
@data = item
@succ = nil
end
@@ -15,13 +15,17 @@ class MyElem
@succ
end
- # obj.data = valפȤȤ˰ۤ˸ƤФ᥽å
+ # the method invoked by ``obj.data = val''
def succ=(new)
@succ = new
end
end
class MyList
+ def initialize
+ @head = nil
+ end
+
def add_to_list(obj)
elt = MyElem.new(obj)
if @head
@@ -40,12 +44,12 @@ class MyList
end
end
- # ֥ȤʸѴ᥽å
- # printǤɽѤ
+ # the method to convert object into string.
+ # redefining this will affect print.
def to_s
str = "<MyList:\n";
for elt in self
- # str = str + elt.data.to_s + "\n"פξά
+ # short form of ``str = str + elt.data.to_s + "\n"''
str += elt.data.to_s + "\n"
end
str += ">"
@@ -64,7 +68,7 @@ class Point
end
end
-# ѿ`$'ǻϤޤ롥
+# global variable name starts with `$'.
$list1 = MyList.new
$list1.add_to_list(10)
$list1.add_to_list(20)
@@ -75,6 +79,6 @@ $list2.add_to_list(20)
$list2.add_to_list(Point.new(4, 5))
$list2.add_to_list($list1)
-# ۣǤʤ¤᥽åɸƤӽФγ̤ϾάǤ
+# parenthesises around method arguments can be omitted unless ambiguous.
print "list1:\n", $list1, "\n"
print "list2:\n", $list2, "\n"
diff --git a/sample/list2.rb b/sample/list2.rb
index 914cb8996e..1d71affc1b 100644
--- a/sample/list2.rb
+++ b/sample/list2.rb
@@ -9,7 +9,7 @@ class Point
sprintf("%d@%d", @x, @y)
end
end
-
+
list1 = [10, 20, Point.new(2, 3), Point.new(4, 5)]
list2 = [20, Point.new(4, 5), list1]
print("list1:\n", list1.join("\n"), "\n")
diff --git a/sample/list3.rb b/sample/list3.rb
index 1d756fdff0..110e405cf9 100644
--- a/sample/list3.rb
+++ b/sample/list3.rb
@@ -7,11 +7,11 @@ class Point
self
end
- def to_s
+ def inspect
sprintf("%d@%d", @x, @y)
end
end
-
+
list1 = [10, 20, Point.new(2, 3), Point.new(4, 5)]
list2 = [20, Point.new(4, 5), list1]
print("list1: ", list1.inspect, "\n")
diff --git a/sample/logger/app.rb b/sample/logger/app.rb
new file mode 100644
index 0000000000..97b62bca30
--- /dev/null
+++ b/sample/logger/app.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+
+require 'logger'
+
+class MyApp < Logger::Application
+ def initialize(a, b, c)
+ super('MyApp')
+
+ # Set logDevice here.
+ logfile = 'app.log'
+ self.log = logfile
+ self.level = INFO
+
+ # Initialize your application...
+ @a = a
+ @b = b
+ @c = c
+ end
+
+ def run
+ @log.info { 'Started.' }
+
+ @log.info { "This block isn't evaled because 'debug' is not severe here." }
+ @log.debug { "Result = " << foo(0) }
+ @log.info { "So nothing is dumped." }
+
+ @log.info { "This block is evaled because 'info' is enough severe here." }
+ @log.info { "Result = " << foo(0) }
+ @log.info { "Above causes exception, so not reached here." }
+
+ @log.info { 'Finished.' }
+ end
+
+private
+
+ def foo(var)
+ 1 / var
+ end
+end
+
+status = MyApp.new(1, 2, 3).start
+
+if status != 0
+ puts 'Some error(s) occurred.'
+ puts 'See "app.log".'
+end
diff --git a/sample/logger/log.rb b/sample/logger/log.rb
new file mode 100644
index 0000000000..aab80e462e
--- /dev/null
+++ b/sample/logger/log.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+
+require 'logger'
+
+log = Logger.new(STDERR)
+
+def do_log(log)
+ log.debug('do_log1') { "debug" }
+ log.info('do_log2') { "info" }
+ log.warn('do_log3') { "warn" }
+ log.error('do_log4') { "error" }
+ log.fatal('do_log6') { "fatal" }
+ log.unknown('do_log7') { "unknown" }
+end
+
+log.level = Logger::DEBUG # Default.
+do_log(log)
+
+puts "Set severity threshold 'WARN'."
+
+log.level = Logger::WARN
+do_log(log)
+
+puts "Change datetime format. Thanks to Daniel Berger."
+
+log.datetime_format = "%d-%b-%Y@%H:%M:%S"
+do_log(log)
diff --git a/sample/logger/shifting.rb b/sample/logger/shifting.rb
new file mode 100644
index 0000000000..bd8852a4ae
--- /dev/null
+++ b/sample/logger/shifting.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require 'logger'
+
+logfile = 'shifting.log'
+# Max 3 age ... logShifting.log, logShifting.log.0, and logShifting.log.1
+shift_age = 3
+# Shift log file about for each 1024 bytes.
+shift_size = 1024
+
+log = Logger.new(logfile, shift_age, shift_size)
+
+def do_log(log)
+ log.debug('do_log1') { 'd' * rand(100) }
+ log.info('do_log2') { 'i' * rand(100) }
+ log.warn('do_log3') { 'w' * rand(100) }
+ log.error('do_log4') { 'e' * rand(100) }
+ log.fatal('do_log5') { 'f' * rand(100) }
+ log.unknown('do_log6') { 'u' * rand(100) }
+end
+
+(1..10).each do
+ do_log(log)
+end
+
+puts 'See shifting.log and shifting.log.[01].'
diff --git a/sample/mine.rb b/sample/mine.rb
new file mode 100755
index 0000000000..77e0204bf6
--- /dev/null
+++ b/sample/mine.rb
@@ -0,0 +1,176 @@
+#! /usr/bin/ruby -Ku
+# -*- coding: utf-8 -*-
+
+require 'io/console'
+
+class Board
+ def clr
+ print "\e[2J"
+ end
+ def pos(x,y)
+ printf "\e[%d;%dH", y+1, x*2+1
+ end
+ def colorstr(id,s)
+ printf "\e[%dm%s\e[0m", id, s
+ end
+ def put(x, y, col, str)
+ pos(x,y); colorstr(43,str)
+ pos(0,@hi); print "残り:",@mc,"/",@total," "
+ pos(x,y)
+ end
+ private :clr, :pos, :colorstr, :put
+ CHR=["・","1","2","3","4","5","6","7","8","★","●","@@"]
+ COL=[46,43,45] # default,opened,over
+ def initialize(h,w,m)
+ # ゲーム盤の生成(h:縦,w:横,m:爆弾の数)
+ @hi=h; @wi=w; @m=m
+ reset
+ end
+ def reset
+ # ゲーム盤を(再)初期化する
+ srand()
+ @cx=0; @cy=0; @mc=@m
+ @over=false
+ @data=Array.new(@hi*@wi)
+ @state=Array.new(@hi*@wi)
+ @total=@hi*@wi
+ @total.times {|i| @data[i]=0}
+ @m.times do
+ loop do
+ j=rand(@total-1)
+ if @data[j] == 0 then
+ @data[j]=1
+ break
+ end
+ end
+ end
+ clr; pos(0,0)
+ @hi.times{|y| pos(0,y); colorstr(COL[0],CHR[0]*@wi)}
+ pos(@cx,@cy)
+ end
+ def mark
+ # 現在のカーソル位置にマークをつける
+ if @state[@wi*@cy+@cx] != nil then return end
+ @state[@wi*@cy+@cx] = "MARK"
+ @mc=@mc-1;
+ @total=@total-1;
+ put(@cx, @cy, COL[1], CHR[9])
+ end
+ def open(x=@cx,y=@cy)
+ # 現在のカーソル位置をオープンにする
+ # 爆弾があればゲームオーバー
+ if @state[@wi*y+x] =="OPEN" then return 0 end
+ if @state[@wi*y+x] == nil then @total=@total-1 end
+ if @state[@wi*y+x] =="MARK" then @mc=@mc+1 end
+ @state[@wi*y+x]="OPEN"
+ if fetch(x,y) == 1 then @over = 1; return end
+ c = count(x,y)
+ put(x, y, COL[1], CHR[c])
+ return 0 if c != 0
+ if x > 0 && y > 0 then open(x-1,y-1) end
+ if y > 0 then open(x, y-1) end
+ if x < @wi-1 && y > 0 then open(x+1,y-1) end
+ if x > 0 then open(x-1,y) end
+ if x < @wi-1 then open(x+1,y) end
+ if x > 0 && y < @hi-1 then open(x-1,y+1) end
+ if y < @hi -1 then open(x,y+1) end
+ if x < @wi-1 && y < @hi-1 then open(x+1,y+1) end
+ pos(@cx,@cy)
+ end
+ def fetch(x,y)
+ # (x,y)の位置の爆弾の数(0 or 1)を返す
+ if x < 0 then 0
+ elsif x >= @wi then 0
+ elsif y < 0 then 0
+ elsif y >= @hi then 0
+ else
+ @data[y*@wi+x]
+ end
+ end
+ def count(x,y)
+ # (x,y)に隣接する爆弾の数を返す
+ fetch(x-1,y-1)+fetch(x,y-1)+fetch(x+1,y-1)+
+ fetch(x-1,y) + fetch(x+1,y)+
+ fetch(x-1,y+1)+fetch(x,y+1)+fetch(x+1,y+1)
+ end
+ def over(win)
+ # ゲームの終了
+ quit
+ unless win
+ pos(@cx,@cy); print CHR[11]
+ end
+ pos(0,@hi)
+ if win then print "*** YOU WIN !! ***"
+ else print "*** GAME OVER ***"
+ end
+ end
+ def over?
+ # ゲームの終了チェック
+ # 終了処理も呼び出す
+ remain = (@mc+@total == 0)
+ if @over || remain
+ over(remain)
+ true
+ else
+ false
+ end
+ end
+ def quit
+ # ゲームの中断(または終了)
+ # 盤面を全て見せる
+ @hi.times do|y|
+ pos(0,y)
+ @wi.times do|x|
+ colorstr(if @state[y*@wi+x] == "MARK" then COL[1] else COL[2] end,
+ if fetch(x,y)==1 then CHR[10] else CHR[count(x,y)] end)
+ end
+ end
+ end
+ def down
+ # カーソルを下に
+ if @cy < @hi-1 then @cy=@cy+1; pos(@cx, @cy) end
+ end
+ def up
+ # カーソルを上に
+ if @cy > 0 then @cy=@cy-1; pos(@cx, @cy) end
+ end
+ def left
+ # カーソルを左に
+ if @cx > 0 then @cx=@cx-1; pos(@cx, @cy) end
+ end
+ def right
+ # カーソルを右に
+ if @cx < @wi-1 then @cx=@cx+1; pos(@cx, @cy) end
+ end
+end
+
+bd=Board.new(10,10,10)
+
+IO.console.raw do
+ loop do
+ case STDIN.getc
+ when ?n # new game
+ bd.reset
+ when ?m # mark
+ bd.mark
+ when ?j
+ bd.down
+ when ?k
+ bd.up
+ when ?h
+ bd.left
+ when ?l
+ bd.right
+ when ?\s
+ bd.open
+ when ?q,?\C-c # quit game
+ bd.quit
+ break
+ end
+ if bd.over?
+ if STDIN.getc == ?q then break end
+ bd.reset
+ end
+ end
+end
+print "\n"
diff --git a/sample/mkproto.rb b/sample/mkproto.rb
index 97006f9f54..e650fe8d47 100644
--- a/sample/mkproto.rb
+++ b/sample/mkproto.rb
@@ -1,24 +1,24 @@
$/ = nil
-while gets()
- if /^((void|VALUE|int|char *\*|ID|struct [\w_]+ *\*|st_table *\*) *)?\n([\w\d_]+)\(.*\)\n\s*((.+;\n)*)\{/
- $_ = $'
+while line = gets()
+ if /^((void|VALUE|int|char *\*|ID|struct \w+ *\*|st_table *\*) *)?\n(\w+)\(.*\)\n\s*((.+;\n)*)\{/ =~ line
+ line = $'
printf "%s %s(", $2, $3
args = []
for arg in $4.split(/;\n\s*/)
- arg.gsub! ' +', ' '
+ arg.gsub!(/ +/, ' ')
if arg =~ /,/
- if arg =~ /(([^*]+) *\** *[\w\d_]+),/
- type = $2.strip!
- args.push $1.strip!
- arg = $'
- else
- type = ""
- end
- while arg.sub!(/(\** *[\w\d_]+)(,|$)/, "")
- args.push type + " " + $1.strip!
- end
+ if arg =~ /(([^*]+) *\** *\w+),/
+ type = $2.strip
+ args.push $1.strip
+ arg = $'
+ else
+ type = ""
+ end
+ while arg.sub!(/(\** *\w+)(,|$)/, "") && $~
+ args.push type + " " + $1.strip
+ end
else
- args.push arg.strip!
+ args.push arg.strip
end
end
printf "%s);\n", args.join(', ')
diff --git a/sample/mpart.rb b/sample/mpart.rb
index 6c40d50e18..eeb895d3de 100644
--- a/sample/mpart.rb
+++ b/sample/mpart.rb
@@ -2,11 +2,29 @@
# split into multi part
# usage: mpart.rb [-nnn] file..
+class MPart < File
+ def self.new(basename, extname, part, parts)
+ super(sprintf("%s.%s%02d", basename, extname, part), "w").
+ begin_mpart(basename, part, parts)
+ end
+
+ def begin_mpart(basename, part, parts)
+ printf("%s part%02d/%02d\n", basename, part, parts)
+ write("BEGIN--cut here--cut here\n")
+ self
+ end
+
+ def close
+ write("END--cut here--cut here\n")
+ super
+ end
+end
+
lines = 1000
if (ARGV[0] =~ /^-(\d+)$/ )
- lines = $1.to_i;
- ARGV.shift;
+ lines = $1.to_i
+ ARGV.shift
end
basename = ARGV[0]
@@ -14,31 +32,23 @@ extname = "part"
part = 1
line = 0
+ofp = nil
fline = 0
-for i in ifp = open(basename)
- fline = fline + 1
-end
-ifp.close
+File.foreach(basename) {fline += 1}
parts = fline / lines + 1
-for i in ifp = open(basename)
+File.foreach(basename) do |i|
if line == 0
- ofp = open(sprintf("%s.%s%02d", basename, extname, part), "w")
- printf(ofp, "%s part%02d/%02d\n", basename, part, parts)
- ofp.write("BEGIN--cut here--cut here\n")
+ ofp = MPart.new(basename, extname, part, parts)
end
ofp.write(i)
- line = line + 1
+ line += 1
if line >= lines
- ofp.write("END--cut here--cut here\n")
ofp.close
- part = part + 1
+ part += 1
line = 0
end
end
-ofp.write("END--cut here--cut here\n")
ofp.close
-
-ifp.close
diff --git a/sample/mrshtest.rb b/sample/mrshtest.rb
deleted file mode 100644
index 402b35ad55..0000000000
--- a/sample/mrshtest.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require "marshal"
-include Marshal
-a = 25.6;
-pt = Struct.new('Point', :x,:y);
-x = pt.new(10, 10)
-y = pt.new(20, 20)
-rt = Struct.new('Rectangle', :origin,:corner);
-z = rt.new(x, y)
-c = Object.new
-s = [a, x, z, c, c, "fff"];
-p s
-d = dump(s);
-p d
-p load(d)
diff --git a/sample/observ.rb b/sample/observ.rb
index 72e5178b38..ef4a9f60f5 100644
--- a/sample/observ.rb
+++ b/sample/observ.rb
@@ -1,6 +1,5 @@
#! /usr/local/bin/ruby
-require "thread"
require "observer"
class Tick
@@ -8,10 +7,10 @@ class Tick
def initialize
Thread.start do
loop do
- sleep 0.999
- now = Time.now
- changed
- notify_observers(now.hour, now.min, now.sec)
+ sleep 0.999
+ now = Time.now
+ changed
+ notify_observers(now.hour, now.min, now.sec)
end
end
end
@@ -28,5 +27,5 @@ class Clock
end
end
-clock = Clock.new(Tick.new)
+Clock.new(Tick.new)
sleep
diff --git a/sample/occur.pl b/sample/occur.pl
index 1f5fcf27a4..331ce50211 100644
--- a/sample/occur.pl
+++ b/sample/occur.pl
@@ -1,9 +1,9 @@
while (<>) {
- for (split(/\W+/)) {
- $freq{$_}++;
- }
+ for (split(/\W+/)) {
+ $freq{$_}++;
+ }
}
for (sort keys %freq) {
- print "$_ -- $freq{$_}\n";
+ print "$_ -- $freq{$_}\n";
}
diff --git a/sample/occur.rb b/sample/occur.rb
index 2141fade38..5927ebc889 100644
--- a/sample/occur.rb
+++ b/sample/occur.rb
@@ -1,12 +1,12 @@
# word occurrence listing
-# usege: ruby occur.rb file..
-freq = {}
-while gets()
- for word in $_.split(/\W+/)
- freq[word] +=1
+# usage: ruby occur.rb file..
+freq = Hash.new(0)
+while line = gets()
+ line.scan(/\w+/) do |word|
+ freq[word] += 1
end
end
-for word in freq.keys.sort
- printf("%s -- %d\n", word, freq[word])
+for word in freq.keys.sort!
+ print word, " -- ", freq[word], "\n"
end
diff --git a/sample/occur2.rb b/sample/occur2.rb
deleted file mode 100644
index c450c30b0f..0000000000
--- a/sample/occur2.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# word occurrence listing
-# usege: ruby occur2.rb file..
-freq = {}
-while gets()
- for word in $_.split(/\W+/)
- begin
- freq[word] = freq[word] + 1
- rescue
- freq[word] = 1
- end
- end
-end
-
-for word in freq.keys.sort
- printf("%s -- %d\n", word, freq[word])
-end
diff --git a/sample/open3.rb b/sample/open3.rb
new file mode 100644
index 0000000000..bc6cdfe3bf
--- /dev/null
+++ b/sample/open3.rb
@@ -0,0 +1,12 @@
+require 'open3'
+
+a = Open3.popen3("nroff -man")
+Thread.start do
+ while line = gets
+ a[0].print line
+ end
+ a[0].close
+end
+while line = a[1].gets
+ print ":", line
+end
diff --git a/sample/openssl/c_rehash.rb b/sample/openssl/c_rehash.rb
new file mode 100644
index 0000000000..8b005bbb84
--- /dev/null
+++ b/sample/openssl/c_rehash.rb
@@ -0,0 +1,173 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+
+class CHashDir
+ include Enumerable
+
+ def initialize(dirpath)
+ @dirpath = dirpath
+ @fingerprint_cache = @cert_cache = @crl_cache = nil
+ end
+
+ def hash_dir(silent = false)
+ # ToDo: Should lock the directory...
+ @silent = silent
+ @fingerprint_cache = Hash.new
+ @cert_cache = Hash.new
+ @crl_cache = Hash.new
+ do_hash_dir
+ end
+
+ def get_certs(name = nil)
+ if name
+ @cert_cache[hash_name(name)]
+ else
+ @cert_cache.values.flatten
+ end
+ end
+
+ def get_crls(name = nil)
+ if name
+ @crl_cache[hash_name(name)]
+ else
+ @crl_cache.values.flatten
+ end
+ end
+
+ def delete_crl(crl)
+ File.unlink(crl_filename(crl))
+ hash_dir(true)
+ end
+
+ def add_crl(crl)
+ File.open(crl_filename(crl), "w") do |f|
+ f << crl.to_pem
+ end
+ hash_dir(true)
+ end
+
+ def load_pem_file(filepath)
+ str = File.read(filepath)
+ begin
+ OpenSSL::X509::Certificate.new(str)
+ rescue
+ begin
+ OpenSSL::X509::CRL.new(str)
+ rescue
+ begin
+ OpenSSL::X509::Request.new(str)
+ rescue
+ nil
+ end
+ end
+ end
+ end
+
+private
+
+ def crl_filename(crl)
+ path(hash_name(crl.issuer)) + '.pem'
+ end
+
+ def do_hash_dir
+ Dir.chdir(@dirpath) do
+ delete_symlink
+ Dir.glob('*.pem') do |pemfile|
+ cert = load_pem_file(pemfile)
+ case cert
+ when OpenSSL::X509::Certificate
+ link_hash_cert(pemfile, cert)
+ when OpenSSL::X509::CRL
+ link_hash_crl(pemfile, cert)
+ else
+ STDERR.puts("WARNING: #{pemfile} does not contain a certificate or CRL: skipping") unless @silent
+ end
+ end
+ end
+ end
+
+ def delete_symlink
+ Dir.entries(".").each do |entry|
+ next unless /^[\da-f]+\.r{0,1}\d+$/ =~ entry
+ File.unlink(entry) if FileTest.symlink?(entry)
+ end
+ end
+
+ def link_hash_cert(org_filename, cert)
+ name_hash = hash_name(cert.subject)
+ fingerprint = fingerprint(cert.to_der)
+ filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
+ "#{name_hash}.#{idx}"
+ }
+ unless filepath
+ unless @silent
+ STDERR.puts("WARNING: Skipping duplicate certificate #{org_filename}")
+ end
+ else
+ (@cert_cache[name_hash] ||= []) << path(filepath)
+ end
+ end
+
+ def link_hash_crl(org_filename, crl)
+ name_hash = hash_name(crl.issuer)
+ fingerprint = fingerprint(crl.to_der)
+ filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
+ "#{name_hash}.r#{idx}"
+ }
+ unless filepath
+ unless @silent
+ STDERR.puts("WARNING: Skipping duplicate CRL #{org_filename}")
+ end
+ else
+ (@crl_cache[name_hash] ||= []) << path(filepath)
+ end
+ end
+
+ def link_hash(org_filename, name, fingerprint)
+ idx = 0
+ filepath = nil
+ while true
+ filepath = yield(idx)
+ break unless FileTest.symlink?(filepath) or FileTest.exist?(filepath)
+ if @fingerprint_cache[filepath] == fingerprint
+ return false
+ end
+ idx += 1
+ end
+ STDOUT.puts("#{org_filename} => #{filepath}") unless @silent
+ symlink(org_filename, filepath)
+ @fingerprint_cache[filepath] = fingerprint
+ filepath
+ end
+
+ def symlink(from, to)
+ begin
+ File.symlink(from, to)
+ rescue
+ File.open(to, "w") do |f|
+ f << File.read(from)
+ end
+ end
+ end
+
+ def path(filename)
+ File.join(@dirpath, filename)
+ end
+
+ def hash_name(name)
+ sprintf("%08x", name.hash)
+ end
+
+ def fingerprint(der)
+ OpenSSL::Digest.hexdigest('MD5', der).upcase
+ end
+end
+
+if $0 == __FILE__
+ dirlist = ARGV
+ dirlist << '/usr/ssl/certs' if dirlist.empty?
+ dirlist.each do |dir|
+ CHashDir.new(dir).hash_dir
+ end
+end
diff --git a/sample/openssl/cert2text.rb b/sample/openssl/cert2text.rb
new file mode 100644
index 0000000000..fe14e51d3a
--- /dev/null
+++ b/sample/openssl/cert2text.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+
+def cert2text(cert_str)
+ [
+ OpenSSL::X509::Certificate,
+ OpenSSL::X509::CRL,
+ OpenSSL::X509::Request,
+ ].each do |klass|
+ begin
+ puts klass.new(cert_str).to_text
+ return
+ rescue
+ end
+ end
+ raise ArgumentError.new('Unknown format.')
+end
+
+if ARGV.empty?
+ cert2text(STDIN.read)
+else
+ ARGV.each do |file|
+ cert2text(File.read(file))
+ end
+end
diff --git a/sample/openssl/certstore.rb b/sample/openssl/certstore.rb
new file mode 100644
index 0000000000..72e59f6dad
--- /dev/null
+++ b/sample/openssl/certstore.rb
@@ -0,0 +1,158 @@
+require 'c_rehash'
+require 'crlstore'
+
+
+class CertStore
+ attr_reader :self_signed_ca
+ attr_reader :other_ca
+ attr_reader :ee
+ attr_reader :crl
+ attr_reader :request
+
+ def initialize(certs_dir)
+ @certs_dir = certs_dir
+ @c_store = CHashDir.new(@certs_dir)
+ @c_store.hash_dir(true)
+ @crl_store = CrlStore.new(@c_store)
+ @x509store = OpenSSL::X509::Store.new
+ @self_signed_ca = @other_ca = @ee = @crl = nil
+
+ # Uncomment this line to let OpenSSL to check CRL for each certs.
+ # @x509store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK | OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+
+ add_path
+ scan_certs
+ end
+
+ def generate_cert(filename)
+ @c_store.load_pem_file(filename)
+ end
+
+ def verify(cert)
+ error, crl_map = do_verify(cert)
+ if error
+ [[false, cert, crl_map[cert.subject], error]]
+ else
+ @x509store.chain.collect { |c| [true, c, crl_map[c.subject], nil] }
+ end
+ end
+
+ def match_cert(cert1, cert2)
+ (cert1.issuer.cmp(cert2.issuer) == 0) and cert1.serial == cert2.serial
+ end
+
+ def is_ca?(cert)
+ case guess_cert_type(cert)
+ when CERT_TYPE_SELF_SIGNED
+ true
+ when CERT_TYPE_OTHER
+ true
+ else
+ false
+ end
+ end
+
+ def scan_certs
+ @self_signed_ca = []
+ @other_ca = []
+ @ee = []
+ @crl = []
+ @request = []
+ load_certs
+ end
+
+private
+
+ def add_path
+ @x509store.add_path(@certs_dir)
+ end
+
+ def do_verify(cert)
+ error_map = {}
+ crl_map = {}
+ result = @x509store.verify(cert) do |ok, ctx|
+ cert = ctx.current_cert
+ if ctx.current_crl
+ crl_map[cert.subject] = true
+ end
+ if ok
+ if !ctx.current_crl
+ if crl = @crl_store.find_crl(cert)
+ crl_map[cert.subject] = true
+ if crl.revoked.find { |revoked| revoked.serial == cert.serial }
+ ok = false
+ error_string = 'certification revoked'
+ end
+ end
+ end
+ end
+ error_map[cert.subject] = error_string if error_string
+ ok
+ end
+ error = if result
+ nil
+ else
+ error_map[cert.subject] || @x509store.error_string
+ end
+ return error, crl_map
+ end
+
+ def load_certs
+ @c_store.get_certs.each do |certfile|
+ cert = generate_cert(certfile)
+ case guess_cert_type(cert)
+ when CERT_TYPE_SELF_SIGNED
+ @self_signed_ca << cert
+ when CERT_TYPE_OTHER
+ @other_ca << cert
+ when CERT_TYPE_EE
+ @ee << cert
+ else
+ raise "Unknown cert type."
+ end
+ end
+ @c_store.get_crls.each do |crlfile|
+ @crl << generate_cert(crlfile)
+ end
+ end
+
+ CERT_TYPE_SELF_SIGNED = 0
+ CERT_TYPE_OTHER = 1
+ CERT_TYPE_EE = 2
+ def guess_cert_type(cert)
+ ca = self_signed = is_cert_self_signed(cert)
+ cert.extensions.each do |ext|
+ # Ignores criticality of extensions. It's 'guess'ing.
+ case ext.oid
+ when 'basicConstraints'
+ /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ext.value
+ ca = ($1 == 'TRUE') unless ca
+ when 'keyUsage'
+ usage = ext.value.split(/\s*,\s*/)
+ ca = usage.include?('Certificate Sign') unless ca
+ when 'nsCertType'
+ usage = ext.value.split(/\s*,\s*/)
+ ca = usage.include?('SSL CA') unless ca
+ end
+ end
+ if ca
+ if self_signed
+ CERT_TYPE_SELF_SIGNED
+ else
+ CERT_TYPE_OTHER
+ end
+ else
+ CERT_TYPE_EE
+ end
+ end
+
+ def is_cert_self_signed(cert)
+ # cert.subject.cmp(cert.issuer) == 0
+ cert.subject.to_s == cert.issuer.to_s
+ end
+end
+
+
+if $0 == __FILE__
+ c = CertStore.new("trust_certs")
+end
diff --git a/sample/openssl/cipher.rb b/sample/openssl/cipher.rb
new file mode 100644
index 0000000000..a33dc3e95c
--- /dev/null
+++ b/sample/openssl/cipher.rb
@@ -0,0 +1,54 @@
+#!/usr/bin/env ruby
+require 'openssl'
+
+def crypt_by_password(alg, pass, salt, text)
+ puts "--Setup--"
+ puts %(cipher alg: "#{alg}")
+ puts %(plain text: "#{text}")
+ puts %(password: "#{pass}")
+ puts %(salt: "#{salt}")
+ puts
+
+ puts "--Encrypting--"
+ enc = OpenSSL::Cipher.new(alg)
+ enc.encrypt
+ enc.pkcs5_keyivgen(pass, salt)
+ cipher = enc.update(text)
+ cipher << enc.final
+ puts %(encrypted text: #{cipher.inspect})
+ puts
+
+ puts "--Decrypting--"
+ dec = OpenSSL::Cipher.new(alg)
+ dec.decrypt
+ dec.pkcs5_keyivgen(pass, salt)
+ plain = dec.update(cipher)
+ plain << dec.final
+ puts %(decrypted text: "#{plain}")
+ puts
+end
+
+def ciphers
+ ciphers = OpenSSL::Cipher.ciphers.sort
+ ciphers.each{|i|
+ if i.upcase != i && ciphers.include?(i.upcase)
+ ciphers.delete(i)
+ end
+ }
+ return ciphers
+end
+
+puts "Supported ciphers in #{OpenSSL::OPENSSL_VERSION}:"
+ciphers.each_with_index{|name, i|
+ printf("%-15s", name)
+ puts if (i + 1) % 5 == 0
+}
+puts
+puts
+
+alg = ARGV.shift || ciphers.first
+pass = "secret password"
+salt = "8 octets" # or nil
+text = "abcdefghijklmnopqrstuvwxyz"
+
+crypt_by_password(alg, pass, salt, text)
diff --git a/sample/openssl/crlstore.rb b/sample/openssl/crlstore.rb
new file mode 100644
index 0000000000..e3a592567c
--- /dev/null
+++ b/sample/openssl/crlstore.rb
@@ -0,0 +1,122 @@
+begin
+ require 'http-access2'
+rescue LoadError
+ STDERR.puts("Cannot load http-access2. CRL might not be fetched.")
+end
+require 'c_rehash'
+
+
+class CrlStore
+ def initialize(c_store)
+ @c_store = c_store
+ @c_store.hash_dir(true)
+ end
+
+ def find_crl(cert)
+ do_find_crl(cert)
+ end
+
+private
+
+ def do_find_crl(cert)
+ unless ca = find_ca(cert)
+ return nil
+ end
+ unless crlfiles = @c_store.get_crls(ca.subject)
+ if crl = renew_crl(cert, ca)
+ @c_store.add_crl(crl)
+ return crl
+ end
+ return nil
+ end
+ crlfiles.each do |crlfile|
+ next unless crl = load_crl(crlfile)
+ if crl.next_update < Time.now
+ if new_crl = renew_crl(cert, ca)
+ @c_store.delete_crl(crl)
+ @c_store.add_crl(new_crl)
+ crl = new_crl
+ end
+ end
+ if check_valid(crl, ca)
+ return crl
+ end
+ end
+ nil
+ end
+
+ def find_ca(cert)
+ @c_store.get_certs(cert.issuer).each do |cafile|
+ ca = load_cert(cafile)
+ if cert.verify(ca.public_key)
+ return ca
+ end
+ end
+ nil
+ end
+
+ def fetch(location)
+ if /\AURI:(.*)\z/ =~ location
+ begin
+ c = HTTPAccess2::Client.new(ENV['http_proxy'] || ENV['HTTP_PROXY'])
+ c.get_content($1)
+ rescue NameError, StandardError
+ nil
+ end
+ else
+ nil
+ end
+ end
+
+ def load_cert(certfile)
+ load_cert_str(File.read(certfile))
+ end
+
+ def load_crl(crlfile)
+ load_crl_str(File.read(crlfile))
+ end
+
+ def load_cert_str(cert_str)
+ OpenSSL::X509::Certificate.new(cert_str)
+ end
+
+ def load_crl_str(crl_str)
+ OpenSSL::X509::CRL.new(crl_str)
+ end
+
+ def check_valid(crl, ca)
+ unless crl.verify(ca.public_key)
+ return false
+ end
+ crl.last_update <= Time.now
+ end
+
+ RE_CDP = /\AcrlDistributionPoints\z/
+ def get_cdp(cert)
+ if cdp_ext = cert.extensions.find { |ext| RE_CDP =~ ext.oid }
+ cdp_ext.value.chomp
+ else
+ false
+ end
+ end
+
+ def renew_crl(cert, ca)
+ if cdp = get_cdp(cert)
+ if new_crl_str = fetch(cdp)
+ new_crl = load_crl_str(new_crl_str)
+ if check_valid(new_crl, ca)
+ return new_crl
+ end
+ end
+ end
+ false
+ end
+end
+
+if $0 == __FILE__
+ dir = "trust_certs"
+ c_store = CHashDir.new(dir)
+ s = CrlStore.new(c_store)
+ c = OpenSSL::X509::Certificate.new(File.read("cert_store/google_codesign.pem"))
+ p s.find_crl(c)
+end
diff --git a/sample/openssl/echo_cli.rb b/sample/openssl/echo_cli.rb
new file mode 100644
index 0000000000..3fbadf3361
--- /dev/null
+++ b/sample/openssl/echo_cli.rb
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+
+require 'socket'
+require 'openssl'
+require 'optparse'
+
+options = ARGV.getopts("p:c:k:C:")
+
+host = ARGV[0] || "localhost"
+port = options["p"] || "2000"
+cert_file = options["c"]
+key_file = options["k"]
+ca_path = options["C"]
+
+ctx = OpenSSL::SSL::SSLContext.new()
+if cert_file && key_file
+ ctx.cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+ ctx.key = OpenSSL::PKey.read(File::read(key_file))
+end
+if ca_path
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.ca_path = ca_path
+else
+ $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!"
+end
+
+s = TCPSocket.new(host, port)
+ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
+ssl.connect # start SSL session
+p ssl.peer_cert
+errors = Hash.new
+OpenSSL::X509.constants.grep(/^V_(ERR_|OK)/).each do |name|
+ errors[OpenSSL::X509.const_get(name)] = name
+end
+p errors[ssl.verify_result]
+
+ssl.sync_close = true # if true the underlying socket will be
+ # closed in SSLSocket#close. (default: false)
+while line = $stdin.gets
+ ssl.write line
+ puts ssl.gets.inspect
+end
+
+ssl.close
diff --git a/sample/openssl/echo_svr.rb b/sample/openssl/echo_svr.rb
new file mode 100644
index 0000000000..f20fb52bf5
--- /dev/null
+++ b/sample/openssl/echo_svr.rb
@@ -0,0 +1,65 @@
+#!/usr/bin/env ruby
+
+require 'socket'
+require 'openssl'
+require 'optparse'
+
+options = ARGV.getopts("p:c:k:C:")
+
+port = options["p"] || "2000"
+cert_file = options["c"]
+key_file = options["k"]
+ca_path = options["C"]
+
+if cert_file && key_file
+ cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+ key = OpenSSL::PKey.read(File::read(key_file))
+else
+ key = OpenSSL::PKey::RSA.new(2048){ print "." }
+ puts
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 0
+ name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
+ cert.subject = name
+ cert.issuer = name
+ cert.not_before = Time.now
+ cert.not_after = Time.now + 3600
+ cert.public_key = key
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
+ cert.extensions = [
+ ef.create_extension("basicConstraints","CA:FALSE"),
+ ef.create_extension("subjectKeyIdentifier","hash"),
+ ef.create_extension("extendedKeyUsage","serverAuth"),
+ ef.create_extension("keyUsage",
+ "keyEncipherment,dataEncipherment,digitalSignature")
+ ]
+ ef.issuer_certificate = cert
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ cert.sign(key, "SHA1")
+end
+
+ctx = OpenSSL::SSL::SSLContext.new()
+ctx.key = key
+ctx.cert = cert
+if ca_path
+ ctx.verify_mode =
+ OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ ctx.ca_path = ca_path
+else
+ $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!"
+end
+
+tcps = TCPServer.new(port)
+ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+loop do
+ ns = ssls.accept
+ puts "connected from #{ns.peeraddr}"
+ while line = ns.gets
+ puts line.inspect
+ ns.write line
+ end
+ puts "connection closed"
+ ns.close
+end
diff --git a/sample/openssl/gen_csr.rb b/sample/openssl/gen_csr.rb
new file mode 100644
index 0000000000..34b23fec1c
--- /dev/null
+++ b/sample/openssl/gen_csr.rb
@@ -0,0 +1,49 @@
+#!/usr/bin/env ruby
+
+require 'optparse'
+require 'openssl'
+
+def usage
+ myname = File::basename($0)
+ $stderr.puts <<EOS
+Usage: #{myname} [--key keypair_file] name
+ name ... ex. /C=JP/O=RRR/OU=CA/CN=NaHi/emailAddress=nahi@example.org
+EOS
+ exit
+end
+
+options = ARGV.getopts(nil, "key:", "csrout:", "keyout:")
+keypair_file = options["key"]
+csrout = options["csrout"] || "csr.pem"
+keyout = options["keyout"] || "keypair.pem"
+
+$stdout.sync = true
+name_str = ARGV.shift or usage()
+name = OpenSSL::X509::Name.parse(name_str)
+
+keypair = nil
+if keypair_file
+ keypair = OpenSSL::PKey.read(File.read(keypair_file))
+else
+ keypair = OpenSSL::PKey::RSA.new(2048) { putc "." }
+ puts
+ puts "Writing #{keyout}..."
+ File.open(keyout, "w", 0400) do |f|
+ f << keypair.to_pem
+ end
+end
+
+puts "Generating CSR for #{name_str}"
+
+req = OpenSSL::X509::Request.new
+req.version = 0
+req.subject = name
+req.public_key = keypair
+req.sign(keypair, "MD5")
+
+puts "Writing #{csrout}..."
+File.open(csrout, "w") do |f|
+ f << req.to_pem
+end
+puts req.to_text
+puts req.to_pem
diff --git a/sample/openssl/smime_read.rb b/sample/openssl/smime_read.rb
new file mode 100644
index 0000000000..b617c6e3a5
--- /dev/null
+++ b/sample/openssl/smime_read.rb
@@ -0,0 +1,22 @@
+require 'optparse'
+require 'openssl'
+
+options = ARGV.getopts("c:k:C:")
+
+cert_file = options["c"]
+key_file = options["k"]
+ca_path = options["C"]
+
+data = $stdin.read
+
+cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+key = OpenSSL::PKey::read(File::read(key_file))
+p7enc = OpenSSL::PKCS7::read_smime(data)
+data = p7enc.decrypt(key, cert)
+
+store = OpenSSL::X509::Store.new
+store.add_path(ca_path)
+p7sig = OpenSSL::PKCS7::read_smime(data)
+if p7sig.verify([], store)
+ puts p7sig.data
+end
diff --git a/sample/openssl/smime_write.rb b/sample/openssl/smime_write.rb
new file mode 100644
index 0000000000..e1254d8748
--- /dev/null
+++ b/sample/openssl/smime_write.rb
@@ -0,0 +1,22 @@
+require 'openssl'
+require 'optparse'
+
+options = ARGV.getopts("c:k:r:")
+
+cert_file = options["c"]
+key_file = options["k"]
+rcpt_file = options["r"]
+
+cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+key = OpenSSL::PKey::read(File::read(key_file))
+
+data = "Content-Type: text/plain\r\n"
+data << "\r\n"
+data << "This is a clear-signed message.\r\n"
+
+p7sig = OpenSSL::PKCS7::sign(cert, key, data, [], OpenSSL::PKCS7::DETACHED)
+smime0 = OpenSSL::PKCS7::write_smime(p7sig)
+
+rcpt = OpenSSL::X509::Certificate.new(File::read(rcpt_file))
+p7enc = OpenSSL::PKCS7::encrypt([rcpt], smime0)
+print OpenSSL::PKCS7::write_smime(p7enc)
diff --git a/sample/openssl/wget.rb b/sample/openssl/wget.rb
new file mode 100644
index 0000000000..ee637204db
--- /dev/null
+++ b/sample/openssl/wget.rb
@@ -0,0 +1,34 @@
+#!/usr/bin/env ruby
+
+require 'net/https'
+require 'optparse'
+
+options = ARGV.getopts('C:')
+
+cert_store = options["C"]
+
+uri = URI.parse(ARGV[0])
+if proxy = ENV['HTTP_PROXY']
+ prx_uri = URI.parse(proxy)
+ prx_host = prx_uri.host
+ prx_port = prx_uri.port
+end
+
+h = Net::HTTP.new(uri.host, uri.port, prx_host, prx_port)
+h.set_debug_output($stderr) if $DEBUG
+if uri.scheme == "https"
+ h.use_ssl = true
+ if cert_store
+ if File.directory?(cert_store)
+ h.ca_path = cert_store
+ else
+ h.ca_file = cert_store
+ end
+ end
+end
+
+path = uri.path.empty? ? "/" : uri.path
+h.get2(path){|resp|
+ STDERR.puts h.peer_cert.inspect if h.peer_cert
+ print resp.body
+}
diff --git a/sample/optparse/opttest.rb b/sample/optparse/opttest.rb
new file mode 100755
index 0000000000..b2013c5253
--- /dev/null
+++ b/sample/optparse/opttest.rb
@@ -0,0 +1,125 @@
+#!/usr/bin/ruby -I.
+
+require 'optparse'
+require 'optparse/time'
+require 'pp'
+
+# keywords
+CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
+CODE_ALIASES = {"jis" => "iso-2022-jp", "sjis" => "shift_jis"}
+POSSIBLE_CODES = "(#{(CODES+CODE_ALIASES.keys).join(',')})"
+
+ARGV.options do
+ |opts|
+ opts.banner << " argv..."
+
+ # separator
+ opts.on_tail
+ opts.on_tail("common options:")
+
+ # no argument, shows at tail
+ opts.on_tail("--help", "show this message") do
+ puts opts
+ exit
+ end
+
+ # mandatory argument
+ opts.on("-r", "--require=LIBRARY", String,
+ "require the LIBRARY, before",
+ "executing your script") do
+ |lib|
+ @library = lib
+ end
+
+ # optional argument
+ opts.on("-i", "--inplace=[EXTENSION]",
+ "edit ARGV files in place", # multiline description
+ "(make backup if EXTENSION supplied)") do
+ |inplace|
+ @inplace = inplace || ''
+ end
+
+ opts.on("-N=[NUM]", Integer) do
+ |num|
+ @number = num
+ end
+
+ # additional class
+ opts.on("-t", "--[no-]time[=TIME]", Time, "it's the time") do
+ |time|
+ @time = time
+ end
+
+ # limit argument syntax
+ opts.on("-[0-7]", "-F", "--irs=[OCTAL]", OptionParser::OctalInteger,
+ "specify record separator", "(\\0, if no argument)") do
+ |irs|
+ @irs = irs
+ end
+
+ # boolean switch(default true)
+ @exec = true
+ opts.on("-n", "--no-exec[=FLAG]", TrueClass, "not really execute") do
+ |exec|
+ @exec = exec
+ end
+
+ # array
+ opts.on("-a", "--list[=LIST,LIST]", Array, "list") do
+ |list|
+ @list = list
+ end
+
+ # fixed size array
+ opts.on("--pair[=car,cdr]", Array, "pair") do
+ |x, y|
+ @x = x
+ @y = y
+ end
+
+ # keyword completion
+ opts.on("--code=CODE", CODES, CODE_ALIASES, "select coding system",
+ "("+CODES.join(",")+",", " "+CODE_ALIASES.keys.join(",")+")") do
+ |c|
+ @code = c
+ end
+
+ # optional argument with keyword completion
+ opts.on("--type[=TYPE]", [:text, :binary], "select type(text, binary)") do
+ |t|
+ @type = t
+ end
+
+ # boolean switch with optional argument(default false)
+ opts.on("-v", "--[no-]verbose=[FLAG]", "run verbosely") do
+ |v|
+ @verbose = v
+ end
+
+ # easy way, set local variable
+ opts.on("-q", "--quit", "quit when ARGV is empty") do
+ |q|
+ @quit = q
+ end
+
+ # adding on the fly
+ opts.on("--add=SWITCH=[ARG]", "add option on the fly", /\A(\w+)(?:=.+)?\Z/) do
+ |opt, var|
+ opts.on("--#{opt}", "added in runtime", &eval("proc {|x|@#{var}=x}"))
+ end
+
+ opts.on_head("specific options:")
+
+ # no argument
+ opts.on_tail("--version", "show version") do
+ puts OptionParser::Version.join('.')
+ exit
+ end
+ opts.parse!
+end
+
+pp self
+begin print ARGV.options; exit end if @quit
+ARGV.options = nil # no more parse
+puts "ARGV = #{ARGV.join(' ')}" if !ARGV.empty?
+#opts.variable.each {|sym| puts "#{sym} = #{opts.send(sym).inspect}"}
diff --git a/sample/optparse/subcommand.rb b/sample/optparse/subcommand.rb
new file mode 100755
index 0000000000..21c42dd60a
--- /dev/null
+++ b/sample/optparse/subcommand.rb
@@ -0,0 +1,19 @@
+#! /usr/bin/ruby
+# contributed by Minero Aoki.
+
+require 'optparse'
+
+parser = OptionParser.new
+parser.on('-i') { puts "-i" }
+parser.on('-o') { puts '-o' }
+
+subparsers = Hash.new {|h,k|
+ $stderr.puts "no such subcommand: #{k}"
+ exit 1
+}
+subparsers['add'] = OptionParser.new.on('-i') { puts "add -i" }
+subparsers['del'] = OptionParser.new.on('-i') { puts "del -i" }
+subparsers['list'] = OptionParser.new.on('-i') { puts "list -i" }
+
+parser.order!(ARGV)
+subparsers[ARGV.shift].parse!(ARGV) unless ARGV.empty?
diff --git a/sample/philos.rb b/sample/philos.rb
index 3ccb052532..c38aa4a1cc 100644
--- a/sample/philos.rb
+++ b/sample/philos.rb
@@ -1,14 +1,13 @@
#
# The Dining Philosophers - thread example
#
-require "thread"
srand
#srand
N=9 # number of philosophers
$forks = []
for i in 0..N-1
- $forks[i] = Mutex.new
+ $forks[i] = Thread::Mutex.new
end
$state = "-o"*N
@@ -25,7 +24,7 @@ def eat(n)
end
def philosopher(n)
- while TRUE
+ while true
think n
$forks[n].lock
if not $forks[(n+1)%N].try_lock
@@ -46,8 +45,8 @@ def philosopher(n)
end
end
-for i in 0..N-1
- Thread.start{philosopher(i)}
+for n in 0..N-1
+ Thread.start(n){|i| philosopher(i)}
sleep 0.1
end
diff --git a/sample/pi.rb b/sample/pi.rb
index 49067cc347..63be974285 100644
--- a/sample/pi.rb
+++ b/sample/pi.rb
@@ -2,7 +2,7 @@
k, a, b, a1, b1 = 2, 4, 1, 12, 4
-while TRUE
+loop do
# Next approximation
p, q, k = k*k, 2*k+1, k+1
a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
diff --git a/sample/prism/find_calls.rb b/sample/prism/find_calls.rb
new file mode 100644
index 0000000000..30af56c719
--- /dev/null
+++ b/sample/prism/find_calls.rb
@@ -0,0 +1,105 @@
+# This script finds calls to a specific method with a certain keyword parameter
+# within a given source file.
+
+require "prism"
+require "pp"
+
+# For deprecation or refactoring purposes, it's often useful to find all of the
+# places that call a specific method with a specific k eyword parameter. This is
+# easily accomplished with a visitor such as this one.
+class QuxParameterVisitor < Prism::Visitor
+ def initialize(calls)
+ @calls = calls
+ end
+
+ def visit_call_node(node)
+ @calls << node if qux?(node)
+ super
+ end
+
+ private
+
+ def qux?(node)
+ # All nodes implement pattern matching, so you can use the `in` operator to
+ # pull out all of their individual fields. As you can see by this extensive
+ # pattern match, this is quite a powerful feature.
+ node in {
+ # This checks that the receiver is the constant Qux or the constant path
+ # ::Qux. We are assuming relative constants are fine in this case.
+ receiver: (
+ Prism::ConstantReadNode[name: :Qux] |
+ Prism::ConstantPathNode[parent: nil, name: :Qux]
+ ),
+ # This checks that the name of the method is qux. We purposefully are not
+ # checking the call operator (., ::, or &.) because we want all of them.
+ # In other ASTs, this would be multiple node types, but prism combines
+ # them all into one for convenience.
+ name: :qux,
+ arguments: Prism::ArgumentsNode[
+ # Here we're going to use the "find" pattern to find the keyword hash
+ # node that has the correct key.
+ arguments: [
+ *,
+ Prism::KeywordHashNode[
+ # Here we'll use another "find" pattern to find the key that we are
+ # specifically looking for.
+ elements: [
+ *,
+ # Finally, we can assert against the key itself. Note that we are
+ # not looking at the value of hash pair, because we are only
+ # specifically looking for a key.
+ Prism::AssocNode[key: Prism::SymbolNode[unescaped: "qux"]],
+ *
+ ]
+ ],
+ *
+ ]
+ ]
+ }
+ end
+end
+
+calls = []
+Prism.parse_stream(DATA).value.accept(QuxParameterVisitor.new(calls))
+
+calls.each do |call|
+ print "CallNode "
+ puts PP.pp(call.location, +"")
+ print " "
+ puts call.slice
+end
+
+# =>
+# CallNode (5,6)-(5,29)
+# Qux.qux(222, qux: true)
+# CallNode (9,6)-(9,30)
+# Qux&.qux(333, qux: true)
+# CallNode (20,6)-(20,51)
+# Qux::qux(888, qux: ::Qux.qux(999, qux: true))
+# CallNode (20,25)-(20,50)
+# ::Qux.qux(999, qux: true)
+
+__END__
+module Foo
+ class Bar
+ def baz1
+ Qux.qux(111)
+ Qux.qux(222, qux: true)
+ end
+
+ def baz2
+ Qux&.qux(333, qux: true)
+ Qux&.qux(444)
+ end
+
+ def baz3
+ qux(555, qux: false)
+ 666.qux(666)
+ end
+
+ def baz4
+ Qux::qux(777)
+ Qux::qux(888, qux: ::Qux.qux(999, qux: true))
+ end
+ end
+end
diff --git a/sample/prism/find_comments.rb b/sample/prism/find_comments.rb
new file mode 100644
index 0000000000..6a26cd32b7
--- /dev/null
+++ b/sample/prism/find_comments.rb
@@ -0,0 +1,100 @@
+# This script finds all of the comments within a given source file for a method.
+
+require "prism"
+
+class FindMethodComments < Prism::Visitor
+ def initialize(target, comments, nesting = [])
+ @target = target
+ @comments = comments
+ @nesting = nesting
+ end
+
+ # These visit methods are specific to each class. Defining a visitor allows
+ # you to group functionality that applies to all node types into a single
+ # class. You can find which method corresponds to which node type by looking
+ # at the class name, calling #type on the node, or by looking at the #accept
+ # method definition on the node.
+ def visit_module_node(node)
+ visitor = FindMethodComments.new(@target, @comments, [*@nesting, node.name])
+ node.compact_child_nodes.each { |child| child.accept(visitor) }
+ end
+
+ def visit_class_node(node)
+ # We could keep track of an internal state where we push the class name here
+ # and then pop it after the visit is complete. However, it is often simpler
+ # and cleaner to generate a new visitor instance when the state changes,
+ # because then the state is immutable and it's easier to reason about. This
+ # also provides for more debugging opportunity in the initializer.
+ visitor = FindMethodComments.new(@target, @comments, [*@nesting, node.name])
+ node.compact_child_nodes.each { |child| child.accept(visitor) }
+ end
+
+ def visit_def_node(node)
+ if [*@nesting, node.name] == @target
+ # Comments are always attached to locations (either inner locations on a
+ # node like the location of a keyword or the location on the node itself).
+ # Nodes are considered either "leading" or "trailing", which means that
+ # they occur before or after the location, respectively. In this case of
+ # documentation, we only want to consider leading comments. You can also
+ # fetch all of the comments on a location with #comments.
+ @comments.concat(node.location.leading_comments)
+ else
+ super
+ end
+ end
+end
+
+# Most of the time, the concept of "finding" something in the AST can be
+# accomplished either with a queue or with a visitor. In this case we will use a
+# visitor, but a queue would work just as well.
+def find_comments(result, path)
+ target = path.split(/::|#/).map(&:to_sym)
+ comments = []
+
+ result.value.accept(FindMethodComments.new(target, comments))
+ comments
+end
+
+result = Prism.parse_stream(DATA)
+result.attach_comments!
+
+find_comments(result, "Foo#foo").each do |comment|
+ puts comment.inspect
+ puts comment.slice
+end
+
+# =>
+# #<Prism::InlineComment @location=#<Prism::Location @start_offset=205 @length=27 start_line=13>>
+# # This is the documentation
+# #<Prism::InlineComment @location=#<Prism::Location @start_offset=235 @length=21 start_line=14>>
+# # for the foo method.
+
+find_comments(result, "Foo::Bar#bar").each do |comment|
+ puts comment.inspect
+ puts comment.slice
+end
+
+# =>
+# #<Prism::InlineComment @location=#<Prism::Location @start_offset=126 @length=23 start_line=7>>
+# # This is documentation
+# #<Prism::InlineComment @location=#<Prism::Location @start_offset=154 @length=21 start_line=8>>
+# # for the bar method.
+
+__END__
+# This is the documentation
+# for the Foo module.
+module Foo
+ # This is documentation
+ # for the Bar class.
+ class Bar
+ # This is documentation
+ # for the bar method.
+ def bar
+ end
+ end
+
+ # This is the documentation
+ # for the foo method.
+ def foo
+ end
+end
diff --git a/sample/prism/locate_nodes.rb b/sample/prism/locate_nodes.rb
new file mode 100644
index 0000000000..7a51db4367
--- /dev/null
+++ b/sample/prism/locate_nodes.rb
@@ -0,0 +1,84 @@
+# This script locates a set of nodes determined by a line and column (in bytes).
+
+require "prism"
+require "pp"
+
+# This method determines if the given location covers the given line and column.
+# It's important to note that columns (and offsets) in prism are always in
+# bytes. This is because prism supports all 90 source encodings that Ruby
+# supports. You can always retrieve the column (or offset) of a location in
+# other units with other provided APIs, like #start_character_column or
+# #start_code_units_column.
+def covers?(location, line:, column:)
+ start_line = location.start_line
+ end_line = location.end_line
+
+ if start_line == end_line
+ # If the location only spans one line, then we only check if the line
+ # matches and that the column is covered by the column range.
+ line == start_line && (location.start_column...location.end_column).cover?(column)
+ else
+ # Otherwise, we check that it is on the start line and the column is greater
+ # than or equal to the start column, or that it is on the end line and the
+ # column is less than the end column, or that it is between the start and
+ # end lines.
+ (line == start_line && column >= location.start_column) ||
+ (line == end_line && column < location.end_column) ||
+ (line > start_line && line < end_line)
+ end
+end
+
+# This method descends down into the AST whose root is `node` and returns the
+# array of all of the nodes that cover the given line and column.
+def locate(node, line:, column:)
+ queue = [node]
+ result = []
+
+ # We could use a recursive method here instead if we wanted, but it's
+ # important to note that that will not work for ASTs that are nested deeply
+ # enough to cause a stack overflow.
+ while (node = queue.shift)
+ result << node
+
+ # Nodes have `child_nodes` and `compact_child_nodes`. `child_nodes` have
+ # consistent indices but include `nil` for optional fields that are not
+ # present, whereas `compact_child_nodes` has inconsistent indices but does
+ # not include `nil` for optional fields that are not present.
+ node.compact_child_nodes.find do |child|
+ queue << child if covers?(child.location, line: line, column: column)
+ end
+ end
+
+ result
+end
+
+result = Prism.parse_stream(DATA)
+locate(result.value, line: 4, column: 14).each_with_index do |node, index|
+ print " " * index
+ print node.class.name.split("::", 2).last
+ print " "
+ puts PP.pp(node.location, +"")
+end
+
+# =>
+# ProgramNode (1,0)-(7,3)
+# StatementsNode (1,0)-(7,3)
+# ModuleNode (1,0)-(7,3)
+# StatementsNode (2,2)-(6,5)
+# ClassNode (2,2)-(6,5)
+# StatementsNode (3,4)-(5,7)
+# DefNode (3,4)-(5,7)
+# StatementsNode (4,6)-(4,21)
+# CallNode (4,6)-(4,21)
+# CallNode (4,6)-(4,15)
+# ArgumentsNode (4,12)-(4,15)
+# IntegerNode (4,12)-(4,15)
+
+__END__
+module Foo
+ class Bar
+ def baz
+ 111 + 222 + 333
+ end
+ end
+end
diff --git a/sample/prism/make_tags.rb b/sample/prism/make_tags.rb
new file mode 100644
index 0000000000..dc770ab1b0
--- /dev/null
+++ b/sample/prism/make_tags.rb
@@ -0,0 +1,302 @@
+# This script generates a tags file using Prism to parse the Ruby files.
+
+require "prism"
+
+# This visitor is responsible for visiting the nodes in the AST and generating
+# the appropriate tags. The tags are stored in the entries array as strings.
+class TagsVisitor < Prism::Visitor
+ # This represents an entry in the tags file, which is a tab-separated line. It
+ # houses the logic for how an entry is constructed.
+ class Entry
+ attr_reader :parts
+
+ def initialize(name, filepath, pattern, type)
+ @parts = [name, filepath, pattern, type]
+ end
+
+ def attribute(key, value)
+ parts << "#{key}:#{value}"
+ end
+
+ def attribute_class(nesting, names)
+ return if nesting.empty? && names.length == 1
+ attribute("class", [*nesting, names].flatten.tap(&:pop).join("."))
+ end
+
+ def attribute_inherits(names)
+ attribute("inherits", names.join(".")) if names
+ end
+
+ def to_line
+ parts.join("\t")
+ end
+ end
+
+ private_constant :Entry
+
+ attr_reader :entries, :filepath, :lines, :nesting, :singleton
+
+ # Initialize the visitor with the given parameters. The first three parameters
+ # are constant throughout the visit, while the last two are controlled by the
+ # visitor as it traverses the AST. These are treated as immutable by virtue of
+ # the visit methods constructing new visitors when they need to change.
+ def initialize(entries, filepath, lines, nesting = [], singleton = false)
+ @entries = entries
+ @filepath = filepath
+ @lines = lines
+ @nesting = nesting
+ @singleton = singleton
+ end
+
+ # Visit a method alias node and generate the appropriate tags.
+ #
+ # alias m2 m1
+ #
+ def visit_alias_method_node(node)
+ enter(node.new_name.unescaped.to_sym, node, "a") do |entry|
+ entry.attribute_class(nesting, [nil])
+ end
+
+ super
+ end
+
+ # Visit a method call to attr_reader, attr_writer, or attr_accessor without a
+ # receiver and generate the appropriate tags. Note that this ignores the fact
+ # that these methods could be overridden, which is a limitation of this
+ # script.
+ #
+ # attr_accessor :m1
+ #
+ def visit_call_node(node)
+ if !node.receiver && %i[attr_reader attr_writer attr_accessor].include?(name = node.name)
+ (node.arguments&.arguments || []).grep(Prism::SymbolNode).each do |argument|
+ if name != :attr_writer
+ enter(:"#{argument.unescaped}", argument, singleton ? "F" : "f") do |entry|
+ entry.attribute_class(nesting, [nil])
+ end
+ end
+
+ if name != :attr_reader
+ enter(:"#{argument.unescaped}=", argument, singleton ? "F" : "f") do |entry|
+ entry.attribute_class(nesting, [nil])
+ end
+ end
+ end
+ end
+
+ super
+ end
+
+ # Visit a class node and generate the appropriate tags.
+ #
+ # class C1
+ # end
+ #
+ def visit_class_node(node)
+ if (names = names_for(node.constant_path))
+ enter(names.last, node, "c") do |entry|
+ entry.attribute_class(nesting, names)
+ entry.attribute_inherits(names_for(node.superclass))
+ end
+
+ node.body&.accept(copy_visitor([*nesting, names], singleton))
+ end
+ end
+
+ # Visit a constant path write node and generate the appropriate tags.
+ #
+ # C1::C2 = 1
+ #
+ def visit_constant_path_write_node(node)
+ if (names = names_for(node.target))
+ enter(names.last, node, "C") do |entry|
+ entry.attribute_class(nesting, names)
+ end
+ end
+
+ super
+ end
+
+ # Visit a constant write node and generate the appropriate tags.
+ #
+ # C1 = 1
+ #
+ def visit_constant_write_node(node)
+ enter(node.name, node, "C") do |entry|
+ entry.attribute_class(nesting, [nil])
+ end
+
+ super
+ end
+
+ # Visit a method definition node and generate the appropriate tags.
+ #
+ # def m1; end
+ #
+ def visit_def_node(node)
+ enter(node.name, node, (node.receiver || singleton) ? "F" : "f") do |entry|
+ entry.attribute_class(nesting, [nil])
+ end
+
+ super
+ end
+
+ # Visit a module node and generate the appropriate tags.
+ #
+ # module M1
+ # end
+ #
+ def visit_module_node(node)
+ if (names = names_for(node.constant_path))
+ enter(names.last, node, "m") do |entry|
+ entry.attribute_class(nesting, names)
+ end
+
+ node.body&.accept(copy_visitor([*nesting, names], singleton))
+ end
+ end
+
+ # Visit a singleton class node and generate the appropriate tags.
+ #
+ # class << self
+ # end
+ #
+ def visit_singleton_class_node(node)
+ case node.expression
+ when Prism::SelfNode
+ node.body&.accept(copy_visitor(nesting, true))
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
+ if (names = names_for(node.expression))
+ node.body&.accept(copy_visitor([*nesting, names], true))
+ end
+ else
+ node.body&.accept(copy_visitor([*nesting, nil], true))
+ end
+ end
+
+ private
+
+ # Generate a new visitor with the given dynamic options. The static options
+ # are copied over automatically.
+ def copy_visitor(nesting, singleton)
+ TagsVisitor.new(entries, filepath, lines, nesting, singleton)
+ end
+
+ # Generate a new entry for the given name, node, and type and add it into the
+ # list of entries. The block is used to add additional attributes to the
+ # entry.
+ def enter(name, node, type)
+ line = lines[node.location.start_line - 1].chomp
+ pattern = "/^#{line.gsub("\\", "\\\\\\\\").gsub("/", "\\/")}$/;\""
+
+ entry = Entry.new(name, filepath, pattern, type)
+ yield entry
+
+ entries << entry.to_line
+ end
+
+ # Retrieve the names for the given node. This is used to construct the class
+ # attribute for the tags.
+ def names_for(node)
+ case node
+ when Prism::ConstantPathNode
+ names = names_for(node.parent)
+ return unless names
+
+ names << node.name
+ when Prism::ConstantReadNode
+ [node.name]
+ when Prism::SelfNode
+ [:self]
+ else
+ # dynamic
+ end
+ end
+end
+
+# Parse the Ruby file and visit all of the nodes in the resulting AST. Once all
+# of the nodes have been visited, the entries array should be populated with the
+# tags.
+result = Prism.parse_stream(DATA)
+result.value.accept(TagsVisitor.new(entries = [], __FILE__, result.source.lines))
+
+# Print the tags to STDOUT.
+puts "!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;\" to lines/"
+puts "!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/"
+puts entries.sort
+
+# =>
+# !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
+# !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
+# C1 sample/prism/make_tags.rb /^ class C1$/;" c class:M1.M2
+# C2 sample/prism/make_tags.rb /^ class C2 < Object$/;" c class:M1.M2.C1 inherits:Object
+# C6 sample/prism/make_tags.rb /^ C6 = 1$/;" C class:M1
+# C7 sample/prism/make_tags.rb /^ C7 = 2$/;" C class:M1
+# C9 sample/prism/make_tags.rb /^ C8::C9 = 3$/;" C class:M1.C8
+# M1 sample/prism/make_tags.rb /^module M1$/;" m
+# M2 sample/prism/make_tags.rb /^ module M2$/;" m class:M1
+# M4 sample/prism/make_tags.rb /^ module M3::M4$/;" m class:M1.M3
+# M5 sample/prism/make_tags.rb /^ module self::M5$/;" m class:M1.self
+# m1 sample/prism/make_tags.rb /^ def m1; end$/;" f class:M1.M2.C1.C2
+# m10 sample/prism/make_tags.rb /^ attr_accessor :m10, :m11$/;" f class:M1.M3.M4
+# m10= sample/prism/make_tags.rb /^ attr_accessor :m10, :m11$/;" f class:M1.M3.M4
+# m11 sample/prism/make_tags.rb /^ attr_accessor :m10, :m11$/;" f class:M1.M3.M4
+# m11= sample/prism/make_tags.rb /^ attr_accessor :m10, :m11$/;" f class:M1.M3.M4
+# m12 sample/prism/make_tags.rb /^ attr_reader :m12, :m13, :m14$/;" f class:M1.M3.M4
+# m13 sample/prism/make_tags.rb /^ attr_reader :m12, :m13, :m14$/;" f class:M1.M3.M4
+# m14 sample/prism/make_tags.rb /^ attr_reader :m12, :m13, :m14$/;" f class:M1.M3.M4
+# m15= sample/prism/make_tags.rb /^ attr_writer :m15$/;" f class:M1.M3.M4
+# m2 sample/prism/make_tags.rb /^ def m2; end$/;" f class:M1.M2.C1.C2
+# m3 sample/prism/make_tags.rb /^ alias m3 m1$/;" a class:M1.M2.C1.C2
+# m4 sample/prism/make_tags.rb /^ alias :m4 :m2$/;" a class:M1.M2.C1.C2
+# m5 sample/prism/make_tags.rb /^ def self.m5; end$/;" F class:M1.M2.C1.C2
+# m6 sample/prism/make_tags.rb /^ def m6; end$/;" F class:M1.M2.C1.C2
+# m7 sample/prism/make_tags.rb /^ def m7; end$/;" F class:M1.M2.C1.C2.C3
+# m8 sample/prism/make_tags.rb /^ def m8; end$/;" F class:M1.M2.C1.C2.C4.C5
+# m9 sample/prism/make_tags.rb /^ def m9; end$/;" F class:M1.M2.C1.C2.
+
+__END__
+module M1
+ module M2
+ class C1
+ class C2 < Object
+ def m1; end
+ def m2; end
+
+ alias m3 m1
+ alias :m4 :m2
+
+ def self.m5; end
+
+ class << self
+ def m6; end
+ end
+
+ class << C3
+ def m7; end
+ end
+
+ class << C4::C5
+ def m8; end
+ end
+
+ class << c
+ def m9; end
+ end
+ end
+ end
+ end
+
+ module M3::M4
+ attr_accessor :m10, :m11
+ attr_reader :m12, :m13, :m14
+ attr_writer :m15
+ end
+
+ module self::M5
+ end
+
+ C6 = 1
+ C7 = 2
+ C8::C9 = 3
+end
diff --git a/sample/prism/multiplex_constants.rb b/sample/prism/multiplex_constants.rb
new file mode 100644
index 0000000000..e39f2c36f6
--- /dev/null
+++ b/sample/prism/multiplex_constants.rb
@@ -0,0 +1,138 @@
+# This script indexes the classes and modules within a set of files using the
+# saved source functionality.
+
+require "prism"
+require "etc"
+require "tempfile"
+
+module Indexer
+ # A class that implements the #enter functionality so that it can be passed to
+ # the various save* APIs. This effectively bundles up all of the node_id and
+ # field_name pairs so that they can be written back to the parent process.
+ class Repository
+ attr_reader :scope, :entries
+
+ def initialize
+ @scope = []
+ @entries = []
+ end
+
+ def with(next_scope)
+ previous_scope = scope
+ @scope = scope + next_scope
+ yield
+ @scope = previous_scope
+ end
+
+ def empty?
+ entries.empty?
+ end
+
+ def enter(node_id, field_name)
+ entries << [scope.join("::"), node_id, field_name]
+ end
+ end
+
+ # Visit the classes and modules in the AST and save their locations into the
+ # repository.
+ class Visitor < Prism::Visitor
+ attr_reader :repository
+
+ def initialize(repository)
+ @repository = repository
+ end
+
+ def visit_class_node(node)
+ repository.with(node.constant_path.full_name_parts) do
+ node.constant_path.save_location(repository)
+ visit(node.body)
+ end
+ end
+
+ def visit_module_node(node)
+ repository.with(node.constant_path.full_name_parts) do
+ node.constant_path.save_location(repository)
+ visit(node.body)
+ end
+ end
+ end
+
+ # Index the classes and modules within a file. If there are any entries,
+ # return them as a serialized string to the parent process.
+ def self.index(filepath)
+ repository = Repository.new
+ Prism.parse_file(filepath).value.accept(Visitor.new(repository))
+ "#{filepath}|#{repository.entries.join("|")}" unless repository.empty?
+ end
+end
+
+def index_glob(glob, count = Etc.nprocessors - 1)
+ process_ids = []
+ filepath_writers = []
+ index_reader, index_writer = IO.pipe
+
+ # For each number in count, fork off a worker that has access to two pipes.
+ # The first pipe is the index_writer, to which it writes all of the results of
+ # indexing the various files. The second pipe is the filepath_reader, from
+ # which it reads the filepaths that it needs to index.
+ count.times do
+ filepath_reader, filepath_writer = IO.pipe
+
+ process_ids << fork do
+ filepath_writer.close
+ index_reader.close
+
+ while (filepath = filepath_reader.gets(chomp: true))
+ results = Indexer.index(filepath)
+ index_writer.puts(results) if results
+ end
+ end
+
+ filepath_reader.close
+ filepath_writers << filepath_writer
+ end
+
+ index_writer.close
+
+ # In a separate thread, write all of the filepaths to the various worker
+ # processes. This is done in a separate threads since puts will eventually
+ # block when each of the pipe buffers fills up. We write in a round-robin
+ # fashion to the various workers. This could be improved using a work-stealing
+ # algorithm, but is fine if you don't end up having a ton of variety in the
+ # size of your files.
+ writer_thread =
+ Thread.new do
+ Dir[glob].each_with_index do |filepath, index|
+ filepath_writers[index % count].puts(filepath)
+ end
+ end
+
+ index = Hash.new { |hash, key| hash[key] = [] }
+
+ # In a separate thread, read all of the results from the various worker
+ # processes and store them in the index. This is done in a separate thread so
+ # that reads and writes can be interleaved. This is important so that the
+ # index pipe doesn't fill up and block the writer.
+ reader_thread =
+ Thread.new do
+ while (line = index_reader.gets(chomp: true))
+ filepath, *entries = line.split("|")
+ repository = Prism::Relocation.filepath(filepath).filepath.lines.code_unit_columns(Encoding::UTF_16LE).leading_comments
+
+ entries.each_slice(3) do |(name, node_id, field_name)|
+ index[name] << repository.enter(Integer(node_id), field_name.to_sym)
+ end
+ end
+ end
+
+ writer_thread.join
+ filepath_writers.each(&:close)
+
+ reader_thread.join
+ index_reader.close
+
+ process_ids.each { |process_id| Process.wait(process_id) }
+ index
+end
+
+index_glob(File.expand_path("../../lib/**/*.rb", __dir__))
diff --git a/sample/prism/relocate_constants.rb b/sample/prism/relocate_constants.rb
new file mode 100644
index 0000000000..faa48f6388
--- /dev/null
+++ b/sample/prism/relocate_constants.rb
@@ -0,0 +1,43 @@
+# This script finds the declaration of all classes and modules and stores them
+# in a hash for an in-memory database of constants.
+
+require "prism"
+
+class RelocationVisitor < Prism::Visitor
+ attr_reader :index, :repository, :scope
+
+ def initialize(index, repository, scope = [])
+ @index = index
+ @repository = repository
+ @scope = scope
+ end
+
+ def visit_class_node(node)
+ next_scope = scope + node.constant_path.full_name_parts
+ index[next_scope.join("::")] << node.constant_path.save(repository)
+ node.body&.accept(RelocationVisitor.new(index, repository, next_scope))
+ end
+
+ def visit_module_node(node)
+ next_scope = scope + node.constant_path.full_name_parts
+ index[next_scope.join("::")] << node.constant_path.save(repository)
+ node.body&.accept(RelocationVisitor.new(index, repository, next_scope))
+ end
+end
+
+# Create an index that will store a mapping between the names of constants to a
+# list of the locations where they are declared or re-opened.
+index = Hash.new { |hash, key| hash[key] = [] }
+
+# Loop through every file in the lib directory of this repository and parse them
+# with Prism. Then visit them using the RelocateVisitor to store their
+# repository entries in the index.
+Dir[File.expand_path("../../lib/**/*.rb", __dir__)].each do |filepath|
+ repository = Prism::Relocation.filepath(filepath).filepath.lines.code_unit_columns(Encoding::UTF_16LE)
+ Prism.parse_file(filepath).value.accept(RelocationVisitor.new(index, repository))
+end
+
+puts index["Prism::ParametersNode"].map { |entry| "#{entry.filepath}:#{entry.start_line}:#{entry.start_code_units_column}" }
+# =>
+# prism/lib/prism/node.rb:13889:8
+# prism/lib/prism/node_ext.rb:267:8
diff --git a/sample/prism/visit_nodes.rb b/sample/prism/visit_nodes.rb
new file mode 100644
index 0000000000..5ba703b0a3
--- /dev/null
+++ b/sample/prism/visit_nodes.rb
@@ -0,0 +1,63 @@
+# This script visits all of the nodes of a specific type within a given source
+# file. It uses the visitor class to traverse the AST.
+
+require "prism"
+require "pp"
+
+class CaseInsensitiveRegularExpressionVisitor < Prism::Visitor
+ def initialize(regexps)
+ @regexps = regexps
+ end
+
+ # As the visitor is walking the tree, this method will only be called when it
+ # encounters a regular expression node. We can then call any regular
+ # expression -specific APIs. In this case, we are only interested in the
+ # regular expressions that are case-insensitive, which we can retrieve with
+ # the #ignore_case? method.
+ def visit_regular_expression_node(node)
+ @regexps << node if node.ignore_case?
+ super
+ end
+
+ def visit_interpolated_regular_expression_node(node)
+ @regexps << node if node.ignore_case?
+
+ # The default behavior of the visitor is to continue visiting the children
+ # of the node. Because Ruby is so dynamic, it's actually possible for
+ # another regular expression to be interpolated in statements contained
+ # within the #{} contained in this interpolated regular expression node. By
+ # calling `super`, we ensure the visitor will continue. Failing to call
+ # `super` will cause the visitor to stop the traversal of the tree, which
+ # can also be useful in some cases.
+ super
+ end
+end
+
+result = Prism.parse_stream(DATA)
+regexps = []
+
+result.value.accept(CaseInsensitiveRegularExpressionVisitor.new(regexps))
+regexps.each do |node|
+ print node.class.name.split("::", 2).last
+ print " "
+ puts PP.pp(node.location, +"")
+
+ if node.is_a?(Prism::RegularExpressionNode)
+ print " "
+ p node.unescaped
+ end
+end
+
+# =>
+# InterpolatedRegularExpressionNode (3,9)-(3,47)
+# RegularExpressionNode (3,16)-(3,22)
+# "bar"
+# RegularExpressionNode (4,9)-(4,15)
+# "bar"
+
+__END__
+class Foo
+ REG1 = /foo/
+ REG2 = /foo #{/bar/i =~ "" ? "bar" : "baz"}/i
+ REG3 = /bar/i
+end
diff --git a/sample/pstore.rb b/sample/pstore.rb
new file mode 100644
index 0000000000..38c2305516
--- /dev/null
+++ b/sample/pstore.rb
@@ -0,0 +1,19 @@
+require 'pstore'
+
+db = PStore.new("/tmp/foo")
+db.transaction do
+ p db.roots
+ ary = db["root"] = [1,2,3,4]
+ ary[1] = [1,1.5]
+end
+
+1000.times do
+ db.transaction do
+ db["root"][0] += 1
+ p db["root"][0]
+ end
+end
+
+db.transaction(true) do
+ p db["root"]
+end
diff --git a/sample/pty/expect_sample.rb b/sample/pty/expect_sample.rb
new file mode 100644
index 0000000000..199d98b79c
--- /dev/null
+++ b/sample/pty/expect_sample.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+#
+# sample program of expect.rb
+#
+# by A. Ito
+#
+# This program reports the latest version of Ruby interpreter
+# by connecting to ftp server at ruby-lang.org.
+#
+require 'pty'
+require 'expect'
+
+fnames = []
+PTY.spawn("ftp ftp.ruby-lang.org") do |r_f,w_f,pid|
+ w_f.sync = true
+
+ $expect_verbose = false
+
+ if !ENV['USER'].nil?
+ username = ENV['USER']
+ elsif !ENV['LOGNAME'].nil?
+ username = ENV['LOGNAME']
+ else
+ username = 'guest'
+ end
+
+ r_f.expect(/^Name.*: /) do
+ w_f.puts("ftp")
+ end
+ r_f.expect(/word:/) do
+ w_f.puts("#{username}@")
+ end
+ r_f.expect(/> /) do
+ w_f.puts("cd pub/ruby")
+ end
+ r_f.expect("> ") do
+ w_f.print "pass\n"
+ end
+ r_f.expect("> ") do
+ w_f.print "dir\n"
+ end
+
+ r_f.expect(/[^\-]> /) do |output|
+ for x in output[0].split("\n")
+ if x =~ /(ruby.*?\.tar\.gz)/ then
+ fnames.push $1
+ end
+ end
+ end
+ begin
+ w_f.print "quit\n"
+ rescue
+ end
+end
+
+print "The latest ruby interpreter is "
+print fnames.sort.pop
+print "\n"
diff --git a/sample/pty/script.rb b/sample/pty/script.rb
new file mode 100644
index 0000000000..c6659a4807
--- /dev/null
+++ b/sample/pty/script.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+require 'pty'
+
+if ARGV.size == 0 then
+ ofile = "typescript"
+else
+ ofile = ARGV[0]
+end
+
+logfile = File.open(ofile,"a")
+
+system "stty -echo raw lnext ^_"
+
+PTY.spawn("/bin/csh") do |r_pty,w_pty,pid|
+
+ Thread.new do
+ while true
+ w_pty.print STDIN.getc.chr
+ w_pty.flush
+ end
+ end
+
+ begin
+ while true
+ c = r_pty.sysread(512)
+ break if c.nil?
+ print c
+ STDOUT.flush
+ logfile.print c
+ end
+ rescue
+ # print $@,':',$!,"\n"
+ logfile.close
+ end
+end
+
+system "stty echo -raw lnext ^v"
diff --git a/sample/pty/shl.rb b/sample/pty/shl.rb
new file mode 100644
index 0000000000..980748e8f5
--- /dev/null
+++ b/sample/pty/shl.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+#
+# old-fashioned 'shl' like program
+# by A. Ito
+#
+# commands:
+# c creates new shell
+# C-z suspends shell
+# p lists all shell
+# 0,1,... choose shell
+# q quit
+
+require 'pty'
+require 'io/console'
+
+$shells = []
+
+$r_pty = nil
+$w_pty = nil
+
+def writer
+ STDIN.raw!
+ begin
+ while true
+ c = STDIN.getc
+ if c == ?\C-z then
+ $reader.raise('Suspend')
+ return 'Suspend'
+ end
+ $w_pty.print c.chr
+ $w_pty.flush
+ end
+ rescue
+ $reader.raise('Exit')
+ return 'Exit'
+ ensure
+ STDIN.cooked!
+ end
+end
+
+$reader = Thread.new {
+ while true
+ begin
+ Thread.stop unless $r_pty
+ c = $r_pty.getc
+ if c.nil? then
+ Thread.main.raise('Exit')
+ Thread.stop
+ end
+ print c.chr
+ STDOUT.flush
+ rescue
+ Thread.stop
+ end
+ end
+}
+
+# $reader.raise(nil)
+
+
+while true
+ print ">> "
+ STDOUT.flush
+ n = nil
+ case gets
+ when /^c/i
+ $shells << PTY.spawn("/bin/csh")
+ n = -1
+ when /^p/i
+ $shells.each_with_index do |s, i|
+ if s
+ print i,"\n"
+ end
+ end
+ when /^([0-9]+)/
+ n = $1.to_i
+ if $shells[n].nil?
+ print "\##{i} doesn't exist\n"
+ n = nil
+ end
+ when /^q/i
+ exit
+ end
+ if n
+ $r_pty, $w_pty, pid = $shells[n]
+ $reader.run
+ if writer == 'Exit' then
+ Process.wait(pid)
+ $shells[n] = nil
+ $shells.pop until $shells.empty? or $shells[-1]
+ end
+ end
+end
diff --git a/sample/rbc.rb b/sample/rbc.rb
deleted file mode 100644
index 83796255cc..0000000000
--- a/sample/rbc.rb
+++ /dev/null
@@ -1,1015 +0,0 @@
-#!/usr/local/bin/ruby
-#
-# rbc.rb -
-# $Release Version: 0.8 $
-# $Revision: 1.8 $
-# $Date: 1998/03/11 05:43:00 $
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
-#
-# --
-# Usage:
-#
-# rbc.rb [options] file_name opts
-# options:
-# -d debug mode (not recommended)
-# -f does not read ~/.irbrc
-# -m bc mode (rational/matrix calc)
-# -r load-module same as `ruby -r'
-# --inspect use inspect for result output
-# (default for non-bc mode)
-# --noinspect does not use inspect for result output
-# --noreadline does not use readline library
-# (default: try to use readline)
-#
-# additional private method (as function):
-# exit, quit terminate the interpreter
-# inspect_mode(sw = nil) toggle inspect mode
-# trace_load(sw = nil) change trace mode for file loading using
-# load/require. (default: trace-mode on)
-#
-require "e2mmap.rb"
-
-$stdout.sync = TRUE
-
-module BC_APPLICATION__
- RCS_ID='-$Id: rbc.rb,v 1.8 1998/03/11 05:43:00 keiju Exp keiju $-'
-
- extend Exception2MessageMapper
- def_exception :UnrecognizedSwitch, "Unrecognized switch: %s"
-
- CONFIG = {}
- CONFIG[0] = $0
- CONFIG[:USE_READLINE] = TRUE
- CONFIG[:LOAD_MODULES] = []
- CONFIG[:INSPECT] = nil
- CONFIG[:TRACE_LOAD] = FALSE
- CONFIG[:RC] = TRUE
-
- CONFIG[:DEBUG] = FALSE
-
- while opt = ARGV.shift
- case opt
- when "-d"
- CONFIG[:DEBUG] = TRUE
- when "-m"
- CONFIG[:INSPECT] = FALSE if CONFIG[:INSPECT].nil?
- require "mathn.rb"
- include Math
- when "-r"
- opt = ARGV.shift
- CONFIG[:LOAD_MODULES].push opt if opt
- when "-f"
- opt = ARGV.shift
- CONFIG[:RC] = FALSE
- when "--inspect"
- CONFIG[:INSPECT] = TRUE
- when "--noinspect"
- CONFIG[:INSPECT] = FALSE
- when "--noreadline"
- CONFIG[:USE_READLINE] = FALSE
- when /^-/
- # print UnrecognizedSwitch.inspect, "\n"
- BC_APPLICATION__.fail UnrecognizedSwitch, opt
- else
- CONFIG[:USE_READLINE] = FALSE
- $0 = opt
- break
- end
- end
- CONFIG[:INSPECT] = TRUE if CONFIG[:INSPECT].nil?
-
- PROMPTi = "rbc%d> "
- PROMPTs = "rbc%d%s "
- PROMPTe = "rbc%d* "
-
- class BC
- def initialize
- lex_init
- end
-
- def eval_input(io, cont, bind)
- line = ''
- @io = io
- @ltype = nil
- @quoted = nil
- @indent = 0
- @lex_state = EXPR_BEG
-
- @io.prompt = format(PROMPTi, @indent)
-
- loop do
- @continue = FALSE
- l = @io.gets
-
- unless l
- break if line == ''
- else
- line = line + l
-
- lex(l) if l != "\n"
- print @quoted.inspect, "\n" if CONFIG[:DEBUG]
- if @ltype
- @io.prompt = format(PROMPTs, @indent, @ltype)
- next
- elsif @continue
- @io.prompt = format(PROMPTe, @indent)
- next
- elsif @indent > 0
- @io.prompt = format(PROMPTi, @indent)
- next
- end
- end
-
- if line != "\n"
- begin
- if CONFIG[:INSPECT]
- print((cont._=eval(line, bind)).inspect, "\n")
- else
- print((cont._=eval(line, bind)), "\n")
- end
- rescue
- # $! = 'exception raised' unless $!
- # print "ERR: ", $!, "\n"
- $! = RuntimeError.new("exception raised") unless $!
- print $!.type, ": ", $!, "\n"
- end
- end
- break if not l
- line = ''
- indent = 0
- @io.prompt = format(PROMPTi, indent)
- end
- print "\n"
- end
-
- EXPR_BEG = :EXPR_BEG
- EXPR_MID = :EXPR_MID
- EXPR_END = :EXPR_END
- EXPR_ARG = :EXPR_ARG
- EXPR_FNAME = :EXPR_FNAME
-
- CLAUSE_STATE_TRANS = {
- "alias" => EXPR_FNAME,
- "and" => EXPR_BEG,
- "begin" => EXPR_BEG,
- "case" => EXPR_BEG,
- "class" => EXPR_BEG,
- "def" => EXPR_FNAME,
- "defined?" => EXPR_END,
- "do" => EXPR_BEG,
- "else" => EXPR_BEG,
- "elsif" => EXPR_BEG,
- "end" => EXPR_END,
- "ensure" => EXPR_BEG,
- "for" => EXPR_BEG,
- "if" => EXPR_BEG,
- "in" => EXPR_BEG,
- "module" => EXPR_BEG,
- "nil" => EXPR_END,
- "not" => EXPR_BEG,
- "or" => EXPR_BEG,
- "rescue" => EXPR_MID,
- "return" => EXPR_MID,
- "self" => EXPR_END,
- "super" => EXPR_END,
- "then" => EXPR_BEG,
- "undef" => EXPR_FNAME,
- "unless" => EXPR_BEG,
- "until" => EXPR_BEG,
- "when" => EXPR_BEG,
- "while" => EXPR_BEG,
- "yield" => EXPR_END
- }
-
- ENINDENT_CLAUSE = [
- "case", "class", "def", "do", "for", "if",
- "module", "unless", "until", "while", "begin" #, "when"
- ]
- DEINDENT_CLAUSE = ["end" #, "when"
- ]
-
- PARCENT_LTYPE = {
- "q" => "\'",
- "Q" => "\"",
- "x" => "\`",
- "r" => "\/"
- }
-
- PARCENT_PAREN = {
- "{" => "}",
- "[" => "]",
- "<" => ">",
- "(" => ")"
- }
-
- def lex_init()
- @OP = Trie.new
- @OP.def_rules("\0", "\004", "\032"){}
- @OP.def_rules(" ", "\t", "\f", "\r", "\13") do
- @space_seen = TRUE
- next
- end
- @OP.def_rule("#") do
- |op, rests|
- @ltype = "#"
- identify_comment(rests)
- end
- @OP.def_rule("\n") do
- print "\\n\n" if CONFIG[:DEBUG]
- if @lex_state == EXPR_BEG || @lex_state == EXPR_FNAME
- @continue = TRUE
- else
- @lex_state = EXPR_BEG
- end
- end
- @OP.def_rules("*", "*=", "**=", "**") {@lex_state = EXPR_BEG}
- @OP.def_rules("!", "!=", "!~") {@lex_state = EXPR_BEG}
- @OP.def_rules("=", "==", "===", "=~", "<=>") {@lex_state = EXPR_BEG}
- @OP.def_rules("<", "<=", "<<") {@lex_state = EXPR_BEG}
- @OP.def_rules(">", ">=", ">>") {@lex_state = EXPR_BEG}
- @OP.def_rules("'", '"') do
- |op, rests|
- @ltype = op
- @quoted = op
- identify_string(rests)
- end
- @OP.def_rules("`") do
- |op, rests|
- if @lex_state != EXPR_FNAME
- @ltype = op
- @quoted = op
- identify_string(rests)
- end
- end
- @OP.def_rules('?') do
- |op, rests|
- @lex_state = EXPR_END
- identify_question(rests)
- end
- @OP.def_rules("&", "&&", "&=", "|", "||", "|=") do
- @lex_state = EXPR_BEG
- end
- @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) {}
- @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) {}
- @OP.def_rules("+=", "-=") {@lex_state = EXPR_BEG}
- @OP.def_rules("+", "-") do
- |op, rests|
- if @lex_state == EXPR_ARG
- if @space_seen and rests[0] =~ /[0-9]/
- identify_number(rests)
- else
- @lex_state = EXPR_BEG
- end
- elsif @lex_state != EXPR_END and rests[0] =~ /[0-9]/
- identify_number(rests)
- else
- @lex_state = EXPR_BEG
- end
- end
- @OP.def_rule(".") do
- |op, rests|
- @lex_state = EXPR_BEG
- if rests[0] =~ /[0-9]/
- rests.unshift op
- identify_number(rests)
- else
- # obj.if ʤɤб
- identify_identifier(rests, TRUE)
- @lex_state = EXPR_ARG
- end
- end
- @OP.def_rules("..", "...") {@lex_state = EXPR_BEG}
-
- lex_int2
- end
-
- def lex_int2
- @OP.def_rules("]", "}", ")") do
- @lex_state = EXPR_END
- @indent -= 1
- end
- @OP.def_rule(":") {|op,rests|
- identify_identifier(rests, TRUE)
- }
- @OP.def_rule("::") {|op,rests|
- identify_identifier(rests, TRUE);
- }
- @OP.def_rule("/") do
- |op, rests|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- @ltype = op
- @quoted = op
- identify_string(rests)
- elsif rests[0] == '='
- rests.shift
- @lex_state = EXPR_BEG
- elsif @lex_state == EXPR_ARG and @space_seen and rests[0] =~ /\s/
- @ltype = op
- @quoted = op
- identify_string(rests)
- else
- @lex_state = EXPR_BEG
- end
- end
- @OP.def_rules("^", "^=") {@lex_state = EXPR_BEG}
- @OP.def_rules(",", ";") {@lex_state = EXPR_BEG}
- @OP.def_rule("~") {@lex_state = EXPR_BEG}
- @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) {}
- @OP.def_rule("(") do
- @lex_state = EXPR_BEG
- @indent += 1
- end
- @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) {}
- @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) {}
- @OP.def_rule("[") do
- @indent += 1
- if @lex_state != EXPR_FNAME
- @lex_state = EXPR_BEG
- end
- end
- @OP.def_rule("{") do
- @lex_state = EXPR_BEG
- @indent += 1
- end
- @OP.def_rule('\\') {|op, rests| identify_escape(rests)} #')
- @OP.def_rule('%') do
- |op, rests|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_quotation(rests)
- elsif rests[0] == '='
- rests.shift
- elsif @lex_state == EXPR_ARG and @space_seen and rests[0] =~ /\s/
- identify_quotation(rests)
- else
- @lex_state = EXPR_BEG
- end
- end
- @OP.def_rule('$') do
- |op, rests|
- identify_gvar(rests)
- end
- @OP.def_rule('@') do
- |op, rests|
- if rests[0] =~ /[\w_]/
- rests.unshift op
- identify_identifier(rests)
- end
- end
- @OP.def_rule("def", proc{|op, chrs| /\s/ =~ chrs[0]}) do
- |op, rests|
- @indent += 1
- @lex_state = EXPR_END
- until rests[0] == "\n" or rests[0] == ";"
- rests.shift
- end
- end
- @OP.def_rule("") do
- |op, rests|
- printf "MATCH: start %s: %s\n", op, rests.inspect if CONFIG[:DEBUG]
- if rests[0] =~ /[0-9]/
- identify_number(rests)
- elsif rests[0] =~ /[\w_]/
- identify_identifier(rests)
- end
- printf "MATCH: end %s: %s\n", op, rests.inspect if CONFIG[:DEBUG]
- end
-
- p @OP if CONFIG[:DEBUG]
- end
-
- def lex(l)
- chrs = l.split(//)
- tokens = []
-
- case @ltype
- when "'", '"', '`', '/'
- identify_string(chrs)
- return if chrs.empty?
- when "#"
- identify_comment(chrs)
- return
- when "="
- if l =~ /^=end/
- $ltype = nil
- return
- end
- else
- if l =~ /^=begin/
- $ltype = "="
- return
- end
- end
-
- until chrs.empty?
- @space_seen = FALSE
- printf "perse: %s\n", chrs.join("") if CONFIG[:DEBUG]
- @OP.match(chrs)
- printf "lex_state: %s continue: %s\n", @lex_state.id2name, @continue if CONFIG[:DEBUG]
- end
- end
-
- def identify_gvar(chrs)
- @lex_state = EXPR_END
-
- ch = chrs.shift
- case ch
- when /[_~*$?!@/\\;,.=:<>"]/ #"
- return
-
- when "-"
- ch = chrs.shift
- return
-
- when "&", "`", "'", "+"
- return
-
- when /[1-9]/
- chrs.unshift ch
- v = "$"
- while (ch = chrs.shift) =~ /[0-9]/
- end
- chrs.unshift ch
- return
-
- when /\w/
- chrs.unshift ch
- chrs.unshift "$"
- identify_identifier(chrs)
- return
-
- else
- chrs.unshift ch
- return
- end
- end
-
- def identify_identifier(chrs, escaped = FALSE)
- token = ""
- token.concat chrs.shift if chrs[0] =~ /[$@]/ or escaped
- while (ch = chrs.shift) =~ /\w|_/
- print ":", ch, ":" if CONFIG[:DEBUG]
- token.concat ch
- end
- chrs.unshift ch
-
- if ch == "!" or ch == "?"
- chrs.shift
- token.concat ch
- end
- # fix token
-
- if token =~ /^[$@]/ or escaped
- @lex_state = EXPR_END
- return
- end
-
- print token, "\n" if CONFIG[:DEBUG]
- if state = CLAUSE_STATE_TRANS[token]
- if @lex_state != EXPR_BEG and token =~ /^(if|unless|while|until)/
- #
- else
- if ENINDENT_CLAUSE.include?(token)
- @indent += 1
- elsif DEINDENT_CLAUSE.include?(token)
- @indent -= 1
- end
- end
- @lex_state = state
- return
- end
- if @lex_state == EXPR_FNAME
- @lex_state = EXPR_END
- if chrs[0] == '='
- chrs.shift
- end
- elsif @lex_state == EXPR_BEG
- @lex_state = EXPR_ARG
- else
- @lex_state = EXPR_END
- end
- end
-
- def identify_quotation(chrs)
- ch = chrs.shift
- if lt = PARCENT_LTYPE[ch]
- ch = chrs.shift
- else
- lt = "\""
- end
- if ch !~ /\W/
- chrs.unshift ch
- next
- end
- @ltype = lt
- unless @quoted = PARCENT_PAREN[ch]
- @quoted = ch
- end
- identify_string(chrs)
- end
-
- def identify_number(chrs)
- @lex_state = EXPR_END
-
- ch = chrs.shift
- case ch
- when /0/
- if (ch = chrs[0]) == "x"
- chrs.shift
- match = /[0-9a-f_]/
- else
- match = /[0-7_]/
- end
- while ch = chrs.shift
- if ch !~ match
- chrs.unshift ch
- break
- end
- end
- return
- end
-
- while ch = chrs.shift
- case ch
- when /[0-9]/
- when "e", "E"
- # type = FLOAT
- unless (ch = chrs.shift) == "+" or ch == "-"
- chrs.unshift ch
- end
- when "."
- # type = FLOAT
- when "_"
- else
- chrs.unshift ch
- return
- end
- end
- end
-
- def identify_question(chrs)
- @lex_state = EXPR_END
-
- if chrs.shift == "\\" #"
- identify_escape(chrs)
- end
- end
-
- def identify_string(chrs)
- while ch = chrs.shift
- if @quoted == ch
- if @ltype == "/"
- if chrs[0] =~ /i|o|n|e|s/
- chrs.shift
- end
- end
- @ltype = nil
- @quoted = nil
- @lex_state = EXPR_END
- break
- elsif ch == '\\' #'
- identify_escape(chrs)
- end
- end
- end
-
- def identify_comment(chrs)
- while ch = chrs.shift
- if ch == "\\" #"
- identify_escape(chrs)
- end
- if ch == "\n"
- @ltype = nil
- chrs.unshift ch
- break
- end
- end
- end
-
- def identify_escape(chrs)
- ch = chrs.shift
- case ch
- when "\n", "\r", "\f"
- @continue = TRUE
- when "\\", "n", "t", "r", "f", "v", "a", "e", "b" #"
- when /[0-7]/
- chrs.unshift ch
- 3.times do
- ch = chrs.shift
- case ch
- when /[0-7]/
- when nil
- break
- else
- chrs.unshift ch
- break
- end
- end
- when "x"
- 2.times do
- ch = chrs.shift
- case ch
- when /[0-9a-fA-F]/
- when nil
- break
- else
- chrs.unshift ch
- break
- end
- end
- when "M"
- if (ch = chrs.shift) != '-'
- chrs.unshift ch
- elsif (ch = chrs.shift) == "\\" #"
- identify_escape(chrs)
- end
- return
- when "C", "c", "^"
- if ch == "C" and (ch = chrs.shift) != "-"
- chrs.unshift ch
- elsif (ch = chrs.shift) == "\\" #"
- identify_escape(chrs)
- end
- return
- end
- end
- end
-
- class Trie
- extend Exception2MessageMapper
- def_exception :ErrNodeNothing, "node nothing"
- def_exception :ErrNodeAlreadyExists, "node already exists"
-
- class Node
- # postprocʤݥΡ, nilʤжݥΡ
- def initialize(preproc = nil, postproc = nil)
- @Tree = {}
- @preproc = preproc
- @postproc = postproc
- end
-
- attr :preproc, TRUE
- attr :postproc, TRUE
-
- def search(chrs, opt = nil)
- return self if chrs.empty?
- ch = chrs.shift
- if node = @Tree[ch]
- node.search(chrs, opt)
- else
- if opt
- chrs.unshift ch
- self.create_subnode(chrs)
- else
- Trie.fail ErrNodeNothing
- end
- end
- end
-
- def create_subnode(chrs, preproc = nil, postproc = nil)
- if chrs.empty?
- if @postproc
- p node
- Trie.fail ErrNodeAlreadyExists
- else
- print "Warn: change abstruct node to real node\n" if CONFIG[:DEBUG]
- @preproc = preproc
- @postproc = postproc
- end
- return self
- end
-
- ch = chrs.shift
- if node = @Tree[ch]
- if chrs.empty?
- if node.postproc
- p node
- Trie.fail ErrNodeAlreadyExists
- else
- print "Warn: change abstruct node to real node\n" if CONFIG[:DEBUG]
- node.preproc = preproc
- node.postproc = postproc
- end
- else
- node.create_subnode(chrs, preproc, postproc)
- end
- else
- if chrs.empty?
- node = Node.new(preproc, postproc)
- else
- node = Node.new
- node.create_subnode(chrs, preproc, postproc)
- end
- @Tree[ch] = node
- end
- node
- end
-
- def match(chrs, op = "")
- print "match>: ", chrs, "op:", op, "\n" if CONFIG[:DEBUG]
- if chrs.empty?
- if @preproc.nil? || @preproc.call(op, chrs)
- printf "op1: %s\n", op if CONFIG[:DEBUG]
- @postproc.call(op, chrs)
- ""
- else
- nil
- end
- else
- ch = chrs.shift
- if node = @Tree[ch]
- if ret = node.match(chrs, op+ch)
- return ch+ret
- else
- chrs.unshift ch
- if @postproc and @preproc.nil? || @preproc.call(op, chrs)
- printf "op2: %s\n", op.inspect if CONFIG[:DEBUG]
- @postproc.call(op, chrs)
- return ""
- else
- return nil
- end
- end
- else
- chrs.unshift ch
- if @postproc and @preproc.nil? || @preproc.call(op, chrs)
- printf "op3: %s\n", op if CONFIG[:DEBUG]
- @postproc.call(op, chrs)
- return ""
- else
- return nil
- end
- end
- end
- end
- end
-
- def initialize
- @head = Node.new("")
- end
-
- def def_rule(token, preproc = nil, postproc = nil)
-# print node.inspect, "\n" if CONFIG[:DEBUG]
- postproc = proc if iterator?
- node = create(token, preproc, postproc)
- end
-
- def def_rules(*tokens)
- if iterator?
- p = proc
- end
- for token in tokens
- def_rule(token, nil, p)
- end
- end
-
- def preporc(token, proc)
- node = search(token)
- node.preproc=proc
- end
-
- def postproc(token)
- node = search(token, proc)
- node.postproc=proc
- end
-
- def search(token)
- @head.search(token.split(//))
- end
-
- def create(token, preproc = nil, postproc = nil)
- @head.create_subnode(token.split(//), preproc, postproc)
- end
-
- def match(token)
- token = token.split(//) if token.kind_of?(String)
- ret = @head.match(token)
- printf "match end: %s:%s", ret, token.inspect if CONFIG[:DEBUG]
- ret
- end
-
- def inspect
- format("<Trie: @head = %s>", @head.inspect)
- end
- end
-
- if /^-tt(.*)$/ =~ ARGV[0]
-# Tracer.on
- case $1
- when "1"
- tr = Trie.new
- print "0: ", tr.inspect, "\n"
- tr.def_rule("=") {print "=\n"}
- print "1: ", tr.inspect, "\n"
- tr.def_rule("==") {print "==\n"}
- print "2: ", tr.inspect, "\n"
-
- print "case 1:\n"
- print tr.match("="), "\n"
- print "case 2:\n"
- print tr.match("=="), "\n"
- print "case 3:\n"
- print tr.match("=>"), "\n"
-
- when "2"
- tr = Trie.new
- print "0: ", tr.inspect, "\n"
- tr.def_rule("=") {print "=\n"}
- print "1: ", tr.inspect, "\n"
- tr.def_rule("==", proc{FALSE}) {print "==\n"}
- print "2: ", tr.inspect, "\n"
-
- print "case 1:\n"
- print tr.match("="), "\n"
- print "case 2:\n"
- print tr.match("=="), "\n"
- print "case 3:\n"
- print tr.match("=>"), "\n"
- end
- exit
- end
-
- module CONTEXT
- def _=(value)
- CONFIG[:_] = value
- eval "_=BC_APPLICATION__::CONFIG[:_]", CONFIG[:BIND]
- end
-
-# def _
-# eval "_", CONFIG[:BIND]
-# end
-
- def quit
- exit
- end
-
- def trace_load(opt = nil)
- if !opt.nil?
- CONFIG[:TRACE_LOAD] = opt
- else
- CONFIG[:TRACE_LOAD] = !CONFIG[:TRACE_LOAD]
- end
- print "Switch to load/require #{unless CONFIG[:TRACE_LOAD]; ' non';end} trace mode.\n"
- if CONFIG[:TRACE_LOAD]
- eval %{
- class << self
- alias load rbc_load
- alias require rbc_require
- end
- }
- else
- eval %{
- class << self
- alias load rbc_load_org
- alias require rbc_require_org
- end
- }
- end
- CONFIG[:TRACE_LOAD]
- end
-
- alias rbc_load_org load
- def rbc_load(file_name)
- return true if load_sub(file_name)
- raise LoadError, "No such file to load -- #{file_name}"
- end
-
- alias rbc_require_org require
- def rbc_require(file_name)
- rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
- return false if $".find{|f| f =~ rex}
-
- case file_name
- when /\.rb$/
- if load_sub(file_name)
- $".push file_name
- return true
- end
- when /\.(so|o|sl)$/
- rbc_require_org(file_name)
- end
-
- if load_sub(f = file_name + ".rb")
- $".push f
- return true
- end
- rbc_require_org(file_name)
- end
-
- def load_sub(fn)
- if fn =~ /^#{Regexp.quote(File::Separator)}/
- return false unless File.exist?(fn)
- BC.new.eval_input FileInputMethod.new(fn), self, CONFIG[:BIND]
- return true
- end
-
- for path in $:
- if File.exist?(f = File.join(path, fn))
- BC.new.eval_input FileInputMethod.new(f), self, CONFIG[:BIND]
- return true
- end
- end
- return false
- end
-
- def inspect_mode(opt = nil)
- if opt
- CONFIG[:INSPECT] = opt
- else
- CONFIG[:INSPECT] = !CONFIG[:INSPECT]
- end
- print "Switch to#{unless CONFIG[:INSPECT]; ' non';end} inspect mode.\n"
- CONFIG[:INSPECT]
- end
-
- def run(bind)
- CONFIG[:BIND] = bind
-
- if CONFIG[:RC]
- rc = File.expand_path("~/.irbrc")
- if File.exists?(rc)
- begin
- load rc
- rescue
- print "load error: #{rc}\n"
- print $!.type, ": ", $!, "\n"
- for err in $@[0, $@.size - 2]
- print "\t", err, "\n"
- end
- end
- end
- end
-
- if CONFIG[:TRACE_LOAD]
- trace_load true
- end
-
- for m in CONFIG[:LOAD_MODULES]
- begin
- require m
- rescue
- print $@[0], ":", $!.type, ": ", $!, "\n"
- end
- end
-
- if !$0.equal?(CONFIG[0])
- io = FileInputMethod.new($0)
- elsif defined? Readline
- io = ReadlineInputMethod.new
- else
- io = StdioInputMethod.new
- end
-
- BC.new.eval_input io, self, CONFIG[:BIND]
- end
- end
-
- class InputMethod
- attr :prompt, TRUE
-
- def gets
- end
- public :gets
- end
-
- class StdioInputMethod < InputMethod
- def gets
- print @prompt
- $stdin.gets
- end
- end
-
- class FileInputMethod < InputMethod
- def initialize(file)
- @io = open(file)
- end
-
- def gets
- l = @io.gets
- print @prompt, l
- l
- end
- end
-
- if CONFIG[:USE_READLINE]
- begin
- require "readline"
- print "use readline module\n"
- class ReadlineInputMethod < InputMethod
- include Readline
- def gets
- if l = readline(@prompt, TRUE)
- l + "\n"
- else
- l
- end
- end
- end
- rescue
- CONFIG[:USE_READLINE] = FALSE
- end
- end
-end
-
-extend BC_APPLICATION__::CONTEXT
-run(binding)
-
diff --git a/sample/rcs.awk b/sample/rcs.awk
index 08979285c9..e64af5b628 100644
--- a/sample/rcs.awk
+++ b/sample/rcs.awk
@@ -1,33 +1,33 @@
BEGIN {
- sw = 40.0;
- dw = 78.0;
- hdw = dw / 2.0;
- w = 20.0;
- h =1.0;
- d = 0.2;
- ss="abcdefghijklmnopqrstuvwxyz0123456789!#$%^&*()-=\\[];'`,./";
- rnd = srand();
+ sw = 40.0;
+ dw = 78.0;
+ hdw = dw / 2.0;
+ w = 20.0;
+ h =1.0;
+ d = 0.2;
+ ss="abcdefghijklmnopqrstuvwxyz0123456789!#$%^&*()-=\\[];'`,./";
+ rnd = srand();
}
{
- xr = -hdw; y = h * 1.0; maxxl = -999;
- s = "";
- while (xr < hdw) {
- x = xr * (1 + y) - y * w / 2;
- i = (x / (1 + h) + sw /2);
- c = (0 < i && i < length($0)) ? substr($0, i, 1) : "0";
- y = h - d * c;
- xl = xr - w * y / (1 + y);
- if (xl < -hdw || xl >= hdw || xl <= maxxl) {
- t = rand() * length(ss);
- c = substr(ss, t, 1);
- }
- else {
- c = substr(s, xl + hdw, 1);
- maxxl = xl;
- }
- s = s c;
- xr = xr + 1;
+ xr = -hdw; y = h * 1.0; maxxl = -999;
+ s = "";
+ while (xr < hdw) {
+ x = xr * (1 + y) - y * w / 2;
+ i = (x / (1 + h) + sw /2);
+ c = (0 < i && i < length($0)) ? substr($0, i, 1) : "0";
+ y = h - d * c;
+ xl = xr - w * y / (1 + y);
+ if (xl < -hdw || xl >= hdw || xl <= maxxl) {
+ t = rand() * length(ss);
+ c = substr(ss, t, 1);
}
- print s;
+ else {
+ c = substr(s, xl + hdw, 1);
+ maxxl = xl;
+ }
+ s = s c;
+ xr = xr + 1;
+ }
+ print s;
}
diff --git a/sample/rcs.rb b/sample/rcs.rb
index 3f74da9ef2..0bdf81c45d 100644
--- a/sample/rcs.rb
+++ b/sample/rcs.rb
@@ -1,29 +1,30 @@
# random dot steraogram
# usage: rcs.rb rcs.dat
-sw = 40.0 # ̃p^[̕
-dw = 78.0 # Random Character Streogram ̕
+sw = 40.0 # width of original pattern
+dw = 78.0 # width of generating Random Character Streogram
hdw = dw / 2.0
-w = 20.0 # ̕
-h =1.0 # ʂƊʂ̋
-d = 0.2 # Pʓ̕オ
+w = 20.0 # distance between eyes
+h =1.0 # distance from screen and base plane
+d = 0.2 # z value unit
ss="abcdefghijklmnopqrstuvwxyz0123456789#!$%^&*()-=\\[];'`,./"
-rnd = srand()
+rnd = srand() # You don't actually need this in ruby - srand() is called
+ # on the first call of rand().
while gets()
# print($_)
xr = -hdw; y = h * 1.0; maxxl = -999
- s = "";
+ s = ""
while xr < hdw
x = xr * (1 + y) - y * w / 2
i = (x / (1 + h) + sw / 2)
- if (1 < i && i < $_.length);
+ if (1 < i && i < $_.length)
c = $_[i, 1].to_i
else
c = 0
end
y = h - d * c
- xl = xr - w * y / (1 + y);
+ xl = xr - w * y / (1 + y)
if xl < -hdw || xl >= hdw || xl <= maxxl
tt = rand(ss.length)
c = ss[tt, 1]
@@ -36,14 +37,3 @@ while gets()
end
print(s, "\n")
end
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sample/rdoc/markup/rdoc2latex.rb b/sample/rdoc/markup/rdoc2latex.rb
new file mode 100644
index 0000000000..aa8079eb90
--- /dev/null
+++ b/sample/rdoc/markup/rdoc2latex.rb
@@ -0,0 +1,15 @@
+#!/usr/local/bin/ruby
+# Illustration of a script to convert an RDoc-style file to a LaTeX document
+
+require 'rdoc/markup'
+require 'rdoc/markup/to_latex'
+
+p = RDoc::Markup.new
+h = RDoc::Markup::ToLaTeX.new
+
+#puts "\\documentclass{report}"
+#puts "\\usepackage{tabularx}"
+#puts "\\usepackage{parskip}"
+#puts "\\begin{document}"
+puts p.convert(ARGF.read, h)
+#puts "\\end{document}"
diff --git a/sample/rdoc/markup/sample.rb b/sample/rdoc/markup/sample.rb
new file mode 100644
index 0000000000..db5d88d150
--- /dev/null
+++ b/sample/rdoc/markup/sample.rb
@@ -0,0 +1,40 @@
+# This program illustrates the basic use of the SimpleMarkup
+# class. It extracts the first comment block from the
+# simple_markup.rb file and converts it into HTML on
+# standard output. Run it using
+#
+# % ruby sample.rb
+#
+# You should be in the sample/rdoc/markup/ directory when you do this,
+# as it hardwires the path to the files it needs to require.
+# This isn't necessary in the code you write once you've
+# installed the package.
+#
+# For a better way of formatting code comment blocks (and more)
+# see the rdoc package.
+#
+
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_html'
+
+# Extract the comment block from the source file
+
+input_string = ""
+
+File.foreach("../../../lib/rdoc/markup/simple_markup.rb") do |line|
+ break unless line.gsub!(/^\# ?/, '')
+ input_string << line
+end
+
+# Create a markup object
+markup = SM::SimpleMarkup.new
+
+# Attach it to an HTML formatter
+h = SM::ToHtml.new
+
+# And convert out comment block to html. Wrap it a body
+# tag pair to let browsers view it
+
+puts "<html><body>"
+puts markup.convert(input_string, h)
+puts "</body></html>"
diff --git a/sample/regx.rb b/sample/regx.rb
deleted file mode 100644
index aaf4b5f1ee..0000000000
--- a/sample/regx.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-st = "\033[7m"
-en = "\033[m"
-#st = "<<"
-#en = ">>"
-
-while TRUE
- print "str> "
- STDOUT.flush
- input = gets
- break if not input
- if input != ""
- str = input
- str.chop!
- end
- print "pat> "
- STDOUT.flush
- re = gets
- break if not re
- re.chop!
- str.gsub! re, "#{st}\\&#{en}"
- print str, "\n"
-end
-print "\n"
diff --git a/sample/rinda-ring.rb b/sample/rinda-ring.rb
new file mode 100644
index 0000000000..f9bd934029
--- /dev/null
+++ b/sample/rinda-ring.rb
@@ -0,0 +1,22 @@
+require 'rinda/ring'
+
+DRb.start_service
+case ARGV.shift
+when 's'
+ require 'rinda/tuplespace'
+ ts = Rinda::TupleSpace.new
+ Rinda::RingServer.new(ts)
+ $stdin.gets
+when 'w'
+ finger = Rinda::RingFinger.new(nil)
+ finger.lookup_ring do |ts2|
+ p ts2
+ ts2.write([:hello, :world])
+ end
+when 'r'
+ finger = Rinda::RingFinger.new(nil)
+ finger.lookup_ring do |ts2|
+ p ts2
+ p ts2.take([nil, nil])
+ end
+end
diff --git a/sample/ripper/ruby2html.rb b/sample/ripper/ruby2html.rb
new file mode 100644
index 0000000000..1e6b3bf550
--- /dev/null
+++ b/sample/ripper/ruby2html.rb
@@ -0,0 +1,116 @@
+#!/usr/bin/env ruby
+# $originalId: ruby2html.rb,v 1.2 2005/09/23 22:53:47 aamine Exp $
+
+TEMPLATE_LINE = __LINE__ + 2
+TEMPLATE = <<-EndTemplate
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= encoding %>">
+<% if css %>
+ <link rel="stylesheet" type="text/css" href="<%= css %>">
+<% end %>
+ <title><%= File.basename(f.path) %></title>
+</head>
+<body>
+<pre>
+<%
+ if print_line_number
+ Ruby2HTML.compile(f).each_with_index do |line, idx|
+%><%= sprintf('%4d %s', idx+1, line) %><%
+ end
+ else
+%><%= Ruby2HTML.compile(f) %><%
+ end
+%>
+</pre>
+</body>
+</html>
+EndTemplate
+
+require 'ripper'
+require 'stringio'
+require 'cgi'
+require 'erb'
+require 'optparse'
+
+def main
+ encoding = 'us-ascii'
+ css = nil
+ print_line_number = false
+ parser = OptionParser.new
+ parser.banner = "Usage: #{File.basename($0)} [-l] [<file>...]"
+ parser.on('--encoding=NAME', 'Character encoding [us-ascii].') {|name|
+ encoding = name
+ }
+ parser.on('--css=URL', 'Set a link to CSS.') {|url|
+ css = url
+ }
+ parser.on('-l', '--line-number', 'Show line number.') {
+ print_line_number = true
+ }
+ parser.on('--help', 'Prints this message and quit.') {
+ puts parser.help
+ exit 0
+ }
+ begin
+ parser.parse!
+ rescue OptionParser::ParseError => err
+ $stderr.puts err
+ $stderr.puts parser.help
+ exit 1
+ end
+ puts ruby2html(ARGF, encoding, css, print_line_number)
+end
+
+class ERB
+ attr_accessor :lineno
+
+ remove_method :result
+ def result(b)
+ eval(@src, b, (@filename || '(erb)'), (@lineno || 1))
+ end
+end
+
+def ruby2html(f, encoding, css, print_line_number)
+ if RUBY_VERSION >= '2.6'
+ erb = ERB.new(TEMPLATE, trim_mode: '>')
+ else
+ erb = ERB.new(TEMPLATE, nil, '>')
+ end
+ erb.filename = __FILE__
+ erb.lineno = TEMPLATE_LINE
+ erb.result(binding())
+end
+
+class Ruby2HTML < Ripper::Filter
+ def Ruby2HTML.compile(f)
+ buf = StringIO.new
+ Ruby2HTML.new(f).parse(buf)
+ buf.string
+ end
+
+ def on_default(event, tok, f)
+ f << CGI.escapeHTML(tok)
+ end
+
+ def on_kw(tok, f)
+ f << %Q[<span class="resword">#{CGI.escapeHTML(tok)}</span>]
+ end
+
+ def on_comment(tok, f)
+ f << %Q[<span class="comment">#{CGI.escapeHTML(tok.rstrip)}</span>\n]
+ end
+
+ def on_tstring_beg(tok, f)
+ f << %Q[<span class="string">#{CGI.escapeHTML(tok)}]
+ end
+
+ def on_tstring_end(tok, f)
+ f << %Q[#{CGI.escapeHTML(tok)}</span>]
+ end
+end
+
+if $0 == __FILE__
+ main
+end
diff --git a/sample/ripper/strip-comment.rb b/sample/ripper/strip-comment.rb
new file mode 100644
index 0000000000..bd26b6a377
--- /dev/null
+++ b/sample/ripper/strip-comment.rb
@@ -0,0 +1,19 @@
+# $Id$
+
+require 'ripper/filter'
+
+class CommentStripper < Ripper::Filter
+ def CommentStripper.strip(src)
+ new(src).parse(nil)
+ end
+
+ def on_default(event, token, data)
+ print token
+ end
+
+ def on_comment(token, data)
+ puts
+ end
+end
+
+CommentStripper.strip(ARGF)
diff --git a/sample/ruby-mode.el b/sample/ruby-mode.el
deleted file mode 100644
index 204654013f..0000000000
--- a/sample/ruby-mode.el
+++ /dev/null
@@ -1,676 +0,0 @@
-;;;
-;;; ruby-mode.el -
-;;;
-;;; $Author$
-;;; $Date$
-;;; created at: Fri Feb 4 14:49:13 JST 1994
-;;;
-
-(defconst ruby-mode-revision "$Revision$")
-
-(defconst ruby-mode-version
- (progn
- (string-match "[0-9.]+" ruby-mode-revision)
- (substring ruby-mode-revision (match-beginning 0) (match-end 0))))
-
-(defconst ruby-block-beg-re
- "class\\|module\\|def\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin\\|do"
- )
-
-(defconst ruby-indent-beg-re
- "\\(\\s *\\(class\\|module\\|def\\)\\)\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin"
- )
-
-(defconst ruby-modifier-re
- "if\\|unless\\|while\\|until"
- )
-
-(defconst ruby-block-mid-re
- "then\\|else\\|elsif\\|when\\|rescue\\|ensure"
- )
-
-(defconst ruby-block-op-re
- "and\\|or\\|not"
- )
-
-(defconst ruby-block-end-re "end")
-
-(defconst ruby-delimiter
- (concat "[?$/%(){}#\"'`.:]\\|\\[\\|\\]\\|\\<\\("
- ruby-block-beg-re
- "\\|" ruby-block-end-re
- "\\)\\>\\|^=begin")
- )
-
-(defconst ruby-negative
- (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|\\("
- ruby-block-end-re "\\)\\>\\|\\}\\|\\]\\)")
- )
-
-(defconst ruby-operator-chars ",.+*/%-&|^~=<>:")
-(defconst ruby-operator-re (concat "[" ruby-operator-chars "]"))
-
-(defconst ruby-symbol-chars "a-zA-Z0-9_")
-(defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]"))
-
-(defvar ruby-mode-abbrev-table nil
- "Abbrev table in use in ruby-mode buffers.")
-
-(define-abbrev-table 'ruby-mode-abbrev-table ())
-
-(defvar ruby-mode-map nil "Keymap used in ruby mode.")
-
-(if ruby-mode-map
- nil
- (setq ruby-mode-map (make-sparse-keymap))
- (define-key ruby-mode-map "{" 'ruby-electric-brace)
- (define-key ruby-mode-map "}" 'ruby-electric-brace)
- (define-key ruby-mode-map "\e\C-a" 'ruby-beginning-of-defun)
- (define-key ruby-mode-map "\e\C-e" 'ruby-end-of-defun)
- (define-key ruby-mode-map "\e\C-b" 'ruby-beginning-of-block)
- (define-key ruby-mode-map "\e\C-f" 'ruby-end-of-block)
- (define-key ruby-mode-map "\e\C-p" 'ruby-beginning-of-block)
- (define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block)
- (define-key ruby-mode-map "\t" 'ruby-indent-command)
- (define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
- (define-key ruby-mode-map "\C-j" 'ruby-reindent-then-newline-and-indent)
- (define-key ruby-mode-map "\C-m" 'newline))
-
-(defvar ruby-mode-syntax-table nil
- "Syntax table in use in ruby-mode buffers.")
-
-(if ruby-mode-syntax-table
- ()
- (setq ruby-mode-syntax-table (make-syntax-table))
- (modify-syntax-entry ?\' "\"" ruby-mode-syntax-table)
- (modify-syntax-entry ?\" "\"" ruby-mode-syntax-table)
- (modify-syntax-entry ?\` "\"" ruby-mode-syntax-table)
- (modify-syntax-entry ?# "<" ruby-mode-syntax-table)
- (modify-syntax-entry ?\n ">" ruby-mode-syntax-table)
- (modify-syntax-entry ?\\ "\\" ruby-mode-syntax-table)
- (modify-syntax-entry ?$ "/" ruby-mode-syntax-table)
- (modify-syntax-entry ?? "_" ruby-mode-syntax-table)
- (modify-syntax-entry ?_ "_" ruby-mode-syntax-table)
- (modify-syntax-entry ?< "." ruby-mode-syntax-table)
- (modify-syntax-entry ?> "." ruby-mode-syntax-table)
- (modify-syntax-entry ?& "." ruby-mode-syntax-table)
- (modify-syntax-entry ?| "." ruby-mode-syntax-table)
- (modify-syntax-entry ?% "." ruby-mode-syntax-table)
- (modify-syntax-entry ?= "." ruby-mode-syntax-table)
- (modify-syntax-entry ?/ "." ruby-mode-syntax-table)
- (modify-syntax-entry ?+ "." ruby-mode-syntax-table)
- (modify-syntax-entry ?* "." ruby-mode-syntax-table)
- (modify-syntax-entry ?- "." ruby-mode-syntax-table)
- (modify-syntax-entry ?\; "." ruby-mode-syntax-table)
- (modify-syntax-entry ?\( "()" ruby-mode-syntax-table)
- (modify-syntax-entry ?\) ")(" ruby-mode-syntax-table)
- (modify-syntax-entry ?\{ "(}" ruby-mode-syntax-table)
- (modify-syntax-entry ?\} "){" ruby-mode-syntax-table)
- (modify-syntax-entry ?\[ "(]" ruby-mode-syntax-table)
- (modify-syntax-entry ?\] ")[" ruby-mode-syntax-table)
- )
-
-(defvar ruby-indent-level 2
- "*Indentation of ruby statements.")
-
-(defun ruby-mode-variables ()
- (set-syntax-table ruby-mode-syntax-table)
- (setq local-abbrev-table ruby-mode-abbrev-table)
- (make-local-variable 'indent-line-function)
- (setq indent-line-function 'ruby-indent-line)
- (make-local-variable 'require-final-newline)
- (setq require-final-newline t)
- (make-variable-buffer-local 'comment-start)
- (setq comment-start "# ")
- (make-variable-buffer-local 'comment-end)
- (setq comment-end "")
- (make-variable-buffer-local 'comment-column)
- (setq comment-column 32)
- (make-variable-buffer-local 'comment-start-skip)
- (setq comment-start-skip "\\(^\\|\\s-\\);?#+ *")
- (make-local-variable 'parse-sexp-ignore-comments)
- (setq parse-sexp-ignore-comments t))
-
-(defun ruby-mode ()
- "Major mode for editing ruby scripts.
-\\[ruby-indent-command] properly indents subexpressions of multi-line
-class, module, def, if, while, for, do, and case statements, taking
-nesting into account.
-
-The variable ruby-indent-level controls the amount of indentation.
-\\{ruby-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (use-local-map ruby-mode-map)
- (setq mode-name "ruby")
- (setq major-mode 'ruby-mode)
- (ruby-mode-variables)
- (run-hooks 'ruby-mode-hook))
-
-(defun ruby-current-indentation ()
- (save-excursion
- (beginning-of-line)
- (back-to-indentation)
- (current-column)))
-
-(defun ruby-indent-line (&optional flag)
- "Correct indentation of the current ruby line."
- (ruby-indent-to (ruby-calculate-indent)))
-
-(defun ruby-indent-command ()
- (interactive)
- (ruby-indent-line t))
-
-(defun ruby-indent-to (x)
- (if x
- (let (shift top beg)
- (and (< x 0)
- (error "invalid nest"))
- (setq shift (current-column))
- (beginning-of-line)
- (setq beg (point))
- (back-to-indentation)
- (setq top (current-column))
- (skip-chars-backward " \t")
- (cond
- ((>= x shift)
- (setq shift 0))
- ((>= shift top)
- (setq shift (- shift top)))
- (t (setq shift 0)))
- (if (and (bolp)
- (= x top))
- (move-to-column (+ x shift))
- (move-to-column top)
- (delete-region beg (point))
- (beginning-of-line)
- (indent-to x)
- (move-to-column (+ x shift))))))
-
-(defun ruby-expr-beg (&optional modifier pnt)
- (save-excursion
- (if (looking-at "\\?")
- (progn
- (or (bolp) (forward-char -1))
- (not (looking-at "\\sw")))
- (store-match-data nil)
- (skip-chars-backward " \t")
- (or (bolp) (forward-char -1))
- (or (bolp)
- (looking-at ruby-operator-re)
- (looking-at "[\\[({]")
- (and (not modifier) (looking-at "[!?]"))
- (and (looking-at ruby-symbol-re)
- (forward-word -1)
- (if (and (not modifier) (bolp))
- t
- (if (or (looking-at ruby-block-beg-re)
- (looking-at ruby-block-op-re)
- (looking-at ruby-block-mid-re))
- (progn
- (goto-char (match-end 0))
- (looking-at "[^_]")))))))))
-
-(defun ruby-parse-region (start end)
- (let ((indent-point end)
- (indent 0)
- (in-string nil)
- (in-paren nil)
- (depth 0)
- (nest nil)
- (pcol nil))
- (save-excursion
- (if start
- (goto-char start)
- (ruby-beginning-of-indent))
- (save-restriction
- (narrow-to-region (point) end)
- (while (and (> indent-point (point))
- (re-search-forward ruby-delimiter indent-point t))
- (let ((pnt (point)) w)
- (goto-char (match-beginning 0))
- (cond
- ((or (looking-at "\"") ;skip string
- (looking-at "'")
- (looking-at "`"))
- (setq w (char-after (point)))
- (cond
- ((and (not (eobp))
- (re-search-forward (format "[^\\]\\(\\\\\\\\\\)*%c" w) indent-point t))
- nil)
- (t
- (setq in-string (point))
- (goto-char indent-point))))
- ((looking-at "/")
- (cond
- ((and (not (eobp)) (ruby-expr-beg))
- (if (re-search-forward "[^\\]/" indent-point t)
- nil
- (setq in-string (point))
- (goto-char indent-point)))
- (t
- (goto-char pnt))))
- ((looking-at "%")
- (cond
- ((and (not (eobp)) (ruby-expr-beg)
- (looking-at "%[Qqrx]?\\(.\\)"))
- (setq w (buffer-substring (match-beginning 1)
- (match-end 1)))
- (cond
- ((string= w "[") (setq w "]"))
- ((string= w "{") (setq w "}"))
- ((string= w "(") (setq w ")"))
- ((string= w "<") (setq w ">")))
- (goto-char (match-end 0))
- (if (search-forward w indent-point t)
- nil
- (setq in-string (point))
- (goto-char indent-point)))
- (t
- (goto-char pnt))))
- ((looking-at "\\?") ;skip ?char
- (cond
- ((ruby-expr-beg)
- (looking-at "?\\(\\\\C-\\|\\\\M-\\)*.")
- (goto-char (match-end 0)))
- (t
- (goto-char pnt))))
- ((looking-at "\\$") ;skip $char
- (goto-char pnt)
- (forward-char 1))
- ((looking-at "#") ;skip comment
- (forward-line 1)
- (goto-char (point))
- )
- ((looking-at "(")
- (setq nest (cons (cons (char-after (point)) pnt) nest))
- (setq pcol (cons (cons pnt depth) pcol))
- (setq depth 0)
- (goto-char pnt)
- )
- ((looking-at "[\\[{]")
- (setq nest (cons (cons (char-after (point)) pnt) nest))
- (setq depth (1+ depth))
- (goto-char pnt)
- )
- ((looking-at ")")
- (setq nest (cdr nest))
- (setq depth (cdr (car pcol)))
- (setq pcol (cdr pcol))
- (goto-char pnt))
- ((looking-at "[])}]")
- (setq nest (cdr nest))
- (setq depth (1- depth))
- (goto-char pnt))
- ((looking-at ruby-block-end-re)
- (if (or (and (not (bolp))
- (progn
- (forward-char -1)
- (eq ?_ (char-after (point)))))
- (progn
- (goto-char pnt)
- (setq w (char-after (point)))
- (or (eq ?_ w)
- (eq ?! w)
- (eq ?? w))))
- nil
- (setq nest (cdr nest))
- (setq depth (1- depth)))
- (goto-char pnt))
- ((looking-at "def\\s +[^(\n;]*")
- (if (or (bolp)
- (progn
- (forward-char -1)
- (not (eq ?_ (char-after (point))))))
- (progn
- (setq nest (cons (cons nil pnt) nest))
- (setq depth (1+ depth))))
- (goto-char (match-end 0)))
- ((looking-at ruby-block-beg-re)
- (and
- (or (bolp)
- (progn
- (forward-char -1)
- (not (eq ?_ (char-after (point))))))
- (goto-char pnt)
- (setq w (char-after (point)))
- (not (eq ?_ w))
- (not (eq ?! w))
- (not (eq ?? w))
- (skip-chars-forward " \t")
- (if (not (eolp))
- (progn
- (goto-char (match-beginning 0))
- (if (looking-at ruby-modifier-re)
- (ruby-expr-beg t)
- t))
- t)
- (goto-char pnt)
- (setq nest (cons (cons nil pnt) nest))
- (setq depth (1+ depth)))
- (goto-char pnt))
- ((looking-at ":\\([a-zA-Z_][a-zA-Z_0-9]*\\)?")
- (goto-char (match-end 0)))
- ((or (looking-at "\\.")
- (looking-at "\\.\\.\\.?")
- (looking-at "\\.[0-9]+")
- (looking-at "\\.[a-zA-Z_0-9]+"))
- (goto-char (match-end 0)))
- ((looking-at "^=begin")
- (if (re-search-forward "^=end" indent-point t)
- (forward-line 1)
- (setq in-string (match-end 0))
- (goto-char indent-point)))
- (t
- (error (format "bad string %s"
- (buffer-substring (point) pnt)
- )))))))
- (list in-string (car nest) depth (car (car pcol))))))
-
-(defun ruby-indent-size (pos nest)
- (+ pos (* (if nest nest 1) ruby-indent-level)))
-
-(defun ruby-calculate-indent (&optional parse-start)
- (save-excursion
- (beginning-of-line)
- (let ((indent-point (point))
- (case-fold-search nil)
- state bol eol
- (indent 0))
- (if parse-start
- (goto-char parse-start)
- (ruby-beginning-of-indent)
- (setq parse-start (point)))
- (back-to-indentation)
- (setq indent (current-column))
- (setq state (ruby-parse-region parse-start indent-point))
- (cond
- ((nth 0 state) ; within string
- (setq indent nil)) ; do nothing
-
- ((car (nth 1 state)) ; in paren
- (goto-char (cdr (nth 1 state)))
- (if (eq (car (nth 1 state)) ?\( )
- (let ((column (current-column))
- (s (ruby-parse-region (point) indent-point)))
- (cond
- ((and (nth 2 s) (> (nth 2 s) 0))
- (goto-char (cdr (nth 1 s)))
- (forward-word -1)
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (setq indent (current-column)))))
- (cond
- ((nth 3 state)
- (goto-char (nth 3 state))
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (goto-char parse-start)
- (back-to-indentation)
- (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
- ))
-
- ((and (nth 2 state)(> (nth 2 state) 0)) ; in nest
- (if (null (cdr (nth 1 state)))
- (error "invalid nest"))
- (goto-char (cdr (nth 1 state)))
- (forward-word -1) ; skip back a keyword
- (cond
- ((looking-at "do") ; iter block is a special case
- (cond
- ((nth 3 state)
- (goto-char (nth 3 state))
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (goto-char parse-start)
- (back-to-indentation)
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))))
- (t
- (setq indent (+ (current-column) ruby-indent-level)))))
-
- ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
- (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
-
- (cond
- (indent
- (goto-char indent-point)
- (end-of-line)
- (setq eol (point))
- (beginning-of-line)
- (cond
- ((re-search-forward ruby-negative eol t)
- (and (not (eq ?_ (char-after (match-end 0))))
- (setq indent (- indent ruby-indent-level))))
- ;;operator terminated lines
- ((and
- (save-excursion
- (beginning-of-line)
- (not (bobp)))
- (or (null (car (nth 1 state))) ;not in parens
- (and (eq (car (nth 1 state)) ?\{)
- (save-excursion ;except non-block braces
- (goto-char (cdr (nth 1 state)))
- (or (bobp) (forward-char -1))
- (not (ruby-expr-beg))))))
- ;; goto beginning of non-empty no-comment line
- (let (end done)
- (while (not done)
- (skip-chars-backward " \t\n")
- (setq end (point))
- (beginning-of-line)
- (if (re-search-forward "^\\s *#" end t)
- (beginning-of-line)
- (setq done t))))
- (setq bol (point))
- (end-of-line)
- (skip-chars-backward " \t")
- (or (bobp) (forward-char -1))
- (and
- (or (and (looking-at ruby-symbol-re)
- (skip-chars-backward ruby-symbol-chars)
- (looking-at ruby-block-op-re)
- (save-excursion
- (goto-char (match-end 0))
- (not (looking-at "[a-z_]"))))
- (and (looking-at ruby-operator-re)
- (or (not (or (eq ?/ (char-after (point)))))
- (null (nth 0 (ruby-parse-region parse-start (point)))))
- (not (eq (char-after (1- (point))) ?$))
- (or (not (eq ?| (char-after (point))))
- (save-excursion
- (or (eolp) (forward-char -1))
- (and (search-backward "|")
- (skip-chars-backward " \t\n")
- (and (not (eolp))
- (progn
- (forward-char -1)
- (not (looking-at "\\{")))
- (progn
- (forward-word -1)
- (not (looking-at "do\\>[^_]")))))))))
- (setq indent (+ indent ruby-indent-level)))))))
- indent)))
-
-(defun ruby-electric-brace (arg)
- (interactive "P")
- (self-insert-command (prefix-numeric-value arg))
- (ruby-indent-line t))
-
-(defun ruby-beginning-of-defun (&optional arg)
- "Move backward to next beginning-of-defun.
-With argument, do this that many times.
-Returns t unless search stops due to end of buffer."
- (interactive "p")
- (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\b")
- nil 'move (or arg 1))
- (progn (beginning-of-line) t)))
-
-(defun ruby-beginning-of-indent ()
- (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\b")
- nil 'move)
- (progn
- (beginning-of-line)
- t)))
-
-(defun ruby-end-of-defun (&optional arg)
- "Move forward to next end of defun.
-An end of a defun is found by moving forward from the beginning of one."
- (interactive "p")
- (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\b[^_]")
- nil 'move (or arg 1))
- (progn (beginning-of-line) t))
- (forward-line 1))
-
-(defun ruby-move-to-block (n)
- (let (start pos done down)
- (setq start (ruby-calculate-indent))
- (if (eobp)
- nil
- (while (and (not (bobp)) (not done))
- (forward-line n)
- (cond
- ((looking-at "^$"))
- ((looking-at "^\\s *#"))
- (t
- (setq pos (current-indentation))
- (cond
- ((< start pos)
- (setq down t))
- ((and down (= pos start))
- (setq done t))
- ((> start pos)
- (setq done t)))))
- (if done
- (progn
- (back-to-indentation)
- (if (looking-at ruby-block-mid-re)
- (setq done nil)))))))
- (back-to-indentation))
-
-(defun ruby-beginning-of-block ()
- "Move backward to next beginning-of-block"
- (interactive)
- (ruby-move-to-block -1))
-
-(defun ruby-end-of-block ()
- "Move forward to next beginning-of-block"
- (interactive)
- (ruby-move-to-block 1))
-
-(defun ruby-reindent-then-newline-and-indent ()
- (interactive "*")
- (save-excursion
- (delete-region (point) (progn (skip-chars-backward " \t") (point))))
- (newline)
- (save-excursion
- (forward-line -1)
- (indent-according-to-mode))
- (indent-according-to-mode))
-
-(fset 'ruby-encomment-region (symbol-function 'comment-region))
-
-(defun ruby-decomment-region (beg end)
- (interactive "r")
- (save-excursion
- (goto-char beg)
- (while (re-search-forward "^\\([ \t]*\\)#" end t)
- (replace-match "\\1" nil nil)
- (save-excursion
- (ruby-indent-line)))))
-
-(defun ruby-insert-end ()
- (interactive)
- (insert "end")
- (ruby-indent-line t)
- (end-of-line))
-
-(cond
- ((featurep 'hilit19)
- (hilit-set-mode-patterns
- 'ruby-mode
- '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string)
- ("[^$\\?]\\('[^\\']*\\(\\\\\\(.\\|\n\\)[^\\']*\\)*'\\)" 1 string)
- ("[^$\\?]\\(`[^\\`]*\\(\\\\\\(.\\|\n\\)[^\\`]*\\)*`\\)" 1 string)
- ("^\\s *#.*$" nil comment)
- ("[^$@?\\]\\(#[^$@{].*$\\)" 1 comment)
- ("[^a-zA-Z_]\\(\\?\\(\\\\[CM]-\\)*.\\)" 1 string)
- ("^\\s *\\(require\\|load\\).*$" nil include)
- ("^\\s *\\(include\\|alias\\|undef\\).*$" nil decl)
- ("^\\s *\\<\\(class\\|def\\|module\\)\\>" "[)\n;]" defun)
- ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|unless\\|rescue\\|then\\|when\\|while\\|until\\|do\\)\\>[^_]" 1 defun)
- ("[^_]\\<\\(and\\|break\\|next\\|raise\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\|self\\|nil\\)\\>[^_]" 1 keyword)
- ("\\$\\(.\\|\\sw+\\)" nil type)
- ("[$@].[a-zA-Z_0-9]*" nil struct)
- ("^__END__" nil label))))
-
- ((featurep 'font-lock)
- (or (boundp 'font-lock-variable-name-face)
- (setq font-lock-variable-name-face font-lock-type-face))
- (defvar ruby-font-lock-keywords
- (list
- (cons (concat
- "\\(^\\|[^_:.]\\|\\.\\.\\)\\b\\("
- (mapconcat
- 'identity
- '("alias"
- "and"
- "begin"
- "break"
- "case"
- "class"
- "do"
- "elsif"
- "else"
- "fail"
- "ensure"
- "for"
- "end"
- "if"
- "in"
- "module"
- "next"
- "not"
- "or"
- "raise"
- "redo"
- "rescue"
- "retry"
- "return"
- "then"
- "self"
- "super"
- "unless"
- "undef"
- "until"
- "when"
- "while"
- )
- "\\|")
- "\\)[ \n\t()]")
- 2)
- ;; variables
- '("\\(^\\|[^_]\\)\\b\\(nil\\|self\\|true\\|false\\)\\b\\([^_]\\|$\\)"
- 2 font-lock-variable-name-face)
- ;; variables
- '("[$@].[a-zA-Z0-9_]*"
- 0 font-lock-variable-name-face)
- ;; constants
- '("\\(^\\|[^_]\\)\\b\\([A-Z]+[a-zA-Z0-9_]*\\)"
- 2 font-lock-type-face)
- ;; functions
- '("^\\s *def[ \t]+[^ \t(]*"
- 0 font-lock-function-name-face t))
- "*Additional expressions to highlight in ruby mode.")
- (if (and (>= (string-to-int emacs-version) 19)
- (not (featurep 'xemacs)))
- (add-hook
- 'ruby-mode-hook
- (lambda ()
- (make-local-variable 'font-lock-defaults)
- (setq font-lock-defaults
- '((ruby-font-lock-keywords) nil nil ((?\_ . "w"))))))
- (add-hook 'ruby-mode-hook
- (lambda ()
- (setq font-lock-keywords ruby-font-lock-keywords))))))
-
-(provide 'ruby-mode)
diff --git a/sample/rubydb2x.el b/sample/rubydb2x.el
deleted file mode 100644
index a74265fb0e..0000000000
--- a/sample/rubydb2x.el
+++ /dev/null
@@ -1,104 +0,0 @@
-(require 'gud)
-(provide 'rubydb)
-
-;; ======================================================================
-;; rubydb functions
-
-;;; History of argument lists passed to rubydb.
-(defvar gud-rubydb-history nil)
-
-(defun gud-rubydb-massage-args (file args)
- (cons "-I" (cons "." (cons "-r" (cons "debug" (cons file args))))))
-
-;; There's no guarantee that Emacs will hand the filter the entire
-;; marker at once; it could be broken up across several strings. We
-;; might even receive a big chunk with several markers in it. If we
-;; receive a chunk of text which looks like it might contain the
-;; beginning of a marker, we save it here between calls to the
-;; filter.
-(defvar gud-rubydb-marker-acc "")
-
-(defun gud-rubydb-marker-filter (string)
- (save-match-data
- (setq gud-marker-acc (concat gud-marker-acc string))
- (let ((output ""))
-
- ;; Process all the complete markers in this chunk.
- (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
- gud-marker-acc)
- (setq
-
- ;; Extract the frame position from the marker.
- gud-last-frame
- (cons (substring gud-marker-acc (match-beginning 1) (match-end 1))
- (string-to-int (substring gud-marker-acc
- (match-beginning 2)
- (match-end 2))))
-
- ;; Append any text before the marker to the output we're going
- ;; to return - we don't include the marker in this text.
- output (concat output
- (substring gud-marker-acc 0 (match-beginning 0)))
-
- ;; Set the accumulator to the remaining text.
- gud-marker-acc (substring gud-marker-acc (match-end 0))))
-
- ;; Does the remaining text look like it might end with the
- ;; beginning of another marker? If it does, then keep it in
- ;; gud-marker-acc until we receive the rest of it. Since we
- ;; know the full marker regexp above failed, it's pretty simple to
- ;; test for marker starts.
- (if (string-match "\032.*\\'" gud-marker-acc)
- (progn
- ;; Everything before the potential marker start can be output.
- (setq output (concat output (substring gud-marker-acc
- 0 (match-beginning 0))))
-
- ;; Everything after, we save, to combine with later input.
- (setq gud-marker-acc
- (substring gud-marker-acc (match-beginning 0))))
-
- (setq output (concat output gud-marker-acc)
- gud-marker-acc ""))
-
- output)))
-
-(defun gud-rubydb-find-file (f)
- (find-file-noselect f))
-
-(defvar rubydb-command-name "ruby"
- "File name for executing ruby.")
-
-;;;###autoload
-(defun rubydb (command-line)
- "Run rubydb on program FILE in buffer *gud-FILE*.
-The directory containing FILE becomes the initial working directory
-and source-file directory for your debugger."
- (interactive
- (list (read-from-minibuffer "Run rubydb (like this): "
- (if (consp gud-rubydb-history)
- (car gud-rubydb-history)
- (concat rubydb-command-name " "))
- nil nil
- '(gud-rubydb-history . 1))))
-
- (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args)
- (gud-marker-filter . gud-rubydb-marker-filter)
- (gud-find-file . gud-rubydb-find-file)
- ))
- (gud-common-init command-line)
-
- (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.")
-; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
- (gud-def gud-step "s" "\C-s" "Step one source line with display.")
- (gud-def gud-next "n" "\C-n" "Step one line (skip functions).")
- (gud-def gud-cont "c" "\C-r" "Continue with display.")
- (gud-def gud-finish "finish" "\C-f" "Finish executing current function.")
- (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).")
- (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).")
- (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.")
-
- (setq comint-prompt-regexp "^(rdb:-) ")
- (setq paragraph-start comint-prompt-regexp)
- (run-hooks 'rubydb-mode-hook)
- )
diff --git a/sample/rubydb3x.el b/sample/rubydb3x.el
deleted file mode 100644
index 9d4e31f90e..0000000000
--- a/sample/rubydb3x.el
+++ /dev/null
@@ -1,104 +0,0 @@
-(require 'gud)
-(provide 'rubydb)
-
-;; ======================================================================
-;; rubydb functions
-
-;;; History of argument lists passed to rubydb.
-(defvar gud-rubydb-history nil)
-
-(defun gud-rubydb-massage-args (file args)
- (cons "-r" (cons "debug" args)))
-
-;; There's no guarantee that Emacs will hand the filter the entire
-;; marker at once; it could be broken up across several strings. We
-;; might even receive a big chunk with several markers in it. If we
-;; receive a chunk of text which looks like it might contain the
-;; beginning of a marker, we save it here between calls to the
-;; filter.
-(defvar gud-rubydb-marker-acc "")
-
-(defun gud-rubydb-marker-filter (string)
- (setq gud-marker-acc (concat gud-marker-acc string))
- (let ((output ""))
-
- ;; Process all the complete markers in this chunk.
- (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
- gud-marker-acc)
- (setq
-
- ;; Extract the frame position from the marker.
- gud-last-frame
- (cons (substring gud-marker-acc (match-beginning 1) (match-end 1))
- (string-to-int (substring gud-marker-acc
- (match-beginning 2)
- (match-end 2))))
-
- ;; Append any text before the marker to the output we're going
- ;; to return - we don't include the marker in this text.
- output (concat output
- (substring gud-marker-acc 0 (match-beginning 0)))
-
- ;; Set the accumulator to the remaining text.
- gud-marker-acc (substring gud-marker-acc (match-end 0))))
-
- ;; Does the remaining text look like it might end with the
- ;; beginning of another marker? If it does, then keep it in
- ;; gud-marker-acc until we receive the rest of it. Since we
- ;; know the full marker regexp above failed, it's pretty simple to
- ;; test for marker starts.
- (if (string-match "\032.*\\'" gud-marker-acc)
- (progn
- ;; Everything before the potential marker start can be output.
- (setq output (concat output (substring gud-marker-acc
- 0 (match-beginning 0))))
-
- ;; Everything after, we save, to combine with later input.
- (setq gud-marker-acc
- (substring gud-marker-acc (match-beginning 0))))
-
- (setq output (concat output gud-marker-acc)
- gud-marker-acc ""))
-
- output))
-
-(defun gud-rubydb-find-file (f)
- (save-excursion
- (let ((buf (find-file-noselect f)))
- (set-buffer buf)
- (gud-make-debug-menu)
- buf)))
-
-(defvar rubydb-command-name "ruby"
- "File name for executing ruby.")
-
-;;;###autoload
-(defun rubydb (command-line)
- "Run rubydb on program FILE in buffer *gud-FILE*.
-The directory containing FILE becomes the initial working directory
-and source-file directory for your debugger."
- (interactive
- (list (read-from-minibuffer "Run rubydb (like this): "
- (if (consp gud-rubydb-history)
- (car gud-rubydb-history)
- (concat rubydb-command-name " "))
- nil nil
- '(gud-rubydb-history . 1))))
-
- (gud-common-init command-line 'gud-rubydb-massage-args
- 'gud-rubydb-marker-filter 'gud-rubydb-find-file)
-
- (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.")
-; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line")
- (gud-def gud-step "s" "\C-s" "Step one source line with display.")
- (gud-def gud-next "n" "\C-n" "Step one line (skip functions).")
- (gud-def gud-cont "c" "\C-r" "Continue with display.")
- (gud-def gud-finish "finish" "\C-f" "Finish executing current function.")
- (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).")
- (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).")
- (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.")
-
- (setq comint-prompt-regexp "^(rdb:-) ")
- (setq paragraph-start comint-prompt-regexp)
- (run-hooks 'rubydb-mode-hook)
- )
diff --git a/sample/sieve.rb b/sample/sieve.rb
index 03ff8a67f4..e0bb21d640 100644
--- a/sample/sieve.rb
+++ b/sample/sieve.rb
@@ -1,18 +1,14 @@
# sieve of Eratosthenes
+max = Integer(ARGV.shift || 100)
sieve = []
-if ! max = ARGV.shift; max = 100; end
-max = max.to_i
+for i in 2 .. max
+ sieve[i] = i
+end
-print "1"
-for i in 2 .. max
- begin
- for d in sieve
- fail if i % d == 0
- end
- print ", "
- print i
- sieve.push(i)
- rescue
+for i in 2 .. Math.sqrt(max)
+ next unless sieve[i]
+ (i*i).step(max, i) do |j|
+ sieve[j] = nil
end
end
-print "\n"
+puts sieve.compact.join(", ")
diff --git a/sample/simple-bench.rb b/sample/simple-bench.rb
new file mode 100644
index 0000000000..607fdbf6e9
--- /dev/null
+++ b/sample/simple-bench.rb
@@ -0,0 +1,140 @@
+require 'benchmark'
+
+def foo0
+end
+def foo3 a, b, c
+end
+def foo6 a, b, c, d, e, f
+end
+
+def iter0
+ yield
+end
+
+def iter1
+ yield 1
+end
+
+def iter3
+ yield 1, 2, 3
+end
+
+def iter6
+ yield 1, 2, 3, 4, 5, 6
+end
+
+(1..6).each{|i|
+ kws = (1..i).map{|e| "k#{e}: #{e}"}
+ eval %Q{
+ def foo_kw#{i}(#{kws.join(', ')})
+ end
+ }
+
+ kws = (1..i).map{|e| "k#{e}:"}
+ eval %Q{
+ def foo_required_kw#{i}(#{kws.join(', ')})
+ end
+ }
+}
+
+(1..6).each{|i|
+ kws = (1..i).map{|e| "k#{e}: #{e} + 1"}
+ eval %Q{
+ def foo_complex_kw#{i}(#{kws.join(', ')})
+ end
+ }
+}
+
+(1..6).each{|i|
+ kws = (1..i).map{|e| "k#{e}: #{e}"}
+ eval %Q{
+ def iter_kw#{i}
+ yield #{kws.join(', ')}
+ end
+ }
+}
+
+ary1 = [1]
+ary2 = [[1, 2, 3, 4, 5]]
+
+test_methods = %Q{
+ # empty 1
+ # empty 2
+ foo0
+ foo3 1, 2, 3
+ foo6 1, 2, 3, 4, 5, 6
+ foo_kw1
+ foo_kw2
+ foo_kw3
+ foo_kw4
+ foo_kw5
+ foo_kw6
+ foo_kw6 k1: 1
+ foo_kw6 k1: 1, k2: 2
+ foo_kw6 k1: 1, k2: 2, k3: 3
+ foo_kw6 k1: 1, k2: 2, k3: 3, k4: 4
+ foo_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5
+ foo_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6
+ foo_required_kw1 k1: 1
+ foo_required_kw2 k1: 1, k2: 2
+ foo_required_kw3 k1: 1, k2: 2, k3: 3
+ foo_required_kw4 k1: 1, k2: 2, k3: 3, k4: 4
+ foo_required_kw5 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5
+ foo_required_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6
+ foo_complex_kw1
+ foo_complex_kw2
+ foo_complex_kw3
+ foo_complex_kw4
+ foo_complex_kw5
+ foo_complex_kw6
+ foo_complex_kw6 k1: 1
+ foo_complex_kw6 k1: 1, k2: 2
+ foo_complex_kw6 k1: 1, k2: 2, k3: 3
+ foo_complex_kw6 k1: 1, k2: 2, k3: 3, k4: 4
+ foo_complex_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5
+ foo_complex_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6
+ iter0{}
+ iter1{}
+ iter1{|a|}
+ iter3{}
+ iter3{|a|}
+ iter3{|a, b, c|}
+ iter6{}
+ iter6{|a|}
+ iter6{|a, b, c, d, e, f, g|}
+ iter0{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ iter_kw1{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ iter_kw2{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ iter_kw3{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ iter_kw4{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ iter_kw5{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ iter_kw6{|k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil|}
+ ary1.each{|e|}
+ ary1.each{|e,|}
+ ary1.each{|a, b, c, d, e|}
+ ary2.each{|e|}
+ ary2.each{|e,|}
+ ary2.each{|a, b, c, d, e|}
+}
+
+N = 10_000_000
+
+max_line = test_methods.each_line.max_by{|line| line.strip.size}
+max_size = max_line.strip.size
+
+Benchmark.bm(max_size){|x|
+
+ str = test_methods.each_line.map{|line| line.strip!
+ next if line.empty?
+ %Q{
+ x.report(#{line.dump}){
+ i = 0
+ while i<#{N}
+ #{line}
+ i+=1
+ end
+ }
+ }
+ }.join("\n")
+ eval str
+}
diff --git a/sample/svr.rb b/sample/svr.rb
index c866407eb0..eb49ccf170 100644
--- a/sample/svr.rb
+++ b/sample/svr.rb
@@ -1,15 +1,18 @@
# socket example - server side
# usage: ruby svr.rb
+# this server might be blocked by an ill-behaved client.
+# see tsvr.rb which is safe from client blocking.
+
require "socket"
-gs = TCPserver.open(0)
+gs = TCPServer.open(0)
addr = gs.addr
addr.shift
-printf("server is on %d\n", addr.join(":"))
+printf("server is on %s\n", addr.join(":"))
socks = [gs]
-while TRUE
+loop do
nsock = select(socks);
next if nsock == nil
for s in nsock[0]
@@ -22,10 +25,9 @@ while TRUE
print(s, " is gone\n")
s.close
socks.delete(s)
- else
- if str = s.gets;
+ # single thread gets may block whole service
+ elsif str = s.gets
s.write(str)
- end
end
end
end
diff --git a/sample/tempfile.rb b/sample/tempfile.rb
new file mode 100644
index 0000000000..5a363614a3
--- /dev/null
+++ b/sample/tempfile.rb
@@ -0,0 +1,8 @@
+require 'tempfile'
+
+f = Tempfile.new("foo")
+f.print("foo\n")
+f.close
+f.open
+p f.gets # => "foo\n"
+f.close!
diff --git a/sample/test.rb b/sample/test.rb
index 0adcb22307..65dd9abd10 100644
--- a/sample/test.rb
+++ b/sample/test.rb
@@ -1,1064 +1,2 @@
-#! /usr/local/bin/ruby
-
-$testnum=0
-$ntest=0
-$failed = 0
-
-def check(what)
- printf "%s\n", what
- $what = what
- $testnum = 0
-end
-
-def ok(cond)
- $testnum+=1
- $ntest+=1
- if cond
- printf "ok %d\n", $testnum
- else
- where = caller[0]
- printf "not ok %s %d -- %s\n", $what, $testnum, where
- $failed+=1
- end
-end
-
-# make sure conditional operators work
-
-check "assignment"
-
-a=[]; a[0] ||= "bar";
-ok(a[0] == "bar")
-h={}; h["foo"] ||= "bar";
-ok(h["foo"] == "bar")
-
-aa = 5
-aa ||= 25
-ok(aa == 5)
-bb ||= 25
-ok(bb == 25)
-cc &&=33
-ok(cc == nil)
-cc = 5
-cc &&=44
-ok(cc == 44)
-
-check "condition"
-
-$x = '0';
-
-$x == $x && ok(true)
-$x != $x && ok(false)
-$x == $x || ok(false)
-$x != $x || ok(true)
-
-# first test to see if we can run the tests.
-
-check "if/unless";
-
-$x = 'test';
-ok(if $x == $x then true else false end)
-$bad = false
-unless $x == $x
- $bad = true
-end
-ok(!$bad)
-ok(unless $x != $x then true else false end)
-
-check "case"
-
-case 5
-when 1, 2, 3, 4, 6, 7, 8
- ok(false)
-when 5
- ok(true)
-end
-
-case 5
-when 5
- ok(true)
-when 1..10
- ok(false)
-end
-
-case 5
-when 1..10
- ok(true)
-else
- ok(false)
-end
-
-case 5
-when 5
- ok(true)
-else
- ok(false)
-end
-
-case "foobar"
-when /^f.*r$/
- ok(true)
-else
- ok(false)
-end
-
-check "while/until";
-
-tmp = open("while_tmp", "w")
-tmp.print "tvi925\n";
-tmp.print "tvi920\n";
-tmp.print "vt100\n";
-tmp.print "Amiga\n";
-tmp.print "paper\n";
-tmp.close
-
-# test break
-
-tmp = open("while_tmp", "r")
-ok(tmp.kind_of?(File))
-
-while tmp.gets()
- break if /vt100/
-end
-
-ok(!tmp.eof? && /vt100/)
-tmp.close
-
-# test next
-$bad = false
-tmp = open("while_tmp", "r")
-while tmp.gets()
- next if /vt100/;
- $bad = 1 if /vt100/;
-end
-ok(!(!tmp.eof? || /vt100/ || $bad))
-tmp.close
-
-# test redo
-$bad = false
-tmp = open("while_tmp", "r")
-while tmp.gets()
- if gsub!('vt100', 'VT100')
- gsub!('VT100', 'Vt100')
- redo;
- end
- $bad = 1 if /vt100/;
- $bad = 1 if /VT100/;
-end
-ok(tmp.eof? && !$bad)
-tmp.close
-
-sum=0
-for i in 1..10
- sum += i
- i -= 1
- if i > 0
- redo
- end
-end
-ok(sum == 220)
-
-# test interval
-$bad = false
-tmp = open("while_tmp", "r")
-while tmp.gets()
- break unless 1..2
- if /vt100/ || /Amiga/ || /paper/
- $bad = true
- break
- end
-end
-ok(!$bad)
-tmp.close
-
-File.unlink "while_tmp" or `/bin/rm -f "while_tmp"`
-ok(!File.exist?("while_tmp"))
-
-i = 0
-until i>4
- i+=1
-end
-ok(i>4)
-
-# exception handling
-check "exception";
-
-begin
- fail "this must be handled"
- ok(false)
-rescue
- ok(true)
-end
-
-$bad = true
-begin
- fail "this must be handled no.2"
-rescue
- if $bad
- $bad = false
- retry
- ok(false)
- end
-end
-ok(true)
-
-# exception in rescue clause
-$string = "this must be handled no.3"
-begin
- begin
- fail "exception in rescue clause"
- rescue
- fail $string
- end
- ok(false)
-rescue
- ok(true) if $! == $string
-end
-
-# exception in ensure clause
-begin
- begin
- fail "this must be handled no.4"
- ensure
- fail "exception in ensure clause"
- end
- ok(false)
-rescue
- ok(true)
-end
-
-$bad = true
-begin
- begin
- fail "this must be handled no.5"
- ensure
- $bad = false
- end
-rescue
-end
-ok(!$bad)
-
-$bad = true
-begin
- begin
- fail "this must be handled no.6"
- ensure
- $bad = false
- end
-rescue
-end
-ok(!$bad)
-
-$bad = true
-while true
- begin
- break
- ensure
- $bad = false
- end
-end
-ok(!$bad)
-
-ok(catch(:foo) {
- loop do
- loop do
- throw :foo, true
- break
- end
- break
- ok(false) # should no reach here
- end
- false
- })
-
-check "array"
-ok([1, 2] + [3, 4] == [1, 2, 3, 4])
-ok([1, 2] * 2 == [1, 2, 1, 2])
-ok([1, 2] * ":" == "1:2")
-
-ok([1, 2].hash == [1, 2].hash)
-
-ok([1,2,3] & [2,3,4] == [2,3])
-ok([1,2,3] | [2,3,4] == [1,2,3,4])
-ok([1,2,3] - [2,3] == [1])
-
-$x = [0, 1, 2, 3, 4, 5]
-ok($x[2] == 2)
-ok($x[1..3] == [1, 2, 3])
-ok($x[1,3] == [1, 2, 3])
-
-$x[0, 2] = 10
-ok($x[0] == 10 && $x[1] == 2)
-
-$x[0, 0] = -1
-ok($x[0] == -1 && $x[1] == 10)
-
-$x[-1, 1] = 20
-ok($x[-1] == 20 && $x.pop == 20)
-
-# array and/or
-ok(([1,2,3]&[2,4,6]) == [2])
-ok(([1,2,3]|[2,4,6]) == [1,2,3,4,6])
-
-# compact
-$x = [nil, 1, nil, nil, 5, nil, nil]
-$x.compact!
-ok($x == [1, 5])
-
-# uniq
-$x = [1, 1, 4, 2, 5, 4, 5, 1, 2]
-$x.uniq!
-ok($x == [1, 4, 2, 5])
-
-# empty?
-ok(!$x.empty?)
-$x = []
-ok($x.empty?)
-
-# sort
-$x = ["it", "came", "to", "pass", "that", "..."]
-$x = $x.sort.join(" ")
-ok($x == "... came it pass that to")
-$x = [2,5,3,1,7]
-$x.sort!{|a,b| a<=>b} # sort with condition
-ok($x == [1,2,3,5,7])
-$x.sort!{|a,b| b-a} # reverse sort
-ok($x == [7,5,3,2,1])
-
-# split test
-$x = "The Book of Mormon"
-ok($x.split(//).reverse!.join == $x.reverse)
-ok($x.reverse == $x.reverse!)
-ok("1 byte string".split(//).reverse.join(":") == "g:n:i:r:t:s: :e:t:y:b: :1")
-$x = "a b c d"
-ok($x.split == ['a', 'b', 'c', 'd'])
-ok($x.split(' ') == ['a', 'b', 'c', 'd'])
-ok(defined? "a".chomp)
-ok("abc".scan(/./) == ["a", "b", "c"])
-ok("1a2b3c".scan(/(\d.)/) == [["1a"], ["2b"], ["3c"]])
-# non-greedy match
-ok("a=12;b=22".scan(/(.*?)=(\d*);?/) == [["a", "12"], ["b", "22"]])
-
-$x = [1]
-ok(($x * 5).join(":") == '1:1:1:1:1')
-ok(($x * 1).join(":") == '1')
-ok(($x * 0).join(":") == '')
-
-*$x = 1..7
-ok($x.size == 7)
-ok($x == [1, 2, 3, 4, 5, 6, 7])
-
-check "hash"
-$x = {1=>2, 2=>4, 3=>6}
-$y = {1, 2, 2, 4, 3, 6}
-
-ok($x[1] == 2)
-
-ok(begin
- for k,v in $y
- fail if k*2 != v
- end
- true
- rescue
- false
- end)
-
-ok($x.length == 3)
-ok($x.has_key?(1))
-ok($x.has_value?(4))
-ok($x.indexes(2,3) == [4,6])
-ok($x == {1=>2, 2=>4, 3=>6})
-
-$z = $y.keys.join(":")
-ok($z == "1:2:3")
-
-$z = $y.values.join(":")
-ok($z == "2:4:6")
-ok($x == $y)
-
-$y.shift
-ok($y.length == 2)
-
-$z = [1,2]
-$y[$z] = 256
-ok($y[$z] == 256)
-
-check "iterator"
-
-ok(!iterator?)
-
-def ttt
- ok(iterator?)
-end
-ttt{}
-
-# yield at top level
-ok(!defined?(yield))
-
-$x = [1, 2, 3, 4]
-$y = []
-
-# iterator over array
-for i in $x
- $y.push i
-end
-ok($x == $y)
-
-# nested iterator
-def tt
- 1.upto(10) {|i|
- yield i
- }
-end
-
-tt{|i| break if i == 5}
-ok(i == 5)
-
-# iterator break/redo/next/retry
-unless defined? loop
- def loop
- while true
- yield
- end
- end
- ok(false)
-else
- ok(true)
-end
-
-done = true
-loop{
- break
- done = false
-}
-ok(done)
-
-done = false
-$bad = false
-loop {
- break if done
- done = true
- next
- $bad = true
-}
-ok(!$bad)
-
-done = false
-$bad = false
-loop {
- break if done
- done = true
- redo
- $bad = true
-}
-ok(!$bad)
-
-$x = []
-for i in 1 .. 7
- $x.push i
-end
-ok($x.size == 7)
-ok($x == [1, 2, 3, 4, 5, 6, 7])
-
-$done = false
-$x = []
-for i in 1 .. 7 # see how retry works in iterator loop
- if i == 4 and not $done
- $done = true
- retry
- end
- $x.push(i)
-end
-ok($x.size == 10)
-ok($x == [1, 2, 3, 1, 2, 3, 4, 5, 6, 7])
-
-# append method to built-in class
-class Array
- def iter_test1
- collect{|e| [e, yield(e)]}.sort{|a,b|a[1]<=>b[1]}
- end
- def iter_test2
- a = collect{|e| [e, yield(e)]}
- a.sort{|a,b|a[1]<=>b[1]}
- end
-end
-$x = [[1,2],[3,4],[5,6]]
-ok($x.iter_test1{|x|x} == $x.iter_test2{|x|x})
-
-check "bignum"
-def fact(n)
- return 1 if n == 0
- f = 1
- while n>0
- f *= n
- n -= 1
- end
- return f
-end
-fact(3)
-$x = fact(40)
-ok($x == $x)
-ok($x == fact(40))
-ok($x < $x+2)
-ok($x > $x-2)
-ok($x == 815915283247897734345611269596115894272000000000)
-ok($x != 815915283247897734345611269596115894272000000001)
-ok($x+1 == 815915283247897734345611269596115894272000000001)
-ok($x/fact(20) == 335367096786357081410764800000)
-$x = -$x
-ok($x == -815915283247897734345611269596115894272000000000)
-ok(2-(2**32) == -(2**32-2))
-ok(2**32 - 5 == (2**32-3)-2)
-
-$good = true;
-for i in 1000..1014
- $good = false if ((1<<i) != (2**i))
-end
-ok($good)
-
-$good = true;
-n1=1<<1000
-for i in 1000..1014
- $good = false if ((1<<i) != n1)
- n1 *= 2
-end
-ok($good)
-
-$good = true;
-n2=n1
-for i in 1..10
- n1 = n1 / 2
- n2 = n2 >> 1
- $good = false if (n1 != n2)
-end
-ok($good)
-
-$good = true;
-for i in 4000..4096
- n1 = 1 << i;
- if (n1**2-1) / (n1+1) != (n1-1)
- $good = false
- end
-end
-ok($good)
-
-check "string & char"
-
-ok("abcd" == "abcd")
-ok("abcd" =~ "abcd")
-ok("abcd" === "abcd")
-ok(("abc" =~ /^$/) == false)
-ok(("abc\n" =~ /^$/) == false)
-ok(("abc" =~ /^d*$/) == false)
-ok(("abc" =~ /d*$/) == 3)
-ok("" =~ /^$/)
-ok("\n" =~ /^$/)
-ok("a\n\n" =~ /^$/)
-"abcabc" =~ /.*a/
-ok($& == "abca")
-"abcabc" =~ /.*c/
-ok($& == "abcabc")
-"abcabc" =~ /.*?a/
-ok($& == "a")
-"abcabc" =~ /.*?c/
-ok($& == "abc")
-
-$foo = "abc"
-ok("#$foo = abc" == "abc = abc")
-ok("#{$foo} = abc" == "abc = abc")
-
-foo = "abc"
-ok("#{foo} = abc" == "abc = abc")
-
-ok('-' * 5 == '-----')
-ok('-' * 1 == '-')
-ok('-' * 0 == '')
-
-foo = '-'
-ok(foo * 5 == '-----')
-ok(foo * 1 == '-')
-ok(foo * 0 == '')
-
-$x = "a.gif"
-ok($x.sub(/.*\.([^\.]+)$/, '\1') == "gif")
-ok($x.sub(/.*\.([^\.]+)$/, 'b.\1') == "b.gif")
-ok($x.sub(/.*\.([^\.]+)$/, '\2') == "")
-ok($x.sub(/.*\.([^\.]+)$/, 'a\2b') == "ab")
-ok($x.sub(/.*\.([^\.]+)$/, '<\&>') == "<a.gif>")
-
-# character constants(assumes ASCII)
-ok("a"[0] == ?a)
-ok(?a == ?a)
-ok(?\C-a == 1)
-ok(?\M-a == 225)
-ok(?\M-\C-a == 129)
-ok("a".upcase![0] == ?A)
-ok("A".downcase![0] == ?a)
-ok("abc".tr!("a-z", "A-Z") == "ABC")
-ok("aabbcccc".tr_s!("a-z", "A-Z") == "ABC")
-ok("abc".tr!("0-9", "A-Z") == nil)
-ok("abcc".squeeze!("a-z") == "abc")
-ok("abcd".delete!("bc") == "ad")
-
-$x = "abcdef"
-$y = [ ?a, ?b, ?c, ?d, ?e, ?f ]
-$bad = false
-$x.each_byte {|i|
- if i != $y.shift
- $bad = true
- break
- end
-}
-ok(!$bad)
-
-check "asignment"
-a = nil
-ok(defined?(a))
-ok(a == nil)
-
-# multiple asignment
-a, b = 1, 2
-ok(a == 1 && b == 2)
-
-a, b = b, a
-ok(a == 2 && b == 1)
-
-a, = 1,2
-ok(a == 1)
-
-a, *b = 1, 2, 3
-ok(a == 1 && b == [2, 3])
-
-*a = 1, 2, 3
-ok(a == [1, 2, 3])
-
-*a = 1..3
-ok(a == [1, 2, 3])
-
-check "call"
-def aaa(a, b=100, *rest)
- res = [a, b]
- res += rest if rest
- return res
-end
-
-# not enough argument
-begin
- aaa() # need at least 1 arg
- ok(false)
-rescue
- ok(true)
-end
-
-begin
- aaa # no arg given (exception raised)
- ok(false)
-rescue
- ok(true)
-end
-
-ok(aaa(1) == [1, 100])
-ok(aaa(1, 2) == [1, 2])
-ok(aaa(1, 2, 3, 4) == [1, 2, 3, 4])
-ok(aaa(1, *[2, 3, 4]) == [1, 2, 3, 4])
-
-check "proc"
-$proc = proc{|i| i}
-ok($proc.call(2) == 2)
-ok($proc.call(3) == 3)
-
-$proc = proc{|i| i*2}
-ok($proc.call(2) == 4)
-ok($proc.call(3) == 6)
-
-proc{
- iii=5 # nested local variable
- $proc = proc{|i|
- iii = i
- }
- $proc2 = proc {
- $x = iii # nested variables shared by procs
- }
- # scope of nested variables
- ok(defined?(iii))
-}.call
-ok(!defined?(iii)) # out of scope
-
-$x=0
-$proc.call(5)
-$proc2.call
-ok($x == 5)
-
-if defined? Process.kill
- check "signal"
-
- $x = 0
- trap "SIGINT", proc{|sig| $x = 2}
- Process.kill "SIGINT", $$
- sleep 0.1
- ok($x == 2)
-
- trap "SIGINT", proc{fail "Interrupt"}
-
- x = false
- begin
- Process.kill "SIGINT", $$
- sleep 0.1
- rescue
- x = $!
- end
- ok(x && /Interrupt/ =~ x)
-end
-
-check "eval"
-ok(eval("") == nil)
-$bad=false
-eval 'while false; $bad = true; print "foo\n" end'
-ok(!$bad)
-
-ok(eval('TRUE'))
-ok(eval('true'))
-ok(!eval('NIL'))
-ok(!eval('nil'))
-ok(!eval('FALSE'))
-ok(!eval('false'))
-
-$foo = 'ok(true)'
-begin
- eval $foo
-rescue
- ok(false)
-end
-
-ok(eval("$foo") == 'ok(true)')
-ok(eval("true") == true)
-i = 5
-ok(eval("i == 5"))
-ok(eval("i") == 5)
-ok(eval("defined? i"))
-
-# eval with binding
-def test_ev
- local1 = "local1"
- lambda {
- local2 = "local2"
- return binding
- }.call
-end
-
-$x = test_ev
-ok(eval("local1", $x) == "local1") # normal local var
-ok(eval("local2", $x) == "local2") # nested local var
-$bad = true
-begin
- p eval("local1")
-rescue NameError # must raise error
- $bad = false
-end
-ok(!$bad)
-
-module EvTest
- EVTEST1 = 25
- evtest2 = 125
- $x = binding
-end
-ok(eval("EVTEST1", $x) == 25) # constant in module
-ok(eval("evtest2", $x) == 125) # local var in module
-$bad = true
-begin
- eval("EVTEST1")
-rescue NameError # must raise error
- $bad = false
-end
-ok(!$bad)
-
-x = proc{}
-eval "i4 = 1", x
-ok(eval("i4", x) == 1)
-x = proc{proc{}}.call
-eval "i4 = 22", x
-ok(eval("i4", x) == 22)
-$x = []
-x = proc{proc{}}.call
-eval "(0..9).each{|i5| $x[i5] = proc{i5*2}}", x
-ok($x[4].call == 8)
-
-x = binding
-eval "i = 1", x
-ok(eval("i", x) == 1)
-x = proc{binding}.call
-eval "i = 22", x
-ok(eval("i", x) == 22)
-$x = []
-x = proc{binding}.call
-eval "(0..9).each{|i5| $x[i5] = proc{i5*2}}", x
-ok($x[4].call == 8)
-
-proc {
- p = binding
- eval "foo11 = 1", p
- proc{foo11=22}.call
- ok(eval("foo11", p) == eval("foo11"))
- ok(eval("foo11") == 1)
-}.call
-
-p1 = proc{i6 = 0; proc{i6}}.call
-ok(p1.call == 0)
-eval "i6=5", p1
-ok(p1.call == 5)
-ok(!defined?(i6))
-
-p1 = proc{i6 = 0; proc{i6}}.call
-i6 = nil
-ok(p1.call == 0)
-eval "i6=1", p1
-ok(p1.call == 1)
-eval "i6=5", p1
-ok(p1.call == 5)
-ok(i6 == nil)
-
-check "system"
-ok(`echo foobar` == "foobar\n")
-ok(`./ruby -e 'print "foobar"'` == 'foobar')
-
-tmp = open("script_tmp", "w")
-tmp.print "print $zzz\n";
-tmp.close
-
-ok(`./ruby -s script_tmp -zzz` == 'true')
-ok(`./ruby -s script_tmp -zzz=555` == '555')
-
-tmp = open("script_tmp", "w")
-tmp.print "#! /usr/local/bin/ruby -s\n";
-tmp.print "print $zzz\n";
-tmp.close
-
-ok(`./ruby script_tmp -zzz=678` == '678')
-
-tmp = open("script_tmp", "w")
-tmp.print "this is a leading junk\n";
-tmp.print "#! /usr/local/bin/ruby -s\n";
-tmp.print "print $zzz\n";
-tmp.print "__END__\n";
-tmp.print "this is a trailing junk\n";
-tmp.close
-
-ok(`./ruby -x script_tmp` == 'nil')
-ok(`./ruby -x script_tmp -zzz=555` == '555')
-
-tmp = open("script_tmp", "w")
-for i in 1..5
- tmp.print i, "\n"
-end
-tmp.close
-
-`./ruby -i.bak -pe 'sub(/^[0-9]+$/){$&.to_i * 5}' script_tmp`
-done = true
-tmp = open("script_tmp", "r")
-while tmp.gets
- if $_.to_i % 5 != 0
- done = false
- break
- end
-end
-tmp.close
-ok(done)
-
-File.unlink "script_tmp" or `/bin/rm -f "script_tmp"`
-File.unlink "script_tmp.bak" or `/bin/rm -f "script_tmp.bak"`
-
-$bad = false
-for script in Dir["{lib,sample}/*.rb"]
- unless `./ruby -c #{script}`.chomp == "Syntax OK"
- $bad = true
- end
-end
-ok(!$bad)
-
-check "const"
-TEST1 = 1
-TEST2 = 2
-
-module Const
- TEST3 = 3
- TEST4 = 4
-end
-
-module Const2
- TEST3 = 6
- TEST4 = 8
-end
-
-include Const
-
-ok([TEST1,TEST2,TEST3,TEST4] == [1,2,3,4])
-
-include Const2
-STDERR.print "intentionally redefines TEST3, TEST4\n" if $VERBOSE
-ok([TEST1,TEST2,TEST3,TEST4] == [1,2,6,8])
-
-check "clone"
-foo = Object.new
-def foo.test
- "test"
-end
-bar = foo.clone
-def bar.test2
- "test2"
-end
-
-ok(bar.test2 == "test2")
-ok(bar.test == "test")
-ok(foo.test == "test")
-
-begin
- foo.test2
- ok false
-rescue
- ok true
-end
-
-check "marshal"
-$x = [1,2,3,[4,5,"foo"],{1=>"bar"},2.5,fact(30)]
-$y = Marshal.dump($x)
-ok($x == Marshal.load($y))
-
-check "pack"
-
-$format = "c2x5CCxsdila6";
-# Need the expression in here to force ary[5] to be numeric. This avoids
-# test2 failing because ary2 goes str->numeric->str and ary does not.
-ary = [1,-100,127,128,32767,987.654321098 / 100.0,12345,123456,"abcdef"]
-$x = ary.pack($format)
-ary2 = $x.unpack($format)
-
-ok(ary.length == ary2.length)
-ok(ary.join(':') == ary2.join(':'))
-ok($x =~ /def/)
-
-check "math"
-ok(Math.sqrt(4) == 2)
-
-include Math
-ok(sqrt(4) == 2)
-
-check "struct"
-struct_test = Struct.new("Test", :foo, :bar)
-ok(struct_test == Struct::Test)
-
-test = struct_test.new(1, 2)
-ok(test.foo == 1 && test.bar == 2)
-ok(test[0] == 1 && test[1] == 2)
-
-a, b = test
-ok(a == 1 && b == 2)
-
-test[0] = 22
-ok(test.foo == 22)
-
-test.bar = 47
-ok(test.bar == 47)
-
-check "variable"
-ok($$.instance_of?(Fixnum))
-
-# read-only variable
-begin
- $$ = 5
- ok false
-rescue
- ok true
-end
-
-foobar = "foobar"
-$_ = foobar
-ok($_ == foobar)
-
-check "trace"
-$x = 1234
-$y = 0
-trace_var :$x, proc{$y = $x}
-$x = 40414
-ok($y == $x)
-
-untrace_var :$x
-$x = 19660208
-ok($y != $x)
-
-trace_var :$x, proc{$x *= 2}
-$x = 5
-ok($x == 10)
-
-untrace_var :$x
-
-check "defined?"
-
-ok(defined?($x)) # global variable
-ok(defined?($x) == 'global-variable')# returns description
-
-foo=5
-ok(defined?(foo)) # local variable
-
-ok(defined?(Array)) # constant
-ok(defined?(Object.new)) # method
-ok(!defined?(Object.print)) # private method
-ok(defined?(1 == 2)) # operator expression
-
-def defined_test
- return !defined?(yield)
-end
-
-ok(defined_test) # not iterator
-ok(!defined_test{}) # called as iterator
-
-check "alias"
-class Alias0
- def foo; "foo" end
-end
-class Alias1<Alias0
- alias bar foo
- def foo; "foo+" + super end
-end
-class Alias2<Alias1
- alias baz foo
- undef foo
-end
-
-x = Alias2.new
-ok(x.bar == "foo")
-ok(x.baz == "foo+foo")
-
-# check for cache
-ok(x.baz == "foo+foo")
-
-class Alias3<Alias2
- def foo
- defined? super
- end
- def bar
- defined? super
- end
- def quux
- defined? super
- end
-end
-x = Alias3.new
-ok(!x.foo)
-ok(x.bar)
-ok(!x.quux)
-
-check "gc"
-begin
- 1.upto(10000) {
- tmp = [0,1,2,3,4,5,6,7,8,9]
- }
- tmp = nil
- ok true
-rescue
- ok false
-end
-
-if $failed > 0
- printf "test: %d failed %d\n", $ntest, $failed
-else
- printf "end of test(test: %d)\n", $ntest
-end
+# backward compatibility for chkbuild
+require_relative '../basictest/test'
diff --git a/sample/time.rb b/sample/time.rb
index f4f4ec4883..e16912052e 100644
--- a/sample/time.rb
+++ b/sample/time.rb
@@ -1,8 +1,12 @@
-#! /usr/local/bin/ruby
-cmd = ARGV.join(" ")
+#! /usr/bin/env ruby
+
b = Time.now
-system(cmd)
+system(*ARGV)
e = Time.now
-ut, st, cut, cst = Time.times
-total = (e - b).to_f
-printf STDERR, "%11.1f real %11.1f user %11.1f sys\n", total, cut, cst
+
+tms = Process.times
+real = e - b
+user = tms.cutime
+sys = tms.cstime
+
+STDERR.printf("%11.1f real %11.1f user %11.1f sys\n", real, user, sys)
diff --git a/sample/timeout.rb b/sample/timeout.rb
new file mode 100644
index 0000000000..ad4459aff0
--- /dev/null
+++ b/sample/timeout.rb
@@ -0,0 +1,42 @@
+require 'timeout'
+
+def progress(n = 5)
+ n.times {|i| print i; STDOUT.flush; sleep 1}
+ puts "never reach"
+end
+
+p Timeout.timeout(5) {
+ 45
+}
+p Timeout.timeout(5, Timeout::Error) {
+ 45
+}
+p Timeout.timeout(nil) {
+ 54
+}
+p Timeout.timeout(0) {
+ 54
+}
+begin
+ Timeout.timeout(5) {progress}
+rescue => e
+ puts e.message
+end
+begin
+ Timeout.timeout(3) {
+ begin
+ Timeout.timeout(5) {progress}
+ rescue => e
+ puts "never reach"
+ end
+ }
+rescue => e
+ puts e.message
+end
+class MyTimeout < StandardError
+end
+begin
+ Timeout.timeout(2, MyTimeout) {progress}
+rescue MyTimeout => e
+ puts e.message
+end
diff --git a/sample/tkbiff.rb b/sample/tkbiff.rb
deleted file mode 100644
index d2d7bf7beb..0000000000
--- a/sample/tkbiff.rb
+++ /dev/null
@@ -1,149 +0,0 @@
-#! /usr/local/bin/ruby
-
-if ARGV[0] != '-d'
- unless $DEBUG
- exit if fork
- end
-else
- ARGV.shift
-end
-
-if ARGV.length == 0
- if ENV['MAIL']
- $spool = ENV['MAIL']
- else
- $spool = '/usr/spool/mail/' + ENV['USER']
- end
-else
- $spool = ARGV[0]
-end
-
-require "parsedate"
-require "base64"
-
-include ParseDate
-
-class Mail
- def Mail.new(f)
- if !f.kind_of?(IO)
- f = open(f, "r")
- me = super
- f.close
- else
- me = super
- end
- return me
- end
-
- def initialize(f)
- @header = {}
- @body = []
- while f.gets()
- $_.chop!
- next if /^From / # skip From-line
- break if /^$/ # end of header
- if /^(\S+):\s*(.*)/
- @header[attr = $1.capitalize] = $2
- elsif attr
- sub(/^\s*/, '')
- @header[attr] += "\n" + $_
- end
- end
-
- return if ! $_
-
- while f.gets()
- break if /^From /
- @body.push($_)
- end
- end
-
- def header
- return @header
- end
-
- def body
- return @body
- end
-
-end
-
-require "tkscrollbox"
-
-$top = TkRoot.new
-$top.withdraw
-$list = TkScrollbox.new($top) {
- relief 'raised'
- width 80
- height 8
- setgrid 'yes'
- pack
-}
-TkButton.new($top) {
- text 'Dismiss'
- command proc {$top.withdraw}
- pack('fill'=>'both','expand'=>'yes')
-}
-$top.bind "Control-c", proc{exit}
-$top.bind "Control-q", proc{exit}
-$top.bind "space", proc{exit}
-
-$spool_size = 0
-$check_time = Time.now
-
-def check
- $check_time = Time.now
- size = File.size($spool)
- if size and size != $spool_size
- $spool_size = size
- pop_up if size > 0
- end
- Tk.after 5000, proc{check}
-end
-
-if defined? Thread
- Thread.start do
- loop do
- sleep 600
- if Time.now - $check_time > 200
- Tk.after 5000, proc{check}
- end
- end
- end
-end
-
-def pop_up
- outcount = 0;
- $list.delete 0, 'end'
- f = open($spool, "r")
- while !f.eof?
- mail = Mail.new(f)
- date, from, subj = mail.header['Date'], mail.header['From'], mail.header['Subject']
- next if !date
- y = m = d = 0
- y, m, d = parsedate(date) if date
- from = "sombody@somewhere" if ! from
- subj = "(nil)" if ! subj
- from = decode_b(from)
- subj = decode_b(subj)
- $list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj)
- outcount += 1
- end
- f.close
- if outcount == 0
- $list.insert 'end', "You have no mail."
- else
- $list.see 'end'
- end
- $top.deiconify
- Tk.after 2000, proc{$top.withdraw}
-end
-
-$list.insert 'end', "You have no mail."
-check
-Tk.after 2000, proc{$top.withdraw}
-begin
- Tk.mainloop
-rescue
- `echo #$! > /tmp/tkbiff`
-end
diff --git a/sample/tkbrowse.rb b/sample/tkbrowse.rb
deleted file mode 100644
index 882f0a489b..0000000000
--- a/sample/tkbrowse.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/local/bin/ruby
-#
-# This script generates a directory browser, which lists the working
-# directory and allows you to open files or subdirectories by
-# double-clicking.
-
-# Create a scrollbar on the right side of the main window and a listbox
-# on the left side.
-
-require "tkscrollbox"
-
-# The procedure below is invoked to open a browser on a given file; if the
-# file is a directory then another instance of this program is invoked; if
-# the file is a regular file then the Mx editor is invoked to display
-# the file.
-
-$dirlist = {}
-
-def browsedir (dir)
- if $dirlist.key? dir
- $dirlist[dir]
- else
- top = if $dirlist.size > 0 then TkToplevel.new else nil end
- list = TkScrollbox.new(top) {
- relief 'raised'
- width 20
- height 20
- setgrid 'yes'
- pack
- }
- list.insert 'end', *`ls #{dir}`.split
-
- # Set up bindings for the browser.
-
- list.focus
- list.bind "Control-q", proc{exit}
- list.bind "Control-c", proc{exit}
- list.bind "Control-p", proc{
- print "selection <", TkSelection.get, ">\n"
- }
-
- list.bind "Double-Button-1", proc{
- for i in TkSelection.get.split
- print "clicked ", i, "\n"
- browse dir, i
- end
- }
- $dirlist[dir] = list
- end
-end
-
-def browse (dir, file)
- file="#{dir}/#{file}"
- if File.directory? file
- browsedir(file)
- else
- if File.file? file
- if ENV['EDITOR']
- system format("%s %s&", ENV['EDITOR'], file)
- else
- system "xedit #{file}&"
- end
- else
- STDERR.print "\"#{file}\" isn't a directory or regular file"
- end
- end
-end
-
-# Fill the listbox with a list of all the files in the directory (run
-# the "ls" command to get that information).
-
-if ARGV.length>0
- dir = ARGV[0]
-else
- dir="."
-end
-
-browsedir(dir)
-Tk.mainloop
diff --git a/sample/tkdialog.rb b/sample/tkdialog.rb
deleted file mode 100644
index e83e16d0a8..0000000000
--- a/sample/tkdialog.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-#! /usr/local/bin/ruby
-require "tk"
-
-root = TkFrame.new
-top = TkFrame.new(root) {
- relief 'raised'
- border 1
-}
-msg = TkMessage.new(top) {
- text "File main.c hasn't been saved to disk since \
-it was last modified. What should I do?"
- justify 'center'
- aspect 200
- font '-Adobe-helvetica-medium-r-normal--*-240*'
- pack('padx'=>5, 'pady'=>5, 'expand'=>'yes')
-}
-top.pack('fill'=>'both')
-root.pack
-
-bot = TkFrame.new(root) {
- relief 'raised'
- border 1
-}
-
-TkFrame.new(bot) { |left|
- relief 'sunken'
- border 1
- pack('side'=>'left', 'expand'=>'yes', 'padx'=>10, 'pady'=> 10)
- TkButton.new(left) {
- text "Save File"
- command "quit 'save'"
- pack('expand'=>'yes','padx'=>6,'pady'=> 6)
- top.bind "Enter", proc{state 'active'}
- msg.bind "Enter", proc{state 'active'}
- bot.bind "Enter", proc{state 'active'}
- top.bind "Leave", proc{state 'normal'}
- msg.bind "Leave", proc{state 'normal'}
- bot.bind "Leave", proc{state 'normal'}
- Tk.root.bind "ButtonRelease-1", proc{quit 'save'}
- Tk.root.bind "Return", proc{quit 'save'}
- }
-}
-TkButton.new(bot) {
- text "Quit Anyway"
- command "quit 'quit'"
- pack('side'=>'left', 'expand'=>'yes', 'padx'=>10)
-}
-TkButton.new(bot) {
- text "Return To Editor"
- command "quit 'return'"
- pack('side'=>'left', 'expand'=>'yes', 'padx'=>10)
-}
-bot.pack
-root.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
-
-def quit(button)
- print "aaa\n"
- print "You pressed the \"#{button}\" button; bye-bye!\n"
- exit
-end
-
-Tk.mainloop
diff --git a/sample/tkfrom.rb b/sample/tkfrom.rb
deleted file mode 100644
index ba0e547799..0000000000
--- a/sample/tkfrom.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-#! /usr/local/bin/ruby
-
-require "parsedate"
-require "base64"
-
-include ParseDate
-
-class Mail
- def Mail.new(f)
- if !f.kind_of?(IO)
- f = open(f, "r")
- me = super(f)
- f.close
- else
- me = super
- end
- return me
- end
-
- def initialize(f)
- @header = {}
- @body = []
- while f.gets()
- $_.chop!
- next if /^From / # skip From-line
- break if /^$/ # end of header
- if /^(\S+):\s*(.*)/
- @header[attr = $1.capitalize] = $2
- elsif attr
- sub(/^\s*/, '')
- @header[attr] += "\n" + $_
- end
- end
-
- return if ! $_
-
- while f.gets()
- break if /^From /
- @body.push($_)
- end
- end
-
- def header
- return @header
- end
-
- def body
- return @body
- end
-
-end
-
-if ARGV.length == 0
- if ENV['MAIL']
- ARGV[0] = ENV['MAIL']
- elsif ENV['USER']
- ARGV[0] = '/usr/spool/mail/' + ENV['USER']
- elsif ENV['LOGNAME']
- ARGV[0] = '/usr/spool/mail/' + ENV['LOGNAME']
- end
-end
-
-require "tk"
-list = scroll = nil
-TkFrame.new{|f|
- list = TkListbox.new(f) {
- yscroll proc{|idx|
- scroll.set *idx
- }
- relief 'raised'
-# geometry "80x5"
- width 80
- height 5
- setgrid 'yes'
- pack('side'=>'left','fill'=>'both','expand'=>'yes')
- }
- scroll = TkScrollbar.new(f) {
- command proc{|idx|
- list.yview *idx
- }
- pack('side'=>'right','fill'=>'y')
- }
- pack
-}
-root = Tk.root
-TkButton.new(root) {
- text 'Dismiss'
- command proc {exit}
- pack('fill'=>'both','expand'=>'yes')
-}
-root.bind "Control-c", proc{exit}
-root.bind "Control-q", proc{exit}
-root.bind "space", proc{exit}
-
-$outcount = 0;
-for file in ARGV
- next if File.exist?(file)
- atime = File.atime(file)
- mtime = File.mtime(file)
- f = open(file, "r")
- begin
- until f.eof
- mail = Mail.new(f)
- date = mail.header['Date']
- next unless date
- from = mail.header['From']
- subj = mail.header['Subject']
- y = m = d = 0
- y, m, d = parsedate(date) if date
- from = "sombody@somewhere" unless from
- subj = "(nil)" unless subj
- from = decode_b(from)
- subj = decode_b(subj)
- list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj)
- $outcount += 1
- end
- ensure
- f.close
- File.utime(atime, mtime, file)
- list.see 'end'
- end
-end
-
-limit = 10000
-if $outcount == 0
- list.insert 'end', "You have no mail."
- limit = 2000
-end
-Tk.after limit, proc{
- exit
-}
-Tk.mainloop
diff --git a/sample/tkhello.rb b/sample/tkhello.rb
deleted file mode 100644
index 5188fe1c8c..0000000000
--- a/sample/tkhello.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require "tk"
-
-TkButton.new(nil,
- 'text' => 'hello',
- 'command' => proc{print "hello\n"}).pack('fill'=>'x')
-TkButton.new(nil,
- 'text' => 'quit',
- 'command' => 'exit').pack('fill'=>'x')
-
-Tk.mainloop
diff --git a/sample/tkline.rb b/sample/tkline.rb
deleted file mode 100644
index 2406b0749f..0000000000
--- a/sample/tkline.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-
-require "tkclass"
-
-$tkline_init = FALSE
-def start_random
- return if $tkline_init
- $tkline_init = TRUE
- if defined? Thread
- Thread.start do
- loop do
- sleep 2
- Line.new($c, rand(400), rand(200), rand(400), rand(200))
- end
- end
- end
-end
-
-$c = Canvas.new
-$c.pack
-$start_x = start_y = 0
-
-def do_press(x, y)
- $start_x = x
- $start_y = y
- $current_line = Line.new($c, x, y, x, y)
- start_random
-end
-def do_motion(x, y)
- if $current_line
- $current_line.coords $start_x, $start_y, x, y
- end
-end
-
-def do_release(x, y)
- if $current_line
- $current_line.coords $start_x, $start_y, x, y
- $current_line.fill 'black'
- $current_line = nil
- end
-end
-
-$c.bind("1", proc{|e| do_press e.x, e.y})
-$c.bind("B1-Motion", proc{|x, y| do_motion x, y}, "%x %y")
-$c.bind("ButtonRelease-1", proc{|x, y| do_release x, y}, "%x %y")
-Tk.mainloop
diff --git a/sample/tktimer.rb b/sample/tktimer.rb
deleted file mode 100644
index 34377e2f39..0000000000
--- a/sample/tktimer.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/local/bin/ruby
-# This script generates a counter with start and stop buttons.
-
-require "tk"
-$label = TkLabel.new {
- text '0.00'
- relief 'raised'
- width 10
- pack('side'=>'bottom', 'fill'=>'both')
-}
-
-TkButton.new {
- text 'Start'
- command proc {
- if $stopped
- $stopped = FALSE
- tick
- end
- }
- pack('side'=>'left','fill'=>'both','expand'=>'yes')
-}
-TkButton.new {
- text 'Stop'
- command proc{
- exit if $stopped
- $stopped = TRUE
- }
- pack('side'=>'right','fill'=>'both','expand'=>'yes')
-}
-
-$seconds=0
-$hundredths=0
-$stopped=TRUE
-
-def tick
- if $stopped then return end
- Tk.after 50, proc{tick}
- $hundredths+=5
- if $hundredths >= 100
- $hundredths=0
- $seconds+=1
- end
- $label.text format("%d.%02d", $seconds, $hundredths)
-end
-
-root = Tk.root
-root.bind "Control-c", proc{root.destroy}
-root.bind "Control-q", proc{root.destroy}
-Tk.root.focus
-Tk.mainloop
diff --git a/sample/trick2013/README.md b/sample/trick2013/README.md
new file mode 100644
index 0000000000..d6458c9d51
--- /dev/null
+++ b/sample/trick2013/README.md
@@ -0,0 +1,15 @@
+This directory contains the award-winning entries of
+the 1st Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2013).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* kinaba/entry.rb: "Best pangram" - Gold award
+* mame/entry.rb: "Most classic" - Bronze award
+* shinh/entry.rb: "Most Readable" - Silver award
+* yhara/entry.rb: "Worst abuse of constants" - Dishonorable mention
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2013
diff --git a/sample/trick2013/kinaba/authors.markdown b/sample/trick2013/kinaba/authors.markdown
new file mode 100644
index 0000000000..84c011ee05
--- /dev/null
+++ b/sample/trick2013/kinaba/authors.markdown
@@ -0,0 +1,3 @@
+* kinaba
+ * kiki@kmonos.net
+ * cctld: jp
diff --git a/sample/trick2013/kinaba/entry.rb b/sample/trick2013/kinaba/entry.rb
new file mode 100644
index 0000000000..8a3f855e46
--- /dev/null
+++ b/sample/trick2013/kinaba/entry.rb
@@ -0,0 +1 @@
+!@THEqQUICKbBROWNfFXjJMPSvVLAZYDGgkyz&[%r{\"}mosx,4>6]|?'while(putc 3_0-~$.+=9/2^5;)<18*7and:`#
diff --git a/sample/trick2013/kinaba/remarks.markdown b/sample/trick2013/kinaba/remarks.markdown
new file mode 100644
index 0000000000..dcdce7e9ae
--- /dev/null
+++ b/sample/trick2013/kinaba/remarks.markdown
@@ -0,0 +1,37 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.0.0p0 (2013-02-24) [i386-mswin32\_100]
+
+### Description
+
+The program prints each ASCII character from 0x20 ' ' to 0x7e '~' exactly once.
+
+The program contains each ASCII character from 0x20 ' ' to 0x7e '~' exactly once.
+
+### Internals
+
+The algorithm is the obvious loop "32.upto(126){|x| putc x}".
+
+It is not so hard to transform it to use each character *at most once*. The only slight difficulty comes from the constraint that we cannot "declare and then use" variables, because then the code will contain the variable name twice. This restriction is worked around by the $. global variable, the best friend of Ruby golfers.
+
+The relatively interesting part is to use all the characters *at least once*. Of course, this is easily accomplished by putting everything into a comment (i.e., #unused...) or to a string literal (%(unused...), note that normal string literals are forbidden since they use quotation marks twice). Hey, but that's not fun at all! I tried to minimize the escapeway.
+
+* "@THEqQUICKbBROWNfFXjJMPSvVLAZYDGgkyz". Trash box of unused alphabet. I wish I could have used "gkyz" somewhere else.
+
+* "%r{\"}mosx". Regex literal, with %-syntax. I don't even know what each m,o,s,x means...
+
+* "?'" Symbol literal. The quote characters (' " \`) are the first obstacle to this trial because they have to be used in pair usually. These are escaped as \" and ?' and :\`.
+
+* "4>6" "3\_0-~$.+=9/2^5" "18\*7". I had to consume many arithmetic operators +-\*/^~<>, but I only have ten literals 0 to 9 and $. as operands. Besides I have to express the print loop. This is an interesting puzzle.
+
+* "(putc ...;)<18*7". Trail semicolon doesn't change the value of the expression.
+
+### Limitation
+
+n/a.
diff --git a/sample/trick2013/mame/authors.markdown b/sample/trick2013/mame/authors.markdown
new file mode 100644
index 0000000000..e99cd71554
--- /dev/null
+++ b/sample/trick2013/mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@tsg.ne.jp
+ * cctld: jp
diff --git a/sample/trick2013/mame/entry.rb b/sample/trick2013/mame/entry.rb
new file mode 100644
index 0000000000..8abfc2be40
--- /dev/null
+++ b/sample/trick2013/mame/entry.rb
@@ -0,0 +1,97 @@
+ eval$C=%q(at_exit{
+ open("/dev/dsp","wb"){|g|h=[0]*80
+ $><<"\s"*18+"eval$C=%q(#$C);S=%:"
+ (S<<m=58).lines{|l|s=[128]*n=20E2
+ t=0; h.map!{|v|d=?!==l[
+ t]?1 :(l[
+ t]== ?#)?
+ 0*v= 6:03
+ (v<1 ?[]:
+ 0..n -1).
+ each {|z|
+ s[z] +=2*
+ M.sin(($*[0] ||1)
+ .to_f*M.sin(y= 40*(z+m)*2**
+ (t/12E0)/463)+ y)*(v-z*d/n)};
+ t+=1;v-d};m+= n;g.flush<<(s.
+ pack"C*"); puts(l)}}};M=
+ Math);S=%:
+
+ Jesu, Joy of Man's Desiring
+ Johann Sebastian Bach
+
+ #
+ | #
+ | #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | #
+ # # # #
+ | | | #
+ | #| #
+ # # | #
+ | | | #
+ | | # #
+ # # # #
+ | | # |
+ | | # #
+ # # # #
+ | | | #
+ | | #
+ # # # #
+ | | # |
+ | # # |
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | | # #
+ # # # #
+ | | | #
+ | # #
+ # # #
+ | | | #
+ | # | #
+ # # # #
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ | | | :
diff --git a/sample/trick2013/mame/remarks.markdown b/sample/trick2013/mame/remarks.markdown
new file mode 100644
index 0000000000..488681d88d
--- /dev/null
+++ b/sample/trick2013/mame/remarks.markdown
@@ -0,0 +1,47 @@
+### Remarks
+
+Run the program under a platform that `/dev/dsp` is available.
+For example, if you are using pulseaudio, use `padsp`:
+
+ padsp ruby entry.rb
+
+Please see Limitation if you want to run this program on os x.
+
+I confirmed the following platforms.
+
+* ruby 2.0.0p0 (2013-02-24 revision 39474) [x86\_64-linux]
+* ruby 1.9.3p194 (2012-04-20 revision 35410) [x86\_64-linux]
+* ruby 1.9.3p327 (2012-11-10 revision 37606) [x86\_64-darwin10.8.0]
+
+For those who are lazy, I'm attaching a screencast.
+
+### Description
+
+This program is a music-box quine.
+It prints itself with playing "Jesu, Joy of Man's Desiring".
+
+### Internal
+
+Like a real music box, this program consists of a mechanical part (code) and a piano roll.
+In the piano roll, `#` represents a pin that hits a note, and `|` represents a slur.
+The leftmost column corresponds 110Hz (low A).
+Every column corresponds a semitone higher than the left one.
+
+This program uses [the frequency modulation synthesis](https://en.wikipedia.org/wiki/Frequency_modulation_synthesis) to play the sound like a music-box.
+You can create a different-sounding tone by changing the parameter.
+For example, the following will play the sound like a harpsichord.
+
+ padsp ruby entry.rb 2.0
+
+Note that this program does *not* use an idiom to remove whitespace, such as `.split.join`. All newlines and spaces do not violate any of the Ruby syntax rules.
+
+### Limitation
+
+On os x, `/dev/dsp` is not available.
+You have to use sox by replacing the following part:
+
+ open("/dev/dsp","wb")
+
+with:
+
+ IO.popen("./pl","wb")
diff --git a/sample/trick2013/shinh/authors.markdown b/sample/trick2013/shinh/authors.markdown
new file mode 100644
index 0000000000..7ea2298a1a
--- /dev/null
+++ b/sample/trick2013/shinh/authors.markdown
@@ -0,0 +1,2 @@
+Shinichiro Hamaji
+Japan, .jp
diff --git a/sample/trick2013/shinh/entry.rb b/sample/trick2013/shinh/entry.rb
new file mode 100644
index 0000000000..cd4517358a
--- /dev/null
+++ b/sample/trick2013/shinh/entry.rb
@@ -0,0 +1,10 @@
+begin with an easy program.
+you should be able to write
+a program unless for you,
+program in ruby language is
+too difficult. At the end
+of your journey towards the
+ultimate program; you must
+be a part of a programming
+language. You will end if
+you != program
diff --git a/sample/trick2013/shinh/remarks.markdown b/sample/trick2013/shinh/remarks.markdown
new file mode 100644
index 0000000000..1cd190db9f
--- /dev/null
+++ b/sample/trick2013/shinh/remarks.markdown
@@ -0,0 +1,4 @@
+This program is a meaningless poem.
+This does nothing for you.
+Almost everything in this code is junk,
+but you and program would confuse you a bit.
diff --git a/sample/trick2013/yhara/authors.markdown b/sample/trick2013/yhara/authors.markdown
new file mode 100644
index 0000000000..c0adc2bfdb
--- /dev/null
+++ b/sample/trick2013/yhara/authors.markdown
@@ -0,0 +1,3 @@
+* Yutaka Hara
+ * yutaka.hara.gmail.com
+ * cctld: jp
diff --git a/sample/trick2013/yhara/entry.rb b/sample/trick2013/yhara/entry.rb
new file mode 100644
index 0000000000..3666f271fa
--- /dev/null
+++ b/sample/trick2013/yhara/entry.rb
@@ -0,0 +1,28 @@
+def _(&b)$><<->(x){x ? (String===x ?x.upcase:
+(Class===x ? x : x.class).name[$a?0:($a=5)]):
+" "}[ begin b[];rescue Exception;$!;end ] end
+
+_ { 1.tap }
+_ { method(:p).unbind }
+_ { eval "{ " }
+_ { Thread.current.join }
+_ { nil }
+_ { select }
+_ { ruby }
+_ { self.class }
+_ { Thread.current.group }
+_ { nil.to_h }
+_ { "\xFF".encode("big5") }
+_ { raise }
+_ { [0][1] }
+_ { Regexp.compile "*" }
+_ { RUBY_COPYRIGHT[32] }
+_ { binding }
+_ { :s.class.name[1] }
+_ { warn }
+_ { [a: :b][0] }
+_ { methods }
+_ { IO.class }
+_ { {}.fetch(0) }
+_ { open " " }
+_ { 1000000.chr }
diff --git a/sample/trick2013/yhara/remarks.en.markdown b/sample/trick2013/yhara/remarks.en.markdown
new file mode 100644
index 0000000000..bd821e882c
--- /dev/null
+++ b/sample/trick2013/yhara/remarks.en.markdown
@@ -0,0 +1,23 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.0.0p0 (2013-02-24 revision 39474) [x86\_64-darwin12.2.1]
+
+### Description
+
+It prints JUST ANOTHER RUBY HACKER
+
+### Internals
+
+This script uses characters in constants in Object class. It
+intentionally raises some exceptions. The second 'U' comes from
+RUBY\_COPYRIGHT, "Yukihiro Matsumoto".
+
+### Limitation
+
+This program does not work on JRuby because "return" does not raise an exception.
diff --git a/sample/trick2013/yhara/remarks.markdown b/sample/trick2013/yhara/remarks.markdown
new file mode 100644
index 0000000000..99cb4b557c
--- /dev/null
+++ b/sample/trick2013/yhara/remarks.markdown
@@ -0,0 +1,24 @@
+### Remarks
+
+引数なしで普通に実行してください。
+
+ ruby entry.rb
+
+以下の実装・プラットフォームで動作確認しています。
+
+* ruby 2.0.0p0 (2013-02-24 revision 39474) [x86\_64-darwin12.2.1]
+
+### Description
+
+JUST ANOTHER RUBY HACKERと表示します。
+
+### Internals
+
+Objectクラスの定数から文字を拾っています。
+そのために、意図的に例外を起こしています。
+「U」が一つしか見つからなかったので、もう一個はRUBY\_COPYRIGHTの
+「Yukihiro Matsumoto」から取っています。
+
+### Limitation
+
+JRubyはreturnがエラーにならなくて、動きませんでした。
diff --git a/sample/trick2015/README.md b/sample/trick2015/README.md
new file mode 100644
index 0000000000..6cae824796
--- /dev/null
+++ b/sample/trick2015/README.md
@@ -0,0 +1,16 @@
+This directory contains the award-winning entries of
+the 2nd Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2015).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* kinaba/entry.rb: "Best piphilology" - **Gold award**
+* ksk\_1/entry.rb: "Most unreadable ALU" - **Silver award**
+* monae/entry.rb: "Doubling amphisbaena award" - **Bronze award**
+* eregon/entry.rb: "Least general solver" - 4th prize
+* ksk\_2/entry.rb: "Most general solver" - 5th prize
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2015
diff --git a/sample/trick2015/eregon/authors.markdown b/sample/trick2015/eregon/authors.markdown
new file mode 100644
index 0000000000..68ca8cdfe0
--- /dev/null
+++ b/sample/trick2015/eregon/authors.markdown
@@ -0,0 +1,3 @@
+* Benoit Daloze (eregon)
+ * eregontp@gmail.com
+ * cctld: be
diff --git a/sample/trick2015/eregon/entry.rb b/sample/trick2015/eregon/entry.rb
new file mode 100644
index 0000000000..51d5c768b2
--- /dev/null
+++ b/sample/trick2015/eregon/entry.rb
@@ -0,0 +1,16 @@
+class String;def[]*a;$*<<a;b;end;end;
+_=0;z="C=Fiber;s=$*;a=*0..8;l=C.new{e
+xit},*a.product(a).select{|r,c|s[r][c
+]==0}."[1,9,_, _,_,8, _,_,5]+"map{|r,
+c|C.ne"[_,_,2, _,5,_, _,8,9]+"w{o=s[r
+][c];l"[8,_,6, 7,4,_, _,_,_]+"oop{(1.
+.9).map{|n|C.yield(s[r][c]=n)if a.non
+e?{|k|"[_,_,_, _,_,4, _,9,2]+"s[r][k]
+==n||s"[_,2,3, _,7,_, 8,1,_]+"[k][c]=
+=n||s["[5,6,_, 8,_,_, _,_,_]+"r-r%3+k
+%3][c-c%3+k/3]==n}};s[r][c]=o;C.yield
+}}},C."[_,_,_, _,2,7, 9,_,3]+"new{loo
+p{puts"[9,3,_, _,8,_, 1,_,_]+" s.map{
+|r|r*'"[2,_,_, 5,_,_, _,4,8]+" '}<<''
+;C.yield}};c=l[i=1];loop{c=l[i+=c.res
+ume ? 1:-1]}";eval z.tr ?\n,''
diff --git a/sample/trick2015/eregon/remarks.markdown b/sample/trick2015/eregon/remarks.markdown
new file mode 100644
index 0000000000..a56f24da71
--- /dev/null
+++ b/sample/trick2015/eregon/remarks.markdown
@@ -0,0 +1,70 @@
+### Remarks
+
+Just run it without arguments:
+
+ ruby entry.rb
+
+I confirmed the following implementations and platforms:
+
+* Linux:
+ * ruby 2.3.0dev (2015-10-30 trunk 52394) [x86\_64-linux]
+ * ruby 2.2.2p95 (2015-04-13 revision 50295) [x86\_64-linux]
+ * ruby 2.0.0p647 (2015-08-18) [x86\_64-linux]
+* Darwin:
+ * ruby 2.0.0p247 (2013-06-27 revision 41674) [x86\_64-darwin10.8.0]
+ * jruby 9.0.3.0 (2.2.2) 2015-10-21 633c9aa Java HotSpot(TM) 64-Bit Server VM 25.11-b03 on 1.8.0\_11-b12 +jit [darwin-x86\_64]
+ * rubinius 2.2.6.n74 (2.1.0 94b3a9b4 2014-03-15 JI) [x86\_64-darwin12.5.0]
+
+### Description
+
+This program shows all solutions of any sudoku puzzle.
+
+The embedded sudoku puzzle can be changed at wish.
+
+Giving an empty puzzle (all `0` or `_`), the program will print every possible completed sudoku puzzle.
+We do not however make any time guarantee on such behavior.
+
+The program is rather small for the task: the solver is actually 302 characters long,
+assuming the sudoku puzzle is in a variable `s` and encoded as an array of rows of numbers.
+
+### Internals
+
+* The program implements backtracking and keeps state in a very elegant way.
+* The whole program never goes deeper than 9 stack frames,
+ but yet can backtrack up to 81 levels!
+* The main loop of a program is a dance between cells.
+ On one end is the solutions, on the other the program ends.
+* The program only uses *infinite* loops and no `break`.
+* The program interleaves the creation of the solver and the puzzle.
+* The program is easy to deobfuscate but finding how it works will be more challenging.
+* The last line contains a smiley.
+
+The author likes good numbers:
+
+ $ wc entry.rb
+ 15 42 600
+
+The inspiration for this entry comes from:
+
+* A newspaper sudoku with multiple solutions
+* An inspiring paper: `Revisiting Coroutines`
+
+Various tricks used for brevity:
+
+* The method defined is one of the fews which may contain neither parenthesis nor spaces.
+* The program uses the return value of Fiber.yield without arguments.
+* `String#b` is used as a very short `self`.
+
+Design issues:
+
+* Since `return`-ing from a Fiber is not allowed, the programs must `exit`.
+* The program reveals that the cartesian product operator is still too long: `a.product(a)` while it could be `a*a`.
+
+Note:
+
+* In the original code, the last cell was: `C.new{loop{yield s; C.yield}}`,
+ implementing some sort of "forwarding coroutine".
+
+### Limitation
+
+* The program does not want any *argument* with you and will quit quietly if you try some.
diff --git a/sample/trick2015/kinaba/authors.markdown b/sample/trick2015/kinaba/authors.markdown
new file mode 100644
index 0000000000..23d4448cf3
--- /dev/null
+++ b/sample/trick2015/kinaba/authors.markdown
@@ -0,0 +1,4 @@
+* kinaba
+ * twitter.com/kinaba
+ * kiki@kmonos.net
+ * cctld: jp
diff --git a/sample/trick2015/kinaba/entry.rb b/sample/trick2015/kinaba/entry.rb
new file mode 100644
index 0000000000..2a75888ef5
--- /dev/null
+++ b/sample/trick2015/kinaba/entry.rb
@@ -0,0 +1,150 @@
+big, temp = Array 100000000**0x04e2
+srand big
+alias $curTerm $initTerm
+
+Numeric
+Interrupt
+
+big += big
+printout _pi_ finish if $never
+init ||= big
+$counter ||= 02
+
+private
+@mainloop
+while 0x00012345 >= $counter
+
+ Rational aprx = 3.141592r
+ numbase = 0o0000
+
+ @justonce
+ def increment
+ $initTerm ||= Integer srand * 0x00000002
+ srand $counter += 0x00000001
+
+ $noaction
+ Integer rand
+ $noaction
+ rand
+ rand
+ alias $line_cnt $.
+ end
+
+ @lets_just
+ @assume
+ diameter = 100000
+
+ @you
+ @then_have
+ permtr |= +3_14159
+
+ return if $nomeaning
+
+ @onlyuse
+ increment
+
+ beautiful computer action if $nothing
+ $sigmaTerm ||= init
+ $curTerm /= srand and init
+ pi, = Integer $sigmaTerm unless $nomean
+
+ iterator?
+ $counter += 1
+ atan real_one multiplied by__four unless
+ srand +big && $counter >> 0b1
+
+ Enumerable
+ String
+ Struct
+ Math
+ Complex
+ Comparable
+ TrueClass
+ Dir
+ Encoding
+ Proc
+ Hash
+ Method
+ Enumerator
+ Exception
+ Fiber
+ Errno
+ FalseClass
+ Mutex
+ NilClass
+ IO
+ GC
+
+ num = numbase |= srand
+
+ ENV
+ Float
+ MatchData
+ Proc
+ TracePoint
+ KeyError
+ p or
+ FileTest
+ File
+ EOFError
+ p
+ p
+ p
+ Binding
+ Time
+ Class
+
+ $sigmaTerm += $curTerm
+ puts a HelloWorld if $nomean
+ SystemExit
+
+ !LoadError
+ 31i
+ 3.1415e0
+ Array 14 + 3
+ IndexError
+ Range
+ false
+ 55555
+ NameError
+
+ Object
+ @ori
+ @ent
+ RubyVM
+
+ pi += 3_3_1_3_8
+
+ @use
+ @lots_of
+ @keywords
+ begin
+ self
+ $noaction
+ not $important
+ nil
+ __FILE__.object_id
+ rescue
+ next
+ redo if __LINE__
+ defined? +$nomeaning
+ $noaction
+ $nomean
+ break $never
+ ensure
+ class PiCompute
+ end
+ end
+
+ This code cannot _ be executed with typical style unless true
+ $curTerm *= num
+end
+
+@ll_set
+@re_U_ok
+
+$Enjoy
+$Superb
+$TRICK15 and a number
+
+print pi
diff --git a/sample/trick2015/kinaba/remarks.markdown b/sample/trick2015/kinaba/remarks.markdown
new file mode 100644
index 0000000000..6316c51fb6
--- /dev/null
+++ b/sample/trick2015/kinaba/remarks.markdown
@@ -0,0 +1,85 @@
+### Remarks
+
+Just run it with no argument:
+
+ $ ruby entry.rb
+
+I confirmed the following implementation/platform:
+
+- ruby 2.2.3p173 (2015-08-18 revision 51636) [x64-mingw32]
+
+
+### Description
+
+The program is a [Piphilology](https://en.wikipedia.org/wiki/Piphilology#Examples_in_English)
+suitable for Rubyists to memorize the digits of [Pi](https://en.wikipedia.org/wiki/Pi).
+
+In English, the poems for memorizing Pi start with a word consisting of 3-letters,
+1-letter, 4-letters, 1-letter, 5-letters, ... and so on. 10-letter words are used for the
+digit `0`. In Ruby, the lengths of the lexical tokens tell you the number.
+
+ $ ruby -r ripper -e \
+ 'puts Ripper.tokenize(STDIN).grep(/\S/).map{|t|t.size%10}.join' < entry.rb
+ 31415926535897932384626433832795028841971693993751058209749445923078164062862...
+
+The program also tells you the first 10000 digits of Pi, by running.
+
+ $ ruby entry.rb
+ 31415926535897932384626433832795028841971693993751058209749445923078164062862...
+
+
+### Internals
+
+Random notes on what you might think interesting:
+
+- The 10000 digits output of Pi is seriously computed with no cheets. It is calculated
+ by the formula `Pi/2 = 1 + 1/3 + 1/3*2/5 + 1/3*2/5*3/7 + 1/3*2/5*3/7*4/9 + ...`.
+
+- Lexical tokens are not just space-separated units. For instance, `a*b + cdef` does
+ not represent [3,1,4]; rather it's [1,1,1,1,4]. The token length
+ burden imposes hard constraints on what we can write.
+
+- That said, Pi is [believed](https://en.wikipedia.org/wiki/Normal_number) to contain
+ all digit sequences in it. If so, you can find any program inside Pi in theory.
+ In practice it isn't that easy particularly under the TRICK's 4096-char
+ limit rule. Suppose we want to embed `g += hij`. We have to find [1,2,3] from Pi.
+ Assuming uniform distribution, it occurs once in 1000 digits, which already consumes
+ 5000 chars in average to reach the point. We need some TRICK.
+
+ - `alias` of global variables was useful. It allows me to access the same value from
+ different token-length positions.
+
+ - `srand` was amazingly useful. Since it returns the "previous seed", the token-length
+ `5` essentially becomes a value-store that can be written without waiting for the
+ 1-letter token `=`.
+
+- Combination of these techniques leads to a carefully chosen 77-token Pi computation
+ program (quoted below), which is embeddable to the first 242 tokens of Pi.
+ Though the remaining 165 tokens are just no-op fillers, it's not so bad compared to
+ the 1000/3 = 333x blowup mentioned above.
+
+
+ big, temp = Array 100000000**0x04e2
+ srand big
+ alias $curTerm $initTerm
+ big += big
+ init ||= big
+ $counter ||= 02
+ while 0x00012345 >= $counter
+ numbase = 0x0000
+ $initTerm ||= Integer srand * 0x00000002
+ srand $counter += 0x00000001
+ $sigmaTerm ||= init
+ $curTerm /= srand
+ pi, = Integer $sigmaTerm
+ $counter += 1
+ srand +big && $counter >> 0b1
+ num = numbase |= srand
+ $sigmaTerm += $curTerm
+ pi += 3_3_1_3_8
+ $curTerm *= num
+ end
+ print pi
+
+- By the way, what's the blowup ratio of the final code, then?
+ It's 242/77, whose first three digits are, of course, 3.14.
diff --git a/sample/trick2015/ksk_1/authors.markdown b/sample/trick2015/ksk_1/authors.markdown
new file mode 100644
index 0000000000..bd6d41f6c7
--- /dev/null
+++ b/sample/trick2015/ksk_1/authors.markdown
@@ -0,0 +1,3 @@
+* Keisuke Nakano
+ * ksk@github, ksknac@twitter
+ * cctld: jp
diff --git a/sample/trick2015/ksk_1/entry.rb b/sample/trick2015/ksk_1/entry.rb
new file mode 100644
index 0000000000..64d3efd799
--- /dev/null
+++ b/sample/trick2015/ksk_1/entry.rb
@@ -0,0 +1 @@
+%%%while eval '_=%%r%%(.)...\1=%%=~[%%%%,,,,,%%%s ?=]*%%%%%%#"]*%%%%3x+1?%%'.% %%",%*p(_||=eval($**%%%))
diff --git a/sample/trick2015/ksk_1/remarks.markdown b/sample/trick2015/ksk_1/remarks.markdown
new file mode 100644
index 0000000000..a0b8bbcdcc
--- /dev/null
+++ b/sample/trick2015/ksk_1/remarks.markdown
@@ -0,0 +1,120 @@
+### Remarks
+
+The program is run with a positive integer as an argument, e.g.,
+```shell
+ ruby entry.rb 27
+```
+It has been confirmed to be run on
+```
+ ruby 1.9.3p385 (2013-02-06 revision 39114) [x86_64-darwin11.4.2]
+ ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin13]
+ ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
+```
+
+
+### Description
+
+The program prints a Collatz sequence started with a given number,
+that is, it repeatedly outputs numbers obtained by applying the
+following Half-Or-Triple-Plus-One (HOTPO) process to the previous
+number:
+
+> If the number is even, divide it by two, otherwise, multiply it by three and add one.
+
+until the number becomes 1. Collatz conjectured that no matter from
+the process starts it always eventually terminates. This is still
+an open problem, hence the program may not terminate for some
+numbers. It is known that there is no such exception below 2<sup>60</sup>.
+
+
+### Internals
+
+The source code does not contain either conditional branch or arithmetic operation.
+The trick shall be revealed step by step.
+
+First, the code is obfuscated by using `%`-notations,
+`*`(String#join), `%`-formatting, restructuring, and so on.
+Here is an equivalent readable program:
+```ruby
+n = ARGV[0].to_i
+begin
+ # do nothing
+end while begin
+ puts n
+ n = (/(.)...\1=/ =~ eval('[",,,,,"'+ '",'*n + ' ?=].join#"].join("3x+1?")'))
+end
+```
+The line
+```ruby
+ n = (/(.)...\1=/ =~ eval('[",,,,,"'+ '",'*n + ' ?=].join#"].join("3x+1?")'))
+```
+performs the HOTPO process.
+The `eval` expression here returns a string as explained in detail later.
+Since *regex*`=~`*str* returns index of first match of *regex* in *str*,
+the regular expression `(.)...\1` must match the string
+at index `n/2` if `n` is even and
+at `3*n+1` if `n` is odd greater than 1.
+The match must fail in the case of `n = 1` so that it returns `nil`.
+
+The key of simulating the even-odd conditional branch on `n` in the
+HOTPO process is an `n`-length sequence of the incomplete fragments
+`",` where the double-quote `"` changes its role of opening/closing
+string literals alternately. If `n` is even, the string in the `eval`
+expression is evaluated as
+```ruby
+ => '[",,,,,"'+ '",' + '",' + '",' + ... + '",' + ' ?=].join#...'
+ => '[",,,,,"",",",...", ?=].join#...'
+```
+where the last double-quote `"` is closing hence the code after `#` is
+ignored as comments. Note that `"ab""cd"` in Ruby is equivalent to
+`"abcd"`. Therefore the `eval` expression is evaluated into
+```ruby
+ ",,,,,...,="
+```
+where the number of commas is `5+n/2`.
+As a result, the regular expression `(.)...\1=` matches `,,,,,=`
+at the end of string, that is, at index `5+n/2-5 = n/2`.
+
+If `n` is odd, the string in the `eval` expression is evaluated as
+```ruby
+ => '[",,,,,"'+ '",' + '",' + '",' + '",' + ... + '",' + ' ?=].join#"].join("3x+1?")'
+ => '[",,,,,"",",",",...,", ?=].join#"].join("3x+1?")'
+```
+where the last element in the array is `", ?=].join#"`. Threfore the
+`eval` expression is evaluated into
+```
+ ",,,,,,3x+1?,3x+1?,...,3x+1?, ?=].join#"
+```
+where the number of `,3x+1?` is `(n-1)/2`. As a result, the regular
+expression `(.)...\1=` matches `?, ?=` at the almost end of string,
+that is, at index `5+(n-1)/2*6-1 = 3n+1`, provided that the match
+fails in the case of `n = 1` because the symbol `?` occurs only once
+in the string.
+
+One may notice that the string `3x+1` in the code could be other
+four-character words. I chose it because the Collatz conjecture is
+also called the 3x+1 problem.
+
+
+### Variant
+
+The Collatz conjecture is equivalently stated as,
+
+> no matter from the HOTPO process starts, it always eventually
+ reaches the cycle of 4, 2, and 1
+
+instead of termination of the process at 1. This alternative
+statement makes the program simpler because we do not have to care the
+case of n = 1. It can be obtained by replacing the regular expression
+is simply `/=/` and removing a padding `",,,,,"`. The program no
+longer terminates, though.
+
+
+### Limitation
+
+The implementation requires to manipulate long strings even for some
+small starting numbers. For example, starting from 1,819, the number
+will reach up to 1,276,936 which causes SystemStackError on Ruby 1.9.3.
+The program works on Ruby 2.0.0 and 2.2.3, though.
+
+
diff --git a/sample/trick2015/ksk_2/abnormal.cnf b/sample/trick2015/ksk_2/abnormal.cnf
new file mode 100644
index 0000000000..084303f041
--- /dev/null
+++ b/sample/trick2015/ksk_2/abnormal.cnf
@@ -0,0 +1,6 @@
+c Example CNF format file
+c
+p cnf 4 3
+1 3 -4 0
+4 0 2
+-3
diff --git a/sample/trick2015/ksk_2/authors.markdown b/sample/trick2015/ksk_2/authors.markdown
new file mode 100644
index 0000000000..bd6d41f6c7
--- /dev/null
+++ b/sample/trick2015/ksk_2/authors.markdown
@@ -0,0 +1,3 @@
+* Keisuke Nakano
+ * ksk@github, ksknac@twitter
+ * cctld: jp
diff --git a/sample/trick2015/ksk_2/entry.rb b/sample/trick2015/ksk_2/entry.rb
new file mode 100644
index 0000000000..55b488e3a8
--- /dev/null
+++ b/sample/trick2015/ksk_2/entry.rb
@@ -0,0 +1 @@
+_='s %sSATISFIABLE';puts eval$<.read.gsub(/.*p.*?(\d+).*?$|\d+/m){$1?%w[?-* +'=-'=~/#{'(-?)'* }-*=(?=]*$1:$&>?0?"\\#$&$|":'$)(?='}+')/x?[_%p%i=0,[*$~].map{|x|x>?-?:v:eval(x+?1)*i-=1}*" "]:_%:UN'
diff --git a/sample/trick2015/ksk_2/quinn.cnf b/sample/trick2015/ksk_2/quinn.cnf
new file mode 100644
index 0000000000..556a9b33f5
--- /dev/null
+++ b/sample/trick2015/ksk_2/quinn.cnf
@@ -0,0 +1,21 @@
+c quinn.cnf
+c
+p cnf 16 18
+ 1 2 0
+ -2 -4 0
+ 3 4 0
+ -4 -5 0
+ 5 -6 0
+ 6 -7 0
+ 6 7 0
+ 7 -16 0
+ 8 -9 0
+ -8 -14 0
+ 9 10 0
+ 9 -10 0
+-10 -11 0
+ 10 12 0
+ 11 12 0
+ 13 14 0
+ 14 -15 0
+ 15 16 0 \ No newline at end of file
diff --git a/sample/trick2015/ksk_2/remarks.markdown b/sample/trick2015/ksk_2/remarks.markdown
new file mode 100644
index 0000000000..187a6804d2
--- /dev/null
+++ b/sample/trick2015/ksk_2/remarks.markdown
@@ -0,0 +1,204 @@
+### Remarks
+
+The program is run with a data file from the standard input, e.g.,
+```shell
+ ruby entry.rb < data
+```
+where ``<`` can be omitted. The data file must be in the DIMACS CNF
+format (see Description for detail). It has been confirmed to be run on
+```
+ ruby 1.9.3p385 (2013-02-06 revision 39114) [x86_64-darwin11.4.2]
+ ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin13]
+ ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
+```
+For particular inputs, the program works differently on these environments
+(see Limitation).
+
+
+### Description
+
+The program is a very small SAT solver with 194 bytes making use of a
+powerful feature of Regexp matching in Ruby. It receives a data file
+from the standard input in the DIMACS CNF that is a standard format
+for inputs of SAT solvers. For example, the text in the DIMACS CNF
+format,
+```
+c
+c This is a sample input file.
+c
+p cnf 3 5
+ 1 -2 3 0
+-1 2 0
+-2 -3 0
+ 1 2 -3 0
+ 1 3 0
+```
+corresponds to a propositional formula in conjunctive normal form,
+
+ (L1 &or; &not;L2 &or; L3) &and;
+ (&not;L1 &or; L2) &and;
+ (&not;L2 &or; &not;L3) &and;
+ (L1 &or; L2 &or; &not;L3) &and;
+ (L1 &or; L3).
+
+In the DIMACS CNF format, the lines starting with ``c`` are comments
+that are allowed only before the line ``p cnf ...``. The line ``p cnf
+3 5`` represents that the problem is given in conjunctive normal form
+with 3 variables (L1,L2,and L3) and 5 clauses. A clause is given by a
+sequence of the indices of positive literals and the negative indices
+of negative literals. Each clause is terminated by ``0``. For the
+input above, the program outputs
+```
+s SATISFIABLE
+v 1 2 -3
+```
+because the formula is satisfiable by L1=true, L2=true, and L3=false.
+If an unsatisfiable formula is given, the program should output
+```
+s UNSATISFIABLE
+```
+This specification is common in most exiting SAT solvers and required
+for entries of [SAT competition](http://www.satcompetition.org/).
+
+The program is very small with no other external libraries thanks to
+the wealth of string manipulations in Ruby. It is much smaller than
+existing small SAT solvers like [minisat](http://minisat.se/) and
+[picosat](http://fmv.jku.at/picosat/)!
+
+
+### Internals
+
+The basic idea of the program is a translation from DIMACS CNF format
+into Ruby. For example, the data file above is translated into a
+``Regexp`` matching expression equivalent to
+```ruby
+ '---=-' =~
+ /(-?)(-?)(-?)-*=(?=\1$|-\2$|\3$|$)(?=-\1$|\2$|$)(?=-\2$|-\3$|$)(?=\1$|\2$|-\3$|$)(?=\1$|\3$|$)(?=)/
+```
+that returns ``MatchData`` if the formula is satisfiable and otherwise
+returns ``nil``. The beginning of regular expression
+``(-?)(-?)(-?)-*=`` matches a string ``"---="`` so that each
+capturing pattern ``(-?)`` matches either ``"-"`` or `""`, which
+corresponds to an assignment of true or false, respectively, for a
+propositional variable. Each clause is translated into positive
+lookahead assertion like ``(?=\1$|-\2$|\3$|$)`` that matches
+``"-"`` only when ``\1`` holds ``"-"``, ``\2`` holds ``""``, or ``\3``
+holds ``"-"``. This exactly corresponds to the condition for
+L1&or;&not;L2&or;L3 to be true. The last case ``|$`` never matches
+``"-"`` but it is required for making the translation simple.
+The last meaningless positive lookahead assertion ``(?=)`` is added
+for a similar reason. This translation is based on
+[Abigail's idea](http://perl.plover.com/NPC/NPC-3SAT.html) where a
+3SAT formula is translated into a similar Perl regular expression.
+The differences are the submitted Ruby program translates directly
+from the DIMACS CNF format and tries to make the code shorter by using
+lookahead assertion which can also make matching more faster.
+
+Thanks to the ``x`` option for regular expression, the input above is
+simply translated into
+```ruby
+ ?-*3+'=-'=~/#{'(-?)'*3}-*=(?=
+ \1$| -\2$| \3$| $)(?=
+ -\1$| \2$| $)(?=
+ -\2$| -\3$| $)(?=
+ \1$| \2$| -\3$| $)(?=
+ \1$| \3$| $)(?=
+ )/x
+```
+which has a structure similar to the DIMACS CNF format.
+
+The part of formatting outputs in the program is obfuscated as an
+inevitable result of 'golfing' the original program
+```ruby
+ if ...the matching expression above... then
+ puts 's SATISFIABLE'
+ puts 'v '+$~[1..-1].map.with_index{|x,i|
+ if x == '-' then
+ i+1
+ else
+ ['-',i+1].join
+ end
+ }.join(' ')
+ else
+ puts 's UNSATISFIABLE'
+ end
+```
+In the satisfiable case, the MatchData ``$~`` obtained by the regular expression
+has the form of
+```
+ #<MatchData "---=" 1:"-" 2:"-" 3:"">
+```
+which should be translated into a string ``1 2 -3``. The golfed code simply
+does it by `eval(x+?1)*i-=1` where ``x`` is matched string ``"x"`` or ``""``
+and ``i`` be a negated index.
+
+
+### Data files
+
+The submission includes some input files in the DIMACS CNF format for
+testing the program.
+
+* [sample.cnf](sample.cnf) : an example shown above.
+
+* [unsat.cnf](unsat.cnf) : an example of an unsatisfiable formula.
+
+* [quinn.cnf](quinn.cnf) : an example from Quinn's text, 16 variables and 18 clauses
+ (available from [http://people.sc.fsu.edu/~jburkardt/data/cnf/cnf.html])
+
+* [abnormal.cnf](abnormal.cnf) : an example from [the unofficial manual of the DIMACS challenge](http://www.domagoj-babic.com/uploads/ResearchProjects/Spear/dimacs-cnf.pdf)
+ where a single clause may be on multiple lines.
+
+* [uf20-01.cnf](uf20-01.cnf) : an example, with 20 variables and 91 clauses, from [SATLIB benchmark suite](http://www.cs.ubc.ca/~hoos/SATLIB/benchm.html). The last two lines are removed from the original because they are illegal in the DIMACS CNF format (all examples in 'Uniform Random-3-SAT' of the linked page need this modification).
+
+
+### Limitation
+
+The program may not work when the number of variables exceeds 99
+because ``\nnn`` in regular expression with number ``nnn`` does not
+always represent backreference but octal notation of characters. For
+example,
+```ruby
+ /#{"(x)"*999}:\502/=~"x"*999+":x"
+ /#{"(x)"*999}:\661/=~"x"*999+":x"
+ /#{"(x)"*999}:\775/=~"x"*999+":x"
+```
+fail due to the syntax error (invalid escape), while
+```ruby
+ /#{"(x)"*999}:\508/=~"x"*999+":x"
+ /#{"(x)"*999}:\691/=~"x"*999+":x"
+ /#{"(x)"*999}:\785/=~"x"*999+":x"
+```
+succeed (to return 0) because 508, 691, and 785 are not in octal notation.
+Since Ruby 1.9.3 incorrectly returns ``nil`` instead of terminating
+with the error for
+```ruby
+ /#{"(x)"*999}:\201/=~"x"*999+":x"
+ /#{"(x)"*999}:\325/=~"x"*999+":x"
+```
+the present SAT solver may unexpectedly return "UNSATISFIABLE" even
+for satisfiable inputs. This happens when the number is in octal
+notation starting with either 2 or 3.
+
+In the case of the number starting with 1, the code like the above
+does work on all versions of Ruby I tried. For example,
+```ruby
+ /#{"(x)"*999}:\101/=~"x"*999+":x"
+ /#{"(x)"*999}:\177/=~"x"*999+":x"
+```
+succeed (to return 0). Interestingly,
+```ruby
+ /#{"(x)"*999}:\101/=~"x"*999+":\101"
+ /#{"(x)"*999}:\177/=~"x"*999+":\177"
+```
+return ``nil``, while
+```ruby
+ /:\101/=~":\101"
+ /:\177/=~":\177"
+```
+succeed to return 0. The meaning of ``\1nn`` in regular expression
+seems to depend on the existence of capturing expressions.
+
+In spite of these Ruby's behaviors, we have a good news! The present
+SAT solver does not suffer from the issues because the program cannot
+return solutions in practical time for inputs with variables more than
+40.
diff --git a/sample/trick2015/ksk_2/sample.cnf b/sample/trick2015/ksk_2/sample.cnf
new file mode 100644
index 0000000000..295f81c942
--- /dev/null
+++ b/sample/trick2015/ksk_2/sample.cnf
@@ -0,0 +1,9 @@
+c
+c This is a sample input file.
+c
+p cnf 3 5
+ 1 -2 3 0
+-1 2 0
+-2 -3 0
+ 1 2 -3 0
+ 1 3 0
diff --git a/sample/trick2015/ksk_2/uf20-01.cnf b/sample/trick2015/ksk_2/uf20-01.cnf
new file mode 100644
index 0000000000..0d9503c451
--- /dev/null
+++ b/sample/trick2015/ksk_2/uf20-01.cnf
@@ -0,0 +1,99 @@
+c This Formular is generated by mcnf
+c
+c horn? no
+c forced? no
+c mixed sat? no
+c clause length = 3
+c
+p cnf 20 91
+ 4 -18 19 0
+3 18 -5 0
+-5 -8 -15 0
+-20 7 -16 0
+10 -13 -7 0
+-12 -9 17 0
+17 19 5 0
+-16 9 15 0
+11 -5 -14 0
+18 -10 13 0
+-3 11 12 0
+-6 -17 -8 0
+-18 14 1 0
+-19 -15 10 0
+12 18 -19 0
+-8 4 7 0
+-8 -9 4 0
+7 17 -15 0
+12 -7 -14 0
+-10 -11 8 0
+2 -15 -11 0
+9 6 1 0
+-11 20 -17 0
+9 -15 13 0
+12 -7 -17 0
+-18 -2 20 0
+20 12 4 0
+19 11 14 0
+-16 18 -4 0
+-1 -17 -19 0
+-13 15 10 0
+-12 -14 -13 0
+12 -14 -7 0
+-7 16 10 0
+6 10 7 0
+20 14 -16 0
+-19 17 11 0
+-7 1 -20 0
+-5 12 15 0
+-4 -9 -13 0
+12 -11 -7 0
+-5 19 -8 0
+1 16 17 0
+20 -14 -15 0
+13 -4 10 0
+14 7 10 0
+-5 9 20 0
+10 1 -19 0
+-16 -15 -1 0
+16 3 -11 0
+-15 -10 4 0
+4 -15 -3 0
+-10 -16 11 0
+-8 12 -5 0
+14 -6 12 0
+1 6 11 0
+-13 -5 -1 0
+-7 -2 12 0
+1 -20 19 0
+-2 -13 -8 0
+15 18 4 0
+-11 14 9 0
+-6 -15 -2 0
+5 -12 -15 0
+-6 17 5 0
+-13 5 -19 0
+20 -1 14 0
+9 -17 15 0
+-5 19 -18 0
+-12 8 -10 0
+-18 14 -4 0
+15 -9 13 0
+9 -5 -1 0
+10 -19 -14 0
+20 9 4 0
+-9 -2 19 0
+-5 13 -17 0
+2 -10 -18 0
+-18 3 11 0
+7 -9 17 0
+-15 -6 -3 0
+-2 3 -13 0
+12 3 -2 0
+-2 -3 17 0
+20 -15 -16 0
+-5 -17 -19 0
+-20 -18 11 0
+-9 1 -5 0
+-19 9 17 0
+12 -2 17 0
+4 -16 -5 0
diff --git a/sample/trick2015/ksk_2/unsat.cnf b/sample/trick2015/ksk_2/unsat.cnf
new file mode 100644
index 0000000000..7283933a9f
--- /dev/null
+++ b/sample/trick2015/ksk_2/unsat.cnf
@@ -0,0 +1,11 @@
+c
+c This is a sample input file.
+c (unsatisfiable)
+c
+p cnf 3 5
+1 -2 3 0
+-1 2 0
+-2 -3 0
+1 2 -3 0
+1 3 0
+-1 -2 3 0
diff --git a/sample/trick2015/monae/authors.markdown b/sample/trick2015/monae/authors.markdown
new file mode 100644
index 0000000000..58d63b16ac
--- /dev/null
+++ b/sample/trick2015/monae/authors.markdown
@@ -0,0 +1 @@
+monae (@monae, jp)
diff --git a/sample/trick2015/monae/entry.rb b/sample/trick2015/monae/entry.rb
new file mode 100644
index 0000000000..6961df21cd
--- /dev/null
+++ b/sample/trick2015/monae/entry.rb
@@ -0,0 +1,26 @@
+ ;; ;; ;; ;;
+ ;; ;; ;; ;;
+ ;;eval$s =%q[i=1#
+ eval(%q[ xxxxxxxx
+ xx xxxx xx xx xxxx xx
+ xx xxxx xx xx xxxx xx
+ xxxxxxxx xxxxxxxx
+ xxxxxxxx xxxxxxxx
+ xx xx xxxxxxxxxx xx xxxxxxxx
+ j, t, p=0,[?;]," ev al$s=%qx
+[#$s]".split*"";i,j,t=i-j,i+j,(x
+ [b=?\s]*j.abs+t).map{|s|r=t.shix
+ft ||b;r.gsub!(?;){p.slice!0}if $x
+ f| |=p>p=p.center(i*i+j*j,?;);r ,x
+ s=[s,r]if(i*j<0);(b*i.abs+s).ljx
+ ust(r.size).gsub(b){r[$`.size]|x
+ |b}}unti l$ f;puts(t)# xx xx
+ xxxxxxxx xx xxxxxxxxxx xx xx
+xxxxxxxx xxxxxxxx
+ xxxxxxxx xxxxxxxx
+xx xxxx xx xx xxxx xx
+ xx xxxx xx xx xxxx xx
+ xxxxxxxx x].gsub\
+ /x.*|\s/ ,"")#];;
+ ;; ;; ;; ;;
+ ;; ;; ;; ;;
diff --git a/sample/trick2015/monae/remarks.markdown b/sample/trick2015/monae/remarks.markdown
new file mode 100644
index 0000000000..48be61bd12
--- /dev/null
+++ b/sample/trick2015/monae/remarks.markdown
@@ -0,0 +1,25 @@
+
+# How to run
+
+```
+ruby entry.rb
+ruby entry.rb | ruby
+ruby entry.rb | ruby | ruby
+ruby entry.rb | ruby | ruby | ruby
+...
+```
+
+Confirmed on the following environments:
+- ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]
+- ruby 2.0.0p353 (2013-11-22) [i386-mingw32]
+
+# Description
+
+A simple quine which prints itself twice
+on a slightly complex base.
+
+> geminum caput amphisbaenae, hoc est et a cauda,
+> tamquam parum esset uno ore fundi venenum.
+> aliis squamas esse, aliis picturas, omnibus exitiale virus.
+>
+> &mdash; <cite>GAIUS PLINIUS SECUNDUS, Naturalis Historia 8.85.1</cite>
diff --git a/sample/trick2018/01-kinaba/authors.markdown b/sample/trick2018/01-kinaba/authors.markdown
new file mode 100644
index 0000000000..d0df0b379d
--- /dev/null
+++ b/sample/trick2018/01-kinaba/authors.markdown
@@ -0,0 +1,3 @@
+* kinaba
+ * twitter.com/kinaba
+ * cctld: jp
diff --git a/sample/trick2018/01-kinaba/entry.rb b/sample/trick2018/01-kinaba/entry.rb
new file mode 100644
index 0000000000..eb8284d5ab
--- /dev/null
+++ b/sample/trick2018/01-kinaba/entry.rb
@@ -0,0 +1,8 @@
+alias BEGIN for unless def class
+super true or return defined? next
+break while begin undef do end
+rescue then retry else undef module
+nil ensure case if yield __LINE__
+self and redo elsif not __FILE__
+alias END in end when __ENCODING__
+end until false end
diff --git a/sample/trick2018/01-kinaba/remarks.markdown b/sample/trick2018/01-kinaba/remarks.markdown
new file mode 100644
index 0000000000..d0b9fdffde
--- /dev/null
+++ b/sample/trick2018/01-kinaba/remarks.markdown
@@ -0,0 +1,55 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+(Anyway it is just a no-op program. The above command only verifies
+that entry.rb is a valid Ruby program.)
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.5.0p0 (2017-12-25 revision 61468) [x64-mingw32]
+
+### Description
+
+First, look at
+
+https://docs.ruby-lang.org/ja/latest/doc/spec=2flexical.html#reserved
+
+and then, look at entry.rb.
+
+The source code of entry.rb consists only of reserved words of Ruby,
+and all the reserved words are used in the code, in a way that the code
+forms a valid Ruby program. No compile error, no warning, or no runtime error.
+
+
+### Internals
+
+Difficult (and interesting) points of the theme are:
+
+* Since many of the reserved words define program structures, we cannot
+ use them independently. For instance, `retry` must be inside `rescue`,
+ or `break`/`next`/`redo` must be inside a looping construct.
+ Or, jump-out statements cannot occur at a position that requires a
+ value; `if return then true end` is a "void value expression" syntax error.
+* Inserting newlines for each 6 word (to match with the spec html) is also
+ an interesting challenge, since Ruby is sensitive to newlines.
+
+Tricks used in the code are:
+
+* def/alias/undef can take even reserved words as parameters.
+ That is, `def class ... end` defines a method named `class`.
+ The feature is crucial since otherwise `BEGIN` etc inevitably
+ introduces non-reserved tokens (like `{}`).
+* `defined?` can take some reserved words too (which I didn't know
+ until trying to write this program.)
+* "void value expression" can be avoided by using `or` or `and`.
+ `if begin return end then true end` is a syntax error, but
+ `if begin false or return end then true end` is not.
+
+
+### Limitation
+
+Sad to say that it's not a "perfect pangram".
+It uses 'alias' and 'undef' twice, and 'end' 4 times.
diff --git a/sample/trick2018/02-mame/authors.markdown b/sample/trick2018/02-mame/authors.markdown
new file mode 100644
index 0000000000..0e420fdf5d
--- /dev/null
+++ b/sample/trick2018/02-mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@ruby-lang.org
+ * cctld: jp
diff --git a/sample/trick2018/02-mame/entry.rb b/sample/trick2018/02-mame/entry.rb
new file mode 100644
index 0000000000..ced791aa3d
--- /dev/null
+++ b/sample/trick2018/02-mame/entry.rb
@@ -0,0 +1,15 @@
+'';eval(r=%q(->z{r="'';eval(r=\
+%q(#{r}))[%q`#{z}`]";i=-040;31.
+times{|n|(15+n%2*15-n/2).times{
+r<<r[i+=(1.-n&2)*(32-n%2*31)]}}
+i=r[524,0]=?\0;eval(r[479..-1])
+c['"']}))[%q`GFEDCBA"+"[e\"'"'t
+kE*;;\";" TRICK2018 ";tb,;{{r
+2E0$ob[us@*0)[90,336])#_i\n}s#i
+0H}>["t]];};o[1,?\n*8];ex"-}eac
+1Hl<1[-1]*2*t=n%2];o[14-n,0)mvk
+8M$<4,?\n];15.times{|n|;o[35ie2
+!Pss.slice!(0,1)+x;sleep(0.0t;0
+'W=%q"<<95<<$s<<95;o=->n,x{n.'1
+;@[2]}|\e../,%@s="'%trick2018!8
+eval$s=%q_eval($s.gsub!(/#{%@`]
diff --git a/sample/trick2018/02-mame/remarks.markdown b/sample/trick2018/02-mame/remarks.markdown
new file mode 100644
index 0000000000..88b32c205a
--- /dev/null
+++ b/sample/trick2018/02-mame/remarks.markdown
@@ -0,0 +1,16 @@
+This program quines with animation.
+
+```
+$ ruby entry.rb
+```
+
+Of course, the output is executable.
+
+```
+$ ruby entry.rb > output
+$ ruby output
+```
+
+Note, we don't cheat. This program uses escape sequences just for moving the cursor. It doesn't use attribution change nor overwrite to hide any code.
+
+The program is crafted so that it works in two ways; it works as a normal program text, and, it also works when it is rearranged in a spiral order. Some parts of the code are actually overlapped.
diff --git a/sample/trick2018/03-tompng/Gemfile b/sample/trick2018/03-tompng/Gemfile
new file mode 100644
index 0000000000..a24ff779dc
--- /dev/null
+++ b/sample/trick2018/03-tompng/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gem 'chunky_png'
diff --git a/sample/trick2018/03-tompng/Gemfile.lock b/sample/trick2018/03-tompng/Gemfile.lock
new file mode 100644
index 0000000000..467f5c3495
--- /dev/null
+++ b/sample/trick2018/03-tompng/Gemfile.lock
@@ -0,0 +1,13 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ chunky_png (1.3.8)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ chunky_png
+
+BUNDLED WITH
+ 1.16.1
diff --git a/sample/trick2018/03-tompng/authors.markdown b/sample/trick2018/03-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2018/03-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2018/03-tompng/entry.rb b/sample/trick2018/03-tompng/entry.rb
new file mode 100644
index 0000000000..26416c7019
--- /dev/null
+++ b/sample/trick2018/03-tompng/entry.rb
@@ -0,0 +1,31 @@
+X=[];class String def-@;replace ?-+self end;def-a;X.reject!{|x|x.
+__id__==__id__};a.replace(self+?-+a) end end;at_exit{eval C=(Zlib
+.inflate((X*?-).tr(?-,'').tr('q-z','0-9').to_i(26).digits(0x100).
+pack'C*'))};def method_missing n;(X<<n.to_s)[-1]end;require'zlib'
+fzygtoxyzgntmdmuwvfoffbpmvzojpkhczvjvjdbtscnldwbdoprackddovivvmkz
+ponzmosvtjciwkgaslscxxxwudeesmmqpfhislxuxnnypulxstzgobyaekqqhbjcg
+mvko------------ddkeys----eivhnccaqyiw---bzyccmt-----------ymtnge
+jwhi--------------pjxf------mdarbtumnv---qasda--------------gmwdt
+wrtk---qtpzgnce----fsl-------fkgzgtbpp---gwnm----pxkpqkdiw---owga
+momz---yjjvpnvar---zeo---v-----duvalwu---nsqt---waofemwakivnyqkjd
+fzag---uhvusmkl----kzb---rhc----iutzjr---mqlh---ayijpwativpweaato
+xexs--------------rvgv---pjdz-----lkkg---uiaw---lovitupw-----fwmn
+kfru------------jvjpgv---jskycf----pal---gbuf---hfdnywog-----iuca
+pntn---apmkqroeuzwuwkw---gqnmgof-----b---hlpl---vkkyhfyrqfr--jwrl
+kmdb---dhspujhmtgrkccu---uonfummdt-------rqfw----bpiactehwp--fncq
+yzvz---gdaxebplhfndran---ytfmviryeh------hqwkl---------------nced
+bibu---fnkdthgldhkxxjg---rwnmpudhbqin----gucoyki------------hfura
+cqdgqpyzqfzknvdjoxxhpjulwwyebtocxdrvklbuviwwcatlmdosxfvwntzbijguy
+iglrvvzlxerflupxvsyujfacuwhrvmnecgtewtqkhtdggcltejiyqcluclkycwvzg
+vvxfysvttfbeglvrlngntdngzyhqrmltazwdydxrsvjploembhgxdvfmmhepbschm
+brn--iqrcdb--evv----tqp------lg--uein-wzut--mr------wkh------foqz
+zsf--srjnjp--ampb--pfio--hgtekx--rrr---fwd--jn--xqkezcz--vsb--nya
+khrc--evlr--oioxs--mqce--bqfmag--bwz---xda--qw--jnuzelr--qzi--itx
+mdxd--duso--wxbot--nmon--ugnbdpc--a--c--e--hlg--twxndre--tby--rhg
+evhbn--zb--dtxmiz--dpia------vie--h--i--t--shh------kfn------owna
+ealmt--kb--scxdjy--smvl--dqmgebk--t--s--t--gfd--updcbnc--rh--dwwp
+dvpnxb----wpljjdy--kolc--qflyleok---xkv---usbj--jhrawbn--ewx--bgf
+eaqwrw----ejwxhet--dice--eoczconm---urz---rqyp--hovvvfc--bskj--el
+aocjcts--jtumwxm----mgy------xpaoq-jtwqr-aipay------dhy--iync--hk
+sckddmvuvvuhhqstumaykvczaaujrumqbbqsdvdycplyrlkkojlxnkrhbbrmnjxyf
+cdtcmpfmjvthwkpzucbblttgumomlxnxwjeypfeagaukfzeokzxjebkpigcvlqnso
diff --git a/sample/trick2018/03-tompng/output.txt b/sample/trick2018/03-tompng/output.txt
new file mode 100644
index 0000000000..ed9a4079cc
--- /dev/null
+++ b/sample/trick2018/03-tompng/output.txt
@@ -0,0 +1,44 @@
+undef p;X=[];class String def-@;replace ?-+dup end;def-a;X.reject!{|x|x.__id__==__id__};a.replace(self+?-+a) end end;at_exit{eval C=
+(Zlib.inflate (X*?-).tr(?-,'').tr('q-z','0-9').to_i(26).digits(256).pack'C*')};def method_missing n;(X<<n.to_s)[-1]end;require'zlib'
+gmlztzdculbtzgtjfetuh---k--htf----d-----------------------------------------------------g-b-----s--t-g--------jmuwescmgchftikfjafccs
+ivchcveidpvxdabnvwyga-f--v-------xf----------------------------------------------------q-v---l-------q---------liiNeawriayymwooxgxqw
+rfosepqsmojseyezmwbhi--------------ew--------------------------------------------------m---k-r-----------vwu--hiotltdmczwyjmlvbyfqwq
+uvvykqdjednoqgtcmtfbzs---------f----o--------------------------------------------------t--a------m----x---f-----dldzsakyofetfozfpmrq
+geusutariiiNiulkjbwlm-----d------------------------------------------------------------j---------o---------x--j-uitzrgwpupwhvendhyno
+uubvnssiywkklwwdufhhi-rw----k---v-------------------------------------------------------sty-----yg---l---c-v----wkffpskpumolqmkeryzg
+zrxdaiposwybbzgxdnegh-----g-----ma--n---------------------------------------------------------j----n--b-n-------yqavmscswdogpcgopygt
+axiqfswlhzeamvymdnteo---q-q-w--------------------------fhrmj-----------------hkou-----------f-----d----u-o------evcuxxegekfgivzzujan
+nslioftsvqvtkeigvfgwr-------------lyco-----------------igyvg-----------------okuk---------m--b-u--d--y------s---dadjrlykfhtermzfyktu
+btoxzfpPicxxfligbivvf--------h----yrat---------------------------------------vjwd---------------------d-ki--o--tyqosehopkwttigwwfskp
+komzvnyrvkjcjwbmdwdkp----------vxphiNdtawn--xms-saketo--jnld----ezulntdaz----nzna-----vhjwt------h----x--x--o--saxxsrkgktqotaluylbkk
+sclegratyaarmgmepheml----------hwgglhlrfcx--znvmpfsgjx-onhju---gtxsmzqprlt---mjzy---frhdk-------------v---mj----dzjujmbgldfwoybgicwu
+tfhgnhlzxlwtdtkgzlaca-------------gmex------arlm--------rvmh-ajtgf-----pqal--wcux-zatyi-------------------------xnluwybcugjclmablshn
+tnjohqtqzivgmyutrssil-------------lcwq------jrf--------gcaii-maie------------vvnfjfqwo--------------------------filivosyhkxcvuwdibwj
+tyxjiopiFqypvwdzoatuq-------------tdln------cnx---------ffuf-ajvq------------tyyypglpzmj------------------------vtqzwewqdsijrbymvpwn
+niNffphoehukpvvmzvhyd-------------ahqd------nfr---------jeqk--toap-----mxhyg-tedv---otrwy-----------------------mjxnrktackwxwiajdnuc
+kkxhuwbvibpvgvcampadi-------------ebmencqz--obf--------wfprz---qmrotkijiqv---ggfp-----hlzw----------------------kastwdpxiyftmypuxbtu
+xetudmwzpomktgnjkcsyc---------------fwpdx---xb----j-----se-k------tllakc-----gjoo-------we------mic---lktk------ubtnrxvrjzuqlrfrsnmf
+okdvfvcdbdqkckjialskk---------------------------v---u-------l----------------------------------z--q--qfg--------aaliNbxbjjpxebboneye
+kcbkjmdclwnfawtfnwkeq----------------------------------j---y-------------------------------a---jmbyo-sgef--gf---extljbozuoofgyvsilct
+xzoqmsqgzjxxpjqwkjkdd------------------------o--------m-------f---------------------------------n--de-ajz-rzv---fhnpbkrwdxoozpxeaxaf
+mbcwxuiqdwcmadheiykaa-----------------------q-f------l---i---------------------------------r----zf---k--y---fi--dcnycheytylcgnioauee
+yekiNacriqoevtdjerqbp----------------------------w---yy-----my----------------------------ko--mnbpskr--c-----j--ozyqpbfovhbhyoprzgqr
+czwtuopxkdbphocfawvbk--------------------------q-s----j--b---------------------------------hd-xsb----bfiNp--w---fmwuvfambdqvxtzldwmh
+xysnyrseydlkjcwfbsjnr-------------------------d-d-------------------------------------------f-enpss---qllpwr----almsdidvjwoigvldfqoa
+lrpbixjpofxocxlflscpo------------------------------q-fyu--z-------------------------------------kfd-z---n-------bqxurujnxzurrdgcojks
+jetyfdkcekckxbyosbfws-------------wdfhgwuvejjmf-----sxjubpvgcsl-------tnmixpv---------eurabjsdvstfv-------------qcyiqhonwoyixqeonfvp
+mopPhywsozohitutgmmrb------------zxwtxe--riedeo---mspgpnv--pimlh------jhtzajk--------qqovvq---ldbrh-------------xtooxpayonpcvvtmvpra
+vvuyiunpoeagdzqjecsub------------klrw------snrc---rrct------aajom--------nsyk--------peea-------azq-------------iNjefdkfhnagjicqwmsm
+mbwwbfgehhbdmvvlflmee---------------------hkejn---jtbo-------jdtje-------jcei---------afyz-----smtc-------------kksvfjyuaqtohxiohhlz
+dvfmfrzcmnsfruhqgjuxz------------------dfxdnlk----kkra-------xmmtf-------jwkw----------rdoozxtcho---------------bbwwferxwnnmdzcniicv
+mfneisdlyeqwynldjgonj----------------jgrjvc-------uxga-------ghnpr-------sers--------scbknx----gmjo-------------moedtnlbflhtlkjibrqk
+gobwqshnpbdcpjmjaeczr--------------iscsxs---------zfpo-------hhfwy-------qbba-------vhlxc-------ntod------------ndwzdomaptumzejiwqbn
+snucynymvfpnadyqkzfcv-------------ggze------------kuvfs-----zuhod--------mylo-------jhwyp-----z-pywd------------dqfmpnevmtqcikbrilto
+aotyxkipebdkassogpcbl-----------wgackesmvvsrihhd---orzndjndlzpb----------eobf-------kkayixzyotqfafa-w-----------mjjxoomwdglwvccozzut
+rthesuszfwycsqqrtxlot-----------ejcqlhriilqbtrys------lwbkzmvp-----------zzwm-------l--qijwfllndzb-ik-----------mmokqomjepdcotnsiNig
+nloryyoswwdmefywnnuhph------------------------------------------------------r--r-nd-----h--x--------------------hlgzeqqslwxgtjgghquf
+nssngjtiudsrvfuxjzclhjhj----------------------------------------------------------t----------------k-f-mp-------obhyehqebtpjbkeepqzt
+ezogzsimfynqmkteaipejo-g-yser-----------------------------------------------e------h-------------i---y----------qpgcqnltivmmsximbbsy
+wtjjolwyoselcumgklqwpldkl-ulm-m---------------------------------------------------------------q---u-f--l--------buixfiitufktsqdtnrei
+tgrtitcewseetlpeuuujb-osdokjozc------------------------------------------n---d-----f--------g--------q--g-------jyyqtezuzmcxgpcwuwfx
+dpPayqmzxrwhbswwalygfurtkruw-u-k---------------------------------------------d---h------i----------c----i-------ulowcddvjbxthqlxjzbe
diff --git a/sample/trick2018/03-tompng/remarks.markdown b/sample/trick2018/03-tompng/remarks.markdown
new file mode 100644
index 0000000000..fe9eec5989
--- /dev/null
+++ b/sample/trick2018/03-tompng/remarks.markdown
@@ -0,0 +1,19 @@
+### Remarks
+
+Bundle install
+ this program depends on `gem chunky_png`
+
+Run it with the following command:
+ bundle exec ruby entry.rb trick.png
+ bundle exec ruby entry.rb [other png file]
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16]
+* ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
+
+### Description
+
+This program is a png image viewer.
+The output is an asciiart of the given png file,
+and it is also a source code of the png viewer itself.
diff --git a/sample/trick2018/03-tompng/trick.png b/sample/trick2018/03-tompng/trick.png
new file mode 100644
index 0000000000..d4bb0bd7c3
--- /dev/null
+++ b/sample/trick2018/03-tompng/trick.png
Binary files differ
diff --git a/sample/trick2018/04-colin/authors.markdown b/sample/trick2018/04-colin/authors.markdown
new file mode 100644
index 0000000000..a846d12535
--- /dev/null
+++ b/sample/trick2018/04-colin/authors.markdown
@@ -0,0 +1,3 @@
+* Colin Fulton
+ * justcolin@gmail.com
+ * cctld: us
diff --git a/sample/trick2018/04-colin/entry.rb b/sample/trick2018/04-colin/entry.rb
new file mode 100644
index 0000000000..442a8ea3a8
--- /dev/null
+++ b/sample/trick2018/04-colin/entry.rb
@@ -0,0 +1,2 @@
+# Copyright 2018. Available for use under the terms of the MIT License.
+$🚀=0;def 🤔 🏷,🤔=0,&b;puts ' '*$🚀+(🤔 ?"":"🚫 ")+🏷;$🚀+=4;b&.[];$🚀-=4;end
diff --git a/sample/trick2018/04-colin/remarks.markdown b/sample/trick2018/04-colin/remarks.markdown
new file mode 100644
index 0000000000..5f4f1a8dfe
--- /dev/null
+++ b/sample/trick2018/04-colin/remarks.markdown
@@ -0,0 +1,62 @@
+### Remarks
+
+Create a Ruby file that requires entry.rb with a series of test in it the run the file using ruby:
+
+```
+ruby name_of_test_file.rb
+```
+
+To create a test, call 🤔 with two arguments. The first is a string describing what this tests, the second argument is the test assertion. If the assertion is truthy, the test passes. If the assertion is falsy, the test fails.
+
+```
+string_1 = "Hello world!"
+string_2 = "This is not the same!"
+
+🤔 "The two strings are equal",
+ string_1 == string_2
+```
+
+To create a group of tests under a label, call 🤔 with a string describing the group and a block containing the tests in that group.
+
+```
+🤔 "This is a group of tests" do
+ # Add other groups and/or tests here.
+end
+```
+
+Here is an example:
+
+```
+require './entry'
+
+🤔 "Math" do
+ 🤔 "Addition" do
+ 🤔 "One plus one equals two.",
+ 1+1 == 2
+ 🤔 "One plus one equals eleven. (This should fail.)",
+ 1+1 == 11
+ end
+
+ 🤔 "Subtraction" do
+ 🤔 "One minus one equals zero.",
+ 1-1 == 0
+ 🤔 "Ten minus one equal nine.",
+ 10-1 == 9
+ end
+end
+```
+
+It has been tested with the following Ruby versions:
+
+* ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
+* ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
+* If you replace `b&.[]` with `b&&b[]` it will work with ruby 2.0.0 as well, but it will be one character longer.
+
+
+### Description
+
+The goal was to create a testing library where the test files looked good and the output looked good in as few characters as possible. The result is 68 characters and has one method to handle everything.
+
+### Limitation
+
+Your terminal program must support Unicode characters for the test output to look correct. If your terminal does not support Unicode, simply replace the 🚫 in the code with whatever character you want to prefix failing tests.
diff --git a/sample/trick2018/05-tompng/authors.markdown b/sample/trick2018/05-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2018/05-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2018/05-tompng/entry.rb b/sample/trick2018/05-tompng/entry.rb
new file mode 100644
index 0000000000..31522b6de2
--- /dev/null
+++ b/sample/trick2018/05-tompng/entry.rb
@@ -0,0 +1,41 @@
+ X=[];def self.method_missing n;n.to_s.chars;end
+ l=[];def l.-a;X<<a=[nil,*a];a;end;def l.+a;self-a;end
+ class Array;def-@;[]-self;end;def-a;replace [*self,nil,*a
+ ]end;alias +@ -@;alias + -;end;def gen3d f;yield;b=['solid obj'];w,
+ h=X[0].size,X.size;X<<[];a=->r,z,dr,dz{;r-=w/2.0;z*=2;r2,z2=r+dr,z+dz*2;if r>0||r2>
+ 0;r=[0,r].max;r2=[0,r2].max;16.times{|i|m=Math;p=m::PI/8;;c,s=m.cos(t=i*p),m.sin(t)
+ c2,s2=m.cos(t=(i+1)*p),m.sin(t);t-=p/2;[[0,1,2],[0,2,3]].map{|a|b.push [:facet,'n'+
+ + 'ormal',dz*m.cos(t),dz*m.sin(t),-dr]*' ','outer loop',a.map{|i|'v'+
+ ++ "ertex #{[[r*c,r*s,z],[r*c2,r*s2,z],[r2*c2,r2*s2,z2],[r2*
+ +c, r2*s,z2]][i]*' '}"},:endloop,:endfacet}}end};(0...h).
+ map{| y|w.times{|x|[X[y-1][x]||a[x,y,1,0],X[y+1][x]||
+ a[x+1,y+
+ 1,-1,0],X[
+ y][x-+1]||a[
+ x,y+1,0,-1],X[y
+ ][x++1]||a[x+1,y,
+ 0,1]]if X[y][x]}}
+ s=[b,'end'+b[0]]*
+ $/;File.write(f,
+ s);X.replace(
+ []);end
+
+gen3d 'wine_glass.stl' do
+ l--ww------------------ww--l
+ l--ww------------------ww--l
+ l--ww++++++++++++++++++ww--l
+ l--ww++++++++++++++++++ww--l
+ l--ww++++++++++++++++++ww--l
+ l--ww++++++++++++++++++ww--l
+ l---ww++++++++++++++++ww---l
+ l----www++++++++++++www----l
+ l------www++++++++www------l
+ l--------wwwwwwwwww--------l
+ l-----------wwww-----------l
+ l------------ww------------l
+ l------------ww------------l
+ l------------ww------------l
+ l-----------wwww-----------l
+ l---------wwwwwwww---------l
+ l----wwwwwwwwwwwwwwwwww----l
+end
diff --git a/sample/trick2018/05-tompng/preview_of_output.png b/sample/trick2018/05-tompng/preview_of_output.png
new file mode 100644
index 0000000000..db511ee2f3
--- /dev/null
+++ b/sample/trick2018/05-tompng/preview_of_output.png
Binary files differ
diff --git a/sample/trick2018/05-tompng/remarks.markdown b/sample/trick2018/05-tompng/remarks.markdown
new file mode 100644
index 0000000000..b4e5708a43
--- /dev/null
+++ b/sample/trick2018/05-tompng/remarks.markdown
@@ -0,0 +1,31 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+I confirmed the following implementations/platforms:
+
+* ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16]
+* ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
+* ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin16]
+
+### Description
+
+This program will generate `wine_glass.stl`, a 3D data file(STL format) of a wine glass.
+You can change the shape by modifying the DSL part.
+For sake cup:
+```ruby
+gen3d 'ochoko.stl' do
+ l------------------------l
+ l-ww------------------ww-l
+ l-ww------------------ww-l
+ l-ww++++++++++++++++++ww-l
+ l-ww++++++++++++++++++ww-l
+ l--ww++++++++++++++++ww--l
+ l---wwww++++++++++wwww---l
+ l----wwwwwwwwwwwwwwww----l
+ l----www----------www----l
+end
+```
+`+` and `-` are the same meaning(just for appearance)
diff --git a/sample/trick2018/README.md b/sample/trick2018/README.md
new file mode 100644
index 0000000000..345500b00a
--- /dev/null
+++ b/sample/trick2018/README.md
@@ -0,0 +1,16 @@
+This directory contains the award-winning entries of
+the 3rd Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2018).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* 01-kinaba/entry.rb: "Most reserved" - **Gold award**
+* 02-mame/entry.rb: "Best spiral" - **Silver award**
+* 03-tompng/entry.rb: "Best png viewer" - **Bronze award**
+* 04-colin/entry.rb: "Best one-liner" - 4th prize
+* 05-tompng/entry.rb: "Most three-dimensional" - 5th prize
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2018
diff --git a/sample/trick2022/01-tompng/Gemfile b/sample/trick2022/01-tompng/Gemfile
new file mode 100644
index 0000000000..982b9de67f
--- /dev/null
+++ b/sample/trick2022/01-tompng/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gem 'matrix'
diff --git a/sample/trick2022/01-tompng/Gemfile.lock b/sample/trick2022/01-tompng/Gemfile.lock
new file mode 100644
index 0000000000..8bb3c69025
--- /dev/null
+++ b/sample/trick2022/01-tompng/Gemfile.lock
@@ -0,0 +1,13 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ matrix (0.4.2)
+
+PLATFORMS
+ x86_64-darwin-20
+
+DEPENDENCIES
+ matrix
+
+BUNDLED WITH
+ 2.3.3
diff --git a/sample/trick2022/01-tompng/authors.markdown b/sample/trick2022/01-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2022/01-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2022/01-tompng/entry.rb b/sample/trick2022/01-tompng/entry.rb
new file mode 100644
index 0000000000..97beacc684
--- /dev/null
+++ b/sample/trick2022/01-tompng/entry.rb
@@ -0,0 +1,40 @@
+ eval((s=%~c=(0..35
+ ).map{s[2*_1+1]}*'';class$Inte
+ ger;def$quXinclude(Math ;spXo(a)=self*
+ a.pow(87X=h=32.chr;g=PI/480;ls=(sp*31X,89)%89;
+ def$abX+'eval((s=%'+(n=? .next)+s*88.chr+[nXs()=[a
+ =self%X+'.split(',sp*25+'?'+88.chr+');(0..36).mapX89,89-
+ a].miX{s[2*_1].split}',sp*31+".join.tr('$',$/)))"]*$/)Xn;end
+ ;reqX.split$/;trap(:INT){puts;exit};q=->t,i{a,y=((t+i*99)Xuire
+ 'matrX%960). ivmod(80);[(a*(7+i)+i*23)%79+(y+a)/(5+i%4)%2,39Xix';1
+ 5.tiX-y/2]};p=->t,u{a=->b,c{(0..5).sum{(u%2-1)*E**(t*(b+c*_1)*gXmes{
+ |i,*X.i+ i*u+=5+sin(u*u))}};x,z=a[5,3]. 5,3].rect;x+=y.Xv|z=
+ *?!Xi a[19,4];z+=w;r=(4+(x.abs+z.i).ab };t=(0..959).fX..?
+ W,?Xind{|t|(0..29).all?{x,y=q[t,_1];(x 2||h=ls[y][x]X[,*
+ ?]..X[/[^!-}]/]}};h=($**h+h).chr;eval( []} ->(x,yX?};a
+ =(0X,a,b){x=x*36+39.5;y=19.5-y*18;b*=1 |i|((yX..1
+ 34)X-b).ceil..y+b).map{|j|((x-i)/a+(y j)/ .times{X.ma
+ p{zXx,z=p[t,_1];l=u```=0;while``````(l<1)``; u+```=0 ;d=x-y;X.in
+dex(Xl+=(d.abs+(z-w``)``.i).ab``s*1.``1 ;x``,z=y``,w;o[v``=``x.r d.imag/Xc[i+
+15*Xd.abs*l*sin(2*``l-t``*g*80``-_1) l*(``1-l)/``6,a``=l*( -l)**2*0.X_1]
+)};X7,a*2]&&o[v,z,``0.0``3,l**`` ``times``{|i``|(8+i).times{|Xw=*
+MatXj|o[sin(i)/2+````` ```sin( `/2.0`````````)*j/200,j*0.0Xrix
+[*(X5-1,0.02,0.1]``}} ``. q[t,``_1];m``[y][x]= };i=-X0..
+44).X1;$><<(['%%','[H .map{|j|(0..79).map{|k|x=(Xmap{
+ |i,X -39.5)/35.8;y=( i+=1;m[j][k]?h:c[i]):ls[j]X*b|
+ v<<X[k];}*''}*$/<<0) 1)%9 te"`")#qv.jSaL{=;q(Q}4fXa.z
+ ip(Xjs(:#tK`Jm))FKO /A9(2'%iorvf7 eEa0uV xv+Q@qUU](L@&Py .1v'X0..
+ ).suXydSEH{-GI|-5(,z G5evpq,[b50 D[ t {on,I?VStS`?G@LoqFCXm{|j
+ ,k|Xj1.QnxKz!mH%o# )b2Seut,]! 48 lBieJGi 5jeNPD#b}H3X-(p
+ =(iXaVz#8*+US,hgF 5#6]y-` 4hy HN hF75WjD!0IxJ$sX+k)
+ .powX+UP"cNUE9- G< tHvV;Ib <-s U T ? vlE xylg=x#X(i+k
+ ,88)XV9u$9lKb9 @C do7+-w >l { v9 { P l ga%]AK<e&'X+1)*
+ (j||(X4ifK/6S+ k} @@*a} 6rS xn"Q[M 8 `|g>$#BrjXb<<p;
+ 0))}XtbDp'Kc t2 Dat9C s C rL+ g,j]Tf B< eMI+zzkWX;b}]
+ .lup.XtVP<ak IM E/+)B jwv uB (Twqed D* dyf_dT7Xsolve
+ (v);13Xn:8 #_ RiSTO, [Fk m O]O#"+ a_ cT_.X5.time
+ s{c[i+X e5 T`FBEC q*f 2 o@{a<eUG aW PX15*_1]
+ =z[a[_1]X z_@`nll 7F1 2 [=^uS0z^ 6X||w.shif
+ t]}};eval(Xfg K#R N bp-E_Xc)~.split(
+ ?X);(0..36).map{s[2*_1].split}
+ .join.tr('$',$/)))
diff --git a/sample/trick2022/01-tompng/remarks.markdown b/sample/trick2022/01-tompng/remarks.markdown
new file mode 100644
index 0000000000..70601908b7
--- /dev/null
+++ b/sample/trick2022/01-tompng/remarks.markdown
@@ -0,0 +1,51 @@
+### Remarks
+
+Just run it with no argument:
+
+ ruby entry.rb
+
+Or run it with one non-ascii half-width character argument:
+
+ ruby entry.rb ⬮
+ ruby entry.rb 𓆡
+
+I confirmed the following implementations/platforms:
+
+* ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
+* ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
+
+### Description
+
+This program is an aquatic quine.
+Some characters in the code are overwritten with `" "`, but this program can restore the missing parts.
+Every frame of this animation is an executable ruby program that let fishes start swimming again from their current position.
+
+### Internals
+
+#### Error Correction
+
+Error correction is performed for each block of length 135.
+It consists of 89 kinds of characters(`[*('!'..'W'), '[', *(']'..'}')]`) and satisfies the following constraint.
+
+```
+matrix(size: 45x135) * block_vector(size: 135) % 89 == zero_vector(size: 45)
+```
+
+To restore the missing characters in the block, we need to solve a linear equation problem in modulo 89.
+This can be achieved by using bundled gem 'matrix' and overwriting some methods.
+
+```ruby
+require 'matrix'
+matrix = Matrix[[3, 1, 4], [1, 5, 9], [2, 6, 5]]
+class Integer
+ def quo(x) = self * x.pow(87, 89) % 89 # Fermat's little theorem. 89 is a prime number.
+ def abs() = [self % 89, 89 - self % 89].min # To avoid division by multiple of 89.
+end
+answer = matrix.lup.solve([1, 2, 3]) #=> Vector[24, 42, 83]
+(matrix * answer).map { _1 % 89 } #=> Vector[1, 2, 3]
+```
+
+#### Resuming Animation
+
+The entire animation of this fish tank is a loop of 960 frames.
+This program uses position of the floating bubbles to detect current frame number from the executed source code.
diff --git a/sample/trick2022/02-tompng/authors.markdown b/sample/trick2022/02-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2022/02-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2022/02-tompng/entry.rb b/sample/trick2022/02-tompng/entry.rb
new file mode 100644
index 0000000000..2e2e2bcf74
--- /dev/null
+++ b/sample/trick2022/02-tompng/entry.rb
@@ -0,0 +1,32 @@
+ q=->{!sleep _1/1e2};p=(
+ c=0..2).map{[_1/9r ,0,5**_1.i/3,1,0]}
+ require'socket';puts'op' "en http://localhost:#{(
+ w=TCPServer.new$*[0]||0).addr[1]}";Thread.new{q[2];f=[-1
+ ]*s=3;t=Time.now.to_f;p.select!{0<_1[3]=[_1[3]+_1[4]/8.0,1
+ ].min};9.times{h=p.map{[2**(_1*t.i)/_4**0.5/(1+Math.sin(2*t-
+ 9*_1%2)**32/16),_2+_4*( _3-_2)]};r=[s*3/2,84].min;g=->{x,y=
+(s*(1+_1+1i)/2).rect;x<0 ||x>=s-1||y<0||y>=s-1?0:((l=f[y+1])[
+x+1]*(a=x%1)+(1-a)*l[x] )*(b=y%1)+(1-b)*((l=f[y])[x+1]*a+(1-
+a)*l[x])};f=(1..r).map {|y|(1..r).map{|x|z=1.5+1.5i-3.0*(y
+.i+x)/r;[h.sum{g[_1.*z +_2]}*0.9,1].min}};s=r};c=f.flatten
+redo};loop{s=w.accept ; Thread.new{r=s.gets;h='HTTP/1.1 '+
+"200 OK\r\nContent-" 'T' "ype:text/html\r\n\r\n";r['/ ']?s.
+ <<(h+'<style>ifram' 'e{' 'opacity:0;height:0;}input{wid'+
+ 'th:252px;}</styl' 'e>' '<form target="i"><input src="'+
+ "g#{rand}\" type" '="im' 'age"><iframe name="i"></ifra'+
+ 'me></form>'):r ['/g'] ?(h[/:.+l/]=?:'image/gif';s<<
+ h+'GIF8' '7a'+[84,
+ 84,246,0,*(0..383).map {15*_1. /(383r)**(3-_1%
+ 3)*17}].pack('v3c*'); loop{ s<<[67434785,5,
+ 44,84,84,7,c.map{_1* 127} .each_slice(126
+ ).map{[127,128,*_1 ] .pack'c*'}*'',
+ 1,129].pack('V3x' 'v2na*c2x');q[
+ 5];q.[]1while(r ==r=c)}):(x,y,
+ z=r.scan(/\d+/).map{_1.to_f/
+ 126-1};z&&p<<[rand-0.5,(
+ z=x+y.i)*1.5,z/(z.
+ abs+0.9),0,-p[
+ -3][4]=-1]
+ s.<<h);s
+ .close
+ }}
diff --git a/sample/trick2022/02-tompng/remarks.markdown b/sample/trick2022/02-tompng/remarks.markdown
new file mode 100644
index 0000000000..3b2d3fd84b
--- /dev/null
+++ b/sample/trick2022/02-tompng/remarks.markdown
@@ -0,0 +1,32 @@
+### Remarks
+
+1. Run it with `ruby entry.rb 8080`
+
+2. Open "http://localhost:8080"
+
+3. Click on the screen and interact with it
+
+I confirmed the following implementations/platforms:
+
+* Ruby Version
+ * ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin20]
+* Browser
+ * Chrome(macOS, Android)
+ * Firefox(macOS)
+ * Edge(macOS)
+
+### Description
+
+This program is an HTTP server that provides a fractal creature playground.
+You can see the heartbeat of a mysterious fractal creature. Clicking on the screen will change the shape of the creature.
+Surprisingly, this interactive webpage is built without JavaScript.
+
+### Internals
+
+Fractal: Iterated function system
+Rendering from server: Streaming animated GIF
+Sending click event to server: `<input type="image" src="streaming.gif">` with `<form target="invisible_iframe">`
+
+### Limitations
+
+Does not work on Safari and iOS.
diff --git a/sample/trick2022/03-mame/authors.markdown b/sample/trick2022/03-mame/authors.markdown
new file mode 100644
index 0000000000..0e420fdf5d
--- /dev/null
+++ b/sample/trick2022/03-mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@ruby-lang.org
+ * cctld: jp
diff --git a/sample/trick2022/03-mame/entry.rb b/sample/trick2022/03-mame/entry.rb
new file mode 100644
index 0000000000..f24595dfa9
--- /dev/null
+++ b/sample/trick2022/03-mame/entry.rb
@@ -0,0 +1,27 @@
+2022;"#
+
+.chars} {puts'TRICK+2022'
+ \ { ;
+#';$><< b
+ ?!
+};#{s=' ' # 0
+! s[0]? ( b=$<.read ;'
+} ub( ,''}
+';a= ''<<32
+b.lines {puts( ?.. *(
+b.size) .gsub(/./) {
+b.sub!( /^#$`\K(\S)
+ /x,a)?$1:a }
+ .rstrip)}):
+ ( [ 12,1,12,11].
+cycle { | i | t = ( s *
+
+10<<
+10)*
+10+ %(\e[A)*
+10
+10. times{t[i*
+ _1 ] = 'TRICK+2022'[
+ _1 ] };$><<t
+ sleep 1})
+ }"
diff --git a/sample/trick2022/03-mame/remarks.markdown b/sample/trick2022/03-mame/remarks.markdown
new file mode 100644
index 0000000000..c38279f016
--- /dev/null
+++ b/sample/trick2022/03-mame/remarks.markdown
@@ -0,0 +1,96 @@
+Execute the program normally.
+
+```
+$ ruby entry.rb
+```
+
+It shakes a string.
+
+... Wait! This is not all.
+
+Next, please apply "leftward gravity" to each letter in the file.
+IOW, if there is a space to the left of a letter, move it to the left.
+Here, you may want to use the following command.
+
+```
+$ sed "s/ //g" entry.rb | tee up.rb
+```
+
+This program applies "upward gravity" to each letter in an input text.
+The following demo will help you understand what this means.
+
+```
+$ cat test.txt
+$ ruby up.rb test.txt
+```
+
+Now, here's where we come in.
+Please apply "upward gravity" to entry.rb.
+
+```
+$ ruby up.rb entry.rb | tee left.rb
+```
+
+I think that you already noticed that.
+This program applies "leftward gravity" to an input text.
+
+```
+$ cat test.txt
+$ ruby left.rb test.txt
+```
+
+`sed` is no longer required to create `up.rb`; just use `left.rb`.
+
+```
+$ ruby left.rb entry.rb > up.rb
+```
+
+We've come to the final stage.
+Please apply `left.rb` to `left.rb`.
+
+```
+$ ruby left.rb left.rb | tee horizontal.rb
+$ ruby horizontal.rb
+```
+
+Of course, it is also possible to apply `up.rb` to `up.rb`.
+
+```
+$ ruby up.rb up.rb | tee vertical.rb
+$ ruby vertical.rb
+```
+
+Can you tell how they work? Enjoy analyzing!
+
+
+
+---
+Code reading tips (spoiler)
+
+Some code fragments are highly reused between the programs.
+For example, note that this program has one code fragment to input a text
+(`b=$>.read`); `up.rb` and `left.rb` share and use this code fragment.
+Also, `horizontal.rb` and `vertical.rb` share the fragment `puts'TRICK+2022'`.
+Sometimes letters in very distant places are reused all over the place.
+
+Can you tell how it detects if it is already aligned or not yet?
+Here is a simplified version of the gimmick to switch behavior when
+left-aligned:
+
+```
+"\ #{puts('not left-aligned yet')}
+ # {puts('left-aligned')}"
+```
+
+And for top-aligned:
+
+```
+"#
+xx{puts('top-aligned')}
+x#{puts('not top-aligned yet')}
+"
+```
+
+It is also necessary to detect "top-left-aligned" and "left-top-aligned".
+I made tons of subtle adjustments and trial-and-error to create the program.
+I no longer know precisely how it works.
diff --git a/sample/trick2022/03-mame/test.txt b/sample/trick2022/03-mame/test.txt
new file mode 100644
index 0000000000..18802153a9
--- /dev/null
+++ b/sample/trick2022/03-mame/test.txt
@@ -0,0 +1,13 @@
+T
+ R
+ I
+ C
+ K
+ |
+ |
+ |
+ |
+ 2
+ 0
+ 2
+ 2
diff --git a/sample/trick2022/README.md b/sample/trick2022/README.md
new file mode 100644
index 0000000000..3b2af6f86b
--- /dev/null
+++ b/sample/trick2022/README.md
@@ -0,0 +1,14 @@
+This directory contains the award-winning entries of
+the 4th Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2022).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* 01-tompng/entry.rb: "Best fishbowl" -- Tomoya Ishida (tompng)
+* 02-tompng/entry.rb: "Most interactive code" -- Tomoya Ishida (tompng)
+* 03-mame/entry.rb: "Most anti-gravity" -- Yusuke Endoh
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2022
diff --git a/sample/trick2025/01-omoikane/authors.markdown b/sample/trick2025/01-omoikane/authors.markdown
new file mode 100644
index 0000000000..5c6823c077
--- /dev/null
+++ b/sample/trick2025/01-omoikane/authors.markdown
@@ -0,0 +1,5 @@
+* Don Yang
+ * omoikane@uguu.org
+ * cctld: us
+ * bsky.app/profile/omoikane.bsky.social
+ * twitter.com/uguu_org
diff --git a/sample/trick2025/01-omoikane/bf.rb b/sample/trick2025/01-omoikane/bf.rb
new file mode 100644
index 0000000000..74f5abe7e4
--- /dev/null
+++ b/sample/trick2025/01-omoikane/bf.rb
@@ -0,0 +1,81 @@
+#!/usr/bin/ruby -w
+# Simple BF interpretor.
+#
+# This works by translating input code into ruby and evaluating the
+# translated ruby code. Doing it this way runs much faster than
+# interpreting BF on our own.
+#
+# There is no error reporting whatsoever. A malformed input may result in
+# a syntax error at run time, but good luck in finding where it came from.
+
+
+# Setup empty tape and initial pointer position. Note that tape size is
+# fixed. We can make it infinite by initializing it to "[]" here and
+# adding some nil checks in the generated code, but avoiding those checks
+# makes the program run ~10% faster.
+$code = "t=Array.new(30000,0); p=0;"
+
+# Counters for pending add or shift operations. We buffer incoming +-<>
+# operators and output a single merged operation when we encounter a
+# different operator.
+$buffered_add = 0
+$buffered_shift = 0
+
+# Flush pending add operations, if any.
+def flush_add
+ if $buffered_add != 0
+ $code += "t[p]+=#{$buffered_add};"
+ $buffered_add = 0
+ end
+end
+
+# Flush pending shift operations, if any.
+def flush_shift
+ if $buffered_shift != 0
+ $code += "p+=#{$buffered_shift};"
+ $buffered_shift = 0
+ end
+end
+
+def flush_pending_ops
+ flush_add
+ flush_shift
+end
+
+# Convert input characters to ruby.
+ARGF.each_char{|c|
+ case c
+ when '+'
+ flush_shift
+ $buffered_add += 1
+ when '-'
+ flush_shift
+ $buffered_add -= 1
+ when '<'
+ flush_add
+ $buffered_shift -= 1
+ when '>'
+ flush_add
+ $buffered_shift += 1
+ when ','
+ flush_pending_ops
+ $code += "if c=STDIN.getc;" +
+ "t[p]=c.ord;" +
+ "else;" +
+ "t[p]=-1;" + # EOF is interpreted as -1.
+ "end;"
+ when '.'
+ flush_pending_ops
+ $code += "print t[p].chr;"
+ when '['
+ flush_pending_ops
+ $code += "while t[p]!=0;"
+ when ']'
+ flush_pending_ops
+ $code += "end;"
+ end
+}
+flush_pending_ops
+
+# Evaluate converted code.
+eval $code
diff --git a/sample/trick2025/01-omoikane/entry.rb b/sample/trick2025/01-omoikane/entry.rb
new file mode 100644
index 0000000000..c84f8079ae
--- /dev/null
+++ b/sample/trick2025/01-omoikane/entry.rb
@@ -0,0 +1,32 @@
+ a=+Math::PI/13
+ #Z---z';#za-mRUBY
+ #A-ZaA-Mn--\[+>+>++
+ '"N-Z(\++\[->++++@"
+ b=\[->+> +>+>\[h_
+ p%{} eact
+ zoraq ;%{ GF. rin);
+ %{eb} r A R p *""\]
+ <<<{{{ }<\]<b
+ ]<l(%w| } ; a;a=%Y/
+ evar{|c)} <][ #pgny\W{f
+ chaa,b)]>++[ ->+>>>>>[40v
+ .tr(= ' ;eval(%w{r=u=b= y =0;%{
+ (ct;c ) ; ] <<->--<<< < < ] >>[>,
+ exi}; a * = A RGV.siz e > 0 ? -1:1;
+ z=[] ; A R G F .ea c h _ l i n e{|i
+|i.eac h _ g r aph e m e _ c l u ster
+{|j|i f ( k = j.o r d ) < 3 3 ; r+=k<
+32?k==9? 8 - r%8 : k = = 1 0 | |k==13
+?[u+=1,-r][ 1]: 0 : 1 ; e lse;z+=[[u,
+r,j]];b+=r;y+=u;r+=1;end;}};if(s=z.si
+ze)>0;b/=s;y/=s;m,n=z[0];i=Math::tan(
+a/2);j=Math::sin(a);z.map!{|d|p=d[1]-
+b;q=d[0]-y;p-=(i*q).round;m=[m,q+=(j*
+ p).round].min;n=[n,p-=(i*q).round].
+ min;[q,p,d[2]]};r=n;u=m;z.sort.eac
+ h{|d|p,b=d;r=(u<p)?n:r;print"\n"
+ *(p-u),"\40"*(b-r),d[2];u=p;r=
+ b+1};print"\n";end}*"");%(]>
+ "tyvuts(}}.--.>--.>+.<++'
+ )b\40"gena.(c)2025<<<
+ #)#ehol""+a*.^_^
diff --git a/sample/trick2025/01-omoikane/remarks.markdown b/sample/trick2025/01-omoikane/remarks.markdown
new file mode 100644
index 0000000000..2aa77d64e4
--- /dev/null
+++ b/sample/trick2025/01-omoikane/remarks.markdown
@@ -0,0 +1,71 @@
+### Summary
+
+This is a rot13 filter. Given an input text, it will **rotate** the text by **pi/13** radians. Two modes of operation are available, selected based on number of command line arguments.
+
+Rotate clockwise:
+
+ ruby entry.rb < input.txt
+
+Rotate counterclockwise:
+
+ ruby entry.rb input.txt
+ ruby entry.rb - < input.txt
+
+### Details
+
+This program interprets input as an ASCII art with each character representing individual square pixels, and produces a rotated image to stdout. All non-whitespace characters are preserved in output, only the positions of those characters are adjusted. While all the characters are preserved, the words and sentences will not be as readable in their newly rotated form. This makes the program suitable for obfuscating text.
+
+ ruby entry.rb original.txt > rotated.txt
+ ruby entry.rb < rotated.txt > unrotated.txt
+
+But note that while `unrotated.txt` is often the same as `original.txt`, there is no hard guarantee due to integer rounding intricacies. Whether the original text can be recovered depends a lot on its shape, be sure to check that the output is reversible if you are using this rot13 filter to post spoilers and such.
+
+Reversibility does hold for `entry.rb`:
+
+ ruby entry.rb entry.rb | ruby entry.rb | diff entry.rb -
+ ruby entry.rb < entry.rb | ruby entry.rb - | diff entry.rb -
+
+Also, there is a bit of text embedded in the rotated version:
+
+ ruby entry.rb entry.rb | ruby
+
+But this text is encrypted! No problem, just rotate `entry.rb` the other way for the decryption tool:
+
+ ruby entry.rb < entry.rb > caesar_cipher_shift_13.rb
+ ruby entry.rb entry.rb | ruby | ruby caesar_cipher_shift_13.rb
+
+If current shell is `bash` or `zsh`, this can be done all in one line:
+
+ ruby entry.rb entry.rb | ruby | ruby <(ruby entry.rb < entry.rb)
+
+### Miscellaneous features
+
+To rotate to a different angle, edit the first line of `entry.rb`. Angles between -pi/2 and pi/2 will work best, anything outside that range produces more distortion than rotation, although the output might still be reversible.
+
+Setting angle to zero makes this program a filter that expands tabs, trim whitespaces, and canonicalize end-of-line sequences.
+
+This program preserves non-ASCII characters since input is tokenized with `each_grapheme_cluster`, although all characters that's not an ASCII space/tab/newline are given the same treatment. For example, the full-width space character (U+3000) will be transformed as if it's a half-width non-whitespace ASCII character.
+
+If input contains only whitespace characters, output will be empty.
+
+The layout is meant to resemble a daruma doll. There was still ~119 bytes of space left after fitting in 3 ruby programs, so I embedded a brainfuck program as well.
+
+ ruby bf.rb entry.rb
+
+A `sample_input.txt` has been included for testing. After rotating this file 26 times either clockwise or counterclockwise, you should get back the original `sample_input.txt`.
+
+ ruby entry.rb < sample_input.txt | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | ruby entry.rb | diff sample_input.txt -
+ ruby entry.rb sample_input.txt | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | ruby entry.rb - | diff sample_input.txt -
+
+Additional development notes can be found in `spoiler_rot13.txt` (rotate clockwise to decode).
+
+ ruby entry.rb < spoiler_rot13.txt
+
+### Compatibility
+
+Program has been verified to work under these environments:
+
+ * ruby 3.2.2 on cygwin.
+ * ruby 2.5.1p57 on linux.
+ * ruby 2.7.4p191 on linux.
+ * ruby 2.7.1p83 on jslinux.
diff --git a/sample/trick2025/01-omoikane/sample_input.txt b/sample/trick2025/01-omoikane/sample_input.txt
new file mode 100644
index 0000000000..244530f265
--- /dev/null
+++ b/sample/trick2025/01-omoikane/sample_input.txt
@@ -0,0 +1,35 @@
+ T U
+ S V
+
+ R T U W
+ S V
+
+ Q R W X
+
+ Q X
+ P i h g f Y
+ j e
+ P k d Y
+
+ O l c Z
+ O Z
+ m b
+
+* N N n + a A A *
+
+ o z
+ M B
+ M p y B
+
+ L q x C
+ r w
+ L s t u v C
+ K D
+
+ K J E D
+
+ I F
+ J H G E
+
+ I F
+ H G
diff --git a/sample/trick2025/01-omoikane/spoiler_rot13.txt b/sample/trick2025/01-omoikane/spoiler_rot13.txt
new file mode 100644
index 0000000000..91636176b8
--- /dev/null
+++ b/sample/trick2025/01-omoikane/spoiler_rot13.txt
@@ -0,0 +1,470 @@
+ .
+ text
+ ted t to
+ rota tex
+ dit HTML
+ to etes oss
+ sier wri acr
+ t earb"), ions .
+ ke itry. etat text
+ o ma "en erpr tive
+ ge te.g. e int: erna
+ L paut ( tiplsteps alt edit,he
+ a HTM inp muling cted ach dit tu
+ -w tes nt as havellow expe er ely ere yo
+ uby neragume can e fo the aftrect whethis
+ in/rt gee ar hat y th t. has stepindicesshat
+ usr/bcripingl ext tt tr texsult ion to prois w
+#!/his s a s of t migh inale re rotatededis aich ts.
+# TTakest. ece you origf th tra ts neike , wh tex
+ # stdou a piles, the ck i e exemenuld lusly ated the
+ # ite ang s tod che f th movu wotaneo rot pen
+ # To wrrent edit, an se orsort yoimul and nd o ne.
+ # diffe ome tion1-2. ecaue cu Whats s inal t, a en do
+ # ke srotaeps ous be thve. tex orig ayou t wh
+ # 1. Maply t st tediecausuitiated hold he l tex this
+ # 2. Apepea is lt bunint rot to rom ted. ated but
+ # 3. R cessficu is l and ough le fenabl upd nce, in
+ # pro diftextgina e en L fiipt the atie dable
+ # Thisalsoive ori larg HTMascr paste of p reay
+ # and rnat thedes: at's te a jav opy& bit its. t is Rubke },
+ # alteeditrovi t th nerawith en c fair l ed thar ine-li, %w{
+ # can pt p ayou to geser , th h a manua textasiequot %()
+ # scri a l ipt brow page witose ing 's eple %{},
+ # Make scrin a TML textn th writt itulti of ter
+ ## 1. thisTML the H dit ly o lly:l, buas m use Ras t
+ # Run ut H via ly euick ficaiviauby hakestes. eral wan
+ # 2. outp its anualte q peciy trse R.rb m quo Gen nd we
+ # y ed to mitera by sactlecauntryouble for rs a
+ # Appl eed you g Rut exs, b. ele+d ithm acte
+ # 3. ll nlet ardins nouageientsing Algor char paperpplyg
+ # stiill regons ilangnvenmon ast ces: are nal ply ardin
+ # Youol w hingtatither e co com "A Feren els rigi sim regato
+ # to re trienny o quit the on diff r pix . O andailsnter
+ # e mole oo ma areourse asedtwo e ous. tionates detn ce o
+ # Onltiped tthatof c is bith sincalue rotardine toatio is n
+ # mumparors and ithm h, w ls, er v r of coot du rot here ns d
+ # coerat//, lgor Paet pixeract enteinput, but to r, tratio, an.
+ # op{}, on aAlan ing cha ng cthe as isspecion. ente opeinput two
+ # %W tati by blendinal ardiing ues th reunct on ctionthe ne or
+ # e roion" for orig reghift vale witer f tatirota of a li
+ # Thotat ort the tionut snate car_cen e roise hapeextr
+ # R supperve idera aboordixtra get stablockwhe s an ons.
+ # No pres consthingd coed e see ore terclon tting tatiugh
+ # - to nal any-basee neble, a mcoun lot nser e roltho
+ # itio sayto 0ng, wersi tingise/ds a by i oducd, a
+ # Addsn'trms undit rev elecockwepenible ] prstea
+ # - doensfor routpu in se clIt dvers pi/2t in ly to
+ # trategehe o care utivt. e re i/2, tex ical
+ # inke t tra onsecr ou mad [-pt the omat
+ # ma h exat c othen be weenstorible. aut
+ # wite theachon ca beto divers sted
+ # Evenantecel tati glesnd te re adju s.
+ # guar cane ro y ane tell b l be ment
+ # willn th Onlrang sti wil ele
+ # ofte gle.that ight idth text
+ # n anide ion m . W ble erialink
+ atiooutsrmat/ 13 entss. edita les. he scan l
+ Rotles nsfo:PI elemcell all tup . T we
+ # Ang traath: edit ter to eme) textthat
+ # the = M of harac ched graph ate uch her.
+ #NGLE ightre c.9em" atta al, rotD, sh ot
+ A e hesqua= "0 be seri me toue I eac
+ Linure GHT me to x, aphe uniqs to
+ # ens_HEI s na (y, d grme aation
+ #LINE clas "t" t of s anaphe rot
+ yle SS = a lis nateh grrent
+ # St_CLA as ordi eaciffe
+ EDIT nput he cogivess d
+ ad i ed td to acro
+ # Lo y neaddeheme
+ # onl is grap 0
+ # Wemberame put _y = {|c|
+ # nuhe sd_in] ursor ster
+ # t loa = [ = c ine|_clu 8
+ defdataor_x 0 ne{|lheme x %
+ cursal =h_li_grap 1 sor_r\n" ]
+ seri.eaceach= " "x +=" cur= "\ , c]
+ ARGFine. c =rsor_ "\t 8 - c = rial
+ l if cu c ==x +=" || x, se
+ lsifrsor_ "\n0 sor_
+ e cu c ==x = 1 cur
+ lsifrsor_y += r_y, t
+ e cursor_ urso thay
+ cu [[c= 1 ilityed bext.
+ lse ta +=_x +1 obabollowal t
+ e daursor += e pron frigin
+ cerial ass. s thtatihe o
+ s of m easee roin t l be hat
+ end nter incrkwislts wilges thave
+ m ce nter clocresu heret ed We
+ } a fro n cee. a sa) o, taighion.
+ dat nter atio, i.e ver zer strotat
+ } turn n ce rotible vic frome.g.he r ,
+ re tatio s asvers (or away er, er t tione.
+ nd e ro mase retion und cent aftcts: rotaitiv
+ e mput er ofll brota e rotionld betifa o. ox. ftern pos
+ # Co centon wiise ow wrotashouse ar zerng box aemai r
+ # ing tatilockw to hthe hey the d ofunding bon r othey
+ # Use roterc due ear an tduce nsteaf bounditati s atbilittion
+ # thcoun ely, ts nd tho re ty iter of bor ro factersiul op
+ # a unattifacaggegs t fini cenner oafte arti revusef f
+ # forte arore jthin e ino be cor and get theost er or
+ # Unsiblut mious itivon to before we losehe m centr fo
+ # vime o var postation ts be thatlso be t theedito
+ # coried owardf rotatinate e is we a to ause an and the t
+ # t nd tter of roordi thes andpears ll catesaddsm in tha
+ # Rou center ol co of ter,s ap s wienerrts rithd for
+ # - Set cenat al all cen mas cterly guppoalgo neer).
+ # - Seth th with ther of charat onso sion reatithe
+ # - suc ened d ofente. ing script alotat a gol e
+ # happnsteang cacts emovhis r thahe re iss to
+ # hat es i Usirtif nd rhy tditoent tther thi
+ # Wplacure.the a ng ais wan eplemsure for
+ # featite ertiich ave reimnot need
+ # desp t ins, whTo h to am any
+ # thashiftt. need. Ie is
+ # Note to g texill riptther
+ # massacin we wvascure
+ # repltes,ed jaot sa)
+ # deleerat am n(dat ze
+ # genl (Ienter ta.si
+ # tooet_cy = 0t| / da
+ #ef g = cach{|1] cy
+ d cxta.e+= t[0] ize,
+ da cx += t[ ta.s es.
+ cy / da inat
+ n cx oorda)
+ } etur to ccy, .
+ r ion cx, 2) gain
+ end rotatta, (a / X a
+ ply te(da:tan(a) | r in
+ # AprotaMath::sinp{|tnts. shea
+ def x = Math:a.maonte then
+ ry = n dater c cx Y,
+ retur Cent1] - cy r in me).
+ r # = t[0] - shead aphe eme.
+ x = t[ X, round , gr graph
+ y r in y).round erial ach
+ Shearx * x).roun x, s nd e
+ # -= (ry * y). (y, arou
+ x += (rx * ted ] pan
+ y -= ( updat[3] ne s
+ x urn 2], ith o
+ # Ret, t[ xt wfix)
+ [y, x L te_pre
+ HTM, id
+ } k ofdata0] n
+ d blocock(ata[ ].min
+ en rate e_bl = d t[1]].mi
+ Geneneratin_xt| _x, t[0]
+ # f ge_y, mch{|[min_y,
+ de minta.eax = [min >\n"
+ da min_y = ix}\"
+ min_ x pref
+ min_y {id_
+ } x = min_=\"# r_y)
+ rsor_y = e id{|t| curso
+ cursor_"<preach y -
+ cuxt = ort.t < y * (
+ teata.sx = or_y"\n"
+ d y, curs += = yin_x
+ if textor_y = m
+ cursor_x
+ curs [3] &" "
+ = t== "amp;"
+ end textext = "&= "<
+ ner_er_text xt =lt;"" ' +
+ in inner_tr_te= "&= "> '">
+ if inninneext xt =gt;" SS +_s +
+ sif er_tr_te= "& x) +_CLA].to
+ el inninneext rsor_EDIT t[2
+ sif er_t - cu"' + ix +
+ el inn (x ass=_pref
+ d " *n cl+ id low.
+ en += "<spa="' t + er be
+ ext '" id_tex numb
+ t 'nneran>" ial ns.
+ i</sp+ 1 ser colum
+ "= x re>" next. by
+ r_x n</p the airs them
+ urso + "\ find er p oup
+ c ext er, numbta) d gr
+ } rn t numbrialap(das an
+ retu ial f sewn_my row
+ nd serst or_dors b
+ e eacha liursoumbe
+ For rns te_cal n ]]
+ # Retuneraseri} |t| ] [t[2
+ #ef geort = {ach{t[1]] += ]
+ d # Sumnsrt.emns[t[1] [t[2]
+ cola.socolumns[ ] = n.
+ dat if colu t[1] olum 0]]) .
+ e mns[ ch c [r[ ight
+ elscolu or ea 1] + he r by
+ rs f e - to t sortnext
+ end umbe .siz mber we the
+ al n r| [1, r l nu o if us
+ } seri [] |_, ip(r eria s, s get
+ Add ls =ach{ r.z xt s inateould )
+ # erians.es += e ners. oordhat w [0]]
+ solumrial s d th pai n) c1, t ials
+ c se rial , finmberta) olum by [ser a
+ n se mberal nup(daw, cials 1] + extr
+ }etur l nuseriht_ma (ro ser e - one
+ r eria of _rig with all .siz get
+ end ch slistrsortartotate [2]}ials . e we ,'
+ r eas a e_cues snd r t| t ser airs sinc + '"
+ # Foturnerattuples a. map{|s[1, ue p) ine, o_s
+ # Re genput tuplbersort.erial valairsnewl 1].t
+ def# Input numta.sip(s keyal_ping + t[
+ # inrial= dals.z s asserirail efix
+ # seals eria pairix, ut t + pr
+ serirn s mber(prefitho "'
+ retu l nu_mapies wate. '":
+ eriarsorentrtempl|t| _s +
+ end at st_cumap the .map{].to
+ Formnverate rom airs+ t[0
+ # f coenerne fal_pfix
+ de # Gewliseri pre es.
+ # nurn "' + valu
+ ret' " nate
+ "\n ordi
+ } * f co) a[0]
+ d ge odata dat
+ en ranize(_x =
+ Getet_s minn_x
+ #ef gn_y,= min_y min 1
+ d mix_x = mi|t| , x].max y +
+ max_y ach{t in_x, x].min min_
+ mata.ex = = [max_x, y].max y -
+ da y, n_x = [min_y, y]. max_
+ mix_x = [max_y 1,
+ man_y = [m n_x +
+ mix_y - mi
+ ma x_x
+ n ma
+ }etur
+ r ut
+ end _inp
+ loadty? ) a) GLE)) /td>
+ a = .emp (data(dat -ANNGLE ")}< >
+ datdatarn nter_size cy,y, A d> r> , "Ltd> }</td
+ if retu t_ce get cx,x, c /hea "><tdata)}</"R")
+ = geht = data,a, c le>< ng="4eft_ "M"ta,
+ end cy heig ate(e(dat /tit addiock(lata,t_da
+ cx,th, rototat est< ellpe_block(drigh
+ wid ta = = r te t 1" cerate_bllock(
+ t_dadata >Rota ng="{generatte_b l>
+ lefght_ OT" itle pacip">#{gennera labe
+ ri <<"Ed><t cells="top">##{ge le)</
+ int <hea "1" align="toop"> "> togg or
+ prtml> der=t" valignn="t edit to l> errght,
+ <hody> bor"left" vvalig id="(ESC"><u izes-hei
+ <bableign="lefft" ox" dit nonet. minimline
+ <td align=="le heckble elay: tex hat sing
+ <td allignble>e="c>Enabdisplace . ne tcreap.
+ <ttd a</ta typdit"yle=" repor. anel he oy deerla
+ </tr>nputr="e" sters =cursct p ep tis bo ov
+ <p><il fohelpractmove sele nd keo thes t
+ <labeid=" chas = ab = s, also d lin
+ <div SCII keyft+to. "> aluean ae the ;
+ <li>Arrow shi und riptng vWe ccaus ght)
+ <li>Aab /+Z = vascpacio. ill tHei
+ <li>TCtrliv> xt/jaer-sratich wM");; offse
+ <<li>></d ="te lettect o muId("HT}" / p.
+ </ul typerentd aspt tontByHEIG dth
+ ipt iffesireg thalemeINE_ etWi
+ <scrry de deasin.getE"#{L 2) offs
+ // To thecrementht = 0.0 - p.
+ // tut ddocuHeig x += em";ght}
+ // bp = line= 0; 2; .0; + "{hei
+ var yle.t_x or =x < 2 = x / #
+ p.st bes_err 0; cingdth}
+ var min x = erSpa#{wi
+ var(var lett.abs( )
+ for yle.Mathr > e
+ { p.ste = erro ; ;
+ var min_ r = e"em"
+ if( erro x + t_x;); }";
+ { min__x = bes("L"IGHT
+ best ng =ByIdE_HE
+ Spaciment{LINt_x;"); T}";
+ } tteretEle= "# besd("REIGH
+ e.lent.gight ng =tByINE_H;
+ } stylcumeneHeSpaciemen#{LIst_x
+ p.= doe.littergetEl = "= be
+ p style.leent.eighting
+ p.stylocumineHrSpac ta))}
+ p. = dle.lette e; t_da }
+ p.style.l tate.fals . (lefa))}ata))
+ p.sty le sit = tion ns. _map(datht_d
+ p itabe_ed posi itio down_map(rig
+ / Wrnabl rsor ; pos rsor_down_map )}
+ /ar e t cu "M0" rsor e_cursor_down data)
+ v rrenor = t cu erate_cursor_ eft_)} ))}
+ / Cucurs o nex generate_cu ap(lata)_data
+ /var ng t "L", generat ht_map(dight
+ appi = map("M", gen _right_map(r
+ // Mdown rsor_map("R", ursor_right_m
+ var t_cursor_map( te_cursor_rig
+ { nvert_cursor_ nerate_cursor
+ #{convert_cu , generate_c
+ #{conver ("L", genera rs.
+ #{co ht = _map("M", ge ) pai
+ }; rig ursor_map("R" text
+ var rt_cursor_map nal
+ { onvert_cursor rigi
+ #{convert_c x, o n))
+ #{conve uffi . (dow
+ #{c (id s []; ions tries
+ }; of ist = osit t.en )
+ Listdo_l or p bjec ght)
+ // r un curs of O s(ri
+ va erse}; ue] ntrie
+ Rev = { {}; val ct.e
+ //r upft =[key, Obje
+ var lenst key; of
+ var(co e] = lue]
+ fo valu , va on.
+ { up[ [key ey; siti
+ onst = k e po ;
+ }or(c alue] . e sam tyle;
+ f ft[v okes$/; t th = style;
+ { le ystr!-~] rs a tyle = style
+ ed ke /^[ actele) x).style = s
+ } cepteys = char sty suffix).style
+ / AcditK for (id, (1);" + suffix).s
+ /ar e tyle tyle bstrd("L" + suffi
+ v et s setS d.sutById("M" +
+ // Stion = iementById("R .
+ func uffixetElementByI tion
+ { ar sent.getElemen posi
+ vocument.getEl n. sor
+ document.g itio s cur . 0";
+ docum posd) viou tion ff808
+ d rsoror(i pre posi nd:# ;
+ } te cuCurs t at rsor ); grou xt])
+ Updan set ligh""); w cu f80"back erTe
+ // ctio highor, t ne :#80f = " .inn
+ fun lear curs ht a oundstyle ion. fix)
+ { // Ctyle( hlig ckgrid). osit suf
+ setS hig "bayId( me p "M" +t;
+ pdate id;sor,entB at sa yId(= text;
+ // Usor =(curElem ers entBext = text;
+ curStyle.get ractxt) ElemnerText = tex
+ setument cha, te .get).innerText
+ doc fort(id 1); umentffix).innerT
+ texteTex str( doc + suffix).in
+ } lace plac .subfix,("L" + suffix
+ Repon re = id[sufById("M" + su
+ //ncti fix ush(mentById("R"
+ fu r sufst.ptElementById
+ { vado_lit.getElement
+ uncument.getEle
+ document.ge it. [1];
+ documen t ed ) ntry[1];
+ do ecen = 0 t = entry[1];
+ st r() th = rText = entry
+ } do moundo leng innerText = e
+ / Untion ist. ); ix).innerTex
+ /func do_l pop( suffix).inne
+ { f( un ist. L" + suffix).
+ i urn; do_l[0];Id("M" + suff
+ { ret = unntryntById("R" +
+ try = elementById("
+ }ar enffixgetElementBy x);
+ var suent.getEleme suffi
+ vdocument.getEit ) ] +
+ document.e_ed or[0
+ documnabl curs ";
+ if( e sor( line
+ { tCur "in
+ se us. lay =
+ } statt() it; disp
+ dit _edi e_ed yle. ;
+ } ge eggle nabl ).st one"
+ Chann to = !e) elp" = "n
+ //nctio dit dit ; Id("h play ;
+ fu le_ele_e sor)ntBy .dis edit
+ { enabenab (curleme tyle able_ )
+ if( rsorgetE ").s = en S}")
+ { etCuent. ; help ked CLAS
+ socum "")yId(" chec DIT_
+ d sor,entB t"). ("#{E
+ } e (curElem ("edi Name
+ els tyle.get ById lass
+ { setSment ment sByC {
+ docu tEle ment =>
+ t.ge s. etEle ent)
+ } umen enernt.g (ev
+ doc listcume ick",
+ ent f do ("cl
+ } d ev t o ener ) => {
+ // Adonst Listedit nt) ) )
+ for(c ventble_ id); (eve "Z"
+ { addE ena r(t. n", y ==
+ t. if( urso ydow t.ke
+ { setC r("ke even
+ tene ||
+ } tLis) e" ) = "z"
+ ); Evendit Escap ey =
+ } .addle_e == " nt.k ) )
+ } mentenab key (eve "Z"
+ docuf( ! ent. (); && y ==
+ i ( ev edit rlKey t.ke
+ { if gle_ t.ct even
+ { tog even " ||
+ if( = "z
+ }lse ; ey =
+ e do() nt.k
+ { un (eve
+ n; y && s) )
+ }etur rlKe tKey
+ r t.ct (edi );
+ } even match .key
+ if( (); key. vent
+ { undo ent. or, er]); )
+ ( ev curscurso wUp" e" )
+ } e if ext(ght[ Arro spac
+ els aceTr(ri == " Back
+ { replurso .key ); n" ) == "
+ setC vent rsor] wDow key
+ f( e p[cu Arro vent. )
+ } se i or(u == " || e " "
+ el Curs .key r]); ft" ==
+ { set vent curso owLe .key
+ f( e own[ "Arr event
+ } se i or(d y == ; || M"}
+ el Curs t.ke or]) ght" ": "L"};
+ { set even [curs owRi , "R": "
+ if( left "Arr "L", "R
+ } lse sor( y == ); "M": "R"
+ e tCur t.ke sor] "R", "M":;
+ { se even t[cur b" ) L": "M", r(1)
+ if( righ "Ta ? {"L": subst
+ }lse sor( ey == Key : {"sor.
+ e tCur nt.k hift cur {
+ { se eve ent.s ]] + ) =>
+ if( = ev or[0 vent
+ }else ext [curs " ) , (e
+ { ar n nextsor); cape ick"
+ v r = (cur "Es ("cl
+ ursorsor ey == ener
+ cetCu nt.k tList
+ s eve ); Even
+ } if( dit( (); .add
+ else le_e fault it")
+ { togg ntDe ("ed
+ reve tById
+ } nt.p emen
+ eve etEl();
+ ; nt.gedit
+ })cumegle_
+ do tog
+ ; pt> >
+ })scri html
+ </ y></
+ /bod
+ <OT
+ E
diff --git a/sample/trick2025/02-mame/authors.markdown b/sample/trick2025/02-mame/authors.markdown
new file mode 100644
index 0000000000..0e420fdf5d
--- /dev/null
+++ b/sample/trick2025/02-mame/authors.markdown
@@ -0,0 +1,3 @@
+* Yusuke Endoh
+ * mame@ruby-lang.org
+ * cctld: jp
diff --git a/sample/trick2025/02-mame/entry.rb b/sample/trick2025/02-mame/entry.rb
new file mode 100644
index 0000000000..d5de370dc9
--- /dev/null
+++ b/sample/trick2025/02-mame/entry.rb
@@ -0,0 +1,34 @@
+From:pd <pd-@example.com> (`)
+Date:Wed,01 Jan 2025 00:00:00 +0000
+Subject:[PATCH] +an(/.{40}/));exit].gsub(/X.*X|\n(\h+\s)?\+?/,E=""))#TRICK2025]}
+
+--- /dev/null
++++ pd.rb
+@@ -0,0 +1,27 @@
++%;`);;BEGIN{eval$s=%q[eval(%q[F=File;puts((dup)?(q="%;`);;BEGIN
++{eval$s=%q[#$s]}";U,*,V=R=(0..33.0).map{|t|q.gsub(/./){i=$`.siz
++e;c=(i/64*2i-26i+i%64-31)*1i**(t/16.5);x,y=c.rect;r=c.abs;r<13?
++4<=r&&r<6&&x>-4||-5<x&&x<=-3&&-6<y&&y<11??.:?X:$&}};B,A="---|%s
++\n+++|%s\n@@|-%s,%s|+%s,%s|@@\nFrom:pd|<pd-@example.com>|(`)\nD
++ate:%a,%d|%b|%Y|%T|%z\nSubject:[PATCH]|".tr(?|,z="\s")[/@.*\n/]
++,$`;(i=R.index(q))?(S,T=i<33?R[i,(f=->i{(Time.gm(2025)+86400*i)
++.strftime$'};o=f[i+1]+(fXXXXXXXXXXXXXXX[0]+q[/.*\z/]+?\n*2+A%["
++/dev/null",v="pd.rb"]+XXXXXXXXXXXXXXXXXXXB%[0,0,1,27]+U.gsub(/^
++/,?+)).lines[-i-2],EXXXXXXXXXXXXXXXXXXXXXXX,a=A%[v,v];V<<"\n(`\
++n#{a+B%[0,0,1,1]}+dXXXXXXXXXXXXXXXXXXXXXXXXXup=(`)";2)]:($*.siz
++e!=2&&abort(["usagXXXXXXXXX.........XXXXXXXXXe:",$0,F,F]*z);$*.
++map{o=A%$*;F.read(XXXXXXXXX..XXXXXX..XXXXXXXX_1)});a=[i=0]*v=(s
++=[s]+S.lines).sizeXXXXXXXXX..XXXXXX..XXXXXXXX;c=b=s.map{c=_1&&[
++c,?-+_1,i+=1]||0};XXXXXXXXX..XXXXXX..XXXXXXXXT.lines{|t|s.map{|
++s|a<<((s)?[x=a[-1]XXXXXXXXX.........XXXXXXXXX,y=a[-v]].max+((f=
++s==t)?1:0):0);c,d=(XXXXXXXX..XXXXXXXXXXXXXXXf)?[v+1,z+t]:s&&(x>
++y)?[1,?-+s]:[v,?++t]XXXXXXX..XXXXXXXXXXXXXX;b<<[b[-c],d,i+=1]}}
++;c=b[-1].flatten;b=c[(XXXXX..XXXXXXXXXXXX1..)%2];(b.map{_1[0]}*
++E).scan(/\s{,3}([-+]\s{,XXXXXXXXXXXXXXX6})*[-+]\s{,3}/){n=c[2*i
++=$`.size];o=o,B%[n%v+1-1/v,(m=c[2.*i+j=$&.size]-n)%v,T>""?n/v+1
++:0,m/v],b[i,j]}):(o=[];a,b=[A,B].map{_1.sub(?+){'\+'}%(['(\S+)'
++]*4)};$<.read=~//;F.write$2,(s=F.readlines$1;o<<[:patching,F,$2
++]*z;(*,n,i,m=[*$~].map{_1.to_i};n+=m;($'=~/\A((-)|\+)?(.*\n)/;$
++2?s[i-=1,1]=[]:$1?s[i-1,0]=$3:n-=1;i+=1;n-=1)while+n>0)while/\A
++#{b}/=~$';s*E)while$'=~/^#{a}/);o):([*[?l]*799,1].shuffle*E).sc
++an(/.{40}/));exit].gsub(/X.*X|\n(\h+\s)?\+?/,E=""))#TRICK2025]}
diff --git a/sample/trick2025/02-mame/remarks.markdown b/sample/trick2025/02-mame/remarks.markdown
new file mode 100644
index 0000000000..8be86ebc2d
--- /dev/null
+++ b/sample/trick2025/02-mame/remarks.markdown
@@ -0,0 +1,141 @@
+# A Lesser "Patch" Program
+
+This program is a minimalistic version of the traditional "patch" command, which looks like a patch.
+
+## Usage as a "Patch" Command
+
+The program reads a unified diff file from standard input and applies the changes to the specified files.
+
+To apply `test.patch` to `sample.rb`, use the following commands:
+
+```
+$ cp sample.orig.rb sample.rb
+$ ruby entry.rb < test.patch
+```
+
+After running these commands, verify that `sample.rb` has been modified.
+
+## Usage as a Patch File
+
+Interestingly, this program is not just a patch-like tools -- it *is* a patch.
+This duality allows it to be applied like a regular patch file.
+
+The following will create a file named pd.rb.
+
+```
+$ patch < entry.rb
+```
+
+Alternatively, you can achieve the same result using `entry.rb`:
+
+```
+$ ruby entry.rb < entry.rb
+```
+
+The generated `pd.rb` produces a new patch.
+
+```
+$ ruby pd.rb
+```
+
+The produced patch is self-referential, targeting `pd.rb` itself.
+To apply it:
+
+```
+$ ruby pd.rb | ruby entry.rb
+```
+
+You'll notice the `p` logo rotates slightly counterclockwise.
+
+The modified `pd.rb` outputs the patch for itself again, apply the patch repeatedly--a total of 33 times!
+
+## From `p` to `d`
+
+The center `p` logo symbolizes a "patch."
+When rotated 180 degrees, it resembles a `d`, signifying a transformation in functionality.
+`pd.rb` now operates as a simplified "diff" command:
+
+```
+$ ruby pd.rb
+usage: pd.rb File File
+
+$ ruby pd.rb sample.orig.rb sample.rb
+--- sample.orig.rb
++++ sample.rb
+...
+```
+
+## Integration with Git
+
+The patches are compatible with Git's `git am` command, which imports patches in mbox format.
+
+Start fresh by removing `pd.rb` and initializing a Git repository:
+
+```
+$ rm -f pd.rb
+$ git init
+Initialized empty Git repository in /home/...
+```
+
+And import `entry.rb` as a patch to the repository:
+
+```
+$ git am --committer-date-is-author-date entry.rb
+Applying: +(/.{40}/));exit].gsub(/X.*X|\n(\h+\s)?\+?/,E=""))#_TRICK2025_]}
+applying to an empty history
+```
+
+Verify the commit history:
+
+```
+$ git log
+commit 1e32693f11c1df77bd797c7b3e9f108a3e139824 (HEAD -> main)
+Author: pd (`) <pd-@example.com>
+Date: Wed Jan 1 00:00:00 2025 +0000
+
+ +an(/.{40}/));exit].gsub(/X.*X|\n(\h+\s)?\+?/,E=""))#TRICK2025]}
+```
+
+Notice that the Author and Date are properly set.
+
+To apply subsequent patches:
+
+```
+$ for i in `seq 0 32`; do ruby pd.rb | git am --committer-date-is-author-date; done
+```
+
+*(A fun details: you will see the `b` logo!)*
+
+Now, view a commit history by the following command:
+
+```
+$ git log --oneline
+```
+
+You will rediscover the original `entry.rb` unexpectedly.
+
+If you set `--committer-date-is-author-date` appropriately, you should be able to run the output of `git log --oneline` as is.
+
+Try this unusual command:
+
+```
+$ git log --oneline | ruby - test.patch
+```
+
+## A Little Something Extra
+
+Interestingly, `pd.rb` -- functioning as a diff command -- is a patch to itself.
+Reveal hidden details with:
+
+```
+$ ruby entry.rb pd.rb
+$ ruby pd.rb
+```
+
+Can you spot the difference?
+
+## Limitations
+
+* I tested it with ruby 3.3.6, git 2.45.2, and GNU patch 2.7.6.
+* No error check at all. The lesser patch do not care if there is a discrepancy between what is written in the patch and the input file, and will write over the existing file without prompt.
+* It is assumed that the text files have a new line at the end.
diff --git a/sample/trick2025/02-mame/sample.orig.rb b/sample/trick2025/02-mame/sample.orig.rb
new file mode 100644
index 0000000000..3d880b387d
--- /dev/null
+++ b/sample/trick2025/02-mame/sample.orig.rb
@@ -0,0 +1,8 @@
+def add(a, b)
+ a + b
+end
+
+if __FILE__ == $0
+ result = add(3, 5)
+ puts "Three plus five is #{ result }"
+end
diff --git a/sample/trick2025/02-mame/test.patch b/sample/trick2025/02-mame/test.patch
new file mode 100644
index 0000000000..0a63ae8a4c
--- /dev/null
+++ b/sample/trick2025/02-mame/test.patch
@@ -0,0 +1,16 @@
+--- sample.rb
++++ sample.rb
+@@ -2,7 +2,13 @@
+ a + b
+ end
+
++def sub(a, b)
++ a - b
++end
++
+ if __FILE__ == $0
+ result = add(3, 5)
+ puts "Three plus five is #{ result }"
++ result = sub(5, 3)
++ puts "five minus three is #{ result }"
+ end
diff --git a/sample/trick2025/03-tompng/authors.markdown b/sample/trick2025/03-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2025/03-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2025/03-tompng/entry.rb b/sample/trick2025/03-tompng/entry.rb
new file mode 100644
index 0000000000..0de2244979
--- /dev/null
+++ b/sample/trick2025/03-tompng/entry.rb
@@ -0,0 +1,74 @@
+eval->{%w[u=*? a..?i;l=*?j..? r;o=*?s..?z,?_
+;m='#-*&|^`!@$ '.chars;s=[[3] ,[0,1,3,4,6],[
+1,5],[1,4],[0, 4,6],[2,4],[2] ,[1,3,4,6],[],
+ [4], ];a= (0.. 7).m ap{[
+ ?;*_ 1+'a 4',: a1,? x*(_
+ 1+2) ]*"T /#{? x.*6 7-_1
+ }/x; "};v =([c =[?x *150
+ ]*4, a.reverse,[[6, 3,0].map{"a#{_
+ 1}T/ #{?x*15}/x"}*( ?;*42)+';xx']*
+ 30,a .map{_1.tr'14' ,'25'},c,]*n=$
+ /).g sub( /(^| ;)(;
+ *);/ ){$1 +?x* $2.s
+ ize+ ?;}; p,e= [0,1
+ ].ma p{|t |g=( ["(m
+ fT/' /;#{a=(0..9).m ap{"f#{_1}T/l#
+ {_1} =1./"}*?;};C"' ;#{a.tr'l',?u}
+ ;?C" ",(0..9).map{| i|a="l#{i}T/'/
+
+
+;C"' ;";b ="#{o[i-1]+?=i fTi>0}%+";(1..
+9).m ap{a <<l[_1-1]+"T/% #{c=m[_1]}/;";
+b<<c +";# {(d=_1+i)>10?' ca='+o[d-11]:d
+>9?' ca': o[d- 1]}=
+%"+c };a+ b+[m [1..
+],?+ ,?"] *';? '},"
+caT/ '/;C "';" +(1.
+.8).map{u[8-_1 ]+"T/#{u[9-_1] }=1./;"}*''+u[
+0]+h='=1;?"',( 1..9).map{|i|" u#{i}T/'/;C"';
+"+(0..8-i).map {u[8-i-_1]+"T/ #{u[8-_1]}=1./
+ ;"}* ''+u [i-1 ]+h}
+ ]*?; ).sp lit( /([^
+ ;]+; )/); ((0. .43)
+ .map {|y| c='' ;q=-
+ >{a= (y-22).abs;b=( c.size+_1-78).
+ abs; [a<7&&b<59||b< 15,(b-30).abs<
+ 14][ t]};110.times{ c+=q[8]?g.shif
+
+
+ t||? ;:q[-t]??;:'T' };c.gsub(';T',
+ 'TT' ).rstrip}*n).g sub(/(;|T)(;;+
+ )(;| $)/){$1+'/'+?x *($2.size-2)+'
+ /'+$ 3}}; F=Fi
+ le;1 0.ti mes{
+ |i|a ="(n fT/m
+ f=l# {i}= '/;n
+ f=f# {i}=?';def/("+ s[i].map!{"a#{
+ _1}" }*','+')=(';F. write"#{i}",a+
+ ?x*( 150-a.size)+n+ v[..-5]+'))&&'
+ +n}; a,*b ="/)
+ &&de f((C nCn<
+ <A"; c=") )./(
+ ";d= 'T}' ,?A,
+ '__= <<B';u.map{|v| b<<v+"TT%(T<<#
+ {v}T )TT";d=d,v,"AC n#{v}=<<B"};o.
+ zip( l,m){a+=?;+_1+ "T/%#{_3}/";c+
+
+
+=(_3[?#]||?#+_
+3)+"))./("+_2+
+?,};F.write'+'
+,n*2 6+[p [..-
+10]+ (a+c ).tr ('!@',%("')).g
+sub( /%([ '"`] )/,){$1},'#{TT
+T'+b *';T T'+d *n,?B,')=']*n+
+n;a, b=[u ,o].map{|v|(0.
+.8). map{ t=s[9-_1];t[0]
+&&t+ =[1] ;v[8-_1]+"T/#{
+t*?= }#/" }*?; +';a3='+?x*25}
+;F.w rite '=', [n*26]*2*e+a+n
++v+n +(b+ n+v) .tr(?a,?b)[..-
+9]+' )if~ exit
+'+n].join.tr('
+TRICK',_1)}[+"
+\x2025"<<-?\\]
diff --git a/sample/trick2025/03-tompng/remarks.markdown b/sample/trick2025/03-tompng/remarks.markdown
new file mode 100644
index 0000000000..7f3f4ac6cf
--- /dev/null
+++ b/sample/trick2025/03-tompng/remarks.markdown
@@ -0,0 +1,146 @@
+### Remarks
+
+Run it with no argument. It will generate 12 files: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + and =.
+
+```sh
+ruby entry.rb
+```
+
+Concat and syntax highlight them.
+
+```sh
+cat 2 0 + 2 5 = | ruby -run -e colorize
+cat 4 + 1 5 + 4 + 1 8 = | ruby -run -e colorize
+```
+
+I confirmed the following implementations/platforms:
+
+* ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +MN +PRISM [arm64-darwin22]
+* ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT +MN [arm64-darwin22]
+
+### Description
+
+Did you know that Ruby syntax can perform additive operations on two-digit numbers without Ruby runtime? This entry demonstrates a syntax level computation of Ruby grammar.
+
+`ruby entry.rb` will generate 12 files: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + and =.
+These files constitute a calculator system that runs on Ruby parser.
+
+To calculate `6 + 7`, concat `6`, `+`, `7` and `=`.
+
+```sh
+cat 6 + 7 =
+```
+
+The concatenated output is a Ruby script that does nothing. It is also an ASCII art of `█ + █ = ██` rotated 90 degrees.
+Now, let's try syntax highlighting that code.
+
+```sh
+cat 6 + 7 = | ruby -run -e colorize
+```
+
+Wow! You can see the calculation result `6 + 7 = 13` as a colorized ASCII art!
+
+This system can also add more than two numbers. All numbers should be one or two digits, and the answer should be less than 100.
+
+```sh
+cat 3 1 + 4 + 1 5 + 9 = | ruby -run -e colorize
+cat 1 + 2 + 4 + 8 + 1 6 + 3 2 = | ruby -run -e colorize
+cat 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 1 0 = | ruby -run -e colorize
+```
+
+If the syntax highlighting is hard to see, use this command to change the terminal color.
+
+```sh
+printf "\e]11;#000000\a\e]10;#333333\a\e]4;1;#ffaaaa\a"
+```
+
+### Internals
+
+To perform calculation, you need a storage and a control flow statement.
+Local variable existence can be used as a storage.
+Ruby syntax provides conditional local variable definition and local variable reset with state carry over which can be used as a control flow statement.
+
+#### Conditional Local Variable Definition
+
+Ruby syntax can define new local variables conditionally.
+
+```ruby
+# Defines x and y if a is defined
+a /x = y = 1./
+# Defines x and y if a is not defined
+a /1#/; x = y = 1
+# Defines x or y depend on the existence of local variable a
+a /(x=1);'/;(y=1);?'
+```
+
+#### Local Variables Reset
+
+Local variables can be cleared by creating a new `def` scope.
+
+```ruby
+x = y = z = 1
+def f
+# x, y, z are cleared
+```
+
+#### State Carry Over
+
+Some state should be carried over to the next `def` scope. There are two tricks to do it.
+
+```ruby
+a /%+/i; b /%-/i; def f(x)# +; def f(y) # -; def f(z)
+```
+
+```ruby
+a %(<<A); b %(<<B); def f
+x=<<C
+A
+y=<<C
+B
+z=<<C
+C
+```
+
+In both examples above, local variable defined in the new scope will be:
+
+```ruby
+x # if both a and b are not defined
+y # if a is defined
+z # if b is defined
+```
+
+Combining these two tricks, Ruby syntax can carry over two states to the next `def` scope. In this system, two states represents upper digit and lower digit.
+
+### File Structure
+
+```ruby
+# File 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
+(code) &&
+```
+
+```ruby
+# File +
+(code) && def f(arg)=
+```
+
+```ruby
+# File =
+(code) if exit
+```
+
+```ruby
+# cat 1 2 + 3 + 4 5 =
+(one) &&
+(two) &&
+(plus) && def f(arg)=
+(three) &&
+(plus) && def f(arg)=
+(four) &&
+(five) &&
+(equal) if exit
+```
+
+### Limitation
+
+Number to be added must be one or two digits.
+Answer of the addition must be less than 100.
diff --git a/sample/trick2025/04-tompng/authors.markdown b/sample/trick2025/04-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2025/04-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2025/04-tompng/entry.rb b/sample/trick2025/04-tompng/entry.rb
new file mode 100644
index 0000000000..881c1af871
--- /dev/null
+++ b/sample/trick2025/04-tompng/entry.rb
@@ -0,0 +1,36 @@
+ $c=%q@E="
+ \e[4%d;37m%s\e[m"
+ ;n=32.chr;pu ts"\e
+ [H\e[J#{$c=n*54+' $c=%
+ q'+[64.chr]*2*$c+';e val$
+ c.'+n*10+"\n"+n*57+"spl it*'
+ '"+n*15}";n=l=0;R=->y=0 {n+=1
+ ;l=$c.lines. map{|m|m=(0..79).chunk{380-n+
+ 36*Math.sin(0.04.*it-n )<9*y}.map{a=_2.map{m[it]}*''
+ ;_1&&E%[6,a]||a}*'';m!=l[~-y +=1]&&$><<"\e[#{y}H#{m}\e[37H
+ ";m}};N=(Integer$* [-1]resc ue+30)*H=44100;alias:r:rand
+ ;F=->e,w=1{a=b=c=0;d=( 1-e)**0 .5*20;->v=r-0.5{a=a*w*e+v
+ ;b=b*w*e*e+v;d.*a-2*b+c=c*w *e**3+ v}};A=->u,n,t{(0..n).
+ map{|i|u=u.shuffle.map{|w|R[]; a=u.s ample;b,c,d=[[0.5
+ ,(0.2+r)*H/3*1.1**i,[[1+r/10,1+r/ 10]][ i]||[1.2+
+ r/10,1.3+r/5]],[0.3,r*H/2,[1,1+r/5 ]]][t
+];e,f=d.shuffle;g=b+r;h=b+r;(0..[w. size/e, a.size/f
++c].max).map{g*(w[it*e]||0)+h*(a[[it-c,0].ma x*f]||0)}}}};j=A[A
+[(0..9).map{a=F[0.998,1i**0.02];(0..28097).m ap{a[].real.*0.1**(8.0*i
+t/H)-8e-6}},14,0].transpose.map{|d|a=[0]*3e3 ;15.times{|i|R [];b=r
+ (3e3);d[i].each_with_index{a[c=_2+b]=(a[c] ||0)+_1*0.63**i}} ;a},9,
+ 1][4..].flatten(1).shuffle;y=(0..3).map{F[ 1-1e-5]};m=[-1,1].map {[F[1
+ -1e-4],F[1-5e-5],it]};u=v=w=0;k=[],[],[] ;z=F[0.7,1i**0.5];File.o pen($
+ *.grep(/[^\d]/)[0]||'output.wav','wb') {|f|f<<'RIFF'+[N*4+36,'WA VEfmt
+ ',32,16,1,2,H,H*4,4,16,'data',N*4].p ack('Va7cVvvVVvva4V');N.tim es{|
+ i|$><<E%[4,?#]if(i+1)*80/N!=i*80 /N;t=[i/1e5,(N-i)/2e5,1].min;a,b,c=k
+ .map{it.shift||(j[20*r,0]=[g =j.pop];a=1+r/3;it[0..]=(0..g.size).m
+ ap{g[it*a]||0};0)};u=u *0.96+r-0.5;v=v*0.99+d=r-0.5;w=w*0.8+d
+ ;x=(z[].*1+0 .59i).imag;e=y.map(&:[]);f.<<m.map{|o,
+ p,q|r=a+(b+c)/2+(b-c)*q/5;s=o[r.abs]
+ ;r=t*t*(3-2*t)*(r+s*w/1e4+p[s]*x/1
+ e7+[[u,0],[v,1]].sum{_1*1.5**(e[
+ _2]+q*e[_2+2]/9)}/32)/9;r/(1
+ +r*r)**0.5*32768}.pack'v
+ *'}};puts@;eval$c.
+ split*''
diff --git a/sample/trick2025/04-tompng/remarks.markdown b/sample/trick2025/04-tompng/remarks.markdown
new file mode 100644
index 0000000000..b2d848c07c
--- /dev/null
+++ b/sample/trick2025/04-tompng/remarks.markdown
@@ -0,0 +1,43 @@
+# Seashore - Nature Sound
+
+Listen to the relaxing sound of ocean waves generated by Ruby.
+
+## Usage
+
+```sh
+ruby entry.rb
+ruby entry.rb seashore.wav 60
+```
+
+The default filename is `output.wav` and the default duration is 30 seconds.
+
+Tested with ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +MN +PRISM [arm64-darwin22]
+
+## Noise
+
+Noise sound is created by applying low-pass/band-pass filter to a white noise signal.
+Volume of the noise should change over time. This is also calculated by using low-pass filter.
+
+## Wave
+
+Sound of a single ocean wave is composed of hundreds of water splash sounds.
+Each water splash sound is composed of thousands of water drops and bubbles.
+This program creates this complicated sound by repeating sound mixing.
+
+```ruby
+sound2 = mix(sound1.change_pitch(rand), sound1.change_pitch(rand).delay(rand))
+sound4 = mix(sound2.change_pitch(rand), sound2.change_pitch(rand).delay(rand))
+...
+sound32768 = mix(sound16384.change_pitch(rand), sound16384.change_pitch(rand).delay(rand))
+
+splash1 = mix(sound1.delay(rand), sound2.delay(rand), sound4.delay(rand), ..., sound16384.delay(rand))
+splash2 = mix(splash1.change_pitch(rand), splash1.change_pitch(rand).delay(rand))
+splash4 = mix(splash2.change_pitch(rand), splash2.change_pitch(rand).delay(rand))
+...
+splash1024 = mix(splash512.change_pitch(rand), splash512.change_pitch(rand).delay(rand))
+
+wave_sound = [splash32, splash64, ..., splash1024].sample
+```
+
+This kind of repetition is often used in fractal rendering. In other words, this operation is rendering a fractal shape to the spectrogram canvas.
+It's an efficient way to create complex structure with low computational cost.
diff --git a/sample/trick2025/05-tompng/authors.markdown b/sample/trick2025/05-tompng/authors.markdown
new file mode 100644
index 0000000000..26ebe24da6
--- /dev/null
+++ b/sample/trick2025/05-tompng/authors.markdown
@@ -0,0 +1,3 @@
+* Tomoya Ishida (tompng)
+ * tomoyapenguin@gmail.com
+ * cctld: jp
diff --git a/sample/trick2025/05-tompng/entry.rb b/sample/trick2025/05-tompng/entry.rb
new file mode 100644
index 0000000000..4d30c5b5a9
--- /dev/null
+++ b/sample/trick2025/05-tompng/entry.rb
@@ -0,0 +1,118 @@
+module ReadableFizzBuzz
+ module Chain
+ end
+end
+
+include ReadableFizzBuzz
+
+Chain::Itself = Chain
+
+module Chain::Itself
+ module Void
+ A = B = C = D = E = F = G = H = I = J = K = L = M = Void
+ N = O = P = Q = R = S = T = U = V = W = X = Y = Z = Void
+
+ module Set
+ end
+
+ module Put
+ end
+
+ module WriteBack
+ end
+
+ module Not
+ include Void
+ end
+ end
+
+ module Off
+ include Void
+ end
+
+ module Nil
+ A = B = C = D = E = F = G = H = I = J = K = L = M = Off
+ N = O = P = Q = R = S = T = U = V = W = X = Y = Z = Off
+ end
+
+ module Next
+ include Nil
+ end
+
+ module Current
+ include Nil
+
+ Not = Off
+ Set = Put = Next
+ WriteBack = Current
+ end
+
+ True = If = Current
+ On = Next
+
+ module On
+ INT = 1
+ FIZZ = 'Fizz'
+ BUZZ = 'Buzz'
+ PREFIX = '0b'
+ FORMAT = "%d%s%s\n"
+ NEXT = __FILE__
+ end
+
+ module Off
+ INT = 0
+ FIZZ = BUZZ = nil
+ PREFIX = '0b1'
+ FORMAT = "%2$s%3$s\n"
+ NEXT = '/dev/null'
+ Not = True
+ end
+
+ module Initial
+ C = D = True
+ end
+
+ module ReadableFizzBuzz::Chain::Current
+ include Initial
+ end
+
+ If::C::Set::E = If::E::Set::F = If::F::Set::C = On
+ If::D::Set::G = If::G::Set::H = If::H::Set::I = If::I::Set::J = If::J::Set::D = On
+ If::F::Not::J::Not::Set::B = On
+ If::K::Not::Set::K = On
+ If::K::WriteBack::L = True
+ If::L::Not::M::Set::M = On
+ If::L::M::Not::Put::M = On
+ If::L::M::WriteBack::N = True
+ If::N::Not::O::Set::O = On
+ If::N::O::Not::Put::O = On
+ If::N::O::WriteBack::P = True
+ If::P::Not::Q::Set::Q = On
+ If::P::Q::Not::Put::Q = On
+ If::P::Q::WriteBack::R = True
+ If::R::Not::S::Set::S = On
+ If::R::S::Not::Put::S = On
+ If::R::S::WriteBack::T = True
+ If::T::Not::U::Set::U = On
+ If::T::U::Not::Put::U = On
+ If::T::U::WriteBack::V = True
+ If::V::Not::W::Set::W = On
+ If::V::W::Not::Put::W = On
+ If::V::W::WriteBack::X = True
+ If::X::Not::Y::Set::Y = On
+ If::X::Y::Not::Put::Y = On
+ If::X::Y::WriteBack::Z = True
+ If::Z::Not::Set::A = On
+end
+
+module Chain::Chain
+ Current = Chain::Next
+end
+
+include Chain
+
+module Chain::Current
+ NUMBER = A::PREFIX, Y::INT, W::INT, U::INT, S::INT, Q::INT, O::INT, M::INT, K::INT
+ printf B::FORMAT, NUMBER.join, C::FIZZ, D::BUZZ
+ load A::NEXT
+end
diff --git a/sample/trick2025/05-tompng/remarks.markdown b/sample/trick2025/05-tompng/remarks.markdown
new file mode 100644
index 0000000000..adb4bf4df8
--- /dev/null
+++ b/sample/trick2025/05-tompng/remarks.markdown
@@ -0,0 +1,106 @@
+### Remarks
+
+Just run it with no argument:
+
+```sh
+ruby entry.rb
+```
+
+I confirmed the following implementations/platforms:
+
+- ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +MN +PRISM [arm64-darwin22]
+- ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [aarch64-linux-musl]
+
+### Description
+
+Readability is important even for a simple fizz buzz program.
+
+These are the major ingredients of a spaghetti that makes program tasty and valuable but unreadable.
+
+- Many class definitions
+- Many method definitions
+- Many method calls
+- Many variables
+- Conditional branches
+
+These are what is acceptable for a readable program.
+
+- Many modules: Using only a single module in a program is not good.
+- Many constants: Better than magic numbers.
+- Module#include: Mixins are what module is for.
+- Many file loads: Usually better than loading a large file only once.
+- Minimal method calls: Needed for printing output.
+
+This program is doing something slightly difficult in the last few lines: print output and load ruby program.
+In contrast, the rest part of this program is extremely simple and easy. Module definition, constant definition and module inclusion. That's all.
+
+### Internals
+
+Called methods
+
+- `Module#include`
+- `Array#join`
+- `Kernel#printf`
+- `Kernel#load`
+
+Deeply nested module chain to avoid constant reassignment
+
+```ruby
+10.times do
+ module Root
+ module Chain
+ module X; end
+ module Y; end
+ module Z; end
+ end
+ end
+ include Root
+
+ module Chain::Chain
+ # Not a constant reassignment because Chain::Chain is always a new module
+ X = Chain::Y
+ Y = Chain::Z
+ Z = Chain::X
+ end
+ include Chain
+ p x: X, chain: Chain
+end
+```
+
+Constant allocation
+
+| Constant | Purpose |
+| ---------------------- | ----------------------- |
+| A | Loop condition |
+| B | Format (!Fizz && !Buzz) |
+| C, E, F | Fizz rotation |
+| D, G, H, I, J | Buzz rotation |
+| K, M, O, Q, S, U, W, Y | Iteration bits |
+| L, N, P, R, T, V, X, Z | Temporary carry bits |
+
+Instruction sequence with constant lookup magic
+
+```ruby
+# B = 1 if A
+If::A::Set::B = On
+
+# B = 1 if !A
+If::A::Not::Set::B = On
+
+# C = 1 if !A && B
+If::A::Not::B::Set::C = On
+
+# C = 1 if !A && !B
+If::A::Not::B::Not::Set::C = On
+```
+
+Loop with `load __FILE__`
+
+```ruby
+# A::NEXT is __FILE__ or '/dev/null'
+load A::NEXT
+```
+
+### Limitation
+
+Needs `/dev/null`
diff --git a/sample/trick2025/README.md b/sample/trick2025/README.md
new file mode 100644
index 0000000000..1d71f54620
--- /dev/null
+++ b/sample/trick2025/README.md
@@ -0,0 +1,16 @@
+This directory contains the top-five entries of
+the 5th Transcendental Ruby Imbroglio Contest for rubyKaigi (TRICK 2025).
+
+THESE ARE BAD EXAMPLES! You must NOT use them as a sample code.
+
+* 1st: "Most Revolutionary" - Don Yang
+* 2nd: "Most Useful" - Yusuke Endoh
+* 3rd: "Most Arithmetic" - Tomoya Ishida
+* 4th: "Best ASMR" - Tomoya Ishida
+* 5th: "Most Maintainable" - Tomoya Ishida
+
+These files are licensed under MIT license.
+
+For the contest outline and other winning entries, see:
+
+https://github.com/tric/trick2025
diff --git a/sample/trojan.rb b/sample/trojan.rb
index edf8ee63ce..cea0dae098 100644
--- a/sample/trojan.rb
+++ b/sample/trojan.rb
@@ -1,12 +1,13 @@
#! /usr/local/bin/ruby
-path = ENV['PATH'].split(/:/)
+
+path = ENV['PATH'].split(File::PATH_SEPARATOR)
for dir in path
if File.directory?(dir)
for f in d = Dir.open(dir)
- fpath = dir+"/"+f
+ fpath = File.join(dir, f)
if File.file?(fpath) && (File.stat(fpath).mode & 022) != 0
- printf("file %s is writable from other users\n", fpath)
+ printf("file %s is writable from other users\n", fpath)
end
end
d.close
diff --git a/sample/tsvr.rb b/sample/tsvr.rb
index fbc6545bb5..2fa6c87ef9 100644
--- a/sample/tsvr.rb
+++ b/sample/tsvr.rb
@@ -2,20 +2,17 @@
# usage: ruby tsvr.rb
require "socket"
-require "thread"
-gs = TCPserver.open(0)
+gs = TCPServer.open(0)
addr = gs.addr
addr.shift
-printf("server is on %d\n", addr.join(":"))
+printf("server is on %s\n", addr.join(":"))
-while TRUE
- ns = gs.accept
- print(ns, " is accepted\n")
- Thread.start do
- s = ns # save to dynamic variable
- while s.gets
- s.write($_)
+loop do
+ Thread.start(gs.accept) do |s|
+ print(s, " is accepted\n")
+ while line = s.gets
+ s.write(line)
end
print(s, " is gone\n")
s.close
diff --git a/sample/uumerge.rb b/sample/uumerge.rb
index 26f28923d5..1b81582c24 100644
--- a/sample/uumerge.rb
+++ b/sample/uumerge.rb
@@ -1,4 +1,4 @@
-#!/usr/local/bin/ruby
+#!/usr/bin/env ruby
if ARGV[0] == "-c"
out_stdout = 1
@@ -8,32 +8,32 @@ end
$sawbegin = 0
$sawend = 0
-while gets()
- if /^begin\s*(\d*)\s*(\S*)/
+while line = gets()
+ if /^begin\s*(\d*)\s*(\S*)/ =~ line
$mode, $file = $1, $2
$sawbegin+=1
if out_stdout
out = STDOUT
else
- out = open($file, "w") if $file != ""
+ out = File.open($file, "w") if $file != ""
end
out.binmode
- next
+ break
end
end
raise "missing begin" unless $sawbegin
out.binmode
-while gets()
- if /^end/
+while line = gets()
+ if /^end/ =~ line
$sawend+=1
out.close unless out_stdout
File.chmod $mode.oct, $file unless out_stdout
next
end
- sub(/[a-z]+$/, "") # handle stupid trailing lowercase letters
- next if /[a-z]/
+ line.sub!(/[a-z]+$/, "") # handle stupid trailing lowercase letters
+ next if /[a-z]/ =~ line
next if !(((($_[0] - 32) & 077) + 2) / 3 == $_.length / 4)
out << $_.unpack("u") if $sawbegin > $sawend
end
diff --git a/sample/weakref.rb b/sample/weakref.rb
new file mode 100644
index 0000000000..b9f38f954f
--- /dev/null
+++ b/sample/weakref.rb
@@ -0,0 +1,9 @@
+require 'weakref'
+
+foo = Object.new
+p foo.to_s # original's class
+foo = WeakRef.new(foo)
+p foo.to_s # should be same class
+ObjectSpace.garbage_collect
+ObjectSpace.garbage_collect
+p foo.to_s # should raise exception (recycled)