summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/deps/mruby/mrbgems
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:57:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:57:58 +0000
commitbe1c7e50e1e8809ea56f2c9d472eccd8ffd73a97 (patch)
tree9754ff1ca740f6346cf8483ec915d4054bc5da2d /web/server/h2o/libh2o/deps/mruby/mrbgems
parentInitial commit. (diff)
downloadnetdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.tar.xz
netdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.zip
Adding upstream version 1.44.3.upstream/1.44.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web/server/h2o/libh2o/deps/mruby/mrbgems')
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox79
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox9
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb811
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c244
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb383
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb286
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb701
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake9
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c507
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h26
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c239
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h14
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c78
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h13
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c436
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c501
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c58
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c64
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c759
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h165
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h16
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h20
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb12
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake33
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c584
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake16
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c336
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake30
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config20
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat42
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb60
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake12
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c254
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb73
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake6
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c155
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c30
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb34
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb30
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c3026
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords50
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def212
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h118
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y6652
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake40
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb711
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb171
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake7
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb163
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb53
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake7
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb645
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb546
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake10
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c100
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c59
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb55
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake7
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c346
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb101
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c24
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c420
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb208
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake8
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb477
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c88
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb294
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c83
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb151
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c243
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb86
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c783
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb152
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb17
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c30
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb28
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb19
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c106
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb11
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb53
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c187
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb60
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb64
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c64
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb42
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c173
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c56
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb96
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c224
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h80
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c349
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h12
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb88
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb31
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c176
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb32
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb9
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c30
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c1113
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb109
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake6
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb355
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c685
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb667
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb103
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c714
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb212
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb65
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c64
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb48
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md7
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c172
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c38
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake187
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb9
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c869
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb228
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake5
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb11
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb24
139 files changed, 31064 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox b/web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox
new file mode 100644
index 00000000..64f05de1
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/default.gembox
@@ -0,0 +1,79 @@
+MRuby::GemBox.new do |conf|
+ # Use standard Kernel#sprintf method
+ conf.gem :core => "mruby-sprintf"
+
+ # Use standard print/puts/p
+ conf.gem :core => "mruby-print"
+
+ # Use standard Math module
+ conf.gem :core => "mruby-math"
+
+ # Use standard Time class
+ conf.gem :core => "mruby-time"
+
+ # Use standard Struct class
+ conf.gem :core => "mruby-struct"
+
+ # Use Enumerable module extension
+ conf.gem :core => "mruby-enum-ext"
+
+ # Use String class extension
+ conf.gem :core => "mruby-string-ext"
+
+ # Use Numeric class extension
+ conf.gem :core => "mruby-numeric-ext"
+
+ # Use Array class extension
+ conf.gem :core => "mruby-array-ext"
+
+ # Use Hash class extension
+ conf.gem :core => "mruby-hash-ext"
+
+ # Use Range class extension
+ conf.gem :core => "mruby-range-ext"
+
+ # Use Proc class extension
+ conf.gem :core => "mruby-proc-ext"
+
+ # Use Symbol class extension
+ conf.gem :core => "mruby-symbol-ext"
+
+ # Use Random class
+ conf.gem :core => "mruby-random"
+
+ # Use Object class extension
+ conf.gem :core => "mruby-object-ext"
+
+ # Use ObjectSpace class
+ conf.gem :core => "mruby-objectspace"
+
+ # Use Fiber class
+ conf.gem :core => "mruby-fiber"
+
+ # Use Enumerator class (require mruby-fiber)
+ conf.gem :core => "mruby-enumerator"
+
+ # Use Enumerator::Lazy class (require mruby-enumerator)
+ conf.gem :core => "mruby-enum-lazy"
+
+ # Use toplevel object (main) methods extension
+ conf.gem :core => "mruby-toplevel-ext"
+
+ # Generate mirb command
+ conf.gem :core => "mruby-bin-mirb"
+
+ # Generate mruby command
+ conf.gem :core => "mruby-bin-mruby"
+
+ # Generate mruby-strip command
+ conf.gem :core => "mruby-bin-strip"
+
+ # Use Kernel module extension
+ conf.gem :core => "mruby-kernel-ext"
+
+ # Use class/module extension
+ conf.gem :core => "mruby-class-ext"
+
+ # Use mruby-compiler to build other mrbgems
+ conf.gem :core => "mruby-compiler"
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox b/web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox
new file mode 100644
index 00000000..9a5b7081
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/full-core.gembox
@@ -0,0 +1,9 @@
+MRuby::GemBox.new do |conf|
+ conf.gem :core => "mruby-sprintf"
+ conf.gem :core => "mruby-print"
+
+ Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x|
+ g = File.basename File.dirname x
+ conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger|test)$/
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake
new file mode 100644
index 00000000..882caf1a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Array class extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb
new file mode 100644
index 00000000..e28e5238
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -0,0 +1,811 @@
+class Array
+ ##
+ # call-seq:
+ # Array.try_convert(obj) -> array or nil
+ #
+ # Tries to convert +obj+ into an array, using +to_ary+ method.
+ # converted array or +nil+ if +obj+ cannot be converted for any reason.
+ # This method can be used to check if an argument is an array.
+ #
+ # Array.try_convert([1]) #=> [1]
+ # Array.try_convert("1") #=> nil
+ #
+ # if tmp = Array.try_convert(arg)
+ # # the argument is an array
+ # elsif tmp = String.try_convert(arg)
+ # # the argument is a string
+ # end
+ #
+ def self.try_convert(obj)
+ if obj.respond_to?(:to_ary)
+ obj.to_ary
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
+ # ary.uniq! -> ary or nil
+ # ary.uniq! { |item| ... } -> ary or nil
+ #
+ # Removes duplicate elements from +self+.
+ # Returns <code>nil</code> if no changes are made (that is, no
+ # duplicates are found).
+ #
+ # a = [ "a", "a", "b", "b", "c" ]
+ # a.uniq! #=> ["a", "b", "c"]
+ # b = [ "a", "b", "c" ]
+ # b.uniq! #=> nil
+ # c = [["student","sam"], ["student","george"], ["teacher","matz"]]
+ # c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
+ #
+ def uniq!(&block)
+ ary = self.dup
+ result = []
+ if block
+ hash = {}
+ while ary.size > 0
+ val = ary.shift
+ key = block.call(val)
+ hash[key] = val unless hash.has_key?(key)
+ end
+ hash.each_value do |value|
+ result << value
+ end
+ else
+ while ary.size > 0
+ result << ary.shift
+ ary.delete(result.last)
+ end
+ end
+ if result.size == self.size
+ nil
+ else
+ self.replace(result)
+ end
+ end
+
+ ##
+ # call-seq:
+ # ary.uniq -> new_ary
+ # ary.uniq { |item| ... } -> new_ary
+ #
+ # Returns a new array by removing duplicate values in +self+.
+ #
+ # a = [ "a", "a", "b", "b", "c" ]
+ # a.uniq #=> ["a", "b", "c"]
+ #
+ # b = [["student","sam"], ["student","george"], ["teacher","matz"]]
+ # b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
+ #
+ def uniq(&block)
+ ary = self.dup
+ ary.uniq!(&block)
+ ary
+ end
+
+ ##
+ # call-seq:
+ # ary - other_ary -> new_ary
+ #
+ # Array Difference---Returns a new array that is a copy of
+ # the original array, removing any items that also appear in
+ # <i>other_ary</i>. (If you need set-like behavior, see the
+ # library class Set.)
+ #
+ # [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
+ #
+ def -(elem)
+ raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
+
+ hash = {}
+ array = []
+ idx = 0
+ len = elem.size
+ while idx < len
+ hash[elem[idx]] = true
+ idx += 1
+ end
+ idx = 0
+ len = size
+ while idx < len
+ v = self[idx]
+ array << v unless hash[v]
+ idx += 1
+ end
+ array
+ end
+
+ ##
+ # call-seq:
+ # ary | other_ary -> new_ary
+ #
+ # Set Union---Returns a new array by joining this array with
+ # <i>other_ary</i>, removing duplicates.
+ #
+ # [ "a", "b", "c" ] | [ "c", "d", "a" ]
+ # #=> [ "a", "b", "c", "d" ]
+ #
+ def |(elem)
+ raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
+
+ ary = self + elem
+ ary.uniq! or ary
+ end
+
+ ##
+ # call-seq:
+ # ary & other_ary -> new_ary
+ #
+ # Set Intersection---Returns a new array
+ # containing elements common to the two arrays, with no duplicates.
+ #
+ # [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
+ #
+ def &(elem)
+ raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
+
+ hash = {}
+ array = []
+ idx = 0
+ len = elem.size
+ while idx < len
+ hash[elem[idx]] = true
+ idx += 1
+ end
+ idx = 0
+ len = size
+ while idx < len
+ v = self[idx]
+ if hash[v]
+ array << v
+ hash.delete v
+ end
+ idx += 1
+ end
+ array
+ end
+
+ ##
+ # call-seq:
+ # ary.flatten -> new_ary
+ # ary.flatten(level) -> new_ary
+ #
+ # Returns a new array that is a one-dimensional flattening of this
+ # array (recursively). That is, for every element that is an array,
+ # extract its elements into the new array. If the optional
+ # <i>level</i> argument determines the level of recursion to flatten.
+ #
+ # s = [ 1, 2, 3 ] #=> [1, 2, 3]
+ # t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+ # a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+ # a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ # a = [ 1, 2, [3, [4, 5] ] ]
+ # a.flatten(1) #=> [1, 2, 3, [4, 5]]
+ #
+ def flatten(depth=nil)
+ res = dup
+ res.flatten! depth
+ res
+ end
+
+ ##
+ # call-seq:
+ # ary.flatten! -> ary or nil
+ # ary.flatten!(level) -> array or nil
+ #
+ # Flattens +self+ in place.
+ # Returns <code>nil</code> if no modifications were made (i.e.,
+ # <i>ary</i> contains no subarrays.) If the optional <i>level</i>
+ # argument determines the level of recursion to flatten.
+ #
+ # a = [ 1, 2, [3, [4, 5] ] ]
+ # a.flatten! #=> [1, 2, 3, 4, 5]
+ # a.flatten! #=> nil
+ # a #=> [1, 2, 3, 4, 5]
+ # a = [ 1, 2, [3, [4, 5] ] ]
+ # a.flatten!(1) #=> [1, 2, 3, [4, 5]]
+ #
+ def flatten!(depth=nil)
+ modified = false
+ ar = []
+ idx = 0
+ len = size
+ while idx < len
+ e = self[idx]
+ if e.is_a?(Array) && (depth.nil? || depth > 0)
+ ar += e.flatten(depth.nil? ? nil : depth - 1)
+ modified = true
+ else
+ ar << e
+ end
+ idx += 1
+ end
+ if modified
+ self.replace(ar)
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
+ # ary.compact -> new_ary
+ #
+ # Returns a copy of +self+ with all +nil+ elements removed.
+ #
+ # [ "a", nil, "b", nil, "c", nil ].compact
+ # #=> [ "a", "b", "c" ]
+ #
+ def compact
+ result = self.dup
+ result.compact!
+ result
+ end
+
+ ##
+ # call-seq:
+ # ary.compact! -> ary or nil
+ #
+ # Removes +nil+ elements from the array.
+ # Returns +nil+ if no changes were made, otherwise returns
+ # <i>ary</i>.
+ #
+ # [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
+ # [ "a", "b", "c" ].compact! #=> nil
+ #
+ def compact!
+ result = self.select { |e| !e.nil? }
+ if result.size == self.size
+ nil
+ else
+ self.replace(result)
+ end
+ end
+
+ # for efficiency
+ def reverse_each(&block)
+ return to_enum :reverse_each unless block
+
+ i = self.size - 1
+ while i>=0
+ block.call(self[i])
+ i -= 1
+ end
+ self
+ end
+
+ NONE=Object.new
+ ##
+ # call-seq:
+ # ary.fetch(index) -> obj
+ # ary.fetch(index, default) -> obj
+ # ary.fetch(index) { |index| block } -> obj
+ #
+ # Tries to return the element at position +index+, but throws an IndexError
+ # exception if the referenced +index+ lies outside of the array bounds. This
+ # error can be prevented by supplying a second argument, which will act as a
+ # +default+ value.
+ #
+ # Alternatively, if a block is given it will only be executed when an
+ # invalid +index+ is referenced.
+ #
+ # Negative values of +index+ count from the end of the array.
+ #
+ # a = [ 11, 22, 33, 44 ]
+ # a.fetch(1) #=> 22
+ # a.fetch(-1) #=> 44
+ # a.fetch(4, 'cat') #=> "cat"
+ # a.fetch(100) { |i| puts "#{i} is out of bounds" }
+ # #=> "100 is out of bounds"
+ #
+
+ def fetch(n=nil, ifnone=NONE, &block)
+ warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
+
+ idx = n
+ if idx < 0
+ idx += size
+ end
+ if idx < 0 || size <= idx
+ return block.call(n) if block
+ if ifnone == NONE
+ raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}"
+ end
+ return ifnone
+ end
+ self[idx]
+ end
+
+ ##
+ # call-seq:
+ # ary.fill(obj) -> ary
+ # ary.fill(obj, start [, length]) -> ary
+ # ary.fill(obj, range ) -> ary
+ # ary.fill { |index| block } -> ary
+ # ary.fill(start [, length] ) { |index| block } -> ary
+ # ary.fill(range) { |index| block } -> ary
+ #
+ # The first three forms set the selected elements of +self+ (which
+ # may be the entire array) to +obj+.
+ #
+ # A +start+ of +nil+ is equivalent to zero.
+ #
+ # A +length+ of +nil+ is equivalent to the length of the array.
+ #
+ # The last three forms fill the array with the value of the given block,
+ # which is passed the absolute index of each element to be filled.
+ #
+ # Negative values of +start+ count from the end of the array, where +-1+ is
+ # the last element.
+ #
+ # a = [ "a", "b", "c", "d" ]
+ # a.fill("x") #=> ["x", "x", "x", "x"]
+ # a.fill("w", -1) #=> ["x", "x", "x", "w"]
+ # a.fill("z", 2, 2) #=> ["x", "x", "z", "z"]
+ # a.fill("y", 0..1) #=> ["y", "y", "z", "z"]
+ # a.fill { |i| i*i } #=> [0, 1, 4, 9]
+ # a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27]
+ # a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27]
+ # a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27]
+ #
+
+ def fill(arg0=nil, arg1=nil, arg2=nil, &block)
+ if arg0.nil? && arg1.nil? && arg2.nil? && !block
+ raise ArgumentError, "wrong number of arguments (0 for 1..3)"
+ end
+
+ beg = len = 0
+ ary = []
+ if block
+ if arg0.nil? && arg1.nil? && arg2.nil?
+ # ary.fill { |index| block } -> ary
+ beg = 0
+ len = self.size
+ elsif !arg0.nil? && arg0.kind_of?(Range)
+ # ary.fill(range) { |index| block } -> ary
+ beg = arg0.begin
+ beg += self.size if beg < 0
+ len = arg0.end
+ len += self.size if len < 0
+ len += 1 unless arg0.exclude_end?
+ elsif !arg0.nil?
+ # ary.fill(start [, length] ) { |index| block } -> ary
+ beg = arg0
+ beg += self.size if beg < 0
+ if arg1.nil?
+ len = self.size
+ else
+ len = arg0 + arg1
+ end
+ end
+ else
+ if !arg0.nil? && arg1.nil? && arg2.nil?
+ # ary.fill(obj) -> ary
+ beg = 0
+ len = self.size
+ elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range)
+ # ary.fill(obj, range ) -> ary
+ beg = arg1.begin
+ beg += self.size if beg < 0
+ len = arg1.end
+ len += self.size if len < 0
+ len += 1 unless arg1.exclude_end?
+ elsif !arg0.nil? && !arg1.nil?
+ # ary.fill(obj, start [, length]) -> ary
+ beg = arg1
+ beg += self.size if beg < 0
+ if arg2.nil?
+ len = self.size
+ else
+ len = beg + arg2
+ end
+ end
+ end
+
+ i = beg
+ if block
+ while i < len
+ self[i] = block.call(i)
+ i += 1
+ end
+ else
+ while i < len
+ self[i] = arg0
+ i += 1
+ end
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # ary.rotate(count=1) -> new_ary
+ #
+ # Returns a new array by rotating +self+ so that the element at +count+ is
+ # the first element of the new array.
+ #
+ # If +count+ is negative then it rotates in the opposite direction, starting
+ # from the end of +self+ where +-1+ is the last element.
+ #
+ # a = [ "a", "b", "c", "d" ]
+ # a.rotate #=> ["b", "c", "d", "a"]
+ # a #=> ["a", "b", "c", "d"]
+ # a.rotate(2) #=> ["c", "d", "a", "b"]
+ # a.rotate(-3) #=> ["b", "c", "d", "a"]
+
+ def rotate(count=1)
+ ary = []
+ len = self.length
+
+ if len > 0
+ idx = (count < 0) ? (len - (~count % len) - 1) : (count % len) # rotate count
+ len.times do
+ ary << self[idx]
+ idx += 1
+ idx = 0 if idx > len-1
+ end
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
+ # ary.rotate!(count=1) -> ary
+ #
+ # Rotates +self+ in place so that the element at +count+ comes first, and
+ # returns +self+.
+ #
+ # If +count+ is negative then it rotates in the opposite direction, starting
+ # from the end of the array where +-1+ is the last element.
+ #
+ # a = [ "a", "b", "c", "d" ]
+ # a.rotate! #=> ["b", "c", "d", "a"]
+ # a #=> ["b", "c", "d", "a"]
+ # a.rotate!(2) #=> ["d", "a", "b", "c"]
+ # a.rotate!(-3) #=> ["a", "b", "c", "d"]
+
+ def rotate!(count=1)
+ self.replace(self.rotate(count))
+ end
+
+ ##
+ # call-seq:
+ # ary.delete_if { |item| block } -> ary
+ # ary.delete_if -> Enumerator
+ #
+ # Deletes every element of +self+ for which block evaluates to +true+.
+ #
+ # The array is changed instantly every time the block is called, not after
+ # the iteration is over.
+ #
+ # See also Array#reject!
+ #
+ # If no block is given, an Enumerator is returned instead.
+ #
+ # scores = [ 97, 42, 75 ]
+ # scores.delete_if {|score| score < 80 } #=> [97]
+
+ def delete_if(&block)
+ return to_enum :delete_if unless block
+
+ idx = 0
+ while idx < self.size do
+ if block.call(self[idx])
+ self.delete_at(idx)
+ else
+ idx += 1
+ end
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # ary.reject! { |item| block } -> ary or nil
+ # ary.reject! -> Enumerator
+ #
+ # Equivalent to Array#delete_if, deleting elements from +self+ for which the
+ # block evaluates to +true+, but returns +nil+ if no changes were made.
+ #
+ # The array is changed instantly every time the block is called, not after
+ # the iteration is over.
+ #
+ # See also Enumerable#reject and Array#delete_if.
+ #
+ # If no block is given, an Enumerator is returned instead.
+
+ def reject!(&block)
+ return to_enum :reject! unless block
+
+ len = self.size
+ idx = 0
+ while idx < self.size do
+ if block.call(self[idx])
+ self.delete_at(idx)
+ else
+ idx += 1
+ end
+ end
+ if self.size == len
+ nil
+ else
+ self
+ end
+ end
+
+ ##
+ # call-seq:
+ # ary.insert(index, obj...) -> ary
+ #
+ # Inserts the given values before the element with the given +index+.
+ #
+ # Negative indices count backwards from the end of the array, where +-1+ is
+ # the last element.
+ #
+ # a = %w{ a b c d }
+ # a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
+ # a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
+
+ def insert(idx, *args)
+ idx += self.size + 1 if idx < 0
+ self[idx, 0] = args
+ self
+ end
+
+ ##
+ # call-seq:
+ # ary.bsearch {|x| block } -> elem
+ #
+ # By using binary search, finds a value from this array which meets
+ # the given condition in O(log n) where n is the size of the array.
+ #
+ # You can use this method in two use cases: a find-minimum mode and
+ # a find-any mode. In either case, the elements of the array must be
+ # monotone (or sorted) with respect to the block.
+ #
+ # In find-minimum mode (this is a good choice for typical use case),
+ # the block must return true or false, and there must be an index i
+ # (0 <= i <= ary.size) so that:
+ #
+ # - the block returns false for any element whose index is less than
+ # i, and
+ # - the block returns true for any element whose index is greater
+ # than or equal to i.
+ #
+ # This method returns the i-th element. If i is equal to ary.size,
+ # it returns nil.
+ #
+ # ary = [0, 4, 7, 10, 12]
+ # ary.bsearch {|x| x >= 4 } #=> 4
+ # ary.bsearch {|x| x >= 6 } #=> 7
+ # ary.bsearch {|x| x >= -1 } #=> 0
+ # ary.bsearch {|x| x >= 100 } #=> nil
+ #
+ # In find-any mode (this behaves like libc's bsearch(3)), the block
+ # must return a number, and there must be two indices i and j
+ # (0 <= i <= j <= ary.size) so that:
+ #
+ # - the block returns a positive number for ary[k] if 0 <= k < i,
+ # - the block returns zero for ary[k] if i <= k < j, and
+ # - the block returns a negative number for ary[k] if
+ # j <= k < ary.size.
+ #
+ # Under this condition, this method returns any element whose index
+ # is within i...j. If i is equal to j (i.e., there is no element
+ # that satisfies the block), this method returns nil.
+ #
+ # ary = [0, 4, 7, 10, 12]
+ # # try to find v such that 4 <= v < 8
+ # ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7
+ # # try to find v such that 8 <= v < 10
+ # ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil
+ #
+ # You must not mix the two modes at a time; the block must always
+ # return either true/false, or always return a number. It is
+ # undefined which value is actually picked up at each iteration.
+
+ def bsearch(&block)
+ return to_enum :bsearch unless block
+
+ if idx = bsearch_index(&block)
+ self[idx]
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
+ # ary.bsearch_index {|x| block } -> int or nil
+ #
+ # By using binary search, finds an index of a value from this array which
+ # meets the given condition in O(log n) where n is the size of the array.
+ #
+ # It supports two modes, depending on the nature of the block and they are
+ # exactly the same as in the case of #bsearch method with the only difference
+ # being that this method returns the index of the element instead of the
+ # element itself. For more details consult the documentation for #bsearch.
+
+ def bsearch_index(&block)
+ return to_enum :bsearch_index unless block
+
+ low = 0
+ high = size
+ satisfied = false
+
+ while low < high
+ mid = ((low+high)/2).truncate
+ res = block.call self[mid]
+
+ case res
+ when 0 # find-any mode: Found!
+ return mid
+ when Numeric # find-any mode: Continue...
+ in_lower_half = res < 0
+ when true # find-min mode
+ in_lower_half = true
+ satisfied = true
+ when false, nil # find-min mode
+ in_lower_half = false
+ else
+ raise TypeError, 'invalid block result (must be numeric, true, false or nil)'
+ end
+
+ if in_lower_half
+ high = mid
+ else
+ low = mid + 1
+ end
+ end
+
+ satisfied ? low : nil
+ end
+
+ ##
+ # call-seq:
+ # ary.delete_if { |item| block } -> ary
+ # ary.delete_if -> Enumerator
+ #
+ # Deletes every element of +self+ for which block evaluates to +true+.
+ #
+ # The array is changed instantly every time the block is called, not after
+ # the iteration is over.
+ #
+ # See also Array#reject!
+ #
+ # If no block is given, an Enumerator is returned instead.
+ #
+ # scores = [ 97, 42, 75 ]
+ # scores.delete_if {|score| score < 80 } #=> [97]
+
+ def delete_if(&block)
+ return to_enum :delete_if unless block
+
+ idx = 0
+ while idx < self.size do
+ if block.call(self[idx])
+ self.delete_at(idx)
+ else
+ idx += 1
+ end
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # ary.keep_if { |item| block } -> ary
+ # ary.keep_if -> Enumerator
+ #
+ # Deletes every element of +self+ for which the given block evaluates to
+ # +false+.
+ #
+ # See also Array#select!
+ #
+ # If no block is given, an Enumerator is returned instead.
+ #
+ # a = [1, 2, 3, 4, 5]
+ # a.keep_if { |val| val > 3 } #=> [4, 5]
+
+ def keep_if(&block)
+ return to_enum :keep_if unless block
+
+ idx = 0
+ len = self.size
+ while idx < self.size do
+ if block.call(self[idx])
+ idx += 1
+ else
+ self.delete_at(idx)
+ end
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # ary.select! {|item| block } -> ary or nil
+ # ary.select! -> Enumerator
+ #
+ # Invokes the given block passing in successive elements from +self+,
+ # deleting elements for which the block returns a +false+ value.
+ #
+ # If changes were made, it will return +self+, otherwise it returns +nil+.
+ #
+ # See also Array#keep_if
+ #
+ # If no block is given, an Enumerator is returned instead.
+
+ def select!(&block)
+ return to_enum :select! unless block
+
+ result = []
+ idx = 0
+ len = size
+ while idx < len
+ elem = self[idx]
+ result << elem if block.call(elem)
+ idx += 1
+ end
+ return nil if len == result.size
+ self.replace(result)
+ end
+
+ ##
+ # call-seq:
+ # ary.index(val) -> int or nil
+ # ary.index {|item| block } -> int or nil
+ #
+ # Returns the _index_ of the first object in +ary+ such that the object is
+ # <code>==</code> to +obj+.
+ #
+ # If a block is given instead of an argument, returns the _index_ of the
+ # first object for which the block returns +true+. Returns +nil+ if no
+ # match is found.
+ #
+ # ISO 15.2.12.5.14
+ def index(val=NONE, &block)
+ return to_enum(:find_index, val) if !block && val == NONE
+
+ if block
+ idx = 0
+ len = size
+ while idx < len
+ return idx if block.call self[idx]
+ idx += 1
+ end
+ else
+ return self.__ary_index(val)
+ end
+ nil
+ end
+
+ ##
+ # call-seq:
+ # ary.to_ary -> ary
+ #
+ # Returns +self+.
+ #
+ def to_ary
+ self
+ end
+
+ ##
+ # call-seq:
+ # ary.dig(idx, ...) -> object
+ #
+ # Extracts the nested value specified by the sequence of <i>idx</i>
+ # objects by calling +dig+ at each step, returning +nil+ if any
+ # intermediate step is +nil+.
+ #
+ def dig(idx,*args)
+ n = self[idx]
+ if args.size > 0
+ n&.dig(*args)
+ else
+ n
+ end
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c
new file mode 100644
index 00000000..e99599b0
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/src/array.c
@@ -0,0 +1,244 @@
+#include <mruby.h>
+#include <mruby/value.h>
+#include <mruby/array.h>
+#include <mruby/range.h>
+#include <mruby/hash.h>
+
+/*
+ * call-seq:
+ * ary.assoc(obj) -> new_ary or nil
+ *
+ * Searches through an array whose elements are also arrays
+ * comparing _obj_ with the first element of each contained array
+ * using obj.==.
+ * Returns the first contained array that matches (that
+ * is, the first associated array),
+ * or +nil+ if no match is found.
+ * See also <code>Array#rassoc</code>.
+ *
+ * s1 = [ "colors", "red", "blue", "green" ]
+ * s2 = [ "letters", "a", "b", "c" ]
+ * s3 = "foo"
+ * a = [ s1, s2, s3 ]
+ * a.assoc("letters") #=> [ "letters", "a", "b", "c" ]
+ * a.assoc("foo") #=> nil
+ */
+
+static mrb_value
+mrb_ary_assoc(mrb_state *mrb, mrb_value ary)
+{
+ mrb_int i;
+ mrb_value v, k;
+
+ mrb_get_args(mrb, "o", &k);
+
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
+ if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 &&
+ mrb_equal(mrb, RARRAY_PTR(v)[0], k))
+ return v;
+ }
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * ary.rassoc(obj) -> new_ary or nil
+ *
+ * Searches through the array whose elements are also arrays. Compares
+ * _obj_ with the second element of each contained array using
+ * <code>==</code>. Returns the first contained array that matches. See
+ * also <code>Array#assoc</code>.
+ *
+ * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
+ * a.rassoc("two") #=> [2, "two"]
+ * a.rassoc("four") #=> nil
+ */
+
+static mrb_value
+mrb_ary_rassoc(mrb_state *mrb, mrb_value ary)
+{
+ mrb_int i;
+ mrb_value v, value;
+
+ mrb_get_args(mrb, "o", &value);
+
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ v = RARRAY_PTR(ary)[i];
+ if (mrb_type(v) == MRB_TT_ARRAY &&
+ RARRAY_LEN(v) > 1 &&
+ mrb_equal(mrb, RARRAY_PTR(v)[1], value))
+ return v;
+ }
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * ary.at(index) -> obj or nil
+ *
+ * Returns the element at _index_. A
+ * negative index counts from the end of +self+. Returns +nil+
+ * if the index is out of range. See also <code>Array#[]</code>.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.at(0) #=> "a"
+ * a.at(-1) #=> "e"
+ */
+
+static mrb_value
+mrb_ary_at(mrb_state *mrb, mrb_value ary)
+{
+ mrb_int pos;
+ mrb_get_args(mrb, "i", &pos);
+
+ return mrb_ary_entry(ary, pos);
+}
+
+static mrb_value
+mrb_ary_values_at(mrb_state *mrb, mrb_value self)
+{
+ mrb_int argc;
+ mrb_value *argv;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+
+ return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref);
+}
+
+/*
+ * call-seq:
+ * ary.to_h -> Hash
+ *
+ * Returns the result of interpreting <i>aray</i> as an array of
+ * <tt>[key, value]</tt> paris.
+ *
+ * [[:foo, :bar], [1, 2]].to_h
+ * # => {:foo => :bar, 1 => 2}
+ *
+ */
+
+static mrb_value
+mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
+{
+ mrb_int i;
+ mrb_value v, hash;
+
+ hash = mrb_hash_new_capa(mrb, 0);
+
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ mrb_value elt = RARRAY_PTR(ary)[i];
+ v = mrb_check_array_type(mrb, elt);
+
+ if (mrb_nil_p(v)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)",
+ mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, elt)),
+ mrb_fixnum_value(i)
+ );
+ }
+
+ if (RARRAY_LEN(v) != 2) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)",
+ mrb_fixnum_value(i),
+ mrb_fixnum_value(RARRAY_LEN(v))
+ );
+ }
+
+ mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]);
+ }
+
+ return hash;
+}
+
+/*
+ * call-seq:
+ * ary.slice!(index) -> obj or nil
+ * ary.slice!(start, length) -> new_ary or nil
+ * ary.slice!(range) -> new_ary or nil
+ *
+ * Deletes the element(s) given by an +index+ (optionally up to +length+
+ * elements) or by a +range+.
+ *
+ * Returns the deleted object (or objects), or +nil+ if the +index+ is out of
+ * range.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.slice!(1) #=> "b"
+ * a #=> ["a", "c"]
+ * a.slice!(-1) #=> "c"
+ * a #=> ["a"]
+ * a.slice!(100) #=> nil
+ * a #=> ["a"]
+ */
+
+static mrb_value
+mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
+{
+ struct RArray *a = mrb_ary_ptr(self);
+ mrb_int i, j, k, len, alen = ARY_LEN(a);
+ mrb_value index;
+ mrb_value val;
+ mrb_value *ptr;
+ mrb_value ary;
+
+ mrb_ary_modify(mrb, a);
+
+ if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
+ switch (mrb_type(index)) {
+ case MRB_TT_RANGE:
+ if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
+ goto delete_pos_len;
+ }
+ else {
+ return mrb_nil_value();
+ }
+ case MRB_TT_FIXNUM:
+ val = mrb_funcall(mrb, self, "delete_at", 1, index);
+ return val;
+ default:
+ val = mrb_funcall(mrb, self, "delete_at", 1, index);
+ return val;
+ }
+ }
+
+ i = mrb_fixnum(index);
+ delete_pos_len:
+ if (i < 0) i += alen;
+ if (i < 0 || alen < i) return mrb_nil_value();
+ if (len < 0) return mrb_nil_value();
+ if (alen == i) return mrb_ary_new(mrb);
+ if (len > alen - i) len = alen - i;
+
+ ary = mrb_ary_new_capa(mrb, len);
+ ptr = ARY_PTR(a);
+ for (j = i, k = 0; k < len; ++j, ++k) {
+ mrb_ary_push(mrb, ary, ptr[j]);
+ }
+
+ ptr += i;
+ for (j = i; j < alen - len; ++j) {
+ *ptr = *(ptr+len);
+ ++ptr;
+ }
+
+ mrb_ary_resize(mrb, self, alen - len);
+ return ary;
+}
+
+void
+mrb_mruby_array_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass * a = mrb->array_class;
+
+ mrb_define_method(mrb, a, "assoc", mrb_ary_assoc, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY());
+ mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0));
+ mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY());
+}
+
+void
+mrb_mruby_array_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb
new file mode 100644
index 00000000..c0db1b1c
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-array-ext/test/array.rb
@@ -0,0 +1,383 @@
+##
+# Array(Ext) Test
+
+assert("Array.try_convert") do
+ assert_nil Array.try_convert(0)
+ assert_nil Array.try_convert(nil)
+ assert_equal [], Array.try_convert([])
+ assert_equal [1,2,3], Array.try_convert([1,2,3])
+end
+
+assert("Array#assoc") do
+ s1 = [ "colors", "red", "blue", "green" ]
+ s2 = [ "letters", "a", "b", "c" ]
+ s3 = "foo"
+ a = [ s1, s2, s3 ]
+
+ assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters")
+ assert_nil a.assoc("foo")
+end
+
+assert("Array#at") do
+ a = [ "a", "b", "c", "d", "e" ]
+ assert_equal "a", a.at(0)
+ assert_equal "e", a.at(-1)
+end
+
+assert("Array#rassoc") do
+ a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
+
+ assert_equal [2, "two"], a.rassoc("two")
+ assert_nil a.rassoc("four")
+end
+
+assert("Array#uniq!") do
+ a = [1, 2, 3, 1]
+ a.uniq!
+ assert_equal [1, 2, 3], a
+
+ b = [ "a", "b", "c" ]
+ assert_nil b.uniq!
+
+ c = [["student","sam"], ["student","george"], ["teacher","matz"]]
+ assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first }
+
+ d = [["student","sam"], ["teacher","matz"]]
+ assert_nil d.uniq! { |s| s.first }
+end
+
+assert("Array#uniq") do
+ a = [1, 2, 3, 1]
+ assert_equal [1, 2, 3], a.uniq
+ assert_equal [1, 2, 3, 1], a
+
+ b = [["student","sam"], ["student","george"], ["teacher","matz"]]
+ assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first }
+end
+
+assert("Array#-") do
+ a = [1, 2, 3, 1]
+ b = [1]
+ c = 1
+
+ assert_raise(TypeError) { a - c }
+ assert_equal [2, 3], (a - b)
+ assert_equal [1, 2, 3, 1], a
+end
+
+assert("Array#|") do
+ a = [1, 2, 3, 1]
+ b = [1, 4]
+ c = 1
+
+ assert_raise(TypeError) { a | c }
+ assert_equal [1, 2, 3, 4], (a | b)
+ assert_equal [1, 2, 3, 1], a
+end
+
+assert("Array#&") do
+ a = [1, 2, 3, 1]
+ b = [1, 4]
+ c = 1
+
+ assert_raise(TypeError) { a & c }
+ assert_equal [1], (a & b)
+ assert_equal [1, 2, 3, 1], a
+end
+
+assert("Array#flatten") do
+ assert_equal [1, 2, "3", {4=>5}, :'6'], [1, 2, "3", {4=>5}, :'6'].flatten
+ assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, 4, 5], 6].flatten
+ assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten
+ assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0)
+ assert_equal [1, 2, [3, [4, [5, [6]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(1)
+ assert_equal [1, 2, 3, [4, [5, [6]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(2)
+ assert_equal [1, 2, 3, 4, [5, [6]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(3)
+ assert_equal [1, 2, 3, 4, 5, [6]], [1, [2, [3, [4, [5, [6]]]]]].flatten(4)
+ assert_equal [1, 2, 3, 4, 5, 6], [1, [2, [3, [4, [5, [6]]]]]].flatten(5)
+end
+
+assert("Array#flatten!") do
+ assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten!
+end
+
+assert("Array#compact") do
+ a = [1, nil, "2", nil, :t, false, nil]
+ assert_equal [1, "2", :t, false], a.compact
+ assert_equal [1, nil, "2", nil, :t, false, nil], a
+end
+
+assert("Array#compact!") do
+ a = [1, nil, "2", nil, :t, false, nil]
+ a.compact!
+ assert_equal [1, "2", :t, false], a
+end
+
+assert("Array#fetch") do
+ a = [ 11, 22, 33, 44 ]
+ assert_equal 22, a.fetch(1)
+ assert_equal 44, a.fetch(-1)
+ assert_equal 'cat', a.fetch(4, 'cat')
+ ret = 0
+ a.fetch(100) { |i| ret = i }
+ assert_equal 100, ret
+ assert_raise(IndexError) { a.fetch(100) }
+end
+
+assert("Array#fill") do
+ a = [ "a", "b", "c", "d" ]
+ assert_equal ["x", "x", "x", "x"], a.fill("x")
+ assert_equal ["x", "x", "x", "w"], a.fill("w", -1)
+ assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2)
+ assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1)
+ assert_equal [0, 1, 4, 9], a.fill { |i| i*i }
+ assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i }
+ assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 }
+ assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 }
+ assert_raise(ArgumentError) { a.fill }
+
+ assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1))
+ assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3))
+ assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4))
+ assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4))
+ assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2))
+ assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2))
+ assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10})
+ assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
+ assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
+ assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
+
+ assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1)
+ assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1)
+ assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2)
+ assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2)
+ assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0)
+end
+
+assert("Array#reverse_each") do
+ a = [ "a", "b", "c", "d" ]
+ b = []
+ a.reverse_each do |i|
+ b << i
+ end
+ assert_equal [ "d", "c", "b", "a" ], b
+
+ if Object.const_defined?(:Enumerator)
+ assert_equal [ "d", "c", "b", "a" ], a.reverse_each.to_a
+ else
+ true
+ end
+end
+
+assert("Array#rotate") do
+ a = ["a", "b", "c", "d"]
+ assert_equal ["b", "c", "d", "a"], a.rotate
+ assert_equal ["a", "b", "c", "d"], a
+ assert_equal ["c", "d", "a", "b"], a.rotate(2)
+ assert_equal ["b", "c", "d", "a"], a.rotate(-3)
+ assert_equal ["c", "d", "a", "b"], a.rotate(10)
+ assert_equal [], [].rotate
+end
+
+assert("Array#rotate!") do
+ a = ["a", "b", "c", "d"]
+ assert_equal ["b", "c", "d", "a"], a.rotate!
+ assert_equal ["b", "c", "d", "a"], a
+ assert_equal ["d", "a", "b", "c"], a.rotate!(2)
+ assert_equal ["a", "b", "c", "d"], a.rotate!(-3)
+ assert_equal ["c", "d", "a", "b"], a.rotate(10)
+ assert_equal [], [].rotate!
+end
+
+assert("Array#delete_if") do
+ a = [1, 2, 3, 4, 5]
+ assert_equal [1, 2, 3, 4, 5], a.delete_if { false }
+ assert_equal [1, 2, 3, 4, 5], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [], a.delete_if { true }
+ assert_equal [], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [1, 2, 3], a.delete_if { |i| i > 3 }
+ assert_equal [1, 2, 3], a
+end
+
+assert("Array#reject!") do
+ a = [1, 2, 3, 4, 5]
+ assert_nil a.reject! { false }
+ assert_equal [1, 2, 3, 4, 5], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [], a.reject! { true }
+ assert_equal [], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [1, 2, 3], a.reject! { |val| val > 3 }
+ assert_equal [1, 2, 3], a
+end
+
+assert("Array#insert") do
+ a = ["a", "b", "c", "d"]
+ assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99)
+ assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3)
+
+ b = ["a", "b", "c", "d"]
+ assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99)
+end
+
+assert("Array#bsearch") do
+ # Find minimum mode
+ a = [0, 2, 4]
+ assert_equal 0, a.bsearch{ |x| x >= -1 }
+ assert_equal 0, a.bsearch{ |x| x >= 0 }
+ assert_equal 2, a.bsearch{ |x| x >= 1 }
+ assert_equal 2, a.bsearch{ |x| x >= 2 }
+ assert_equal 4, a.bsearch{ |x| x >= 3 }
+ assert_equal 4, a.bsearch{ |x| x >= 4 }
+ assert_nil a.bsearch{ |x| x >= 5 }
+
+ # Find any mode
+ a = [0, 4, 8]
+ def between(lo, x, hi)
+ if x < lo
+ 1
+ elsif x > hi
+ -1
+ else
+ 0
+ end
+ end
+ assert_nil a.bsearch{ |x| between(-3, x, -1) }
+ assert_equal 0, a.bsearch{ |x| between(-1, x, 1) }
+ assert_nil a.bsearch{ |x| between( 1, x, 3) }
+ assert_equal 4, a.bsearch{ |x| between( 3, x, 5) }
+ assert_nil a.bsearch{ |x| between( 5, x, 7) }
+ assert_equal 8, a.bsearch{ |x| between( 7, x, 9) }
+ assert_nil a.bsearch{ |x| between( 9, x, 11) }
+
+ assert_equal 0, a.bsearch{ |x| between( 0, x, 3) }
+ assert_equal 4, a.bsearch{ |x| between( 0, x, 4) }
+ assert_equal 4, a.bsearch{ |x| between( 4, x, 8) }
+ assert_equal 8, a.bsearch{ |x| between( 5, x, 8) }
+
+ # Invalid block result
+ assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do
+ a.bsearch{ 'I like to watch the world burn' }
+ end
+end
+
+assert("Array#bsearch_index") do
+ # tested through Array#bsearch
+end
+
+assert("Array#delete_if") do
+ a = [1, 2, 3, 4, 5]
+ assert_equal [1, 2, 3, 4, 5], a.delete_if { false }
+ assert_equal [1, 2, 3, 4, 5], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [], a.delete_if { true }
+ assert_equal [], a
+
+ a = [ 1, 2, 3, 4, 5 ]
+ assert_equal [1, 2, 3], a.delete_if { |val| val > 3 }
+end
+
+assert("Array#keep_if") do
+ a = [1, 2, 3, 4, 5]
+ assert_equal [1, 2, 3, 4, 5], a.keep_if { true }
+ assert_equal [1, 2, 3, 4, 5], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [], a.keep_if { false }
+ assert_equal [], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [4, 5], a.keep_if { |val| val > 3 }
+ assert_equal [4, 5], a
+end
+
+assert("Array#select!") do
+ a = [1, 2, 3, 4, 5]
+ assert_nil a.select! { true }
+ assert_equal [1, 2, 3, 4, 5], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [], a.select! { false }
+ assert_equal [], a
+
+ a = [1, 2, 3, 4, 5]
+ assert_equal [4, 5], a.select! { |val| val > 3 }
+ assert_equal [4, 5], a
+end
+
+assert('Array#values_at') do
+ a = %w{red green purple white none}
+
+ assert_equal %w{red purple none}, a.values_at(0, 2, 4)
+ assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7)
+ assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7)
+ assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3)
+ assert_raise(TypeError) { a.values_at 'tt' }
+end
+
+assert('Array#to_h') do
+ assert_equal({}, [].to_h)
+ assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h)
+
+ assert_raise(TypeError) { [1].to_h }
+ assert_raise(ArgumentError) { [[1]].to_h }
+end
+
+assert('Array#to_h (Modified)') do
+ class A
+ def to_ary
+ $a.clear
+ nil
+ end
+ end
+ $a = [A.new]
+ assert_raise(TypeError) { $a.to_h }
+end
+
+assert("Array#index (block)") do
+ assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
+ assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
+end
+
+assert("Array#to_ary") do
+ assert_equal [], [].to_ary
+ assert_equal [1,2,3], [1,2,3].to_ary
+end
+
+assert("Array#dig") do
+ h = [[[1]], 0]
+ assert_equal(1, h.dig(0, 0, 0))
+ assert_nil(h.dig(2, 0))
+ assert_raise(TypeError) {h.dig(:a)}
+end
+
+assert("Array#slice!") do
+ a = [1, 2, 3]
+ b = a.slice!(0)
+ c = [1, 2, 3, 4, 5]
+ d = c.slice!(0, 2)
+ e = [1, 2, 3, 4, 5]
+ f = e.slice!(1..3)
+ g = [1, 2, 3]
+ h = g.slice!(-1)
+ i = [1, 2, 3]
+ j = i.slice!(0, -1)
+
+ assert_equal(a, [2, 3])
+ assert_equal(b, 1)
+ assert_equal(c, [3, 4, 5])
+ assert_equal(d, [1, 2])
+ assert_equal(e, [1, 5])
+ assert_equal(f, [2, 3, 4])
+ assert_equal(g, [1, 2])
+ assert_equal(h, 3)
+ assert_equal(i, [1, 2, 3])
+ assert_equal(j, nil)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
new file mode 100644
index 00000000..26f3138a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
@@ -0,0 +1,286 @@
+require 'open3'
+require 'tempfile'
+
+class BinTest_MrubyBinDebugger
+ @debug1=false
+ @debug2=true
+ @debug3=true
+ def self.test(rubysource, testcase)
+ script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
+
+ # .rb
+ script.write rubysource
+ script.flush
+
+ # compile
+ `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
+
+ # add mrdb quit
+ testcase << {:cmd=>"quit"}
+
+ stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
+
+ ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
+ o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
+
+ exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
+ unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)}
+
+if @debug1
+ o.split("\n").each_with_index do |i,actual|
+ p [i,actual]
+ end
+end
+ # compare actual / expected
+ o.split("\n").each do |actual|
+ next if actual.empty?
+ exp = exp_vals.shift
+if @debug2
+ a = true
+ a = actual.include?(exp) unless exp.nil?
+ p [actual, exp] unless a
+end
+ assert_true actual.include?(exp) unless exp.nil?
+ end
+ # compare actual / unexpected
+ o.split("\n").each do |actual|
+ next if actual.empty?
+ unexp = unexp_vals.shift
+if @debug3
+ a = false
+ a = actual.include?(unexp) unless unexp.nil?
+ p [actual, unexp] if a
+end
+ assert_false actual.include?(unexp) unless unexp.nil?
+ end
+ end
+ end
+end
+
+INVCMD = "invalid command"
+
+assert('mruby-bin-debugger(mrdb) command line') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ str = ""
+ 103.times {
+ str += "1234567890"
+ }
+ cmd = "p a=#{str}"
+
+ # test case
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "break"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"b", :unexp=>INVCMD}
+ tc << {:cmd=>"br", :unexp=>INVCMD}
+ tc << {:cmd=>"brea", :unexp=>INVCMD}
+ tc << {:cmd=>"break", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "continue"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue", :unexp=>INVCMD}])
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "delete"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"d 1", :unexp=>INVCMD}
+ tc << {:cmd=>"de 1", :unexp=>INVCMD}
+ tc << {:cmd=>"delet 1", :unexp=>INVCMD}
+ tc << {:cmd=>"delete 1", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "disable"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"dis", :unexp=>INVCMD}
+ tc << {:cmd=>"disa", :unexp=>INVCMD}
+ tc << {:cmd=>"disabl", :unexp=>INVCMD}
+ tc << {:cmd=>"disable", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "enable"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"en", :unexp=>INVCMD}
+ tc << {:cmd=>"ena", :unexp=>INVCMD}
+ tc << {:cmd=>"enabl", :unexp=>INVCMD}
+ tc << {:cmd=>"enable", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "eval"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"ev", :unexp=>INVCMD}
+ tc << {:cmd=>"eva", :unexp=>INVCMD}
+ tc << {:cmd=>"eval", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "help"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"h", :unexp=>INVCMD}
+ tc << {:cmd=>"he", :unexp=>INVCMD}
+ tc << {:cmd=>"hel", :unexp=>INVCMD}
+ tc << {:cmd=>"help", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"i b", :unexp=>INVCMD}
+ tc << {:cmd=>"in b", :unexp=>INVCMD}
+ tc << {:cmd=>"i br", :unexp=>INVCMD}
+ tc << {:cmd=>"inf breakpoint", :unexp=>INVCMD}
+ tc << {:cmd=>"info breakpoints", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "list"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"l", :unexp=>INVCMD}
+ tc << {:cmd=>"li", :unexp=>INVCMD}
+ tc << {:cmd=>"lis", :unexp=>INVCMD}
+ tc << {:cmd=>"list", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "print"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"p", :unexp=>INVCMD}
+ tc << {:cmd=>"pr", :unexp=>INVCMD}
+ tc << {:cmd=>"prin", :unexp=>INVCMD}
+ tc << {:cmd=>"print", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "quit"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}])
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "run"') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru", :unexp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}])
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}])
+end
+
+assert('mruby-bin-debugger(mrdb) command: "step"') do
+ # ruby source
+ src = <<"SRC"
+while true
+ foo = 'foo'
+end
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s", :unexp=>INVCMD}
+ tc << {:cmd=>"st", :unexp=>INVCMD}
+ tc << {:cmd=>"ste", :unexp=>INVCMD}
+ tc << {:cmd=>"step", :unexp=>INVCMD}
+ BinTest_MrubyBinDebugger.test(src, tc)
+
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss", :exp=>INVCMD}])
+ BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}])
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb
new file mode 100644
index 00000000..1bc96c47
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb
@@ -0,0 +1,701 @@
+require 'open3'
+require 'tempfile'
+
+class BinTest_MrubyBinDebugger
+ @debug1=false
+ @debug2=true
+ def self.test(rubysource, testcase)
+ script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
+
+ # .rb
+ script.write rubysource
+ script.flush
+
+ # compile
+ `./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
+
+ # add mrdb quit
+ testcase << {:cmd=>"quit"}
+
+ stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
+
+ ["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
+ o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
+
+ exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
+=begin
+if @debug1
+ o.split("\n").each_with_index do |i,actual|
+ p [i,actual]
+ end
+end
+ # compare actual / expected
+ o.split("\n").each do |actual|
+ next if actual.empty?
+ exp = exp_vals.shift
+if @debug2
+ a = true
+ a = actual.include?(exp) unless exp.nil?
+ p [actual, exp] unless a
+end
+ assert_true actual.include?(exp) unless exp.nil?
+ end
+=end
+ idx = 0
+ exp_vals.each do |exp|
+ next if exp.nil?
+ idx = o.index(exp, idx)
+ assert_false idx.nil?
+ break unless idx
+ idx += 1
+ end
+ end
+ end
+end
+
+assert('mruby-bin-debugger(print) invalid arguments') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"p", :exp=>"Parameter not specified."}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) nomal') do
+ # ruby source
+ src = <<"SRC"
+foo = 'foo'
+bar = foo
+baz = bar
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"p (1+2)", :exp=>'$1 = 3'}
+ tc << {:cmd=>"p foo", :exp=>'$2 = "foo"'}
+ tc << {:cmd=>"p foo*=2", :exp=>'$3 = "foofoo"'}
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"p bar", :exp=>'$4 = "foofoo"'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) error') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"p (1+2", :exp=>'$1 = SyntaxError'}
+ tc << {:cmd=>"p bar", :exp=>'$2 = NoMethodError'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+# Kernel#instance_eval(string) does't work multiple statements.
+=begin
+assert('mruby-bin-debugger(print) multiple statements') do
+ # ruby source
+ src = <<"SRC"
+x = 0
+y = 0
+z = 0
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s",}
+ tc << {:cmd=>"p x=1;x+=2", :exp=>"3"}
+ tc << {:cmd=>"s",}
+ tc << {:cmd=>"p x", :exp=>"3"}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+=end
+
+assert('mruby-bin-debugger(print) scope:top') do
+ # ruby source (bp is break point)
+ src = "bp=nil\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"p self", :exp=>'$1 = main'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) scope:class') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+class TestClassScope
+ bp = nil
+end
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"p self", :exp=>'$1 = TestClassScope'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) scope:module') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+class TestModuleScope
+ bp = nil
+end
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"p self", :exp=>'$1 = TestModuleScope'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) scope:instance method') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+class TestMethodScope
+ def m
+ bp = nil
+ end
+end
+TestMethodScope.new.m
+SRC
+
+ tc = []
+ tc << {:cmd=>"b 3"}
+ tc << {:cmd=>"r"}
+ tc << {:cmd=>"p self", :exp=>'$1 = #<TestMethodScope:'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) scope:class method') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+class TestClassMethodScope
+ def self.cm
+ bp = nil
+ end
+end
+TestClassMethodScope.cm
+SRC
+
+ tc = []
+ tc << {:cmd=>"b 3"}
+ tc << {:cmd=>"r"}
+ tc << {:cmd=>"p self", :exp=>'$1 = TestClassMethodScope'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) scope:block') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+1.times do
+ bp = nil
+end
+class TestBlockScope
+ 1.times do
+ bp = nil
+ end
+ def m
+ 1.times do
+ bp = nil
+ end
+ end
+end
+TestBlockScope.new.m
+SRC
+
+ tc = []
+ tc << {:cmd=>"b 2"}
+ tc << {:cmd=>"b 6"}
+ tc << {:cmd=>"b 10"}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p self", :exp=>'$1 = main'}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p self", :exp=>'$3 = #<TestBlockScope:'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) same name:local variabe') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+lv = 'top'
+class TestLocalVariableName
+ lv = 'class'
+ def m
+ lv = 'instance method'
+ bp = nil
+ end
+ bp = nil
+end
+TestLocalVariableName.new.m
+bp = nil
+SRC
+
+ tc = []
+ tc << {:cmd=>"b 6"}
+ tc << {:cmd=>"b 8"}
+ tc << {:cmd=>"b 11"}
+ tc << {:cmd=>"r"}
+ tc << {:cmd=>"p lv", :exp=>'$1 = "class"'}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p lv", :exp=>'$3 = "top"'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) same name:instance variabe') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+@iv = 'top'
+class TestInstanceVariableName
+ def initialize(v)
+ @iv = v
+ end
+ def m
+ bp = nil
+ end
+end
+i1 = TestInstanceVariableName.new('instance1')
+i2 = TestInstanceVariableName.new('instance2')
+i1.m
+i2.m
+bp = nil
+SRC
+
+ tc = []
+ tc << {:cmd=>"b 7"}
+ tc << {:cmd=>"b 14"}
+ tc << {:cmd=>"r"}
+ tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'}
+ tc << {:cmd=>"c"}
+ tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+# Kernel#instance_eval(string) does't work const.
+=begin
+assert('mruby-bin-debugger(print) same name:const') do
+ # ruby source (bp is break point)
+ src = <<"SRC"
+CONST='top'
+class TestConstNameSuperClass
+ CONST='super class'
+ def m
+ bp = nil
+ end
+end
+class TestConstNameSubClass < TestConstNameSuperClass
+ CONST='sub class'
+ def m
+ bp = nil
+ end
+end
+
+TestConstNameSuperClass.new.m()
+TestConstNameSubClass.new.m()
+bp = nil
+SRC
+
+ # todo: wait for 'break' to be implimented
+ tc = []
+ 9.times { tc << {:cmd=>"s"} }
+ tc << {:cmd=>"p CONST", :exp=>"super class"}
+ 3.times { tc << {:cmd=>"s"} }
+ tc << {:cmd=>"p CONST", :exp=>"sub class"}
+ 1.times { tc << {:cmd=>"s"} }
+ tc << {:cmd=>"p CONST", :exp=>"top"}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+=end
+
+assert('mruby-bin-debugger(print) Literal:Numeric') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>"p 100", :exp=>'$1 = 100'}
+ tc << {:cmd=>"p -0b100", :exp=>'$2 = -4'}
+ tc << {:cmd=>"p +0100", :exp=>'$3 = 64'}
+ tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'}
+ tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'}
+ tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"}
+ tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"}
+
+ tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'}
+ tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'}
+ tc << {:cmd=>"p +12.000", :exp=>'$10 = 12.0'}
+ tc << {:cmd=>"p 1e4", :exp=>'$11 = 10000.0'}
+ tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Literal:String') do
+ # ruby source
+ src = <<"SRC"
+foo = 'foo'
+bar = "bar"
+baz = "baz"
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"s"}
+
+ tc << {:cmd=>'p "str"', :exp=>'$1 = "str"'}
+ tc << {:cmd=>'p "s\tt\rr\n"', :exp=>'$2 = "s\\tt\\rr\\n"'}
+ tc << {:cmd=>'p "\C-a\C-z"', :exp=>'$3 = "\\001\\032"'}
+ tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'}
+
+ tc << {:cmd=>'p \'str\'', :exp=>'$5 = "str"'}
+ tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'}
+ tc << {:cmd=>'p \'\\C-a\\C-z\'', :exp=>'$7 = "\\\\C-a\\\\C-z"'}
+ tc << {:cmd=>'p \'#{foo+bar}\'', :exp=>'$8 = "#{foo+bar}"'}
+
+ tc << {:cmd=>'p %!str!', :exp=>'$9 = "str"'}
+ tc << {:cmd=>'p %!s\tt\rr\n!', :exp=>'$10 = "s\\tt\\rr\\n"'}
+ tc << {:cmd=>'p %!\C-a\C-z!', :exp=>'$11 = "\\001\\032"'}
+ tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'}
+
+ tc << {:cmd=>'p %Q!str!', :exp=>'$13 = "str"'}
+ tc << {:cmd=>'p %Q!s\tt\rr\n!', :exp=>'$14 = "s\\tt\\rr\\n"'}
+ tc << {:cmd=>'p %Q!\C-a\C-z!', :exp=>'$15 = "\\001\\032"'}
+ tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'}
+
+ tc << {:cmd=>'p %q!str!', :exp=>'$17 = "str"'}
+ tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'}
+ tc << {:cmd=>'p %q!\\C-a\\C-z!', :exp=>'$19 = "\\\\C-a\\\\C-z"'}
+ tc << {:cmd=>'p %q!#{foo+bar}!', :exp=>'$20 = "#{foo+bar}"'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Literal:Array') do
+ # ruby source
+ src = <<"SRC"
+foo = 'foo'
+bar = "bar"
+baz = "baz"
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"s"}
+
+ tc << {:cmd=>'p []', :exp=>'$1 = []'}
+ tc << {:cmd=>'p [ 5, 12, 8, 10, ]', :exp=>'$2 = [5, 12, 8, 10]'}
+ tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]', :exp=>'$3 = [1, 2.5, "foobar"]'}
+ tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "#{foo}"]'}
+ tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Literal:Hash') do
+ # ruby source
+ src = <<"SRC"
+foo = 'foo'
+bar = "bar"
+baz = "baz"
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"s"}
+
+ tc << {:cmd=>'p {}', :exp=>'$1 = {}'}
+ tc << {:cmd=>'p {"one"=>1,"two"=>2}', :exp=>'$2 = {"one"=>1, "two"=>2}'}
+ tc << {:cmd=>'p {:eins=>"1", :zwei=>"2", }', :exp=>'$3 = {:eins=>"1", :zwei=>"2"}'}
+ tc << {:cmd=>'p {uno:"one", dos: 2}', :exp=>'$4 = {:uno=>"one", :dos=>2}'}
+ tc << {:cmd=>'p {"one"=>1, :zwei=>2, tres:3}', :exp=>'$5 = {"one"=>1, :zwei=>2, :tres=>3}'}
+ tc << {:cmd=>'p {:foo=>"#{foo}",:bar=>"#{bar}"}', :exp=>'$6 = {:foo=>"foo", :bar=>"bar"}'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Literal:Range') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>'p 1..10', :exp=>'$1 = 1..10'}
+ tc << {:cmd=>'p 1...10', :exp=>'$2 = 1...10'}
+ tc << {:cmd=>'p 100..10', :exp=>'$3 = 100..10'}
+ tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'}
+
+ tc << {:cmd=>'p "1" .. "9"', :exp=>'$5 = "1".."9"'}
+ tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Literal:Symbol') do
+ # ruby source
+ src = <<"SRC"
+foo = 'foo'
+bar = "bar"
+baz = "baz"
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>"s"}
+ tc << {:cmd=>"s"}
+
+ tc << {:cmd=>'p :sym', :exp=>'$1 = :sym'}
+ tc << {:cmd=>'p :"sd"', :exp=>'$2 = :sd'}
+ tc << {:cmd=>"p :'ss'", :exp=>'$3 = :ss'}
+ tc << {:cmd=>'p :"123"', :exp=>'$4 = :"123"'}
+ tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'}
+ tc << {:cmd=>'p %s!symsym!', :exp=>'$6 = :symsym'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Unary operation') do
+ # ruby source
+ src = "foo = 'foo'\n"
+
+ # test case
+ tc = []
+ tc << {:cmd=>'p +10', :exp=>'$1 = 10'}
+ tc << {:cmd=>'p -100', :exp=>'$2 = -100'}
+ tc << {:cmd=>'p !true', :exp=>'$3 = false'}
+ tc << {:cmd=>'p !false', :exp=>'$4 = true'}
+ tc << {:cmd=>'p !nil', :exp=>'$5 = true'}
+ tc << {:cmd=>'p !1', :exp=>'$6 = false'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Binary operation') do
+ # ruby source
+ src = <<"SRC"
+CONST = 100
+a,b,c = 1, 5, 8
+foo,bar,baz = 'foo','bar','baz'
+ary = []
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+
+ tc << {:cmd=>'p a+1', :exp=>'$1 = 2'}
+ tc << {:cmd=>'p 2-b', :exp=>'$2 = -3'}
+ tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'}
+ tc << {:cmd=>'p a/b', :exp=>'$4 = 0.2'}
+ tc << {:cmd=>'p c%b', :exp=>'$5 = 3'}
+ tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'}
+ tc << {:cmd=>'p ~3', :exp=>'$7 = -4'}
+
+ tc << {:cmd=>'p 1<<2', :exp=>'$8 = 4'}
+ tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'}
+
+ tc << {:cmd=>'p a|c', :exp=>'$10 = 9'}
+ tc << {:cmd=>'p a&b', :exp=>'$11 = 1'}
+ tc << {:cmd=>'p a^b', :exp=>'$12 = 4'}
+
+ tc << {:cmd=>'p a>b', :exp=>'$13 = false'}
+ tc << {:cmd=>'p a<b', :exp=>'$14 = true'}
+ tc << {:cmd=>'p b>=5', :exp=>'$15 = true'}
+ tc << {:cmd=>'p b<=5', :exp=>'$16 = true'}
+
+ tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'}
+ tc << {:cmd=>'p "A"=="B"', :exp=>'$18 = false'}
+ tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'}
+ tc << {:cmd=>'p "A"!="B"', :exp=>'$20 = true'}
+
+ tc << {:cmd=>'p false || true', :exp=>'$21 = true'}
+ tc << {:cmd=>'p false && true', :exp=>'$22 = false'}
+
+ tc << {:cmd=>'p not nil', :exp=>'$23 = true'}
+ tc << {:cmd=>'p false or true', :exp=>'$24 = true'}
+ tc << {:cmd=>'p false and true', :exp=>'$25 = false'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Ternary operation') do
+ # ruby source
+ src = <<"SRC"
+CONST = 100
+a,b,c = 1, 5, -10
+foo,bar,baz = 'foo','bar','baz'
+ary = []
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+
+ tc << {:cmd=>'p (a < b) ? a : b', :exp=>'$1 = 1'}
+ tc << {:cmd=>'p (a > b) ? a : b', :exp=>'$2 = 5'}
+ tc << {:cmd=>'p true ? "true" : "false"', :exp=>'$3 = "true"'}
+ tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'}
+ tc << {:cmd=>'p nil ? "true" : "false"', :exp=>'$5 = "false"'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Substitution:simple') do
+ # ruby source
+ src = <<"SRC"
+CONST = 100
+a,b,c = 1, 5, -10
+foo,bar,baz = 'foo','bar','baz'
+ary = []
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+
+ tc << {:cmd=>'p a=2', :exp=>'$1 = 2'}
+ tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'}
+
+ tc << {:cmd=>'p undefined=-1', :exp=>'$3 = -1'}
+ tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = NoMethodError'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Substitution:self') do
+ # ruby source
+ src = <<"SRC"
+CONST = 100
+a,b,c = 1, 5, -10
+foo,bar,baz = 'foo','bar','baz'
+ary = []
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+
+ tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'}
+ tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'}
+ tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
+ tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'}
+ tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'}
+
+ tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
+ tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'}
+
+ tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'}
+ tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
+
+ tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
+ tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'}
+ tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'}
+ tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'}
+
+ tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'}
+ tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Substitution:multiple') do
+ # ruby source
+ src = <<"SRC"
+CONST = 100
+a,b,c = 1, 5, -10
+foo,bar,baz = 'foo','bar','baz'
+ary = []
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+
+ tc << {:cmd=>'p a,b=[10,20]', :exp=>'$1 = [10, 20]'}
+ tc << {:cmd=>'p [a,b,c]', :exp=>'$2 = [10, 20, -10]'}
+
+ tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'}
+ tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'}
+
+ tc << {:cmd=>'p a,foo=foo,a', :exp=>'$5 = ["FOO", 10]'}
+ tc << {:cmd=>'p [a,foo]', :exp=>'$6 = ["FOO", 10]'}
+
+# tc << {:cmd=>'p a,*b=[123, 456, 789]'}
+# tc << {:cmd=>'p [a,b]', :exp=>'[123, [456, 789]]'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
+assert('mruby-bin-debugger(print) Substitution:self') do
+ # ruby source
+ src = <<"SRC"
+CONST = 100
+a,b,c = 1, 5, -10
+foo,bar,baz = 'foo','bar','baz'
+ary = []
+SRC
+
+ # test case
+ tc = []
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+ tc << {:cmd=>'s'}
+
+ tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'}
+ tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'}
+ tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
+ tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'}
+ tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'}
+
+ tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
+ tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'}
+
+ tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'}
+ tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
+
+ tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
+ tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'}
+ tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'}
+ tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'}
+
+ tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'}
+ tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'}
+
+ BinTest_MrubyBinDebugger.test(src, tc)
+end
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake
new file mode 100644
index 00000000..764f431a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/mrbgem.rake
@@ -0,0 +1,9 @@
+MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby debugger command'
+
+ spec.add_dependency('mruby-eval', :core => 'mruby-eval')
+
+ spec.bins = %w(mrdb)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
new file mode 100644
index 00000000..dead4b2a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -0,0 +1,507 @@
+/*
+** apibreak.c
+**
+*/
+
+#include <string.h>
+#include <mruby.h>
+#include <mruby/irep.h>
+#include "mrdb.h"
+#include <mruby/debug.h>
+#include <mruby/opcode.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/variable.h>
+#include "mrdberror.h"
+#include "apibreak.h"
+
+#define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024)
+#define MRB_DEBUG_BP_FILE_OK (0x0001)
+#define MRB_DEBUG_BP_LINENO_OK (0x0002)
+
+static uint16_t
+check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno)
+{
+ uint32_t count = info_file->line_entry_count;
+ uint16_t l_idx;
+
+ if (info_file->line_type == mrb_debug_line_ary) {
+ for (l_idx = 0; l_idx < count; ++l_idx) {
+ if (lineno == info_file->lines.ary[l_idx]) {
+ return lineno;
+ }
+ }
+ }
+ else {
+ for (l_idx = 0; l_idx < count; ++l_idx) {
+ if (lineno == info_file->lines.flat_map[l_idx].line) {
+ return lineno;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int32_t
+get_break_index(mrb_debug_context *dbg, uint32_t bpno)
+{
+ uint32_t i;
+ int32_t index;
+ char hit = FALSE;
+
+ for(i = 0 ; i < dbg->bpnum; i++) {
+ if (dbg->bp[i].bpno == bpno) {
+ hit = TRUE;
+ index = i;
+ break;
+ }
+ }
+
+ if (hit == FALSE) {
+ return MRB_DEBUG_BREAK_INVALID_NO;
+ }
+
+ return index;
+}
+
+static void
+free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
+{
+ switch(bp->type) {
+ case MRB_DEBUG_BPTYPE_LINE:
+ mrb_free(mrb, (void*)bp->point.linepoint.file);
+ break;
+ case MRB_DEBUG_BPTYPE_METHOD:
+ mrb_free(mrb, (void*)bp->point.methodpoint.method_name);
+ if (bp->point.methodpoint.class_name != NULL) {
+ mrb_free(mrb, (void*)bp->point.methodpoint.class_name);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static uint16_t
+check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
+{
+ mrb_irep_debug_info_file *info_file;
+ uint16_t result = 0;
+ uint16_t f_idx;
+ uint16_t fix_lineno;
+ uint16_t i;
+
+ for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+ info_file = irep->debug_info->files[f_idx];
+ if (!strcmp(info_file->filename, file)) {
+ result = MRB_DEBUG_BP_FILE_OK;
+
+ fix_lineno = check_lineno(info_file, lineno);
+ if (fix_lineno != 0) {
+ return result | MRB_DEBUG_BP_LINENO_OK;
+ }
+ }
+ for (i=0; i < irep->rlen; ++i) {
+ result |= check_file_lineno(irep->reps[i], file, lineno);
+ if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
+ return result;
+ }
+ }
+ }
+ return result;
+}
+
+static int32_t
+compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
+{
+ const char* class_name;
+ const char* method_name;
+ struct RProc* m;
+ struct RClass* sc;
+ const char* sn;
+ mrb_sym ssym;
+ mrb_debug_methodpoint *method_p;
+ mrb_bool is_defined;
+
+ method_name = mrb_sym2name(mrb, method_sym);
+
+ method_p = &bp->point.methodpoint;
+ if (strcmp(method_p->method_name, method_name) == 0) {
+ class_name = mrb_class_name(mrb, class_obj);
+ if (class_name == NULL) {
+ if (method_p->class_name == NULL) {
+ return bp->bpno;
+ }
+ }
+ else if (method_p->class_name != NULL) {
+ m = mrb_method_search_vm(mrb, &class_obj, method_sym);
+ if (m == NULL) {
+ return MRB_DEBUG_OK;
+ }
+ if (MRB_PROC_CFUNC_P(m)) {
+ *isCfunc = TRUE;
+ }
+
+ is_defined = mrb_class_defined(mrb, method_p->class_name);
+ if (is_defined == FALSE) {
+ return MRB_DEBUG_OK;
+ }
+
+ sc = mrb_class_get(mrb, method_p->class_name);
+ ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
+ m = mrb_method_search_vm(mrb, &sc, ssym);
+ if (m == NULL) {
+ return MRB_DEBUG_OK;
+ }
+
+ class_name = mrb_class_name(mrb, class_obj);
+ sn = mrb_class_name(mrb, sc);
+ if (strcmp(sn, class_name) == 0) {
+ return bp->bpno;
+ }
+ }
+ }
+ return MRB_DEBUG_OK;
+}
+
+int32_t
+mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
+{
+ int32_t index;
+ char* set_file;
+ uint16_t result;
+
+ if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ if (dbg->bpnum >= MAX_BREAKPOINT) {
+ return MRB_DEBUG_BREAK_NUM_OVER;
+ }
+
+ if (dbg->next_bpno > MAX_BREAKPOINTNO) {
+ return MRB_DEBUG_BREAK_NO_OVER;
+ }
+
+ /* file and lineno check (line type mrb_debug_line_ary only.) */
+ result = check_file_lineno(dbg->root_irep, file, lineno);
+ if (result == 0) {
+ return MRB_DEBUG_BREAK_INVALID_FILE;
+ }
+ else if (result == MRB_DEBUG_BP_FILE_OK) {
+ return MRB_DEBUG_BREAK_INVALID_LINENO;
+ }
+
+ set_file = mrb_malloc(mrb, strlen(file) + 1);
+
+ index = dbg->bpnum;
+ dbg->bp[index].bpno = dbg->next_bpno;
+ dbg->next_bpno++;
+ dbg->bp[index].enable = TRUE;
+ dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE;
+ dbg->bp[index].point.linepoint.lineno = lineno;
+ dbg->bpnum++;
+
+ strncpy(set_file, file, strlen(file) + 1);
+
+ dbg->bp[index].point.linepoint.file = set_file;
+
+ return dbg->bp[index].bpno;
+}
+
+int32_t
+mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name)
+{
+ int32_t index;
+ char* set_class;
+ char* set_method;
+
+ if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ if (dbg->bpnum >= MAX_BREAKPOINT) {
+ return MRB_DEBUG_BREAK_NUM_OVER;
+ }
+
+ if (dbg->next_bpno > MAX_BREAKPOINTNO) {
+ return MRB_DEBUG_BREAK_NO_OVER;
+ }
+
+ if (class_name != NULL) {
+ set_class = mrb_malloc(mrb, strlen(class_name) + 1);
+ strncpy(set_class, class_name, strlen(class_name) + 1);
+ }
+ else {
+ set_class = NULL;
+ }
+
+ set_method = mrb_malloc(mrb, strlen(method_name) + 1);
+
+ strncpy(set_method, method_name, strlen(method_name) + 1);
+
+ index = dbg->bpnum;
+ dbg->bp[index].bpno = dbg->next_bpno;
+ dbg->next_bpno++;
+ dbg->bp[index].enable = TRUE;
+ dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD;
+ dbg->bp[index].point.methodpoint.method_name = set_method;
+ dbg->bp[index].point.methodpoint.class_name = set_class;
+ dbg->bpnum++;
+
+ return dbg->bp[index].bpno;
+}
+
+int32_t
+mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg)
+{
+ if ((mrb == NULL) || (dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ return dbg->bpnum;
+}
+
+int32_t
+mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp)
+{
+ uint32_t get_size = 0;
+
+ if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ if (dbg->bpnum >= size) {
+ get_size = size;
+ }
+ else {
+ get_size = dbg->bpnum;
+ }
+
+ memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size);
+
+ return get_size;
+}
+
+int32_t
+mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp)
+{
+ int32_t index;
+
+ if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ index = get_break_index(dbg, bpno);
+ if (index == MRB_DEBUG_BREAK_INVALID_NO) {
+ return MRB_DEBUG_BREAK_INVALID_NO;
+ }
+
+ bp->bpno = dbg->bp[index].bpno;
+ bp->enable = dbg->bp[index].enable;
+ bp->point = dbg->bp[index].point;
+ bp->type = dbg->bp[index].type;
+
+ return 0;
+}
+
+int32_t
+mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
+{
+ uint32_t i;
+ int32_t index;
+
+ if ((mrb == NULL) ||(dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ index = get_break_index(dbg, bpno);
+ if (index == MRB_DEBUG_BREAK_INVALID_NO) {
+ return MRB_DEBUG_BREAK_INVALID_NO;
+ }
+
+ free_breakpoint(mrb, &dbg->bp[index]);
+
+ for(i = index ; i < dbg->bpnum; i++) {
+ if ((i + 1) == dbg->bpnum) {
+ memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint));
+ }
+ else {
+ memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint));
+ }
+ }
+
+ dbg->bpnum--;
+
+ return MRB_DEBUG_OK;
+}
+
+int32_t
+mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg)
+{
+ uint32_t i;
+
+ if ((mrb == NULL) || (dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ for(i = 0 ; i < dbg->bpnum ; i++) {
+ free_breakpoint(mrb, &dbg->bp[i]);
+ }
+
+ dbg->bpnum = 0;
+
+ return MRB_DEBUG_OK;
+}
+
+int32_t
+mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
+{
+ int32_t index = 0;
+
+ if ((mrb == NULL) || (dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ index = get_break_index(dbg, bpno);
+ if (index == MRB_DEBUG_BREAK_INVALID_NO) {
+ return MRB_DEBUG_BREAK_INVALID_NO;
+ }
+
+ dbg->bp[index].enable = TRUE;
+
+ return MRB_DEBUG_OK;
+}
+
+int32_t
+mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
+{
+ uint32_t i;
+
+ if ((mrb == NULL) || (dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ for(i = 0 ; i < dbg->bpnum; i++) {
+ dbg->bp[i].enable = TRUE;
+ }
+
+ return MRB_DEBUG_OK;
+}
+
+int32_t
+mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
+{
+ int32_t index = 0;
+
+ if ((mrb == NULL) || (dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ index = get_break_index(dbg, bpno);
+ if (index == MRB_DEBUG_BREAK_INVALID_NO) {
+ return MRB_DEBUG_BREAK_INVALID_NO;
+ }
+
+ dbg->bp[index].enable = FALSE;
+
+ return MRB_DEBUG_OK;
+}
+
+int32_t
+mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
+{
+ uint32_t i;
+
+ if ((mrb == NULL) || (dbg == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ for(i = 0 ; i < dbg->bpnum; i++) {
+ dbg->bp[i].enable = FALSE;
+ }
+
+ return MRB_DEBUG_OK;
+}
+
+static mrb_bool
+check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
+{
+ if (pc > irep->iseq) {
+ if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int32_t
+mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line)
+{
+ mrb_debug_breakpoint *bp;
+ mrb_debug_linepoint *line_p;
+ uint32_t i;
+
+ if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
+ return MRB_DEBUG_OK;
+ }
+
+ bp = dbg->bp;
+ for(i=0; i<dbg->bpnum; i++) {
+ switch (bp->type) {
+ case MRB_DEBUG_BPTYPE_LINE:
+ if (bp->enable == TRUE) {
+ line_p = &bp->point.linepoint;
+ if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
+ return bp->bpno;
+ }
+ }
+ break;
+ case MRB_DEBUG_BPTYPE_METHOD:
+ break;
+ case MRB_DEBUG_BPTYPE_NONE:
+ default:
+ return MRB_DEBUG_OK;
+ }
+ bp++;
+ }
+ return MRB_DEBUG_OK;
+}
+
+
+int32_t
+mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
+{
+ mrb_debug_breakpoint *bp;
+ int32_t bpno;
+ uint32_t i;
+
+ if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ bp = dbg->bp;
+ for(i=0; i<dbg->bpnum; i++) {
+ if (bp->type == MRB_DEBUG_BPTYPE_METHOD) {
+ if (bp->enable == TRUE) {
+ bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc);
+ if (bpno > 0) {
+ return bpno;
+ }
+ }
+ }
+ else if (bp->type == MRB_DEBUG_BPTYPE_NONE) {
+ break;
+ }
+ bp++;
+ }
+
+ return 0;
+}
+
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h
new file mode 100644
index 00000000..08f1d808
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h
@@ -0,0 +1,26 @@
+/*
+** apibreak.h
+**
+*/
+
+#ifndef APIBREAK_H_
+#define APIBREAK_H_
+
+#include <mruby.h>
+#include "mrdb.h"
+
+int32_t mrb_debug_set_break_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
+int32_t mrb_debug_set_break_method(mrb_state *, mrb_debug_context *, const char *, const char *);
+int32_t mrb_debug_get_breaknum(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_get_break_all(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]);
+int32_t mrb_debug_get_break(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint *);
+int32_t mrb_debug_delete_break(mrb_state *, mrb_debug_context *, uint32_t);
+int32_t mrb_debug_delete_break_all(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_enable_break(mrb_state *, mrb_debug_context *, uint32_t);
+int32_t mrb_debug_enable_break_all(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_disable_break(mrb_state *, mrb_debug_context *, uint32_t);
+int32_t mrb_debug_disable_break_all(mrb_state *, mrb_debug_context *);
+int32_t mrb_debug_check_breakpoint_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
+int32_t mrb_debug_check_breakpoint_method(mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool*);
+
+#endif /* APIBREAK_H_ */
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
new file mode 100644
index 00000000..8aaa30bc
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
@@ -0,0 +1,239 @@
+/*
+ * apilist.c
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mrdb.h"
+#include "mrdberror.h"
+#include "apilist.h"
+#include <mruby/compile.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+
+#define LINE_BUF_SIZE MAX_COMMAND_LINE
+
+typedef struct source_file {
+ char *path;
+ uint16_t lineno;
+ FILE *fp;
+} source_file;
+
+static void
+source_file_free(mrb_state *mrb, source_file *file)
+{
+ if (file != NULL) {
+ if (file->path != NULL) {
+ mrb_free(mrb, file->path);
+ }
+ if (file->fp != NULL) {
+ fclose(file->fp);
+ file->fp = NULL;
+ }
+ mrb_free(mrb, file);
+ }
+}
+
+static char*
+build_path(mrb_state *mrb, const char *dir, const char *base)
+{
+ int len;
+ char *path = NULL;
+
+ len = strlen(base) + 1;
+
+ if (strcmp(dir, ".")) {
+ len += strlen(dir) + sizeof("/") - 1;
+ }
+
+ path = mrb_malloc(mrb, len);
+ memset(path, 0, len);
+
+ if (strcmp(dir, ".")) {
+ strcat(path, dir);
+ strcat(path, "/");
+ }
+ strcat(path, base);
+
+ return path;
+}
+
+static char*
+dirname(mrb_state *mrb, const char *path)
+{
+ size_t len;
+ char *p, *dir;
+
+ if (path == NULL) {
+ return NULL;
+ }
+
+ p = strrchr(path, '/');
+ len = p != NULL ? (size_t)(p - path) : strlen(path);
+
+ dir = mrb_malloc(mrb, len + 1);
+ strncpy(dir, path, len);
+ dir[len] = '\0';
+
+ return dir;
+}
+
+static source_file*
+source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
+{
+ source_file *file = NULL;
+
+ file = mrb_malloc(mrb, sizeof(source_file));
+
+ memset(file, '\0', sizeof(source_file));
+ file->fp = fopen(filename, "rb");
+
+ if (file->fp == NULL) {
+ source_file_free(mrb, file);
+ return NULL;
+ }
+
+ file->lineno = 1;
+ file->path = mrb_malloc(mrb, strlen(filename) + 1);
+ strcpy(file->path, filename);
+ return file;
+}
+
+static mrb_bool
+remove_newlines(char *s, FILE *fp)
+{
+ int c;
+ char *p;
+ size_t len;
+
+ if ((len = strlen(s)) == 0) {
+ return FALSE;
+ }
+
+ p = s + len - 1;
+
+ if (*p != '\r' && *p != '\n') {
+ return FALSE;
+ }
+
+ if (*p == '\r') {
+ /* peek the next character and skip '\n' */
+ if ((c = fgetc(fp)) != '\n') {
+ ungetc(c, fp);
+ }
+ }
+
+ /* remove trailing newline characters */
+ while (s <= p && (*p == '\r' || *p == '\n')) {
+ *p-- = '\0';
+ }
+
+ return TRUE;
+}
+
+static void
+show_lines(source_file *file, uint16_t line_min, uint16_t line_max)
+{
+ char buf[LINE_BUF_SIZE];
+ int show_lineno = 1, found_newline = 0, is_printed = 0;
+
+ if (file->fp == NULL) {
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), file->fp) != NULL) {
+ found_newline = remove_newlines(buf, file->fp);
+
+ if (line_min <= file->lineno) {
+ if (show_lineno) {
+ printf("%-8d", file->lineno);
+ }
+ show_lineno = found_newline;
+ printf(found_newline ? "%s\n" : "%s", buf);
+ is_printed = 1;
+ }
+
+ if (found_newline) {
+ if (line_max < ++file->lineno) {
+ break;
+ }
+ }
+ }
+
+ if (is_printed && !found_newline) {
+ printf("\n");
+ }
+}
+
+char*
+mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename)
+{
+ int i;
+ FILE *fp;
+ const char *search_path[3];
+ char *path = NULL;
+ const char *srcname = strrchr(filename, '/');
+
+ if (srcname) srcname++;
+ else srcname = filename;
+
+ search_path[0] = srcpath;
+ search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0));
+ search_path[2] = ".";
+
+ for (i = 0; i < 3; i++) {
+ if (search_path[i] == NULL) {
+ continue;
+ }
+
+ if ((path = build_path(mrb, search_path[i], srcname)) == NULL) {
+ continue;
+ }
+
+ if ((fp = fopen(path, "rb")) == NULL) {
+ mrb_free(mrb, path);
+ path = NULL;
+ continue;
+ }
+ fclose(fp);
+ break;
+ }
+
+ mrb_free(mrb, (void *)search_path[1]);
+
+ return path;
+}
+
+int32_t
+mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max)
+{
+ char *ext;
+ source_file *file;
+
+ if (mrb == NULL || dbg == NULL || filename == NULL) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ ext = strrchr(filename, '.');
+
+ if (ext == NULL || strcmp(ext, ".rb")) {
+ printf("List command only supports .rb file.\n");
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ if (line_min > line_max) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ if ((file = source_file_new(mrb, dbg, filename)) != NULL) {
+ show_lines(file, line_min, line_max);
+ source_file_free(mrb, file);
+ return MRB_DEBUG_OK;
+ }
+ else {
+ printf("Invalid source file named %s.\n", filename);
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h
new file mode 100644
index 00000000..6c410788
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h
@@ -0,0 +1,14 @@
+/*
+ * apilist.h
+ */
+
+#ifndef APILIST_H_
+#define APILIST_H_
+
+#include <mruby.h>
+#include "mrdb.h"
+
+int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t);
+char* mrb_debug_get_source(mrb_state *, mrdb_state *, const char *, const char *);
+
+#endif /* APILIST_H_ */
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
new file mode 100644
index 00000000..a9c895b5
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
@@ -0,0 +1,78 @@
+/*
+** apiprint.c
+**
+*/
+
+#include <string.h>
+#include "mrdb.h"
+#include <mruby/value.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include "apiprint.h"
+
+static void
+mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len)
+{
+ mrbc_context *c;
+
+ c = mrbc_context_new(mrb);
+ c->no_exec = TRUE;
+ c->capture_errors = TRUE;
+ c->filename = (char*)dbg->prvfile;
+ c->lineno = dbg->prvline;
+
+ /* Load program */
+ mrb_load_nstring_cxt(mrb, expr, len, c);
+
+ mrbc_context_free(mrb, c);
+}
+
+mrb_value
+mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
+{
+ void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
+ mrb_value ruby_code;
+ mrb_value s;
+ mrb_value v;
+ mrb_value recv;
+
+ /* disable code_fetch_hook */
+ tmp = mrb->code_fetch_hook;
+ mrb->code_fetch_hook = NULL;
+
+ mrdb_check_syntax(mrb, dbg, expr, len);
+ if (mrb->exc) {
+ v = mrb_obj_value(mrb->exc);
+ mrb->exc = 0;
+ }
+ else {
+ /*
+ * begin
+ * expr
+ * rescue => e
+ * e
+ * end
+ */
+ ruby_code = mrb_str_new_lit(mrb, "begin\n");
+ ruby_code = mrb_str_cat(mrb, ruby_code, expr, len);
+ ruby_code = mrb_str_cat_lit(mrb, ruby_code, "\nrescue => e\ne\nend");
+
+ recv = dbg->regs[0];
+
+ v = mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code);
+ }
+
+ if (exc) {
+ *exc = mrb_obj_is_kind_of(mrb, v, mrb->eException_class);
+ }
+
+ s = mrb_funcall(mrb, v, "inspect", 0);
+
+ /* enable code_fetch_hook */
+ mrb->code_fetch_hook = tmp;
+
+ return s;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
new file mode 100644
index 00000000..e256f626
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
@@ -0,0 +1,13 @@
+/*
+ * apiprint.h
+ */
+
+#ifndef APIPRINT_H_
+#define APIPRINT_H_
+
+#include <mruby.h>
+#include "mrdb.h"
+
+mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
+
+#endif /* APIPRINT_H_ */
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
new file mode 100644
index 00000000..8e590175
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
@@ -0,0 +1,436 @@
+/*
+** cmdbreak.c
+**
+*/
+
+#include <ctype.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/dump.h>
+#include <mruby/debug.h>
+#include <mruby/string.h>
+#include "mrdb.h"
+#include "mrdberror.h"
+#include "apibreak.h"
+
+#define BREAK_SET_MSG_LINE "Breakpoint %d: file %s, line %d.\n"
+#define BREAK_SET_MSG_METHOD "Breakpoint %d: method %s.\n"
+#define BREAK_SET_MSG_CLASS_METHOD "Breakpoint %d: class %s, method %s.\n"
+#define BREAK_INFO_MSG_HEADER "Num Type Enb What"
+#define BREAK_INFO_MSG_LINEBREAK "%-8ubreakpoint %s at %s:%u\n"
+#define BREAK_INFO_MSG_METHODBREAK "%-8ubreakpoint %s in %s:%s\n"
+#define BREAK_INFO_MSG_METHODBREAK_NOCLASS "%-8ubreakpoint %s in %s\n"
+#define BREAK_INFO_MSG_ENABLE "y"
+#define BREAK_INFO_MSG_DISABLE "n"
+
+#define BREAK_ERR_MSG_INVALIDARG "Internal error."
+#define BREAK_ERR_MSG_BLANK "Try \'help break\' for more information."
+#define BREAK_ERR_MSG_RANGEOVER "The line number range is from 1 to 65535."
+#define BREAK_ERR_MSG_NUMOVER "Exceeded the setable number of breakpoint."
+#define BREAK_ERR_MSG_NOOVER "Breakno is over the available number.Please 'quit' and restart mrdb."
+#define BREAK_ERR_MSG_INVALIDSTR "String \'%s\' is invalid.\n"
+#define BREAK_ERR_MSG_INVALIDLINENO "Line %d in file \"%s\" is unavailable.\n"
+#define BREAK_ERR_MSG_INVALIDCLASS "Class name \'%s\' is invalid.\n"
+#define BREAK_ERR_MSG_INVALIDMETHOD "Method name \'%s\' is invalid.\n"
+#define BREAK_ERR_MSG_INVALIDFILE "Source file named \"%s\" is unavailable.\n"
+#define BREAK_ERR_MSG_INVALIDBPNO "warning: bad breakpoint number at or near '%s'\n"
+#define BREAK_ERR_MSG_INVALIDBPNO_INFO "Args must be numbers variables."
+#define BREAK_ERR_MSG_NOBPNO "No breakpoint number %d.\n"
+#define BREAK_ERR_MSG_NOBPNO_INFO "No breakpoint matching '%d'\n"
+#define BREAK_ERR_MSG_NOBPNO_INFOALL "No breakpoints."
+
+#define LINENO_MAX_DIGIT 6
+#define BPNO_LETTER_NUM 9
+
+typedef int32_t (*all_command_func)(mrb_state *, mrb_debug_context *);
+typedef int32_t (*select_command_func)(mrb_state *, mrb_debug_context *, uint32_t);
+
+static void
+print_api_common_error(int32_t error)
+{
+ switch(error) {
+ case MRB_DEBUG_INVALID_ARGUMENT:
+ puts(BREAK_ERR_MSG_INVALIDARG);
+ break;
+ default:
+ break;
+ }
+}
+
+#undef STRTOUL
+#define STRTOUL(ul,s) { \
+ int i; \
+ ul = 0; \
+ for(i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0'); \
+}
+
+static int32_t
+parse_breakpoint_no(char* args)
+{
+ char* ps = args;
+ uint32_t l;
+
+ if ((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) {
+ return 0;
+ }
+
+ while (!(ISBLANK(*ps)||ISCNTRL(*ps))) {
+ if (!ISDIGIT(*ps)) {
+ return 0;
+ }
+ ps++;
+ }
+
+ STRTOUL(l, args);
+ return l;
+}
+
+static mrb_bool
+exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func)
+{
+ int32_t ret = MRB_DEBUG_OK;
+
+ if (mrdb->wcnt == 1) {
+ ret = func(mrb, mrdb->dbg);
+ print_api_common_error(ret);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func)
+{
+ char* ps;
+ int32_t ret = MRB_DEBUG_OK;
+ int32_t bpno = 0;
+ int32_t i;
+
+ for(i=1; i<mrdb->wcnt; i++) {
+ ps = mrdb->words[i];
+ bpno = parse_breakpoint_no(ps);
+ if (bpno == 0) {
+ printf(BREAK_ERR_MSG_INVALIDBPNO, ps);
+ break;
+ }
+ ret = func(mrb, mrdb->dbg, (uint32_t)bpno);
+ if (ret == MRB_DEBUG_BREAK_INVALID_NO) {
+ printf(BREAK_ERR_MSG_NOBPNO, bpno);
+ }
+ else if (ret != MRB_DEBUG_OK) {
+ print_api_common_error(ret);
+ }
+ }
+}
+
+mrb_debug_bptype
+check_bptype(char* args)
+{
+ char* ps = args;
+
+ if (ISBLANK(*ps)||ISCNTRL(*ps)) {
+ puts(BREAK_ERR_MSG_BLANK);
+ return MRB_DEBUG_BPTYPE_NONE;
+ }
+
+ if (!ISDIGIT(*ps)) {
+ return MRB_DEBUG_BPTYPE_METHOD;
+ }
+
+ while (!(ISBLANK(*ps)||ISCNTRL(*ps))) {
+ if (!ISDIGIT(*ps)) {
+ printf(BREAK_ERR_MSG_INVALIDSTR, args);
+ return MRB_DEBUG_BPTYPE_NONE;
+ }
+ ps++;
+ }
+
+ if ((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) {
+ puts(BREAK_ERR_MSG_RANGEOVER);
+ return MRB_DEBUG_BPTYPE_NONE;
+ }
+
+ return MRB_DEBUG_BPTYPE_LINE;
+}
+
+static void
+print_breakpoint(mrb_debug_breakpoint *bp)
+{
+ const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE};
+
+ if (bp->type == MRB_DEBUG_BPTYPE_LINE) {
+ printf(BREAK_INFO_MSG_LINEBREAK,
+ bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno);
+ }
+ else {
+ if (bp->point.methodpoint.class_name == NULL) {
+ printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS,
+ bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name);
+ }
+ else {
+ printf(BREAK_INFO_MSG_METHODBREAK,
+ bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name);
+ }
+ }
+}
+
+static void
+info_break_all(mrb_state *mrb, mrdb_state *mrdb)
+{
+ int32_t bpnum = 0;
+ int32_t i = 0;
+ int32_t ret = MRB_DEBUG_OK;
+ mrb_debug_breakpoint *bp_list;
+
+ bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg);
+ if (bpnum < 0) {
+ print_api_common_error(bpnum);
+ return;
+ }
+ else if (bpnum == 0) {
+ puts(BREAK_ERR_MSG_NOBPNO_INFOALL);
+ return;
+ }
+ bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint));
+
+ ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list);
+ if (ret < 0) {
+ print_api_common_error(ret);
+ return;
+ }
+ puts(BREAK_INFO_MSG_HEADER);
+ for(i = 0 ; i < bpnum ; i++) {
+ print_breakpoint(&bp_list[i]);
+ }
+
+ mrb_free(mrb, bp_list);
+}
+
+static void
+info_break_select(mrb_state *mrb, mrdb_state *mrdb)
+{
+ int32_t ret = MRB_DEBUG_OK;
+ int32_t bpno = 0;
+ char* ps = mrdb->command;
+ mrb_debug_breakpoint bp;
+ mrb_bool isFirst = TRUE;
+ int32_t i;
+
+ for(i=2; i<mrdb->wcnt; i++) {
+ ps = mrdb->words[i];
+ bpno = parse_breakpoint_no(ps);
+ if (bpno == 0) {
+ puts(BREAK_ERR_MSG_INVALIDBPNO_INFO);
+ break;
+ }
+
+ ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp);
+ if (ret == MRB_DEBUG_BREAK_INVALID_NO) {
+ printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno);
+ break;
+ }
+ else if (ret != MRB_DEBUG_OK) {
+ print_api_common_error(ret);
+ break;
+ }
+ else if (isFirst == TRUE) {
+ isFirst = FALSE;
+ puts(BREAK_INFO_MSG_HEADER);
+ }
+ print_breakpoint(&bp);
+ }
+}
+
+mrb_debug_bptype
+parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
+{
+ mrb_debug_context *dbg = mrdb->dbg;
+ char *args;
+ char *body;
+ mrb_debug_bptype type;
+ uint32_t l;
+
+ if (mrdb->wcnt <= 1) {
+ puts(BREAK_ERR_MSG_BLANK);
+ return MRB_DEBUG_BPTYPE_NONE;
+ }
+
+ args = mrdb->words[1];
+ if ((body = strrchr(args, ':')) == NULL) {
+ body = args;
+ type = check_bptype(body);
+ }
+ else {
+ if (body == args) {
+ printf(BREAK_ERR_MSG_INVALIDSTR, args);
+ return MRB_DEBUG_BPTYPE_NONE;
+ }
+ *body = '\0';
+ type = check_bptype(++body);
+ }
+
+ switch(type) {
+ case MRB_DEBUG_BPTYPE_LINE:
+ STRTOUL(l, body);
+ if (l <= 65535) {
+ *line = l;
+ *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args;
+ }
+ else {
+ puts(BREAK_ERR_MSG_RANGEOVER);
+ type = MRB_DEBUG_BPTYPE_NONE;
+ }
+ break;
+ case MRB_DEBUG_BPTYPE_METHOD:
+ if (body == args) {
+ /* method only */
+ if (ISUPPER(*body)||ISLOWER(*body)||(*body == '_')) {
+ *method = body;
+ *cname = NULL;
+ }
+ else {
+ printf(BREAK_ERR_MSG_INVALIDMETHOD, args);
+ type = MRB_DEBUG_BPTYPE_NONE;
+ }
+ }
+ else {
+ if (ISUPPER(*args)) {
+ switch(*body) {
+ case '@': case '$': case '?': case '.': case ',': case ':':
+ case ';': case '#': case '\\': case '\'': case '\"':
+ printf(BREAK_ERR_MSG_INVALIDMETHOD, body);
+ type = MRB_DEBUG_BPTYPE_NONE;
+ break;
+ default:
+ *method = body;
+ *cname = args;
+ break;
+ }
+ }
+ else {
+ printf(BREAK_ERR_MSG_INVALIDCLASS, args);
+ type = MRB_DEBUG_BPTYPE_NONE;
+ }
+ }
+ break;
+ case MRB_DEBUG_BPTYPE_NONE:
+ default:
+ break;
+ }
+
+ return type;
+}
+
+dbgcmd_state
+dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_debug_bptype type;
+ mrb_debug_context *dbg = mrdb->dbg;
+ const char *file = NULL;
+ uint32_t line = 0;
+ char *cname = NULL;
+ char *method = NULL;
+ int32_t ret;
+
+ type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
+ switch (type) {
+ case MRB_DEBUG_BPTYPE_LINE:
+ ret = mrb_debug_set_break_line(mrb, dbg, file, line);
+ break;
+ case MRB_DEBUG_BPTYPE_METHOD:
+ ret = mrb_debug_set_break_method(mrb, dbg, cname, method);
+ break;
+ case MRB_DEBUG_BPTYPE_NONE:
+ default:
+ return DBGST_PROMPT;
+ }
+
+ if (ret >= 0) {
+ if (type == MRB_DEBUG_BPTYPE_LINE) {
+ printf(BREAK_SET_MSG_LINE, ret, file, line);
+ }
+ else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) {
+ printf(BREAK_SET_MSG_METHOD, ret, method);
+ }
+ else {
+ printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method);
+ }
+ }
+ else {
+ switch (ret) {
+ case MRB_DEBUG_BREAK_INVALID_LINENO:
+ printf(BREAK_ERR_MSG_INVALIDLINENO, line, file);
+ break;
+ case MRB_DEBUG_BREAK_INVALID_FILE:
+ printf(BREAK_ERR_MSG_INVALIDFILE, file);
+ break;
+ case MRB_DEBUG_BREAK_NUM_OVER:
+ puts(BREAK_ERR_MSG_NUMOVER);
+ break;
+ case MRB_DEBUG_BREAK_NO_OVER:
+ puts(BREAK_ERR_MSG_NOOVER);
+ break;
+ case MRB_DEBUG_INVALID_ARGUMENT:
+ puts(BREAK_ERR_MSG_INVALIDARG);
+ break;
+ case MRB_DEBUG_NOBUF:
+ puts("T.B.D.");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb)
+{
+ if (mrdb->wcnt == 2) {
+ info_break_all(mrb, mrdb);
+ }
+ else {
+ info_break_select(mrb, mrdb);
+ }
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_bool ret = FALSE;
+
+ ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all);
+ if (ret != TRUE) {
+ exe_set_command_select(mrb, mrdb, mrb_debug_delete_break);
+ }
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_bool ret = FALSE;
+
+ ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all);
+ if (ret != TRUE) {
+ exe_set_command_select(mrb, mrdb, mrb_debug_enable_break);
+ }
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_bool ret = FALSE;
+
+ ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all);
+ if (ret != TRUE) {
+ exe_set_command_select(mrb, mrdb, mrb_debug_disable_break);
+ }
+ return DBGST_PROMPT;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
new file mode 100644
index 00000000..5984b623
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
@@ -0,0 +1,501 @@
+/*
+** cmdmisc.c - mruby debugger miscellaneous command functions
+**
+*/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "apilist.h"
+#include <mruby/compile.h>
+
+typedef struct help_msg {
+ const char *cmd1;
+ const char *cmd2;
+ const char *short_msg;
+ const char *long_msg;
+} help_msg;
+
+static help_msg help_msg_list[] = {
+ {
+ "b[reak]", NULL, "Set breakpoint",
+ "Usage: break [file:]line\n"
+ " break [class:]method\n"
+ "\n"
+ "Set breakpoint at specified line or method.\n"
+ "If \'[file:]line\' is specified, break at start of code for that line (in a file).\n"
+ "If \'[class:]method\' is specified, break at start of code for that method (of the class).\n"
+ },
+ {
+ "c[ontinue]", NULL, "Continue program being debugged",
+ "Usage: continue [N]\n"
+ "\n"
+ "Continue program stopped by a breakpoint.\n"
+ "If N, which is non negative value, is passed,\n"
+ "proceed program until the N-th breakpoint is coming.\n"
+ "If N is not passed, N is assumed 1.\n"
+ },
+ {
+ "d[elete]", NULL, "Delete some breakpoints",
+ "Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n"
+ "\n"
+ "Delete some breakpoints.\n"
+ "Arguments are breakpoint numbers with spaces in between.\n"
+ "To delete all breakpoints, give no argument.\n"
+ },
+ {
+ "dis[able]", NULL, "Disable some breakpoints",
+ "Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n"
+ "\n"
+ "Disable some breakpoints.\n"
+ "Arguments are breakpoint numbers with spaces in between.\n"
+ "To disable all breakpoints, give no argument.\n"
+ },
+ {
+ "en[able]", NULL, "Enable some breakpoints",
+ "Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n"
+ "\n"
+ "Enable some breakpoints.\n"
+ "Arguments are breakpoint numbers with spaces in between.\n"
+ "To enable all breakpoints, give no argument.\n"
+ },
+ {
+ "ev[al]", NULL, "Evaluate expression",
+ "Usage: eval expr\n"
+ "\n"
+ "It evaluates and prints the value of the mruby expression.\n"
+ "This is equivalent to the \'print\' command.\n"
+ },
+ {
+ "h[elp]", NULL, "Print this help",
+ "Usage: help [command]\n"
+ "\n"
+ "With no arguments, help displays a short list of commands.\n"
+ "With a command name as help argument, help displays how to use that command.\n"
+ },
+ {
+ "i[nfo]", "b[reakpoints]", "Status of breakpoints",
+ "Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n"
+ "\n"
+ "Status of specified breakpoints (all user-settable breakpoints if no argument).\n"
+ "Arguments are breakpoint numbers with spaces in between.\n"
+ },
+ {
+ "l[ist]", NULL, "List specified line",
+ "Usage: list\n"
+ " list first[,last]\n"
+ " list filename:first[,last]\n"
+ "\n"
+ "Print lines from a source file.\n"
+ "\n"
+ "With first and last, list prints lines from first to last.\n"
+ "When last is empty, it stands for ten lines away from first.\n"
+ "With filename, list prints lines in the specified source file.\n"
+ },
+ {
+ "p[rint]", NULL, "Print value of expression",
+ "Usage: print expr\n"
+ "\n"
+ "It evaluates and prints the value of the mruby expression.\n"
+ "This is equivalent to the \'eval\' command.\n"
+ },
+ {
+ "q[uit]", NULL, "Exit mrdb",
+ "Usage: quit\n"
+ "\n"
+ "Exit mrdb.\n"
+ },
+ {
+ "r[un]", NULL, "Start debugged program",
+ "Usage: run\n"
+ "\n"
+ "Start debugged program.\n"
+ },
+ {
+ "s[tep]", NULL, "Step program until it reaches a different source line",
+ "Usage: step\n"
+ "\n"
+ "Step program until it reaches a different source line.\n"
+ },
+ { NULL, NULL, NULL, NULL }
+};
+
+typedef struct listcmd_parser_state {
+ mrb_bool parse_error;
+ mrb_bool has_line_min;
+ mrb_bool has_line_max;
+ char *filename;
+ uint16_t line_min;
+ uint16_t line_max;
+} listcmd_parser_state;
+
+static listcmd_parser_state*
+listcmd_parser_state_new(mrb_state *mrb)
+{
+ listcmd_parser_state *st = mrb_malloc(mrb, sizeof(listcmd_parser_state));
+ memset(st, 0, sizeof(listcmd_parser_state));
+ return st;
+}
+
+static void
+listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st)
+{
+ if (st != NULL) {
+ if (st->filename != NULL) {
+ mrb_free(mrb, st->filename);
+ }
+ mrb_free(mrb, st);
+ }
+}
+
+static mrb_bool
+parse_uint(char **sp, uint16_t *n)
+{
+ char *p;
+ int i;
+
+ if (*sp == NULL || **sp == '\0') {
+ return FALSE;
+ }
+
+ for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ;
+
+ if (p != *sp && (i = atoi(*sp)) >= 0) {
+ *n = (uint16_t)i;
+ *sp = p;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static mrb_bool
+skip_char(char **sp, char c)
+{
+ if (*sp != NULL && **sp == c) {
+ ++*sp;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static mrb_bool
+parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st)
+{
+ if (*sp == NULL || **sp == '\0') {
+ return FALSE;
+ }
+
+ st->has_line_min = FALSE;
+ st->has_line_max = FALSE;
+
+ if (parse_uint(sp, &st->line_min)) {
+ st->has_line_min = TRUE;
+ }
+ else {
+ return FALSE;
+ }
+
+ if (skip_char(sp, ',')) {
+ if (parse_uint(sp, &st->line_max)) {
+ st->has_line_max = TRUE;
+ }
+ else {
+ st->parse_error = TRUE;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static mrb_bool
+parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st)
+{
+ char *p;
+ int len;
+
+ if (st->filename != NULL) {
+ mrb_free(mrb, st->filename);
+ st->filename = NULL;
+ }
+
+ if ((p = strchr(*sp, ':')) != NULL) {
+ len = p - *sp;
+ }
+ else {
+ len = strlen(*sp);
+ }
+
+ if (len > 0) {
+ st->filename = mrb_malloc(mrb, len + 1);
+ strncpy(st->filename, *sp, len);
+ st->filename[len] = '\0';
+ *sp += len;
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+char*
+replace_ext(mrb_state *mrb, const char *filename, const char *ext)
+{
+ size_t len;
+ char *p, *s;
+
+ if (filename == NULL) {
+ return NULL;
+ }
+
+ if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) {
+ len = p - filename;
+ }
+ else {
+ len = strlen(filename);
+ }
+
+ s = mrb_malloc(mrb, len + strlen(ext) + 1);
+ memset(s, '\0', len + strlen(ext) + 1);
+ strncpy(s, filename, len);
+ strcat(s, ext);
+
+ return s;
+}
+
+static mrb_bool
+parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st)
+{
+ char *p;
+
+ switch (mrdb->wcnt) {
+ case 2:
+ p = mrdb->words[1];
+
+ /* mrdb->words[1] ::= <lineno> | <filename> ':' <lineno> | <filename> */
+ if (!parse_lineno(mrb, &p, st)) {
+ if (parse_filename(mrb, &p, st)) {
+ if (skip_char(&p, ':')) {
+ if (!parse_lineno(mrb, &p, st)) {
+ st->parse_error = TRUE;
+ }
+ }
+ }
+ else {
+ st->parse_error = TRUE;
+ }
+ }
+ if (*p != '\0') {
+ st->parse_error = TRUE;
+ }
+ break;
+ case 1:
+ case 0:
+ /* do nothing */
+ break;
+ default:
+ st->parse_error = TRUE;
+ printf("too many arguments\n");
+ break;
+ }
+
+ if (!st->parse_error) {
+ if (!st->has_line_min) {
+ st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1;
+ }
+
+ if (!st->has_line_max) {
+ st->line_max = st->line_min + 9;
+ }
+
+ if (st->filename == NULL) {
+ if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) {
+ st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb");
+ }
+ }
+ }
+
+ if (st->parse_error || st->filename == NULL) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static mrb_bool
+check_cmd_pattern(const char *pattern, const char *cmd)
+{
+ char *lbracket, *rbracket, *p, *q;
+
+ if (pattern == NULL && cmd == NULL) {
+ return TRUE;
+ }
+ if (pattern == NULL || cmd == NULL) {
+ return FALSE;
+ }
+ if ((lbracket = strchr(pattern, '[')) == NULL) {
+ return !strcmp(pattern, cmd);
+ }
+ if ((rbracket = strchr(pattern, ']')) == NULL) {
+ return FALSE;
+ }
+ if (strncmp(pattern, cmd, lbracket - pattern)) {
+ return FALSE;
+ }
+
+ p = lbracket + 1;
+ q = (char *)cmd + (lbracket - pattern);
+
+ for ( ; p < rbracket && *q != '\0'; p++, q++) {
+ if (*p != *q) {
+ break;
+ }
+ }
+ return *q == '\0';
+}
+
+static help_msg*
+get_help_msg(char *cmd1, char *cmd2)
+{
+ help_msg *p;
+
+ if (cmd1 == NULL) {
+ return NULL;
+ }
+ for (p = help_msg_list; p->cmd1 != NULL; p++) {
+ if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static mrb_bool
+show_short_help(void)
+{
+ help_msg *p;
+
+ printf("Commands\n");
+
+ for (p = help_msg_list; p->cmd1 != NULL; p++) {
+ if (p->cmd2 == NULL) {
+ printf(" %s -- %s\n", p->cmd1, p->short_msg);
+ }
+ else {
+ printf(" %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg);
+ }
+ }
+ return TRUE;
+}
+
+static mrb_bool
+show_long_help(char *cmd1, char *cmd2)
+{
+ help_msg *help;
+
+ if ((help = get_help_msg(cmd1, cmd2)) == NULL) {
+ return FALSE;
+ }
+ printf("%s", help->long_msg);
+ return TRUE;
+}
+
+dbgcmd_state
+dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb)
+{
+ char *filename;
+ listcmd_parser_state *st = listcmd_parser_state_new(mrb);
+
+ if (parse_listcmd_args(mrb, mrdb, st)) {
+ if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) {
+ filename = st->filename;
+ }
+ mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max);
+
+ if (filename != NULL && filename != st->filename) {
+ mrb_free(mrb, filename);
+ }
+ listcmd_parser_state_free(mrb, st);
+ }
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_bool is_valid;
+ int i;
+
+ switch (mrdb->wcnt) {
+ case 0:
+ case 1:
+ is_valid = show_short_help();
+ break;
+ case 2:
+ is_valid = show_long_help(mrdb->words[1], NULL);
+ break;
+ case 3:
+ is_valid = show_long_help(mrdb->words[1], mrdb->words[2]);
+ break;
+ default:
+ is_valid = FALSE;
+ break;
+ }
+
+ if (!is_valid) {
+ printf("Invalid command \"");
+ for (i = 1; i < mrdb->wcnt; i++) {
+ printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]);
+ }
+ printf("\". Try \"help\".\n");
+ }
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
+{
+ switch (mrdb->dbg->xm) {
+ case DBG_RUN:
+ case DBG_STEP:
+ case DBG_NEXT:
+ while (1) {
+ char c;
+ int buf;
+
+ printf("The program is running. Exit anyway? (y or n) ");
+ fflush(stdout);
+
+ if ((buf = getchar()) == EOF) {
+ mrdb->dbg->xm = DBG_QUIT;
+ break;
+ }
+ c = buf;
+ while (buf != '\n' && (buf = getchar()) != EOF) ;
+
+ if (c == 'y' || c == 'Y') {
+ mrdb->dbg->xm = DBG_QUIT;
+ break;
+ }
+ else if (c == 'n' || c == 'N') {
+ break;
+ }
+ else {
+ printf("Please answer y or n.\n");
+ }
+ }
+ break;
+ default:
+ mrdb->dbg->xm = DBG_QUIT;
+ break;
+ }
+
+ if (mrdb->dbg->xm == DBG_QUIT) {
+ struct RClass *exc;
+ exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception"));
+ mrb_raise(mrb, exc, "Exit mrdb.");
+ }
+ return DBGST_PROMPT;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
new file mode 100644
index 00000000..cca63671
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
@@ -0,0 +1,58 @@
+/*
+** cmdprint.c - mruby debugger print command functions
+**
+*/
+
+#include <string.h>
+#include "mrdb.h"
+#include <mruby/value.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include "apiprint.h"
+
+dbgcmd_state
+dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_value expr;
+ mrb_value result;
+ mrb_value s;
+ uint8_t wcnt;
+ int ai;
+
+ if (mrdb->wcnt <= 1) {
+ puts("Parameter not specified.");
+ return DBGST_PROMPT;
+ }
+
+ ai = mrb_gc_arena_save(mrb);
+
+ /* eval expr */
+ expr = mrb_str_new_cstr(mrb, NULL);
+ for (wcnt=1; wcnt<mrdb->wcnt; wcnt++) {
+ expr = mrb_str_cat_lit(mrb, expr, " ");
+ expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
+ }
+
+ result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
+
+ /* $print_no = result */
+ s = mrb_str_cat_lit(mrb, result, "\0");
+ printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s));
+
+ if (mrdb->print_no == 0) {
+ mrdb->print_no = 1;
+ }
+
+ mrb_gc_arena_restore(mrb, ai);
+
+ return DBGST_PROMPT;
+}
+
+dbgcmd_state
+dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
+{
+ return dbgcmd_print(mrb, mrdb);
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
new file mode 100644
index 00000000..cb4c738f
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
@@ -0,0 +1,64 @@
+/*
+** cmdrun.c - mruby debugger run command functions
+**
+*/
+
+#include <mruby/opcode.h>
+#include "mrdb.h"
+
+dbgcmd_state
+dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_debug_context *dbg = mrdb->dbg;
+
+ if (dbg->xm == DBG_INIT){
+ dbg->xm = DBG_RUN;
+ }
+ else {
+ dbg->xm = DBG_QUIT;
+ if (dbg->xphase == DBG_PHASE_RUNNING){
+ struct RClass *exc;
+ puts("Start it from the beginning.");
+ exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception"));
+ mrb_raise(mrb, exc, "Restart mrdb.");
+ }
+ }
+
+ return DBGST_RESTART;
+}
+
+dbgcmd_state
+dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_debug_context *dbg = mrdb->dbg;
+ int ccnt = 1;
+
+ if (mrdb->wcnt > 1){
+ sscanf(mrdb->words[1], "%d", &ccnt);
+ }
+ dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1); /* count of continue */
+
+ if (dbg->xphase == DBG_PHASE_AFTER_RUN){
+ puts("The program is not running.");
+ dbg->xm = DBG_QUIT;
+ }
+ else {
+ dbg->xm = DBG_RUN;
+ }
+ return DBGST_CONTINUE;
+}
+
+dbgcmd_state
+dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrdb->dbg->xm = DBG_STEP;
+ return DBGST_CONTINUE;
+}
+
+dbgcmd_state
+dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrdb->dbg->xm = DBG_NEXT;
+ mrdb->dbg->prvci = mrb->c->ci;
+ return DBGST_CONTINUE;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
new file mode 100644
index 00000000..d12dcd5d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
@@ -0,0 +1,759 @@
+/*
+** mrdb.c - mruby debugger
+**
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <mruby.h>
+#include <mruby/dump.h>
+#include <mruby/debug.h>
+#include <mruby/class.h>
+#include <mruby/opcode.h>
+#include <mruby/variable.h>
+
+#include "mrdb.h"
+#include "apibreak.h"
+#include "apilist.h"
+
+void mrdb_state_free(mrb_state *);
+
+static mrb_debug_context *_debug_context = NULL;
+static mrdb_state *_mrdb_state = NULL;
+
+struct _args {
+ FILE *rfp;
+ char* fname;
+ char* srcpath;
+ int argc;
+ char** argv;
+ mrb_bool mrbfile : 1;
+};
+
+typedef struct debug_command {
+ const char *cmd1;
+ const char *cmd2;
+ uint8_t len1;
+ uint8_t len2;
+ uint8_t div;
+ debug_command_id id;
+ debug_command_func func;
+} debug_command;
+
+static const debug_command debug_command_list[] = {
+ {"break", NULL, 1, 0, 0, DBGCMD_BREAK, dbgcmd_break}, /* b[reak] */
+ {"continue", NULL, 1, 0, 0, DBGCMD_CONTINUE, dbgcmd_continue}, /* c[ontinue] */
+ {"delete", NULL, 1, 0, 1, DBGCMD_DELETE, dbgcmd_delete}, /* d[elete] */
+ {"disable", NULL, 3, 0, 1, DBGCMD_DISABLE, dbgcmd_disable}, /* dis[able] */
+ {"enable", NULL, 2, 0, 1, DBGCMD_ENABLE, dbgcmd_enable}, /* en[able] */
+ {"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */
+ {"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */
+ {"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */
+ {"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */
+ {"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */
+ {"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */
+ {"run", NULL, 1, 0, 0, DBGCMD_RUN, dbgcmd_run}, /* r[un] */
+ {"step", NULL, 1, 0, 1, DBGCMD_STEP, dbgcmd_step}, /* s[tep] */
+ {"next", NULL, 1, 0, 1, DBGCMD_NEXT, dbgcmd_next}, /* n[ext] */
+ {NULL}
+};
+
+
+static void
+usage(const char *name)
+{
+ static const char *const usage_msg[] = {
+ "switches:",
+ "-b load and execute RiteBinary (mrb) file",
+ "-d specify source directory",
+ "--version print the version",
+ "--copyright print the copyright",
+ NULL
+ };
+ const char *const *p = usage_msg;
+
+ printf("Usage: %s [switches] programfile\n", name);
+ while (*p) {
+ printf(" %s\n", *p++);
+ }
+}
+
+static int
+parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
+{
+ char **origargv = argv;
+ static const struct _args args_zero = { 0 };
+
+ *args = args_zero;
+
+ for (argc--,argv++; argc > 0; argc--,argv++) {
+ char *item;
+ if (argv[0][0] != '-') break;
+
+ item = argv[0] + 1;
+ switch (*item++) {
+ case 'b':
+ args->mrbfile = TRUE;
+ break;
+ case 'd':
+ if (item[0]) {
+ goto append_srcpath;
+ }
+ else if (argc > 1) {
+ argc--; argv++;
+ item = argv[0];
+append_srcpath:
+ if (!args->srcpath) {
+ size_t buflen;
+ char *buf;
+
+ buflen = strlen(item) + 1;
+ buf = (char *)mrb_malloc(mrb, buflen);
+ memcpy(buf, item, buflen);
+ args->srcpath = buf;
+ }
+ else {
+ size_t srcpathlen;
+ size_t itemlen;
+
+ srcpathlen = strlen(args->srcpath);
+ itemlen = strlen(item);
+ args->srcpath =
+ (char *)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2);
+ args->srcpath[srcpathlen] = '\n';
+ memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1);
+ }
+ }
+ else {
+ printf("%s: No path specified for -d\n", *origargv);
+ return EXIT_SUCCESS;
+ }
+ break;
+ case '-':
+ if (strcmp((*argv) + 2, "version") == 0) {
+ mrb_show_version(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp((*argv) + 2, "copyright") == 0) {
+ mrb_show_copyright(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (args->rfp == NULL) {
+ if (*argv == NULL) {
+ printf("%s: Program file not specified.\n", *origargv);
+ return EXIT_FAILURE;
+ }
+ else {
+ args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
+ if (args->rfp == NULL) {
+ printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
+ return EXIT_FAILURE;
+ }
+ args->fname = argv[0];
+ argc--; argv++;
+ }
+ }
+ args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
+ memcpy(args->argv, argv, (argc+1) * sizeof(char*));
+ args->argc = argc;
+
+ return EXIT_SUCCESS;
+}
+
+static void
+cleanup(mrb_state *mrb, struct _args *args)
+{
+ if (args->rfp)
+ fclose(args->rfp);
+ if (args->srcpath)
+ mrb_free(mrb, args->srcpath);
+ if (args->argv)
+ mrb_free(mrb, args->argv);
+ mrdb_state_free(mrb);
+ mrb_close(mrb);
+}
+
+static mrb_debug_context*
+mrb_debug_context_new(mrb_state *mrb)
+{
+ mrb_debug_context *dbg = mrb_malloc(mrb, sizeof(mrb_debug_context));
+
+ memset(dbg, 0, sizeof(mrb_debug_context));
+
+ dbg->xm = DBG_INIT;
+ dbg->xphase = DBG_PHASE_BEFORE_RUN;
+ dbg->next_bpno = 1;
+
+ return dbg;
+}
+
+mrb_debug_context*
+mrb_debug_context_get(mrb_state *mrb)
+{
+ if (!_debug_context) {
+ _debug_context = mrb_debug_context_new(mrb);
+ }
+ return _debug_context;
+}
+
+void
+mrb_debug_context_set(mrb_debug_context *dbg)
+{
+ _debug_context = dbg;
+}
+
+void
+mrb_debug_context_free(mrb_state *mrb)
+{
+ if (_debug_context) {
+ mrb_debug_delete_break_all(mrb, _debug_context);
+ mrb_free(mrb, _debug_context);
+ _debug_context = NULL;
+ }
+}
+
+static mrdb_state*
+mrdb_state_new(mrb_state *mrb)
+{
+ mrdb_state *mrdb = mrb_malloc(mrb, sizeof(mrdb_state));
+
+ memset(mrdb, 0, sizeof(mrdb_state));
+
+ mrdb->dbg = mrb_debug_context_get(mrb);
+ mrdb->command = mrb_malloc(mrb, MAX_COMMAND_LINE+1);
+ mrdb->print_no = 1;
+
+ return mrdb;
+}
+
+mrdb_state*
+mrdb_state_get(mrb_state *mrb)
+{
+ if (!_mrdb_state) {
+ _mrdb_state = mrdb_state_new(mrb);
+ }
+ return _mrdb_state;
+}
+
+void
+mrdb_state_set(mrdb_state *mrdb)
+{
+ _mrdb_state = mrdb;
+}
+
+void
+mrdb_state_free(mrb_state *mrb)
+{
+ mrb_debug_context_free(mrb);
+ if (_mrdb_state) {
+ mrb_free(mrb, _mrdb_state->command);
+ mrb_free(mrb, _mrdb_state);
+ _mrdb_state = NULL;
+ }
+}
+
+static char*
+get_command(mrb_state *mrb, mrdb_state *mrdb)
+{
+ int i;
+ int c;
+
+ for (i=0; i<MAX_COMMAND_LINE; i++) {
+ if ((c=getchar()) == EOF || c == '\n') break;
+ mrdb->command[i] = c;
+ }
+
+ if (i == 0 && feof(stdin)) {
+ clearerr(stdin);
+ strcpy(mrdb->command, "quit");
+ i += sizeof("quit") - 1;
+ }
+
+ if (i == MAX_COMMAND_LINE) {
+ for ( ; (c=getchar()) != EOF && c !='\n'; i++) ;
+ }
+
+ if (i > MAX_COMMAND_LINE) {
+ printf("command line too long.\n");
+ i = 0; /* discard command data */
+ }
+ mrdb->command[i] = '\0';
+
+ return mrdb->command;
+}
+
+static char*
+pick_out_word(mrb_state *mrb, char **pp)
+{
+ char *ps;
+
+ for (ps=*pp; ISBLANK(*ps); ps++) ;
+ if (*ps == '\0') {
+ return NULL;
+ }
+
+ if (*ps == '\"' || *ps == '\'') {
+ *pp = strchr(ps+1, *ps);
+ if (*pp) (*pp)++;
+ }
+ else {
+ *pp = strpbrk(ps, " \t");
+ }
+
+ if (!*pp) {
+ *pp = ps + strlen(ps);
+ }
+
+ if (**pp != '\0') {
+ **pp = '\0';
+ (*pp)++;
+ }
+
+ return ps;
+}
+
+static debug_command*
+parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf)
+{
+ debug_command *cmd = NULL;
+ char *p = buf;
+ size_t wlen;
+
+ /* get word #1 */
+ mrdb->words[0] = pick_out_word(mrb, &p);
+ if (!mrdb->words[0]) {
+ return NULL;
+ }
+ mrdb->wcnt = 1;
+ /* set remain parameter */
+ for ( ; *p && ISBLANK(*p); p++) ;
+ if (*p) {
+ mrdb->words[mrdb->wcnt++] = p;
+ }
+
+ /* check word #1 */
+ for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) {
+ wlen = strlen(mrdb->words[0]);
+ if (wlen >= cmd->len1 &&
+ strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) {
+ break;
+ }
+ }
+
+ if (cmd->cmd2) {
+ if (mrdb->wcnt > 1) {
+ /* get word #2 */
+ mrdb->words[1] = pick_out_word(mrb, &p);
+ if (mrdb->words[1]) {
+ /* update remain parameter */
+ for ( ; *p && ISBLANK(*p); p++) ;
+ if (*p) {
+ mrdb->words[mrdb->wcnt++] = p;
+ }
+ }
+ }
+
+ /* check word #1,#2 */
+ for ( ; cmd->cmd1; cmd++) {
+ wlen = strlen(mrdb->words[0]);
+ if (wlen < cmd->len1 ||
+ strncmp(mrdb->words[0], cmd->cmd1, wlen)) {
+ continue;
+ }
+
+ if (!cmd->cmd2) break; /* word #1 only */
+
+ if (mrdb->wcnt == 1) continue; /* word #2 not specified */
+
+ wlen = strlen(mrdb->words[1]);
+ if (wlen >= cmd->len2 &&
+ strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) {
+ break; /* word #1 and #2 */
+ }
+ }
+ }
+
+ /* divide remain parameters */
+ if (cmd->cmd1 && cmd->div) {
+ p = mrdb->words[--mrdb->wcnt];
+ for ( ; mrdb->wcnt<MAX_COMMAND_WORD; mrdb->wcnt++) {
+ mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p);
+ if (!mrdb->words[mrdb->wcnt]) {
+ break;
+ }
+ }
+ }
+
+ return cmd->cmd1 ? cmd : NULL;
+}
+
+static void
+print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_debug_breakpoint bp;
+ int32_t ret;
+ uint16_t lineno;
+ const char *file;
+ const char *method_name;
+ const char *class_name;
+
+ ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp);
+ if (ret == 0) {
+ switch(bp.type) {
+ case MRB_DEBUG_BPTYPE_LINE:
+ file = bp.point.linepoint.file;
+ lineno = bp.point.linepoint.lineno;
+ printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno);
+ break;
+ case MRB_DEBUG_BPTYPE_METHOD:
+ method_name = bp.point.methodpoint.method_name;
+ class_name = bp.point.methodpoint.class_name;
+ if (class_name == NULL) {
+ printf("Breakpoint %d, %s\n", bp.bpno, method_name);
+ }
+ else {
+ printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name);
+ }
+ if (mrdb->dbg->isCfunc) {
+ printf("Stopped before calling the C function.\n");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb)
+{
+ const char* file = mrdb->dbg->prvfile;
+ uint16_t lineno = mrdb->dbg->prvline;
+ printf("%s:%d\n", file, lineno);
+}
+
+static void
+print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb)
+{
+ char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile);
+ uint16_t lineno = mrdb->dbg->prvline;
+ if (file != NULL) {
+ mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno);
+ mrb_free(mrb, file);
+ }
+}
+
+static void
+print_info_stopped(mrb_state *mrb, mrdb_state *mrdb)
+{
+ switch(mrdb->dbg->bm) {
+ case BRK_BREAK:
+ print_info_stopped_break(mrb, mrdb);
+ print_info_stopped_code(mrb, mrdb);
+ break;
+ case BRK_STEP:
+ case BRK_NEXT:
+ print_info_stopped_step_next(mrb, mrdb);
+ print_info_stopped_code(mrb, mrdb);
+ break;
+ default:
+ break;
+ }
+}
+
+static debug_command*
+get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb)
+{
+ debug_command *cmd = NULL;
+ char *p;
+ int i;
+
+ while (!cmd) {
+ for (p=NULL; !p || *p=='\0'; ) {
+ printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline);
+ fflush(stdout);
+ p = get_command(mrb, mrdb);
+ }
+
+ cmd = parse_command(mrb, mrdb, p);
+#ifdef _DBG_MRDB_PARSER_
+ for (i=0; i<mrdb->wcnt; i++) {
+ printf("%d: %s\n", i, mrdb->words[i]);
+ }
+#endif
+ if (!cmd) {
+ printf("invalid command (");
+ for (i=0; i<mrdb->wcnt; i++) {
+ if (i>0) {
+ printf(" ");
+ }
+ printf("%s", mrdb->words[i]);
+ }
+ puts(")");
+ }
+ }
+ return cmd;
+}
+
+static int32_t
+check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
+{
+ struct RClass* c;
+ mrb_sym sym;
+ int32_t bpno;
+ mrb_bool isCfunc;
+
+ mrb_debug_context *dbg = mrb_debug_context_get(mrb);
+
+ isCfunc = FALSE;
+ bpno = dbg->method_bpno;
+ dbg->method_bpno = 0;
+
+ switch(GET_OPCODE(*pc)) {
+ case OP_SEND:
+ case OP_SENDB:
+ c = mrb_class(mrb, regs[GETARG_A(*pc)]);
+ sym = irep->syms[GETARG_B(*pc)];
+ break;
+ case OP_SUPER:
+ c = mrb->c->ci->target_class->super;
+ sym = mrb->c->ci->mid;
+ break;
+ default:
+ sym = 0;
+ break;
+ }
+ if (sym != 0) {
+ dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc);
+ if (isCfunc) {
+ bpno = dbg->method_bpno;
+ dbg->method_bpno = 0;
+ }
+ }
+ dbg->isCfunc = isCfunc;
+ return bpno;
+}
+
+static void
+mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
+{
+ const char *file;
+ int32_t line;
+ int32_t bpno;
+
+ mrb_debug_context *dbg = mrb_debug_context_get(mrb);
+
+ mrb_assert(dbg);
+
+ dbg->irep = irep;
+ dbg->pc = pc;
+ dbg->regs = regs;
+
+ if (dbg->xphase == DBG_PHASE_RESTART) {
+ dbg->root_irep = irep;
+ dbg->prvfile = NULL;
+ dbg->prvline = 0;
+ dbg->prvci = NULL;
+ dbg->xm = DBG_RUN;
+ dbg->xphase = DBG_PHASE_RUNNING;
+ }
+
+ file = mrb_debug_get_filename(irep, pc - irep->iseq);
+ line = mrb_debug_get_line(irep, pc - irep->iseq);
+
+ switch (dbg->xm) {
+ case DBG_STEP:
+ if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
+ return;
+ }
+ dbg->method_bpno = 0;
+ dbg->bm = BRK_STEP;
+ break;
+
+ case DBG_NEXT:
+ if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
+ return;
+ }
+ if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) {
+ return;
+ }
+ dbg->prvci = NULL;
+ dbg->method_bpno = 0;
+ dbg->bm = BRK_NEXT;
+ break;
+
+ case DBG_RUN:
+ bpno = check_method_breakpoint(mrb, irep, pc, regs);
+ if (bpno > 0) {
+ dbg->stopped_bpno = bpno;
+ dbg->bm = BRK_BREAK;
+ break;
+ }
+ if (dbg->prvfile != file || dbg->prvline != line) {
+ bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line);
+ if (bpno > 0) {
+ dbg->stopped_bpno = bpno;
+ dbg->bm = BRK_BREAK;
+ break;
+ }
+ }
+ dbg->prvfile = file;
+ dbg->prvline = line;
+ return;
+ case DBG_INIT:
+ dbg->root_irep = irep;
+ dbg->bm = BRK_INIT;
+ if (!file || line < 0) {
+ puts("Cannot get debugging information.");
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ dbg->prvfile = file;
+ dbg->prvline = line;
+
+ if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) {
+ return;
+ }
+ dbg->break_hook(mrb, dbg);
+
+ dbg->xphase = DBG_PHASE_RUNNING;
+}
+
+static mrdb_exemode
+mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg)
+{
+ debug_command *cmd;
+ dbgcmd_state st = DBGST_CONTINUE;
+ mrdb_state *mrdb = mrdb_state_get(mrb);
+
+ print_info_stopped(mrb, mrdb);
+
+ while (1) {
+ cmd = get_and_parse_command(mrb, mrdb);
+ mrb_assert(cmd);
+
+ st = cmd->func(mrb, mrdb);
+
+ if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break;
+ }
+ return dbg->xm;
+}
+
+int
+main(int argc, char **argv)
+{
+ mrb_state *mrb = mrb_open();
+ int n = -1;
+ struct _args args;
+ mrb_value v;
+ mrdb_state *mrdb;
+ mrdb_state *mrdb_backup;
+ mrb_debug_context* dbg_backup;
+ debug_command *cmd;
+
+ l_restart:
+
+ if (mrb == NULL) {
+ fputs("Invalid mrb_state, exiting mruby\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ /* parse command parameters */
+ n = parse_args(mrb, argc, argv, &args);
+ if (n == EXIT_FAILURE || args.rfp == NULL) {
+ cleanup(mrb, &args);
+ usage(argv[0]);
+ return n;
+ }
+
+ /* initialize debugger information */
+ mrdb = mrdb_state_get(mrb);
+ mrb_assert(mrdb && mrdb->dbg);
+ mrdb->srcpath = args.srcpath;
+
+ if (mrdb->dbg->xm == DBG_QUIT) {
+ mrdb->dbg->xphase = DBG_PHASE_RESTART;
+ }
+ else {
+ mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN;
+ }
+ mrdb->dbg->xm = DBG_INIT;
+ mrdb->dbg->ccnt = 1;
+
+ /* setup hook functions */
+ mrb->code_fetch_hook = mrb_code_fetch_hook;
+ mrdb->dbg->break_hook = mrb_debug_break_hook;
+
+ if (args.mrbfile) { /* .mrb */
+ v = mrb_load_irep_file(mrb, args.rfp);
+ }
+ else { /* .rb */
+ mrbc_context *cc = mrbc_context_new(mrb);
+ mrbc_filename(mrb, cc, args.fname);
+ v = mrb_load_file_cxt(mrb, args.rfp, cc);
+ mrbc_context_free(mrb, cc);
+ }
+ if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) {
+ const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc));
+ if (!strcmp(classname, "DebuggerExit")) {
+ cleanup(mrb, &args);
+ return 0;
+ }
+ if (!strcmp(classname, "DebuggerRestart")) {
+ mrdb_backup = mrdb_state_get(mrb);
+ dbg_backup = mrb_debug_context_get(mrb);
+
+ mrdb_state_set(NULL);
+ mrb_debug_context_set(NULL);
+
+ cleanup(mrb, &args);
+ mrb = mrb_open();
+
+ mrdb_state_set(mrdb_backup);
+ mrb_debug_context_set(dbg_backup);
+
+ goto l_restart;
+ }
+ }
+ puts("mruby application exited.");
+ mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN;
+ if (!mrb_undef_p(v)) {
+ if (mrb->exc) {
+ mrb_print_error(mrb);
+ }
+ else {
+ printf(" => ");
+ mrb_p(mrb, v);
+ }
+ }
+
+ mrdb->dbg->prvfile = "-";
+ mrdb->dbg->prvline = 0;
+
+ while (1) {
+ cmd = get_and_parse_command(mrb, mrdb);
+ mrb_assert(cmd);
+
+ if (cmd->id == DBGCMD_QUIT) {
+ break;
+ }
+
+ if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart;
+ }
+
+ cleanup(mrb, &args);
+
+ return 0;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
new file mode 100644
index 00000000..5ac12c1c
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
@@ -0,0 +1,165 @@
+/*
+** mrdb.h - mruby debugger
+**
+*/
+
+#ifndef MRDB_H
+#define MRDB_H
+
+#include <mruby.h>
+
+#include "mrdbconf.h"
+
+#ifdef _MSC_VER
+# define __func__ __FUNCTION__
+#endif
+
+#define MAX_COMMAND_WORD (16)
+
+typedef enum debug_command_id {
+ DBGCMD_RUN,
+ DBGCMD_CONTINUE,
+ DBGCMD_NEXT,
+ DBGCMD_STEP,
+ DBGCMD_BREAK,
+ DBGCMD_INFO_BREAK,
+ DBGCMD_WATCH,
+ DBGCMD_INFO_WATCH,
+ DBGCMD_ENABLE,
+ DBGCMD_DISABLE,
+ DBGCMD_DELETE,
+ DBGCMD_PRINT,
+ DBGCMD_DISPLAY,
+ DBGCMD_INFO_DISPLAY,
+ DBGCMD_DELETE_DISPLAY,
+ DBGCMD_EVAL,
+ DBGCMD_BACKTRACE,
+ DBGCMD_LIST,
+ DBGCMD_HELP,
+ DBGCMD_QUIT,
+ DBGCMD_UNKNOWN
+} debug_command_id;
+
+typedef enum dbgcmd_state {
+ DBGST_CONTINUE,
+ DBGST_PROMPT,
+ DBGST_COMMAND_ERROR,
+ DBGST_MAX,
+ DBGST_RESTART
+} dbgcmd_state;
+
+typedef enum mrdb_exemode {
+ DBG_INIT,
+ DBG_RUN,
+ DBG_STEP,
+ DBG_NEXT,
+ DBG_QUIT,
+} mrdb_exemode;
+
+typedef enum mrdb_exephase {
+ DBG_PHASE_BEFORE_RUN,
+ DBG_PHASE_RUNNING,
+ DBG_PHASE_AFTER_RUN,
+ DBG_PHASE_RESTART,
+} mrdb_exephase;
+
+typedef enum mrdb_brkmode {
+ BRK_INIT,
+ BRK_BREAK,
+ BRK_STEP,
+ BRK_NEXT,
+ BRK_QUIT,
+} mrdb_brkmode;
+
+typedef enum {
+ MRB_DEBUG_BPTYPE_NONE,
+ MRB_DEBUG_BPTYPE_LINE,
+ MRB_DEBUG_BPTYPE_METHOD,
+} mrb_debug_bptype;
+
+struct mrb_irep;
+struct mrbc_context;
+struct mrb_debug_context;
+
+typedef struct mrb_debug_linepoint {
+ const char *file;
+ uint16_t lineno;
+} mrb_debug_linepoint;
+
+typedef struct mrb_debug_methodpoint {
+ const char *class_name;
+ const char *method_name;
+} mrb_debug_methodpoint;
+
+typedef struct mrb_debug_breakpoint {
+ uint32_t bpno;
+ uint8_t enable;
+ mrb_debug_bptype type;
+ union point {
+ mrb_debug_linepoint linepoint;
+ mrb_debug_methodpoint methodpoint;
+ } point;
+} mrb_debug_breakpoint;
+
+typedef struct mrb_debug_context {
+ struct mrb_irep *root_irep;
+ struct mrb_irep *irep;
+ mrb_code *pc;
+ mrb_value *regs;
+
+ const char *prvfile;
+ int32_t prvline;
+ mrb_callinfo *prvci;
+
+ mrdb_exemode xm;
+ mrdb_exephase xphase;
+ mrdb_brkmode bm;
+ int16_t bmi;
+
+ uint16_t ccnt;
+ uint16_t scnt;
+
+ mrb_debug_breakpoint bp[MAX_BREAKPOINT];
+ uint32_t bpnum;
+ int32_t next_bpno;
+ int32_t method_bpno;
+ int32_t stopped_bpno;
+ mrb_bool isCfunc;
+
+ mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg);
+
+} mrb_debug_context;
+
+typedef struct mrdb_state {
+ char *command;
+ uint8_t wcnt;
+ uint8_t pi;
+ char *words[MAX_COMMAND_WORD];
+ const char *srcpath;
+ uint32_t print_no;
+
+ mrb_debug_context *dbg;
+} mrdb_state;
+
+typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*);
+
+/* cmdrun.c */
+dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*);
+/* cmdbreak.c */
+dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*);
+/* cmdprint.c */
+dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*);
+/* cmdmisc.c */
+dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*);
+dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*);
+
+#endif
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
new file mode 100644
index 00000000..f17f9c57
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
@@ -0,0 +1,16 @@
+/*
+** mrdbconf.h - mruby debugger configuration
+**
+*/
+
+#ifndef MRDBCONF_H
+#define MRDBCONF_H
+
+/* configuration options: */
+/* maximum size for command buffer */
+#define MAX_COMMAND_LINE 1024
+
+/* maximum number of setable breakpoint */
+#define MAX_BREAKPOINT 5
+
+#endif
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
new file mode 100644
index 00000000..c7812b0d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
@@ -0,0 +1,20 @@
+/*
+** mrdberror.h - mruby debugger error code
+**
+*/
+
+#ifndef MRDBERROR_H
+#define MRDBERROR_H
+
+#define MRB_DEBUG_OK (0)
+#define MRB_DEBUG_NOBUF (-1)
+#define MRB_DEBUG_INVALID_ARGUMENT (-2)
+
+#define MRB_DEBUG_BREAK_INVALID_LINENO (-11)
+#define MRB_DEBUG_BREAK_INVALID_FILE (-12)
+#define MRB_DEBUG_BREAK_INVALID_NO (-13)
+#define MRB_DEBUG_BREAK_NUM_OVER (-14)
+#define MRB_DEBUG_BREAK_NO_OVER (-15)
+
+#endif
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb
new file mode 100644
index 00000000..ed53321b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/bintest/mirb.rb
@@ -0,0 +1,12 @@
+require 'open3'
+
+assert('mirb normal operations') do
+ o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1\nb=2\na+b\n")
+ assert_true o.include?('=> 3')
+ assert_true o.include?('=> 2')
+end
+
+assert('regression for #1563') do
+ o, s = Open3.capture2('bin/mirb', :stdin_data => "a=1;b=2;c=3\nb\nc")
+ assert_true o.include?('=> 3')
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake
new file mode 100644
index 00000000..a74871d8
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/mrbgem.rake
@@ -0,0 +1,33 @@
+MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mirb command'
+
+ if spec.build.cc.search_header_path 'readline/readline.h'
+ spec.cc.defines << "ENABLE_READLINE"
+ if spec.build.cc.search_header_path 'termcap.h'
+ if MRUBY_BUILD_HOST_IS_CYGWIN || MRUBY_BUILD_HOST_IS_OPENBSD
+ if spec.build.cc.search_header_path 'termcap.h'
+ if MRUBY_BUILD_HOST_IS_CYGWIN then
+ spec.linker.libraries << 'ncurses'
+ else
+ spec.linker.libraries << 'termcap'
+ end
+ end
+ end
+ end
+ if RUBY_PLATFORM.include?('netbsd')
+ spec.linker.libraries << 'edit'
+ else
+ spec.linker.libraries << 'readline'
+ if spec.build.cc.search_header_path 'curses.h'
+ spec.linker.libraries << 'ncurses'
+ end
+ end
+ elsif spec.build.cc.search_header_path 'linenoise.h'
+ spec.cc.defines << "ENABLE_LINENOISE"
+ end
+
+ spec.bins = %w(mirb)
+ spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
new file mode 100644
index 00000000..fe311d83
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
@@ -0,0 +1,584 @@
+/*
+** mirb - Embeddable Interactive Ruby Shell
+**
+** This program takes code from the user in
+** an interactive way and executes it
+** immediately. It's a REPL...
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <signal.h>
+#include <setjmp.h>
+
+#ifdef ENABLE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#define MIRB_ADD_HISTORY(line) add_history(line)
+#define MIRB_READLINE(ch) readline(ch)
+#define MIRB_WRITE_HISTORY(path) write_history(path)
+#define MIRB_READ_HISTORY(path) read_history(path)
+#define MIRB_USING_HISTORY() using_history()
+#elif defined(ENABLE_LINENOISE)
+#define ENABLE_READLINE
+#include <linenoise.h>
+#define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line)
+#define MIRB_READLINE(ch) linenoise(ch)
+#define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path)
+#define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path)
+#define MIRB_USING_HISTORY()
+#endif
+
+#ifndef _WIN32
+#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1)
+#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val)
+#define SIGJMP_BUF sigjmp_buf
+#else
+#define MIRB_SIGSETJMP(env) setjmp(env)
+#define MIRB_SIGLONGJMP(env, val) longjmp(env, val)
+#define SIGJMP_BUF jmp_buf
+#endif
+
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/string.h>
+
+#ifdef ENABLE_READLINE
+
+static const char history_file_name[] = ".mirb_history";
+
+static char *
+get_history_path(mrb_state *mrb)
+{
+ char *path = NULL;
+ const char *home = getenv("HOME");
+
+#ifdef _WIN32
+ if (home != NULL) {
+ home = getenv("USERPROFILE");
+ }
+#endif
+
+ if (home != NULL) {
+ int len = snprintf(NULL, 0, "%s/%s", home, history_file_name);
+ if (len >= 0) {
+ size_t size = len + 1;
+ path = (char *)mrb_malloc_simple(mrb, size);
+ if (path != NULL) {
+ int n = snprintf(path, size, "%s/%s", home, history_file_name);
+ if (n != len) {
+ mrb_free(mrb, path);
+ path = NULL;
+ }
+ }
+ }
+ }
+
+ return path;
+}
+
+#endif
+
+static void
+p(mrb_state *mrb, mrb_value obj, int prompt)
+{
+ mrb_value val;
+
+ val = mrb_funcall(mrb, obj, "inspect", 0);
+ if (prompt) {
+ if (!mrb->exc) {
+ fputs(" => ", stdout);
+ }
+ else {
+ val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
+ }
+ }
+ if (!mrb_string_p(val)) {
+ val = mrb_obj_as_string(mrb, obj);
+ }
+ fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
+ putc('\n', stdout);
+}
+
+/* Guess if the user might want to enter more
+ * or if he wants an evaluation of his code now */
+static mrb_bool
+is_code_block_open(struct mrb_parser_state *parser)
+{
+ mrb_bool code_block_open = FALSE;
+
+ /* check for heredoc */
+ if (parser->parsing_heredoc != NULL) return TRUE;
+ if (parser->heredoc_end_now) {
+ parser->heredoc_end_now = FALSE;
+ return FALSE;
+ }
+
+ /* check for unterminated string */
+ if (parser->lex_strterm) return TRUE;
+
+ /* check if parser error are available */
+ if (0 < parser->nerr) {
+ const char unexpected_end[] = "syntax error, unexpected $end";
+ const char *message = parser->error_buffer[0].message;
+
+ /* a parser error occur, we have to check if */
+ /* we need to read one more line or if there is */
+ /* a different issue which we have to show to */
+ /* the user */
+
+ if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) {
+ code_block_open = TRUE;
+ }
+ else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
+ code_block_open = FALSE;
+ }
+ else if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) {
+ code_block_open = FALSE;
+ }
+ return code_block_open;
+ }
+
+ switch (parser->lstate) {
+
+ /* all states which need more code */
+
+ case EXPR_BEG:
+ /* beginning of a statement, */
+ /* that means previous line ended */
+ code_block_open = FALSE;
+ break;
+ case EXPR_DOT:
+ /* a message dot was the last token, */
+ /* there has to come more */
+ code_block_open = TRUE;
+ break;
+ case EXPR_CLASS:
+ /* a class keyword is not enough! */
+ /* we need also a name of the class */
+ code_block_open = TRUE;
+ break;
+ case EXPR_FNAME:
+ /* a method name is necessary */
+ code_block_open = TRUE;
+ break;
+ case EXPR_VALUE:
+ /* if, elsif, etc. without condition */
+ code_block_open = TRUE;
+ break;
+
+ /* now all the states which are closed */
+
+ case EXPR_ARG:
+ /* an argument is the last token */
+ code_block_open = FALSE;
+ break;
+
+ /* all states which are unsure */
+
+ case EXPR_CMDARG:
+ break;
+ case EXPR_END:
+ /* an expression was ended */
+ break;
+ case EXPR_ENDARG:
+ /* closing parenthese */
+ break;
+ case EXPR_ENDFN:
+ /* definition end */
+ break;
+ case EXPR_MID:
+ /* jump keyword like break, return, ... */
+ break;
+ case EXPR_MAX_STATE:
+ /* don't know what to do with this token */
+ break;
+ default:
+ /* this state is unexpected! */
+ break;
+ }
+
+ return code_block_open;
+}
+
+struct _args {
+ FILE *rfp;
+ mrb_bool verbose : 1;
+ int argc;
+ char** argv;
+};
+
+static void
+usage(const char *name)
+{
+ static const char *const usage_msg[] = {
+ "switches:",
+ "-v print version number, then run in verbose mode",
+ "--verbose run in verbose mode",
+ "--version print the version",
+ "--copyright print the copyright",
+ NULL
+ };
+ const char *const *p = usage_msg;
+
+ printf("Usage: %s [switches]\n", name);
+ while (*p)
+ printf(" %s\n", *p++);
+}
+
+static int
+parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
+{
+ static const struct _args args_zero = { 0 };
+
+ *args = args_zero;
+
+ for (argc--,argv++; argc > 0; argc--,argv++) {
+ char *item;
+ if (argv[0][0] != '-') break;
+
+ item = argv[0] + 1;
+ switch (*item++) {
+ case 'v':
+ if (!args->verbose) mrb_show_version(mrb);
+ args->verbose = TRUE;
+ break;
+ case '-':
+ if (strcmp((*argv) + 2, "version") == 0) {
+ mrb_show_version(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp((*argv) + 2, "verbose") == 0) {
+ args->verbose = TRUE;
+ break;
+ }
+ else if (strcmp((*argv) + 2, "copyright") == 0) {
+ mrb_show_copyright(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (args->rfp == NULL) {
+ if (*argv != NULL) {
+ args->rfp = fopen(argv[0], "r");
+ if (args->rfp == NULL) {
+ printf("Cannot open program file. (%s)\n", *argv);
+ return EXIT_FAILURE;
+ }
+ argc--; argv++;
+ }
+ }
+ args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
+ memcpy(args->argv, argv, (argc+1) * sizeof(char*));
+ args->argc = argc;
+
+ return EXIT_SUCCESS;
+}
+
+static void
+cleanup(mrb_state *mrb, struct _args *args)
+{
+ if (args->rfp)
+ fclose(args->rfp);
+ mrb_free(mrb, args->argv);
+ mrb_close(mrb);
+}
+
+/* Print a short remark for the user */
+static void
+print_hint(void)
+{
+ printf("mirb - Embeddable Interactive Ruby Shell\n\n");
+}
+
+#ifndef ENABLE_READLINE
+/* Print the command line prompt of the REPL */
+static void
+print_cmdline(int code_block_open)
+{
+ if (code_block_open) {
+ printf("* ");
+ }
+ else {
+ printf("> ");
+ }
+ fflush(stdout);
+}
+#endif
+
+void mrb_codedump_all(mrb_state*, struct RProc*);
+
+static int
+check_keyword(const char *buf, const char *word)
+{
+ const char *p = buf;
+ size_t len = strlen(word);
+
+ /* skip preceding spaces */
+ while (*p && isspace((unsigned char)*p)) {
+ p++;
+ }
+ /* check keyword */
+ if (strncmp(p, word, len) != 0) {
+ return 0;
+ }
+ p += len;
+ /* skip trailing spaces */
+ while (*p) {
+ if (!isspace((unsigned char)*p)) return 0;
+ p++;
+ }
+ return 1;
+}
+
+
+#ifndef ENABLE_READLINE
+volatile sig_atomic_t input_canceled = 0;
+void
+ctrl_c_handler(int signo)
+{
+ input_canceled = 1;
+}
+#else
+SIGJMP_BUF ctrl_c_buf;
+void
+ctrl_c_handler(int signo)
+{
+ MIRB_SIGLONGJMP(ctrl_c_buf, 1);
+}
+#endif
+
+int
+main(int argc, char **argv)
+{
+ char ruby_code[4096] = { 0 };
+ char last_code_line[1024] = { 0 };
+#ifndef ENABLE_READLINE
+ int last_char;
+ size_t char_index;
+#else
+ char *history_path;
+ char* line;
+#endif
+ mrbc_context *cxt;
+ struct mrb_parser_state *parser;
+ mrb_state *mrb;
+ mrb_value result;
+ struct _args args;
+ mrb_value ARGV;
+ int n;
+ int i;
+ mrb_bool code_block_open = FALSE;
+ int ai;
+ unsigned int stack_keep = 0;
+
+ /* new interpreter instance */
+ mrb = mrb_open();
+ if (mrb == NULL) {
+ fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ n = parse_args(mrb, argc, argv, &args);
+ if (n == EXIT_FAILURE) {
+ cleanup(mrb, &args);
+ usage(argv[0]);
+ return n;
+ }
+
+ ARGV = mrb_ary_new_capa(mrb, args.argc);
+ for (i = 0; i < args.argc; i++) {
+ char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+ if (utf8) {
+ mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+ mrb_utf8_free(utf8);
+ }
+ }
+ mrb_define_global_const(mrb, "ARGV", ARGV);
+
+#ifdef ENABLE_READLINE
+ history_path = get_history_path(mrb);
+ if (history_path == NULL) {
+ fputs("failed to get history path\n", stderr);
+ mrb_close(mrb);
+ return EXIT_FAILURE;
+ }
+
+ MIRB_USING_HISTORY();
+ MIRB_READ_HISTORY(history_path);
+#endif
+
+ print_hint();
+
+ cxt = mrbc_context_new(mrb);
+ cxt->capture_errors = TRUE;
+ cxt->lineno = 1;
+ mrbc_filename(mrb, cxt, "(mirb)");
+ if (args.verbose) cxt->dump_result = TRUE;
+
+ ai = mrb_gc_arena_save(mrb);
+
+ while (TRUE) {
+ char *utf8;
+
+ if (args.rfp) {
+ if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
+ goto done;
+ break;
+ }
+
+#ifndef ENABLE_READLINE
+ print_cmdline(code_block_open);
+
+ signal(SIGINT, ctrl_c_handler);
+ char_index = 0;
+ while ((last_char = getchar()) != '\n') {
+ if (last_char == EOF) break;
+ if (char_index >= sizeof(last_code_line)-2) {
+ fputs("input string too long\n", stderr);
+ continue;
+ }
+ last_code_line[char_index++] = last_char;
+ }
+ signal(SIGINT, SIG_DFL);
+ if (input_canceled) {
+ ruby_code[0] = '\0';
+ last_code_line[0] = '\0';
+ code_block_open = FALSE;
+ puts("^C");
+ input_canceled = 0;
+ continue;
+ }
+ if (last_char == EOF) {
+ fputs("\n", stdout);
+ break;
+ }
+
+ last_code_line[char_index++] = '\n';
+ last_code_line[char_index] = '\0';
+#else
+ if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) {
+ ;
+ }
+ else {
+ ruby_code[0] = '\0';
+ last_code_line[0] = '\0';
+ code_block_open = FALSE;
+ puts("^C");
+ }
+ signal(SIGINT, ctrl_c_handler);
+ line = MIRB_READLINE(code_block_open ? "* " : "> ");
+ signal(SIGINT, SIG_DFL);
+
+ if (line == NULL) {
+ printf("\n");
+ break;
+ }
+ if (strlen(line) > sizeof(last_code_line)-2) {
+ fputs("input string too long\n", stderr);
+ continue;
+ }
+ strcpy(last_code_line, line);
+ strcat(last_code_line, "\n");
+ MIRB_ADD_HISTORY(line);
+ free(line);
+#endif
+
+done:
+
+ if (code_block_open) {
+ if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) {
+ fputs("concatenated input string too long\n", stderr);
+ continue;
+ }
+ strcat(ruby_code, last_code_line);
+ }
+ else {
+ if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) {
+ break;
+ }
+ strcpy(ruby_code, last_code_line);
+ }
+
+ utf8 = mrb_utf8_from_locale(ruby_code, -1);
+ if (!utf8) abort();
+
+ /* parse code */
+ parser = mrb_parser_new(mrb);
+ if (parser == NULL) {
+ fputs("create parser state error\n", stderr);
+ break;
+ }
+ parser->s = utf8;
+ parser->send = utf8 + strlen(utf8);
+ parser->lineno = cxt->lineno;
+ mrb_parser_parse(parser, cxt);
+ code_block_open = is_code_block_open(parser);
+ mrb_utf8_free(utf8);
+
+ if (code_block_open) {
+ /* no evaluation of code */
+ }
+ else {
+ if (0 < parser->nerr) {
+ /* syntax error */
+ printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message);
+ }
+ else {
+ /* generate bytecode */
+ struct RProc *proc = mrb_generate_code(mrb, parser);
+ if (proc == NULL) {
+ fputs("codegen error\n", stderr);
+ mrb_parser_free(parser);
+ break;
+ }
+
+ if (args.verbose) {
+ mrb_codedump_all(mrb, proc);
+ }
+ /* pass a proc for evaluation */
+ /* evaluate the bytecode */
+ result = mrb_vm_run(mrb,
+ proc,
+ mrb_top_self(mrb),
+ stack_keep);
+ stack_keep = proc->body.irep->nlocals;
+ /* did an exception occur? */
+ if (mrb->exc) {
+ p(mrb, mrb_obj_value(mrb->exc), 0);
+ mrb->exc = 0;
+ }
+ else {
+ /* no */
+ if (!mrb_respond_to(mrb, result, mrb_intern_lit(mrb, "inspect"))){
+ result = mrb_any_to_s(mrb, result);
+ }
+ p(mrb, result, 1);
+ }
+ }
+ ruby_code[0] = '\0';
+ last_code_line[0] = '\0';
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ mrb_parser_free(parser);
+ cxt->lineno++;
+ }
+
+#ifdef ENABLE_READLINE
+ MIRB_WRITE_HISTORY(history_path);
+ mrb_free(mrb, history_path);
+#endif
+
+ mrbc_context_free(mrb, cxt);
+ mrb_close(mrb);
+
+ return 0;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake
new file mode 100644
index 00000000..e710b5a4
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/mrbgem.rake
@@ -0,0 +1,16 @@
+MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby compiler executable'
+
+ spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler'
+
+ exec = exefile("#{build.build_dir}/bin/mrbc")
+ mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten
+
+ file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t|
+ build.linker.run t.name, t.prerequisites
+ end
+
+ build.bins << 'mrbc' unless build.bins.find { |v| v == 'mrbc' }
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
new file mode 100644
index 00000000..580c2e25
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
@@ -0,0 +1,336 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/proc.h>
+
+#define RITEBIN_EXT ".mrb"
+#define C_EXT ".c"
+
+struct mrbc_args {
+ int argc;
+ char **argv;
+ int idx;
+ const char *prog;
+ const char *outfile;
+ const char *initname;
+ mrb_bool check_syntax : 1;
+ mrb_bool verbose : 1;
+ unsigned int flags : 4;
+};
+
+static void
+usage(const char *name)
+{
+ static const char *const usage_msg[] = {
+ "switches:",
+ "-c check syntax only",
+ "-o<outfile> place the output into <outfile>",
+ "-v print version number, then turn on verbose mode",
+ "-g produce debugging information",
+ "-B<symbol> binary <symbol> output in C language format",
+ "-e generate little endian iseq data",
+ "-E generate big endian iseq data",
+ "--verbose run at verbose mode",
+ "--version print the version",
+ "--copyright print the copyright",
+ NULL
+ };
+ const char *const *p = usage_msg;
+
+ printf("Usage: %s [switches] programfile\n", name);
+ while (*p)
+ printf(" %s\n", *p++);
+}
+
+static char *
+get_outfilename(mrb_state *mrb, char *infile, const char *ext)
+{
+ size_t infilelen;
+ size_t extlen;
+ char *outfile;
+ char *p;
+
+ infilelen = strlen(infile);
+ extlen = strlen(ext);
+ outfile = (char*)mrb_malloc(mrb, infilelen + extlen + 1);
+ memcpy(outfile, infile, infilelen + 1);
+ if (*ext) {
+ if ((p = strrchr(outfile, '.')) == NULL)
+ p = outfile + infilelen;
+ memcpy(p, ext, extlen + 1);
+ }
+
+ return outfile;
+}
+
+static int
+parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
+{
+ char *outfile = NULL;
+ static const struct mrbc_args args_zero = { 0 };
+ int i;
+
+ *args = args_zero;
+ args->argc = argc;
+ args->argv = argv;
+ args->prog = argv[0];
+
+ for (i=1; i<argc; i++) {
+ if (argv[i][0] == '-') {
+ switch ((argv[i])[1]) {
+ case 'o':
+ if (args->outfile) {
+ fprintf(stderr, "%s: an output file is already specified. (%s)\n",
+ args->prog, outfile);
+ return -1;
+ }
+ if (argv[i][2] == '\0' && argv[i+1]) {
+ i++;
+ args->outfile = get_outfilename(mrb, argv[i], "");
+ }
+ else {
+ args->outfile = get_outfilename(mrb, argv[i] + 2, "");
+ }
+ break;
+ case 'B':
+ if (argv[i][2] == '\0' && argv[i+1]) {
+ i++;
+ args->initname = argv[i];
+ }
+ else {
+ args->initname = argv[i]+2;
+ }
+ if (*args->initname == '\0') {
+ fprintf(stderr, "%s: function name is not specified.\n", args->prog);
+ return -1;
+ }
+ break;
+ case 'c':
+ args->check_syntax = TRUE;
+ break;
+ case 'v':
+ if (!args->verbose) mrb_show_version(mrb);
+ args->verbose = TRUE;
+ break;
+ case 'g':
+ args->flags |= DUMP_DEBUG_INFO;
+ break;
+ case 'E':
+ args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK);
+ break;
+ case 'e':
+ args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK);
+ break;
+ case 'h':
+ return -1;
+ case '-':
+ if (argv[i][1] == '\n') {
+ return i;
+ }
+ if (strcmp(argv[i] + 2, "version") == 0) {
+ mrb_show_version(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp(argv[i] + 2, "verbose") == 0) {
+ args->verbose = TRUE;
+ break;
+ }
+ else if (strcmp(argv[i] + 2, "copyright") == 0) {
+ mrb_show_copyright(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ return -1;
+ default:
+ return i;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) {
+ fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n",
+ args->prog, bigendian_p() ? "big" : "little");
+ }
+ return i;
+}
+
+static void
+cleanup(mrb_state *mrb, struct mrbc_args *args)
+{
+ mrb_free(mrb, (void*)args->outfile);
+ mrb_close(mrb);
+}
+
+static int
+partial_hook(struct mrb_parser_state *p)
+{
+ mrbc_context *c = p->cxt;
+ struct mrbc_args *args = (struct mrbc_args *)c->partial_data;
+ const char *fn;
+
+ if (p->f) fclose(p->f);
+ if (args->idx >= args->argc) {
+ p->f = NULL;
+ return -1;
+ }
+ fn = args->argv[args->idx++];
+ p->f = fopen(fn, "r");
+ if (p->f == NULL) {
+ fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, fn);
+ return -1;
+ }
+ mrb_parser_set_filename(p, fn);
+ return 0;
+}
+
+static mrb_value
+load_file(mrb_state *mrb, struct mrbc_args *args)
+{
+ mrbc_context *c;
+ mrb_value result;
+ char *input = args->argv[args->idx];
+ FILE *infile;
+ mrb_bool need_close = FALSE;
+
+ c = mrbc_context_new(mrb);
+ if (args->verbose)
+ c->dump_result = TRUE;
+ c->no_exec = TRUE;
+ if (input[0] == '-' && input[1] == '\0') {
+ infile = stdin;
+ }
+ else {
+ need_close = TRUE;
+ if ((infile = fopen(input, "r")) == NULL) {
+ fprintf(stderr, "%s: cannot open program file. (%s)\n", args->prog, input);
+ return mrb_nil_value();
+ }
+ }
+ mrbc_filename(mrb, c, input);
+ args->idx++;
+ if (args->idx < args->argc) {
+ need_close = FALSE;
+ mrbc_partial_hook(mrb, c, partial_hook, (void*)args);
+ }
+
+ result = mrb_load_file_cxt(mrb, infile, c);
+ if (need_close) fclose(infile);
+ mrbc_context_free(mrb, c);
+ if (mrb_undef_p(result)) {
+ return mrb_nil_value();
+ }
+ return result;
+}
+
+static int
+dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, struct mrbc_args *args)
+{
+ int n = MRB_DUMP_OK;
+ mrb_irep *irep = proc->body.irep;
+
+ if (args->initname) {
+ n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
+ if (n == MRB_DUMP_INVALID_ARGUMENT) {
+ fprintf(stderr, "%s: invalid C language symbol name\n", args->initname);
+ }
+ }
+ else {
+ n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp);
+ }
+ if (n != MRB_DUMP_OK) {
+ fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n);
+ }
+ return n;
+}
+
+int
+main(int argc, char **argv)
+{
+ mrb_state *mrb = mrb_open();
+ int n, result;
+ struct mrbc_args args;
+ FILE *wfp;
+ mrb_value load;
+
+ if (mrb == NULL) {
+ fputs("Invalid mrb_state, exiting mrbc\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ n = parse_args(mrb, argc, argv, &args);
+ if (n < 0) {
+ cleanup(mrb, &args);
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ if (n == argc) {
+ fprintf(stderr, "%s: no program file given\n", args.prog);
+ return EXIT_FAILURE;
+ }
+ if (args.outfile == NULL && !args.check_syntax) {
+ if (n + 1 == argc) {
+ args.outfile = get_outfilename(mrb, argv[n], args.initname ? C_EXT : RITEBIN_EXT);
+ }
+ else {
+ fprintf(stderr, "%s: output file should be specified to compile multiple files\n", args.prog);
+ return EXIT_FAILURE;
+ }
+ }
+
+ args.idx = n;
+ load = load_file(mrb, &args);
+ if (mrb_nil_p(load)) {
+ cleanup(mrb, &args);
+ return EXIT_FAILURE;
+ }
+ if (args.check_syntax) {
+ printf("%s:%s:Syntax OK\n", args.prog, argv[n]);
+ }
+
+ if (args.check_syntax) {
+ cleanup(mrb, &args);
+ return EXIT_SUCCESS;
+ }
+
+ if (args.outfile) {
+ if (strcmp("-", args.outfile) == 0) {
+ wfp = stdout;
+ }
+ else if ((wfp = fopen(args.outfile, "wb")) == NULL) {
+ fprintf(stderr, "%s: cannot open output file:(%s)\n", args.prog, args.outfile);
+ return EXIT_FAILURE;
+ }
+ }
+ else {
+ fprintf(stderr, "Output file is required\n");
+ return EXIT_FAILURE;
+ }
+ result = dump_file(mrb, wfp, args.outfile, mrb_proc_ptr(load), &args);
+ fclose(wfp);
+ cleanup(mrb, &args);
+ if (result != MRB_DUMP_OK) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+void
+mrb_init_mrblib(mrb_state *mrb)
+{
+}
+
+#ifndef DISABLE_GEMS
+void
+mrb_init_mrbgems(mrb_state *mrb)
+{
+}
+
+void
+mrb_final_mrbgems(mrb_state *mrb)
+{
+}
+#endif
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake
new file mode 100644
index 00000000..66d6ef80
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mrbgem.rake
@@ -0,0 +1,30 @@
+module MRuby
+ class Build
+ def exefile(name)
+ if name.is_a?(Array)
+ name.flatten.map { |n| exefile(n) }
+ elsif name !~ /\./
+ "#{name}#{exts.executable}"
+ else
+ name
+ end
+ end
+ end
+end
+
+MRuby.each_target do
+ next if kind_of? MRuby::CrossBuild
+
+ mruby_config = 'mruby-config' + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
+ mruby_config_path = "#{build_dir}/bin/#{mruby_config}"
+ @bins << mruby_config
+
+ file mruby_config_path => libfile("#{build_dir}/lib/libmruby") do |t|
+ FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name
+ config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}]
+ IO.write(t.name, File.open(t.name) {|f|
+ f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
+ })
+ FileUtils.chmod(0755, t.name)
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config
new file mode 100644
index 00000000..57346c03
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ --cflags) echo MRUBY_CFLAGS;;
+ --ldflags) echo MRUBY_LDFLAGS;;
+ --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;;
+ --libs) echo MRUBY_LIBS;;
+ --libmruby-path) echo MRUBY_LIBMRUBY_PATH;;
+ --help) echo "Usage: mruby-config [switches]"
+ echo " switches:"
+ echo " --cflags print flags passed to compiler"
+ echo " --ldflags print flags passed to linker"
+ echo " --ldflags-before-libs print flags passed to linker before linked libraries"
+ echo " --libs print linked libraries"
+ echo " --libmruby-path print libmruby path"
+ exit 0;;
+ esac
+ shift
+done
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat
new file mode 100644
index 00000000..a1f7bfdd
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby-config/mruby-config.bat
@@ -0,0 +1,42 @@
+@echo off
+
+:top
+shift
+if "%0" equ "" goto :eof
+if "%0" equ "--cflags" goto cflags
+if "%0" equ "--ldflags" goto ldflags
+if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs
+if "%0" equ "--libs" goto libs
+if "%0" equ "--libmruby-path" goto libmrubypath
+if "%0" equ "--help" goto showhelp
+echo Invalid Option
+goto :eof
+
+:cflags
+echo MRUBY_CFLAGS
+goto top
+
+:libs
+echo MRUBY_LIBS
+goto top
+
+:ldflags
+echo MRUBY_LDFLAGS
+goto top
+
+:ldflagsbeforelibs
+echo MRUBY_LDFLAGS_BEFORE_LIBS
+goto top
+
+:libmrubypath
+echo MRUBY_LIBMRUBY_PATH
+goto top
+
+:showhelp
+echo Usage: mruby-config [switches]
+echo switches:
+echo --cflags print flags passed to compiler
+echo --ldflags print flags passed to linker
+echo --ldflags-before-libs print flags passed to linker before linked libraries
+echo --libs print linked libraries
+echo --libmruby-path print libmruby path
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb
new file mode 100644
index 00000000..b6b09018
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -0,0 +1,60 @@
+require 'tempfile'
+
+assert('regression for #1564') do
+ o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1`
+ assert_include o, "-e:1:2: syntax error"
+ o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1`
+ assert_include o, "-e:1:3: syntax error"
+end
+
+assert('regression for #1572') do
+ script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
+ File.write script.path, 'p "ok"'
+ system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
+ o = `#{cmd('mruby')} -b #{bin.path}`.strip
+ assert_equal o, '"ok"'
+end
+
+assert '$0 value' do
+ script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
+
+ # .rb script
+ script.write "p $0\n"
+ script.flush
+ assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp
+
+ # .mrb file
+ `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"`
+ assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp
+
+ # one liner
+ assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
+end
+
+assert '__END__', '8.6' do
+ script = Tempfile.new('test.rb')
+
+ script.write <<EOS
+p 'test'
+ __END__ = 'fin'
+p __END__
+__END__
+p 'legend'
+EOS
+ script.flush
+ assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}`
+end
+
+assert('garbage collecting built-in classes') do
+ script = Tempfile.new('test.rb')
+
+ script.write <<RUBY
+NilClass = nil
+GC.start
+Array.dup
+print nil.class.to_s
+RUBY
+ script.flush
+ assert_equal "NilClass", `#{cmd('mruby')} #{script.path}`
+ assert_equal 0, $?.exitstatus
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake
new file mode 100644
index 00000000..fbec1384
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/mrbgem.rake
@@ -0,0 +1,12 @@
+MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby command'
+ spec.bins = %w(mruby)
+ spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
+ spec.add_dependency('mruby-error', :core => 'mruby-error')
+
+ if build.cxx_exception_enabled?
+ build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
new file mode 100644
index 00000000..61d4cde9
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
@@ -0,0 +1,254 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/variable.h>
+
+#ifdef MRB_DISABLE_STDIO
+static void
+p(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value val = mrb_inspect(mrb, obj);
+
+ fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
+ putc('\n', stdout);
+}
+#else
+#define p(mrb,obj) mrb_p(mrb,obj)
+#endif
+
+struct _args {
+ FILE *rfp;
+ char* cmdline;
+ mrb_bool fname : 1;
+ mrb_bool mrbfile : 1;
+ mrb_bool check_syntax : 1;
+ mrb_bool verbose : 1;
+ int argc;
+ char** argv;
+};
+
+static void
+usage(const char *name)
+{
+ static const char *const usage_msg[] = {
+ "switches:",
+ "-b load and execute RiteBinary (mrb) file",
+ "-c check syntax only",
+ "-e 'command' one line of script",
+ "-v print version number, then run in verbose mode",
+ "--verbose run in verbose mode",
+ "--version print the version",
+ "--copyright print the copyright",
+ NULL
+ };
+ const char *const *p = usage_msg;
+
+ printf("Usage: %s [switches] programfile\n", name);
+ while (*p)
+ printf(" %s\n", *p++);
+}
+
+static int
+parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
+{
+ char **origargv = argv;
+ static const struct _args args_zero = { 0 };
+
+ *args = args_zero;
+
+ for (argc--,argv++; argc > 0; argc--,argv++) {
+ char *item;
+ if (argv[0][0] != '-') break;
+
+ if (strlen(*argv) <= 1) {
+ argc--; argv++;
+ args->rfp = stdin;
+ break;
+ }
+
+ item = argv[0] + 1;
+ switch (*item++) {
+ case 'b':
+ args->mrbfile = TRUE;
+ break;
+ case 'c':
+ args->check_syntax = TRUE;
+ break;
+ case 'e':
+ if (item[0]) {
+ goto append_cmdline;
+ }
+ else if (argc > 1) {
+ argc--; argv++;
+ item = argv[0];
+append_cmdline:
+ if (!args->cmdline) {
+ size_t buflen;
+ char *buf;
+
+ buflen = strlen(item) + 1;
+ buf = (char *)mrb_malloc(mrb, buflen);
+ memcpy(buf, item, buflen);
+ args->cmdline = buf;
+ }
+ else {
+ size_t cmdlinelen;
+ size_t itemlen;
+
+ cmdlinelen = strlen(args->cmdline);
+ itemlen = strlen(item);
+ args->cmdline =
+ (char *)mrb_realloc(mrb, args->cmdline, cmdlinelen + itemlen + 2);
+ args->cmdline[cmdlinelen] = '\n';
+ memcpy(args->cmdline + cmdlinelen + 1, item, itemlen + 1);
+ }
+ }
+ else {
+ printf("%s: No code specified for -e\n", *origargv);
+ return EXIT_SUCCESS;
+ }
+ break;
+ case 'v':
+ if (!args->verbose) mrb_show_version(mrb);
+ args->verbose = TRUE;
+ break;
+ case '-':
+ if (strcmp((*argv) + 2, "version") == 0) {
+ mrb_show_version(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp((*argv) + 2, "verbose") == 0) {
+ args->verbose = TRUE;
+ break;
+ }
+ else if (strcmp((*argv) + 2, "copyright") == 0) {
+ mrb_show_copyright(mrb);
+ exit(EXIT_SUCCESS);
+ }
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (args->rfp == NULL && args->cmdline == NULL) {
+ if (*argv == NULL) args->rfp = stdin;
+ else {
+ args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
+ if (args->rfp == NULL) {
+ printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
+ return EXIT_FAILURE;
+ }
+ args->fname = TRUE;
+ args->cmdline = argv[0];
+ argc--; argv++;
+ }
+ }
+ args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
+ memcpy(args->argv, argv, (argc+1) * sizeof(char*));
+ args->argc = argc;
+
+ return EXIT_SUCCESS;
+}
+
+static void
+cleanup(mrb_state *mrb, struct _args *args)
+{
+ if (args->rfp && args->rfp != stdin)
+ fclose(args->rfp);
+ if (!args->fname)
+ mrb_free(mrb, args->cmdline);
+ mrb_free(mrb, args->argv);
+ mrb_close(mrb);
+}
+
+int
+main(int argc, char **argv)
+{
+ mrb_state *mrb = mrb_open();
+ int n = -1;
+ int i;
+ struct _args args;
+ mrb_value ARGV;
+ mrbc_context *c;
+ mrb_value v;
+ mrb_sym zero_sym;
+
+ if (mrb == NULL) {
+ fputs("Invalid mrb_state, exiting mruby\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ n = parse_args(mrb, argc, argv, &args);
+ if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
+ cleanup(mrb, &args);
+ usage(argv[0]);
+ return n;
+ }
+ else {
+ int ai = mrb_gc_arena_save(mrb);
+ ARGV = mrb_ary_new_capa(mrb, args.argc);
+ for (i = 0; i < args.argc; i++) {
+ char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+ if (utf8) {
+ mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+ mrb_utf8_free(utf8);
+ }
+ }
+ mrb_define_global_const(mrb, "ARGV", ARGV);
+
+ c = mrbc_context_new(mrb);
+ if (args.verbose)
+ c->dump_result = TRUE;
+ if (args.check_syntax)
+ c->no_exec = TRUE;
+
+ /* Set $0 */
+ zero_sym = mrb_intern_lit(mrb, "$0");
+ if (args.rfp) {
+ const char *cmdline;
+ cmdline = args.cmdline ? args.cmdline : "-";
+ mrbc_filename(mrb, c, cmdline);
+ mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
+ }
+ else {
+ mrbc_filename(mrb, c, "-e");
+ mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e"));
+ }
+
+ /* Load program */
+ if (args.mrbfile) {
+ v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
+ }
+ else if (args.rfp) {
+ v = mrb_load_file_cxt(mrb, args.rfp, c);
+ }
+ else {
+ char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
+ if (!utf8) abort();
+ v = mrb_load_string_cxt(mrb, utf8, c);
+ mrb_utf8_free(utf8);
+ }
+
+ mrb_gc_arena_restore(mrb, ai);
+ mrbc_context_free(mrb, c);
+ if (mrb->exc) {
+ if (mrb_undef_p(v)) {
+ mrb_p(mrb, mrb_obj_value(mrb->exc));
+ }
+ else {
+ mrb_print_error(mrb);
+ }
+ n = -1;
+ }
+ else if (args.check_syntax) {
+ printf("Syntax OK\n");
+ }
+ }
+ cleanup(mrb, &args);
+
+ return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
new file mode 100644
index 00000000..bb664a2b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
@@ -0,0 +1,73 @@
+require 'tempfile'
+
+assert('no files') do
+ o = `#{cmd('mruby-strip')} 2>&1`
+ assert_equal 1, $?.exitstatus
+ assert_equal "no files to strip", o.split("\n")[0]
+end
+
+assert('file not found') do
+ o = `#{cmd('mruby-strip')} not_found.mrb 2>&1`
+ assert_equal 1, $?.exitstatus
+ assert_equal "can't open file for reading not_found.mrb\n", o
+end
+
+assert('not irep file') do
+ t = Tempfile.new('script.rb')
+ t.write 'p test\n'
+ t.flush
+ o = `#{cmd('mruby-strip')} #{t.path} 2>&1`
+ assert_equal 1, $?.exitstatus
+ assert_equal "can't read irep file #{t.path}\n", o
+end
+
+assert('success') do
+ script_file, compiled1, compiled2 =
+ Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
+ script_file.write "p 'test'\n"
+ script_file.flush
+ `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}`
+ `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}`
+
+ o = `#{cmd('mruby-strip')} #{compiled1.path}`
+ assert_equal 0, $?.exitstatus
+ assert_equal "", o
+ assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} -b #{compiled1.path}`
+
+ o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}`
+ assert_equal 0, $?.exitstatus
+ assert_equal "", o
+end
+
+assert('check debug section') do
+ script_file, with_debug, without_debug =
+ Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
+ script_file.write "p 'test'\n"
+ script_file.flush
+ `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}`
+ `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}`
+
+ assert_true with_debug.size >= without_debug.size
+
+ `#{cmd('mruby-strip')} #{with_debug.path}`
+ assert_equal without_debug.size, with_debug.size
+end
+
+assert('check lv section') do
+ script_file, with_lv, without_lv =
+ Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
+ script_file.write <<EOS
+a, b = 0, 1
+a += b
+p Kernel.local_variables
+EOS
+ script_file.flush
+ `#{cmd('mrbc')} -o #{with_lv.path} #{script_file.path}`
+ `#{cmd('mrbc')} -o #{without_lv.path} #{script_file.path}`
+
+ `#{cmd('mruby-strip')} -l #{without_lv.path}`
+ assert_true without_lv.size < with_lv.size
+
+ assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
+ assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake
new file mode 100644
index 00000000..2abd25ee
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/mrbgem.rake
@@ -0,0 +1,6 @@
+MRuby::Gem::Specification.new('mruby-bin-strip') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'irep dump debug section remover command'
+ spec.bins = %w(mruby-strip)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
new file mode 100644
index 00000000..deb66d54
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
@@ -0,0 +1,155 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/dump.h>
+
+struct strip_args {
+ int argc_start;
+ int argc;
+ char **argv;
+ mrb_bool lvar;
+};
+
+
+static void
+irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
+{
+ int i;
+
+ if (irep->lv) {
+ mrb_free(mrb, irep->lv);
+ irep->lv = NULL;
+ }
+
+ for (i = 0; i < irep->rlen; ++i) {
+ irep_remove_lv(mrb, irep->reps[i]);
+ }
+}
+
+static void
+print_usage(const char *f)
+{
+ printf("Usage: %s [switches] irepfiles\n", f);
+ printf("switches:\n");
+ printf(" -l, --lvar remove LVAR section too.\n");
+}
+
+static int
+parse_args(int argc, char **argv, struct strip_args *args)
+{
+ int i;
+
+ args->argc_start = 0;
+ args->argc = argc;
+ args->argv = argv;
+ args->lvar = FALSE;
+
+ for (i = 1; i < argc; ++i) {
+ const size_t len = strlen(argv[i]);
+ if (len >= 2 && argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'l':
+ args->lvar = TRUE;
+ break;
+ case '-':
+ if (strncmp((*argv) + 2, "lvar", len) == 0) {
+ args->lvar = TRUE;
+ break;
+ }
+ default:
+ return -1;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ args->argc_start = i;
+ return i;
+}
+
+static int
+strip(mrb_state *mrb, struct strip_args *args)
+{
+ int i;
+
+ for (i = args->argc_start; i < args->argc; ++i) {
+ char *filename;
+ FILE *rfile;
+ mrb_irep *irep;
+ FILE *wfile;
+ int dump_result;
+
+ filename = args->argv[i];
+ rfile = fopen(filename, "rb");
+ if (rfile == NULL) {
+ fprintf(stderr, "can't open file for reading %s\n", filename);
+ return EXIT_FAILURE;
+ }
+
+ irep = mrb_read_irep_file(mrb, rfile);
+ fclose(rfile);
+ if (irep == NULL) {
+ fprintf(stderr, "can't read irep file %s\n", filename);
+ return EXIT_FAILURE;
+ }
+
+ /* clear lv if --lvar is enabled */
+ if (args->lvar) {
+ irep_remove_lv(mrb, irep);
+ }
+
+ wfile = fopen(filename, "wb");
+ if (wfile == NULL) {
+ fprintf(stderr, "can't open file for writing %s\n", filename);
+ mrb_irep_decref(mrb, irep);
+ return EXIT_FAILURE;
+ }
+
+ /* debug flag must always be false */
+ dump_result = mrb_dump_irep_binary(mrb, irep, FALSE, wfile);
+
+ fclose(wfile);
+ mrb_irep_decref(mrb, irep);
+
+ if (dump_result != MRB_DUMP_OK) {
+ fprintf(stderr, "error occurred during dumping %s\n", filename);
+ return EXIT_FAILURE;
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct strip_args args;
+ int args_result;
+ mrb_state *mrb;
+ int ret;
+
+ if (argc <= 1) {
+ printf("no files to strip\n");
+ print_usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ args_result = parse_args(argc, argv, &args);
+ if (args_result < 0) {
+ print_usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ mrb = mrb_open_core(mrb_default_allocf, NULL);
+ if (mrb == NULL) {
+ fputs("Invalid mrb_state, exiting mruby-strip\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ ret = strip(mrb, &args);
+
+ mrb_close(mrb);
+ return ret;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake
new file mode 100644
index 00000000..a384b1ee
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-class-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'class/module extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c
new file mode 100644
index 00000000..5506c482
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/src/class.c
@@ -0,0 +1,30 @@
+#include "mruby.h"
+#include "mruby/class.h"
+#include "mruby/string.h"
+
+static mrb_value
+mrb_mod_name(mrb_state *mrb, mrb_value self)
+{
+ mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
+ return mrb_nil_p(name)? name : mrb_str_dup(mrb, name);
+}
+
+static mrb_value
+mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self)
+{
+ return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS);
+}
+
+void
+mrb_mruby_class_ext_gem_init(mrb_state *mrb)
+{
+ struct RClass *mod = mrb->module_class;
+
+ mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE());
+ mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_class_ext_gem_final(mrb_state *mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb
new file mode 100644
index 00000000..65abde10
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-class-ext/test/module.rb
@@ -0,0 +1,34 @@
+assert 'Module#name' do
+ module Outer
+ class Inner; end
+ const_set :SetInner, Class.new
+ end
+
+ assert_equal 'Outer', Outer.name
+ assert_equal 'Outer::Inner', Outer::Inner.name
+ assert_equal 'Outer::SetInner', Outer::SetInner.name
+
+ outer = Module.new do
+ const_set :SetInner, Class.new
+ end
+ Object.const_set :SetOuter, outer
+
+ assert_equal 'SetOuter', SetOuter.name
+ assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name
+
+ mod = Module.new
+ cls = Class.new
+
+ assert_nil mod.name
+ assert_nil cls.name
+end
+
+assert 'Module#singleton_class?' do
+ mod = Module.new
+ cls = Class.new
+ scl = cls.singleton_class
+
+ assert_false mod.singleton_class?
+ assert_false cls.singleton_class?
+ assert_true scl.singleton_class?
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb
new file mode 100644
index 00000000..f4d9208b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/bintest/mrbc.rb
@@ -0,0 +1,30 @@
+require 'tempfile'
+
+assert('Compiling multiple files without new line in last line. #2361') do
+ a, b, out = Tempfile.new('a.rb'), Tempfile.new('b.rb'), Tempfile.new('out.mrb')
+ a.write('module A; end')
+ a.flush
+ b.write('module B; end')
+ b.flush
+ result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1`
+ assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
+ assert_equal 0, $?.exitstatus
+end
+
+assert('parsing function with void argument') do
+ a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
+ a.write('f ()')
+ a.flush
+ result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
+ assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
+ assert_equal 0, $?.exitstatus
+end
+
+assert('embedded document with invalid terminator') do
+ a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb')
+ a.write("=begin\n=endx\n")
+ a.flush
+ result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
+ assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp
+ assert_equal 1, $?.exitstatus
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c
new file mode 100644
index 00000000..8f15a9b1
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c
@@ -0,0 +1,3026 @@
+/*
+** codegen.c - mruby code generator
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
+#include "node.h"
+#include <mruby/opcode.h>
+#include <mruby/re.h>
+#include <mruby/throw.h>
+
+#ifndef MRB_CODEGEN_LEVEL_MAX
+#define MRB_CODEGEN_LEVEL_MAX 1024
+#endif
+
+typedef mrb_ast_node node;
+typedef struct mrb_parser_state parser_state;
+
+enum looptype {
+ LOOP_NORMAL,
+ LOOP_BLOCK,
+ LOOP_FOR,
+ LOOP_BEGIN,
+ LOOP_RESCUE,
+};
+
+struct loopinfo {
+ enum looptype type;
+ int pc1, pc2, pc3, acc;
+ int ensure_level;
+ struct loopinfo *prev;
+};
+
+typedef struct scope {
+ mrb_state *mrb;
+ mrb_pool *mpool;
+ struct mrb_jmpbuf jmp;
+
+ struct scope *prev;
+
+ node *lv;
+
+ int sp;
+ int pc;
+ int lastlabel;
+ int ainfo:15;
+ mrb_bool mscope:1;
+
+ struct loopinfo *loop;
+ int ensure_level;
+ char const *filename;
+ uint16_t lineno;
+
+ mrb_code *iseq;
+ uint16_t *lines;
+ int icapa;
+
+ mrb_irep *irep;
+ int pcapa, scapa, rcapa;
+
+ uint16_t nlocals;
+ uint16_t nregs;
+ int ai;
+
+ int debug_start_pos;
+ uint16_t filename_index;
+ parser_state* parser;
+
+ int rlev; /* recursion levels */
+} codegen_scope;
+
+static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
+static void scope_finish(codegen_scope *s);
+static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
+static void loop_break(codegen_scope *s, node *tree);
+static void loop_pop(codegen_scope *s, int val);
+
+static void gen_assignment(codegen_scope *s, node *tree, int sp, int val);
+static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
+
+static void codegen(codegen_scope *s, node *tree, int val);
+static void raise_error(codegen_scope *s, const char *msg);
+
+static void
+codegen_error(codegen_scope *s, const char *message)
+{
+ if (!s) return;
+ while (s->prev) {
+ codegen_scope *tmp = s->prev;
+ mrb_free(s->mrb, s->iseq);
+ mrb_pool_close(s->mpool);
+ s = tmp;
+ }
+#ifndef MRB_DISABLE_STDIO
+ if (s->filename && s->lineno) {
+ fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
+ }
+ else {
+ fprintf(stderr, "codegen error: %s\n", message);
+ }
+#endif
+ MRB_THROW(&s->jmp);
+}
+
+static void*
+codegen_palloc(codegen_scope *s, size_t len)
+{
+ void *p = mrb_pool_alloc(s->mpool, len);
+
+ if (!p) codegen_error(s, "pool memory allocation");
+ return p;
+}
+
+static void*
+codegen_malloc(codegen_scope *s, size_t len)
+{
+ void *p = mrb_malloc_simple(s->mrb, len);
+
+ if (!p) codegen_error(s, "mrb_malloc");
+ return p;
+}
+
+static void*
+codegen_realloc(codegen_scope *s, void *p, size_t len)
+{
+ p = mrb_realloc_simple(s->mrb, p, len);
+
+ if (!p && len > 0) codegen_error(s, "mrb_realloc");
+ return p;
+}
+
+static int
+new_label(codegen_scope *s)
+{
+ s->lastlabel = s->pc;
+ return s->pc;
+}
+
+static inline int
+genop(codegen_scope *s, mrb_code i)
+{
+ if (s->pc == s->icapa) {
+ s->icapa *= 2;
+ s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
+ if (s->lines) {
+ s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
+ s->irep->lines = s->lines;
+ }
+ }
+ s->iseq[s->pc] = i;
+ if (s->lines) {
+ s->lines[s->pc] = s->lineno;
+ }
+ return s->pc++;
+}
+
+#define NOVAL 0
+#define VAL 1
+
+static mrb_bool
+no_optimize(codegen_scope *s)
+{
+ if (s && s->parser && s->parser->no_optimize)
+ return TRUE;
+ return FALSE;
+}
+
+static int
+genop_peep(codegen_scope *s, mrb_code i, int val)
+{
+ /* peephole optimization */
+ if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) {
+ mrb_code i0 = s->iseq[s->pc-1];
+ int c1 = GET_OPCODE(i);
+ int c0 = GET_OPCODE(i0);
+
+ switch (c1) {
+ case OP_MOVE:
+ if (GETARG_A(i) == GETARG_B(i)) {
+ /* skip useless OP_MOVE */
+ return 0;
+ }
+ if (val) break;
+ switch (c0) {
+ case OP_MOVE:
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ /* skip overriden OP_MOVE */
+ s->pc--;
+ s->iseq[s->pc] = i;
+ }
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
+ /* skip swapping OP_MOVE */
+ return 0;
+ }
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->pc--;
+ return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
+ }
+ break;
+ case OP_LOADI:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
+ return 0;
+ }
+ break;
+ case OP_ARRAY:
+ case OP_HASH:
+ case OP_RANGE:
+ case OP_AREF:
+ case OP_GETUPVAR:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
+ return 0;
+ }
+ break;
+ case OP_LOADSYM:
+ case OP_GETGLOBAL:
+ case OP_GETIV:
+ case OP_GETCV:
+ case OP_GETCONST:
+ case OP_GETSPECIAL:
+ case OP_LOADL:
+ case OP_STRING:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
+ return 0;
+ }
+ break;
+ case OP_SCLASS:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
+ return 0;
+ }
+ break;
+ case OP_LOADNIL:
+ case OP_LOADSELF:
+ case OP_LOADT:
+ case OP_LOADF:
+ case OP_OCLASS:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case OP_SETIV:
+ case OP_SETCV:
+ case OP_SETCONST:
+ case OP_SETMCNST:
+ case OP_SETGLOBAL:
+ if (val) break;
+ if (c0 == OP_MOVE) {
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
+ return 0;
+ }
+ }
+ break;
+ case OP_SETUPVAR:
+ if (val) break;
+ if (c0 == OP_MOVE) {
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
+ return 0;
+ }
+ }
+ break;
+ case OP_EPOP:
+ if (c0 == OP_EPOP) {
+ s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
+ return 0;
+ }
+ break;
+ case OP_POPERR:
+ if (c0 == OP_POPERR) {
+ s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
+ return 0;
+ }
+ break;
+ case OP_RETURN:
+ switch (c0) {
+ case OP_RETURN:
+ return 0;
+ case OP_MOVE:
+ if (GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
+ return 0;
+ }
+ break;
+ case OP_SETIV:
+ case OP_SETCV:
+ case OP_SETCONST:
+ case OP_SETMCNST:
+ case OP_SETUPVAR:
+ case OP_SETGLOBAL:
+ s->pc--;
+ genop_peep(s, i0, NOVAL);
+ i0 = s->iseq[s->pc-1];
+ return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
+#if 0
+ case OP_SEND:
+ if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
+ return;
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ break;
+ case OP_ADD:
+ case OP_SUB:
+ if (c0 == OP_LOADI) {
+ int c = GETARG_sBx(i0);
+
+ if (c1 == OP_SUB) c = -c;
+ if (c > 127 || c < -127) break;
+ if (0 <= c)
+ s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
+ else
+ s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
+ return 0;
+ }
+ case OP_STRCAT:
+ if (c0 == OP_STRING) {
+ mrb_value v = s->irep->pool[GETARG_Bx(i0)];
+
+ if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
+ s->pc--;
+ return 0;
+ }
+ }
+ if (c0 == OP_LOADNIL) {
+ if (GETARG_B(i) == GETARG_A(i0)) {
+ s->pc--;
+ return 0;
+ }
+ }
+ break;
+ case OP_JMPIF:
+ case OP_JMPNOT:
+ if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
+ return s->pc-1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return genop(s, i);
+}
+
+static void
+scope_error(codegen_scope *s)
+{
+ exit(EXIT_FAILURE);
+}
+
+static inline void
+dispatch(codegen_scope *s, int pc)
+{
+ int diff = s->pc - pc;
+ mrb_code i = s->iseq[pc];
+ int c = GET_OPCODE(i);
+
+ s->lastlabel = s->pc;
+ switch (c) {
+ case OP_JMP:
+ case OP_JMPIF:
+ case OP_JMPNOT:
+ case OP_ONERR:
+ break;
+ default:
+#ifndef MRB_DISABLE_STDIO
+ fprintf(stderr, "bug: dispatch on non JMP op\n");
+#endif
+ scope_error(s);
+ break;
+ }
+ if (diff > MAXARG_sBx) {
+ codegen_error(s, "too distant jump address");
+ }
+ s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
+}
+
+static void
+dispatch_linked(codegen_scope *s, int pc)
+{
+ mrb_code i;
+ int pos;
+
+ if (!pc) return;
+ for (;;) {
+ i = s->iseq[pc];
+ pos = GETARG_sBx(i);
+ dispatch(s, pc);
+ if (!pos) break;
+ pc = pos;
+ }
+}
+
+#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
+static void
+push_(codegen_scope *s)
+{
+ if (s->sp > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp++;
+ nregs_update;
+}
+
+static void
+push_n_(codegen_scope *s, int n)
+{
+ if (s->sp+n > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp+=n;
+ nregs_update;
+}
+
+#define push() push_(s)
+#define push_n(n) push_n_(s,n)
+#define pop_(s) ((s)->sp--)
+#define pop() pop_(s)
+#define pop_n(n) (s->sp-=(n))
+#define cursp() (s->sp)
+
+static inline int
+new_lit(codegen_scope *s, mrb_value val)
+{
+ int i;
+ mrb_value *pv;
+
+ switch (mrb_type(val)) {
+ case MRB_TT_STRING:
+ for (i=0; i<s->irep->plen; i++) {
+ mrb_int len;
+ pv = &s->irep->pool[i];
+
+ if (mrb_type(*pv) != MRB_TT_STRING) continue;
+ if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue;
+ if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0)
+ return i;
+ }
+ break;
+ case MRB_TT_FLOAT:
+ for (i=0; i<s->irep->plen; i++) {
+ pv = &s->irep->pool[i];
+ if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
+ if (mrb_float(*pv) == mrb_float(val)) return i;
+ }
+ break;
+ case MRB_TT_FIXNUM:
+ for (i=0; i<s->irep->plen; i++) {
+ pv = &s->irep->pool[i];
+ if (!mrb_fixnum_p(*pv)) continue;
+ if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i;
+ }
+ break;
+ default:
+ /* should not happen */
+ return 0;
+ }
+
+ if (s->irep->plen == s->pcapa) {
+ s->pcapa *= 2;
+ s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa);
+ }
+
+ pv = &s->irep->pool[s->irep->plen];
+ i = s->irep->plen++;
+
+ switch (mrb_type(val)) {
+ case MRB_TT_STRING:
+ *pv = mrb_str_pool(s->mrb, val);
+ break;
+
+ case MRB_TT_FLOAT:
+#ifdef MRB_WORD_BOXING
+ *pv = mrb_float_pool(s->mrb, mrb_float(val));
+ break;
+#endif
+ case MRB_TT_FIXNUM:
+ *pv = val;
+ break;
+
+ default:
+ /* should not happen */
+ break;
+ }
+ return i;
+}
+
+/* method symbols should be fit in 9 bits */
+#define MAXMSYMLEN 512
+/* maximum symbol numbers */
+#define MAXSYMLEN 65536
+
+static int
+new_msym(codegen_scope *s, mrb_sym sym)
+{
+ int i, len;
+
+ mrb_assert(s->irep);
+
+ len = s->irep->slen;
+ if (len > MAXMSYMLEN) len = MAXMSYMLEN;
+ for (i=0; i<len; i++) {
+ if (s->irep->syms[i] == sym) return i;
+ if (s->irep->syms[i] == 0) break;
+ }
+ if (i == MAXMSYMLEN) {
+ codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
+ }
+ s->irep->syms[i] = sym;
+ if (i == s->irep->slen) s->irep->slen++;
+ return i;
+}
+
+static int
+new_sym(codegen_scope *s, mrb_sym sym)
+{
+ int i;
+
+ for (i=0; i<s->irep->slen; i++) {
+ if (s->irep->syms[i] == sym) return i;
+ }
+ if (s->irep->slen == MAXSYMLEN) {
+ codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
+ }
+
+ if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
+ s->scapa = MAXSYMLEN;
+ s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
+ for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
+ static const mrb_sym mrb_sym_zero = { 0 };
+ s->irep->syms[i] = mrb_sym_zero;
+ }
+ s->irep->slen = MAXMSYMLEN;
+ }
+ s->irep->syms[s->irep->slen] = sym;
+ return s->irep->slen++;
+}
+
+static int
+node_len(node *tree)
+{
+ int n = 0;
+
+ while (tree) {
+ n++;
+ tree = tree->cdr;
+ }
+ return n;
+}
+
+#define nsym(x) ((mrb_sym)(intptr_t)(x))
+#define lv_name(lv) nsym((lv)->car)
+static int
+lv_idx(codegen_scope *s, mrb_sym id)
+{
+ node *lv = s->lv;
+ int n = 1;
+
+ while (lv) {
+ if (lv_name(lv) == id) return n;
+ n++;
+ lv = lv->cdr;
+ }
+ return 0;
+}
+
+static void
+for_body(codegen_scope *s, node *tree)
+{
+ codegen_scope *prev = s;
+ int idx;
+ struct loopinfo *lp;
+ node *n2;
+ mrb_code c;
+
+ /* generate receiver */
+ codegen(s, tree->cdr->car, VAL);
+ /* generate loop-block */
+ s = scope_new(s->mrb, s, NULL);
+ if (s == NULL) {
+ raise_error(prev, "unexpected scope");
+ }
+
+ push(); /* push for a block parameter */
+
+ /* generate loop variable */
+ n2 = tree->car;
+ genop(s, MKOP_Ax(OP_ENTER, 0x40000));
+ if (n2->car && !n2->car->cdr && !n2->cdr) {
+ gen_assignment(s, n2->car->car, 1, NOVAL);
+ }
+ else {
+ gen_vmassignment(s, n2, 1, VAL);
+ }
+ /* construct loop */
+ lp = loop_push(s, LOOP_FOR);
+ lp->pc2 = new_label(s);
+
+ /* loop body */
+ codegen(s, tree->cdr->cdr->car, VAL);
+ pop();
+ if (s->pc > 0) {
+ c = s->iseq[s->pc-1];
+ if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ loop_pop(s, NOVAL);
+ scope_finish(s);
+ s = prev;
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+ push();pop(); /* space for a block */
+ pop();
+ idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
+ genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
+}
+
+static int
+lambda_body(codegen_scope *s, node *tree, int blk)
+{
+ mrb_code c;
+ codegen_scope *parent = s;
+ s = scope_new(s->mrb, s, tree->car);
+ if (s == NULL) {
+ raise_error(parent, "unexpected scope");
+ }
+
+ s->mscope = !blk;
+
+ if (blk) {
+ struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
+ lp->pc1 = new_label(s);
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ mrb_aspec a;
+ int ma, oa, ra, pa, ka, kd, ba;
+ int pos, i;
+ node *n, *opt;
+
+ ma = node_len(tree->car->car);
+ n = tree->car->car;
+ while (n) {
+ n = n->cdr;
+ }
+ oa = node_len(tree->car->cdr->car);
+ ra = tree->car->cdr->cdr->car ? 1 : 0;
+ pa = node_len(tree->car->cdr->cdr->cdr->car);
+ ka = kd = 0;
+ ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+
+ if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
+ codegen_error(s, "too many formal arguments");
+ }
+ a = ((mrb_aspec)(ma & 0x1f) << 18)
+ | ((mrb_aspec)(oa & 0x1f) << 13)
+ | ((ra & 1) << 12)
+ | ((pa & 0x1f) << 7)
+ | ((ka & 0x1f) << 2)
+ | ((kd & 1)<< 1)
+ | (ba & 1);
+ s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
+ | ((ra & 1) << 5)
+ | (pa & 0x1f);
+ genop(s, MKOP_Ax(OP_ENTER, a));
+ pos = new_label(s);
+ for (i=0; i<oa; i++) {
+ new_label(s);
+ genop(s, MKOP_sBx(OP_JMP, 0));
+ }
+ if (oa > 0) {
+ genop(s, MKOP_sBx(OP_JMP, 0));
+ }
+ opt = tree->car->cdr->car;
+ i = 0;
+ while (opt) {
+ int idx;
+
+ dispatch(s, pos+i);
+ codegen(s, opt->car->cdr, VAL);
+ idx = lv_idx(s, nsym(opt->car->car));
+ pop();
+ genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+ i++;
+ opt = opt->cdr;
+ }
+ if (oa > 0) {
+ dispatch(s, pos+i);
+ }
+ }
+ codegen(s, tree->cdr->car, VAL);
+ pop();
+ if (s->pc > 0) {
+ c = s->iseq[s->pc-1];
+ if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
+ if (s->nregs == 0) {
+ genop(s, MKOP_A(OP_LOADNIL, 0));
+ genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
+ }
+ else {
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ }
+ }
+ if (blk) {
+ loop_pop(s, NOVAL);
+ }
+ scope_finish(s);
+ return parent->irep->rlen - 1;
+}
+
+static int
+scope_body(codegen_scope *s, node *tree, int val)
+{
+ codegen_scope *scope = scope_new(s->mrb, s, tree->car);
+ if (scope == NULL) {
+ raise_error(s, "unexpected scope");
+ }
+
+ codegen(scope, tree->cdr, VAL);
+ if (!s->iseq) {
+ genop(scope, MKOP_A(OP_STOP, 0));
+ }
+ else if (!val) {
+ genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
+ }
+ else {
+ if (scope->nregs == 0) {
+ genop(scope, MKOP_A(OP_LOADNIL, 0));
+ genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
+ }
+ else {
+ genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
+ }
+ }
+ scope_finish(scope);
+ if (!s->irep) {
+ /* should not happen */
+ return 0;
+ }
+ return s->irep->rlen - 1;
+}
+
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
+
+static mrb_bool
+nosplat(node *t)
+{
+ while (t) {
+ if (nint(t->car->car) == NODE_SPLAT) return FALSE;
+ t = t->cdr;
+ }
+ return TRUE;
+}
+
+static mrb_sym
+attrsym(codegen_scope *s, mrb_sym a)
+{
+ const char *name;
+ mrb_int len;
+ char *name2;
+
+ name = mrb_sym2name_len(s->mrb, a, &len);
+ name2 = (char *)codegen_palloc(s,
+ (size_t)len
+ + 1 /* '=' */
+ + 1 /* '\0' */
+ );
+ mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
+ memcpy(name2, name, (size_t)len);
+ name2[len] = '=';
+ name2[len+1] = '\0';
+
+ return mrb_intern(s->mrb, name2, len+1);
+}
+
+#define CALL_MAXARGS 127
+
+static int
+gen_values(codegen_scope *s, node *t, int val, int extra)
+{
+ int n = 0;
+ int is_splat;
+
+ while (t) {
+ is_splat = nint(t->car->car) == NODE_SPLAT; /* splat mode */
+ if (
+ n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
+ || is_splat) {
+ if (val) {
+ if (is_splat && n == 0 && nint(t->car->cdr->car) == NODE_ARRAY) {
+ codegen(s, t->car->cdr, VAL);
+ pop();
+ }
+ else {
+ pop_n(n);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ push();
+ codegen(s, t->car, VAL);
+ pop(); pop();
+ if (is_splat) {
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ }
+ t = t->cdr;
+ while (t) {
+ push();
+ codegen(s, t->car, VAL);
+ pop(); pop();
+ if (nint(t->car->car) == NODE_SPLAT) {
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ t = t->cdr;
+ }
+ }
+ else {
+ while (t) {
+ codegen(s, t->car, NOVAL);
+ t = t->cdr;
+ }
+ }
+ return -1;
+ }
+ /* normal (no splat) mode */
+ codegen(s, t->car, val);
+ n++;
+ t = t->cdr;
+ }
+ return n;
+}
+
+static void
+gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
+{
+ mrb_sym sym = name ? name : nsym(tree->cdr->car);
+ int idx, skip = 0;
+ int n = 0, noop = 0, sendv = 0, blk = 0;
+
+ codegen(s, tree->car, VAL); /* receiver */
+ if (safe) {
+ int recv = cursp()-1;
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
+ push_n(2); pop_n(2); /* space for one arg and a block */
+ pop();
+ idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
+ genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
+ skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ }
+ idx = new_msym(s, sym);
+ tree = tree->cdr->cdr->car;
+ if (tree) {
+ n = gen_values(s, tree->car, VAL, sp?1:0);
+ if (n < 0) {
+ n = noop = sendv = 1;
+ push();
+ }
+ }
+ if (sp) {
+ if (sendv) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+ push();
+ }
+ else {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+ push();
+ n++;
+ }
+ }
+ if (tree && tree->cdr) {
+ noop = 1;
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ else {
+ blk = cursp();
+ }
+ push();pop();
+ pop_n(n+1);
+ {
+ mrb_int symlen;
+ const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
+
+ if (!noop && symlen == 1 && symname[0] == '+' && n == 1) {
+ genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
+ }
+ else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) {
+ genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
+ }
+ else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) {
+ genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) {
+ genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) {
+ genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) {
+ genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) {
+ genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) {
+ genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) {
+ genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
+ }
+ else {
+ if (sendv) n = CALL_MAXARGS;
+ if (blk > 0) { /* no block */
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
+ }
+ }
+ }
+ if (safe) {
+ dispatch(s, skip);
+ }
+ if (val) {
+ push();
+ }
+}
+
+static void
+gen_assignment(codegen_scope *s, node *tree, int sp, int val)
+{
+ int idx;
+ int type = nint(tree->car);
+
+ tree = tree->cdr;
+ switch (type) {
+ case NODE_GVAR:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+ break;
+ case NODE_LVAR:
+ idx = lv_idx(s, nsym(tree));
+ if (idx > 0) {
+ if (idx != sp) {
+ genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+ }
+ break;
+ }
+ else { /* upvar */
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ idx = lv_idx(up, nsym(tree));
+ if (idx > 0) {
+ genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+ break;
+ }
+ lv++;
+ up = up->prev;
+ }
+ }
+ break;
+ case NODE_IVAR:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+ break;
+ case NODE_CVAR:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+ break;
+ case NODE_CONST:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+ break;
+ case NODE_COLON2:
+ idx = new_sym(s, nsym(tree->cdr));
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+ push();
+ codegen(s, tree->car, VAL);
+ pop_n(2);
+ genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+ break;
+
+ case NODE_CALL:
+ case NODE_SCALL:
+ push();
+ gen_call(s, tree, attrsym(s, nsym(tree->cdr->car)), sp, NOVAL,
+ type == NODE_SCALL);
+ pop();
+ if (val) {
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
+ }
+ break;
+
+ case NODE_MASGN:
+ gen_vmassignment(s, tree->car, sp, val);
+ break;
+
+ /* splat without assignment */
+ case NODE_NIL:
+ break;
+
+ default:
+#ifndef MRB_DISABLE_STDIO
+ fprintf(stderr, "unknown lhs %d\n", type);
+#endif
+ break;
+ }
+ if (val) push();
+}
+
+static void
+gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
+{
+ int n = 0, post = 0;
+ node *t, *p;
+
+ if (tree->car) { /* pre */
+ t = tree->car;
+ n = 0;
+ while (t) {
+ genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ n++;
+ t = t->cdr;
+ }
+ }
+ t = tree->cdr;
+ if (t) {
+ if (t->cdr) { /* post count */
+ p = t->cdr->car;
+ while (p) {
+ post++;
+ p = p->cdr;
+ }
+ }
+ if (val) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ }
+ else {
+ pop();
+ }
+ push_n(post);
+ pop_n(post);
+ genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
+ n = 1;
+ if (t->car) { /* rest */
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ }
+ if (t->cdr && t->cdr->car) {
+ t = t->cdr->car;
+ while (t) {
+ gen_assignment(s, t->car, cursp()+n, NOVAL);
+ t = t->cdr;
+ n++;
+ }
+ }
+ if (!val) {
+ push();
+ }
+ }
+}
+
+static void
+gen_send_intern(codegen_scope *s)
+{
+ push();pop(); /* space for a block */
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
+ push();
+}
+static void
+gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
+{
+ if (val) {
+ int i = 0, j = 0;
+
+ while (tree) {
+ switch (nint(tree->car->car)) {
+ case NODE_STR:
+ if ((tree->cdr == NULL) && (nint(tree->car->cdr->cdr) == 0))
+ break;
+ /* fall through */
+ case NODE_BEGIN:
+ codegen(s, tree->car, VAL);
+ ++j;
+ break;
+
+ case NODE_LITERAL_DELIM:
+ if (j > 0) {
+ j = 0;
+ ++i;
+ if (sym)
+ gen_send_intern(s);
+ }
+ break;
+ }
+ if (j >= 2) {
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ j = 1;
+ }
+ tree = tree->cdr;
+ }
+ if (j > 0) {
+ ++i;
+ if (sym)
+ gen_send_intern(s);
+ }
+ pop_n(i);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
+ push();
+ }
+ else {
+ while (tree) {
+ switch (nint(tree->car->car)) {
+ case NODE_BEGIN: case NODE_BLOCK:
+ codegen(s, tree->car, NOVAL);
+ }
+ tree = tree->cdr;
+ }
+ }
+}
+
+static void
+raise_error(codegen_scope *s, const char *msg)
+{
+ int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
+
+ genop(s, MKOP_ABx(OP_ERR, 1, idx));
+}
+
+static double
+readint_float(codegen_scope *s, const char *p, int base)
+{
+ const char *e = p + strlen(p);
+ double f = 0;
+ int n;
+
+ if (*p == '+') p++;
+ while (p < e) {
+ char c = *p;
+ c = tolower((unsigned char)c);
+ for (n=0; n<base; n++) {
+ if (mrb_digitmap[n] == c) {
+ f *= base;
+ f += n;
+ break;
+ }
+ }
+ if (n == base) {
+ codegen_error(s, "malformed readint input");
+ }
+ p++;
+ }
+ return f;
+}
+
+static mrb_int
+readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
+{
+ const char *e = p + strlen(p);
+ mrb_int result = 0;
+ int n;
+
+ mrb_assert(base >= 2 && base <= 36);
+ if (*p == '+') p++;
+ while (p < e) {
+ char c = *p;
+ c = tolower((unsigned char)c);
+ for (n=0; n<base; n++) {
+ if (mrb_digitmap[n] == c) {
+ break;
+ }
+ }
+ if (n == base) {
+ codegen_error(s, "malformed readint input");
+ }
+
+ if (neg) {
+ if ((MRB_INT_MIN + n)/base > result) {
+ *overflow = TRUE;
+ return 0;
+ }
+ result *= base;
+ result -= n;
+ }
+ else {
+ if ((MRB_INT_MAX - n)/base < result) {
+ *overflow = TRUE;
+ return 0;
+ }
+ result *= base;
+ result += n;
+ }
+ p++;
+ }
+ *overflow = FALSE;
+ return result;
+}
+
+static void
+gen_retval(codegen_scope *s, node *tree)
+{
+ if (nint(tree->car) == NODE_SPLAT) {
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
+ push();
+ codegen(s, tree, VAL);
+ pop(); pop();
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ codegen(s, tree, VAL);
+ pop();
+ }
+}
+
+static void
+codegen(codegen_scope *s, node *tree, int val)
+{
+ int nt;
+ int rlev = s->rlev;
+
+ if (!tree) {
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ return;
+ }
+
+ s->rlev++;
+ if (s->rlev > MRB_CODEGEN_LEVEL_MAX) {
+ codegen_error(s, "too complex expression");
+ }
+ if (s->irep && s->filename_index != tree->filename_index) {
+ s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
+ mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
+ s->debug_start_pos = s->pc;
+ s->filename_index = tree->filename_index;
+ s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
+ }
+
+ nt = nint(tree->car);
+ s->lineno = tree->lineno;
+ tree = tree->cdr;
+ switch (nt) {
+ case NODE_BEGIN:
+ if (val && !tree) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ while (tree) {
+ codegen(s, tree->car, tree->cdr ? NOVAL : val);
+ tree = tree->cdr;
+ }
+ break;
+
+ case NODE_RESCUE:
+ {
+ int onerr, noexc, exend, pos1, pos2, tmp;
+ struct loopinfo *lp;
+
+ if (tree->car == NULL) goto exit;
+ onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+ lp = loop_push(s, LOOP_BEGIN);
+ lp->pc1 = onerr;
+ codegen(s, tree->car, VAL);
+ pop();
+ lp->type = LOOP_RESCUE;
+ noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+ dispatch(s, onerr);
+ tree = tree->cdr;
+ exend = 0;
+ pos1 = 0;
+ if (tree->car) {
+ node *n2 = tree->car;
+ int exc = cursp();
+
+ genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+ push();
+ while (n2) {
+ node *n3 = n2->car;
+ node *n4 = n3->car;
+
+ if (pos1) dispatch(s, pos1);
+ pos2 = 0;
+ do {
+ if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
+ codegen(s, n4->car, VAL);
+ genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+ push_n(2); pop_n(2); /* space for one arg and a block */
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ }
+ else {
+ if (n4) {
+ codegen(s, n4->car, VAL);
+ }
+ else {
+ genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
+ push();
+ }
+ pop();
+ genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
+ }
+ tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ pos2 = tmp;
+ if (n4) {
+ n4 = n4->cdr;
+ }
+ } while (n4);
+ pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch_linked(s, pos2);
+
+ pop();
+ if (n3->cdr->car) {
+ gen_assignment(s, n3->cdr->car, exc, NOVAL);
+ }
+ if (n3->cdr->cdr->car) {
+ codegen(s, n3->cdr->cdr->car, val);
+ if (val) pop();
+ }
+ tmp = genop(s, MKOP_sBx(OP_JMP, exend));
+ exend = tmp;
+ n2 = n2->cdr;
+ push();
+ }
+ if (pos1) {
+ dispatch(s, pos1);
+ genop(s, MKOP_A(OP_RAISE, exc));
+ }
+ }
+ pop();
+ tree = tree->cdr;
+ dispatch(s, noexc);
+ genop(s, MKOP_A(OP_POPERR, 1));
+ if (tree->car) {
+ codegen(s, tree->car, val);
+ }
+ else if (val) {
+ push();
+ }
+ dispatch_linked(s, exend);
+ loop_pop(s, NOVAL);
+ }
+ break;
+
+ case NODE_ENSURE:
+ if (!tree->cdr || !tree->cdr->cdr ||
+ (nint(tree->cdr->cdr->car) == NODE_BEGIN &&
+ tree->cdr->cdr->cdr)) {
+ int idx;
+ int epush = s->pc;
+
+ genop(s, MKOP_Bx(OP_EPUSH, 0));
+ s->ensure_level++;
+ codegen(s, tree->car, val);
+ idx = scope_body(s, tree->cdr, NOVAL);
+ s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
+ s->ensure_level--;
+ genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+ }
+ else { /* empty ensure ignored */
+ codegen(s, tree->car, val);
+ }
+ break;
+
+ case NODE_LAMBDA:
+ if (val) {
+ int idx = lambda_body(s, tree, 1);
+
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+ push();
+ }
+ break;
+
+ case NODE_BLOCK:
+ if (val) {
+ int idx = lambda_body(s, tree, 1);
+
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+ push();
+ }
+ break;
+
+ case NODE_IF:
+ {
+ int pos1, pos2;
+ node *e = tree->cdr->cdr->car;
+
+ if (!tree->car) {
+ codegen(s, e, val);
+ goto exit;
+ }
+ switch (nint(tree->car->car)) {
+ case NODE_TRUE:
+ case NODE_INT:
+ case NODE_STR:
+ codegen(s, tree->cdr->car, val);
+ goto exit;
+ case NODE_FALSE:
+ case NODE_NIL:
+ codegen(s, e, val);
+ goto exit;
+ }
+ codegen(s, tree->car, VAL);
+ pop();
+ pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
+
+ codegen(s, tree->cdr->car, val);
+ if (e) {
+ if (val) pop();
+ pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch(s, pos1);
+ codegen(s, e, val);
+ dispatch(s, pos2);
+ }
+ else {
+ if (val) {
+ pop();
+ pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch(s, pos1);
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ dispatch(s, pos2);
+ push();
+ }
+ else {
+ dispatch(s, pos1);
+ }
+ }
+ }
+ break;
+
+ case NODE_AND:
+ {
+ int pos;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+ codegen(s, tree->cdr, val);
+ dispatch(s, pos);
+ }
+ break;
+
+ case NODE_OR:
+ {
+ int pos;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ codegen(s, tree->cdr, val);
+ dispatch(s, pos);
+ }
+ break;
+
+ case NODE_WHILE:
+ {
+ struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
+
+ lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc2 = new_label(s);
+ codegen(s, tree->cdr, NOVAL);
+ dispatch(s, lp->pc1);
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
+
+ loop_pop(s, val);
+ }
+ break;
+
+ case NODE_UNTIL:
+ {
+ struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
+
+ lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc2 = new_label(s);
+ codegen(s, tree->cdr, NOVAL);
+ dispatch(s, lp->pc1);
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
+
+ loop_pop(s, val);
+ }
+ break;
+
+ case NODE_FOR:
+ for_body(s, tree);
+ if (val) push();
+ break;
+
+ case NODE_CASE:
+ {
+ int head = 0;
+ int pos1, pos2, pos3, tmp;
+ node *n;
+
+ pos3 = 0;
+ if (tree->car) {
+ head = cursp();
+ codegen(s, tree->car, VAL);
+ }
+ tree = tree->cdr;
+ while (tree) {
+ n = tree->car->car;
+ pos1 = pos2 = 0;
+ while (n) {
+ codegen(s, n->car, VAL);
+ if (head) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), head));
+ push_n(2); pop_n(2); /* space for one arg and a block */
+ pop();
+ if (nint(n->car->car) == NODE_SPLAT) {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
+ }
+ }
+ else {
+ pop();
+ }
+ tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ pos2 = tmp;
+ n = n->cdr;
+ }
+ if (tree->car->car) {
+ pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch_linked(s, pos2);
+ }
+ codegen(s, tree->car->cdr, val);
+ if (val) pop();
+ tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
+ pos3 = tmp;
+ if (pos1) dispatch(s, pos1);
+ tree = tree->cdr;
+ }
+ if (val) {
+ int pos = cursp();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ if (pos3) dispatch_linked(s, pos3);
+ if (head) pop();
+ if (cursp() != pos) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ }
+ push();
+ }
+ else {
+ if (pos3) {
+ dispatch_linked(s, pos3);
+ }
+ if (head) {
+ pop();
+ }
+ }
+ }
+ break;
+
+ case NODE_SCOPE:
+ scope_body(s, tree, NOVAL);
+ break;
+
+ case NODE_FCALL:
+ case NODE_CALL:
+ gen_call(s, tree, 0, 0, val, 0);
+ break;
+ case NODE_SCALL:
+ gen_call(s, tree, 0, 0, val, 1);
+ break;
+
+ case NODE_DOT2:
+ codegen(s, tree->car, val);
+ codegen(s, tree->cdr, val);
+ if (val) {
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
+ push();
+ }
+ break;
+
+ case NODE_DOT3:
+ codegen(s, tree->car, val);
+ codegen(s, tree->cdr, val);
+ if (val) {
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
+ push();
+ }
+ break;
+
+ case NODE_COLON2:
+ {
+ int sym = new_sym(s, nsym(tree->cdr));
+
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ if (val) push();
+ }
+ break;
+
+ case NODE_COLON3:
+ {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ if (val) push();
+ }
+ break;
+
+ case NODE_ARRAY:
+ {
+ int n;
+
+ n = gen_values(s, tree, val, 0);
+ if (n >= 0) {
+ if (val) {
+ pop_n(n);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ push();
+ }
+ }
+ else if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_HASH:
+ {
+ int len = 0;
+ mrb_bool update = FALSE;
+
+ while (tree) {
+ codegen(s, tree->car->car, val);
+ codegen(s, tree->car->cdr, val);
+ len++;
+ tree = tree->cdr;
+ if (val && len == 126) {
+ pop_n(len*2);
+ genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
+ if (update) {
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ }
+ push();
+ update = TRUE;
+ len = 0;
+ }
+ }
+ if (val) {
+ pop_n(len*2);
+ genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
+ if (update) {
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ }
+ push();
+ }
+ }
+ break;
+
+ case NODE_SPLAT:
+ codegen(s, tree, val);
+ break;
+
+ case NODE_ASGN:
+ codegen(s, tree->cdr, VAL);
+ pop();
+ gen_assignment(s, tree->car, cursp(), val);
+ break;
+
+ case NODE_MASGN:
+ {
+ int len = 0, n = 0, post = 0;
+ node *t = tree->cdr, *p;
+ int rhs = cursp();
+
+ if (nint(t->car) == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
+ /* fixed rhs */
+ t = t->cdr;
+ while (t) {
+ codegen(s, t->car, VAL);
+ len++;
+ t = t->cdr;
+ }
+ tree = tree->car;
+ if (tree->car) { /* pre */
+ t = tree->car;
+ n = 0;
+ while (t) {
+ if (n < len) {
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ n++;
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ }
+ t = t->cdr;
+ }
+ }
+ t = tree->cdr;
+ if (t) {
+ if (t->cdr) { /* post count */
+ p = t->cdr->car;
+ while (p) {
+ post++;
+ p = p->cdr;
+ }
+ }
+ if (t->car) { /* rest (len - pre - post) */
+ int rn;
+
+ if (len < post + n) {
+ rn = 0;
+ }
+ else {
+ rn = len - post - n;
+ }
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ n += rn;
+ }
+ if (t->cdr && t->cdr->car) {
+ t = t->cdr->car;
+ while (n<len) {
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ t = t->cdr;
+ n++;
+ }
+ }
+ }
+ pop_n(len);
+ if (val) {
+ genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+ push();
+ }
+ }
+ else {
+ /* variable rhs */
+ codegen(s, t, VAL);
+ gen_vmassignment(s, tree->car, rhs, val);
+ if (!val) {
+ pop();
+ }
+ }
+ }
+ break;
+
+ case NODE_OP_ASGN:
+ {
+ mrb_sym sym = nsym(tree->cdr->car);
+ mrb_int len;
+ const char *name = mrb_sym2name_len(s->mrb, sym, &len);
+ int idx, callargs = -1, vsp = -1;
+
+ if ((len == 2 && name[0] == '|' && name[1] == '|') &&
+ (nint(tree->car->car) == NODE_CONST ||
+ nint(tree->car->car) == NODE_CVAR)) {
+ int onerr, noexc, exc;
+ struct loopinfo *lp;
+
+ onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+ lp = loop_push(s, LOOP_BEGIN);
+ lp->pc1 = onerr;
+ exc = cursp();
+ codegen(s, tree->car, VAL);
+ lp->type = LOOP_RESCUE;
+ genop(s, MKOP_A(OP_POPERR, 1));
+ noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+ dispatch(s, onerr);
+ genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+ genop(s, MKOP_A(OP_LOADF, exc));
+ dispatch(s, noexc);
+ loop_pop(s, NOVAL);
+ }
+ else if (nint(tree->car->car) == NODE_CALL) {
+ node *n = tree->car->cdr;
+ int base, i, nargs = 0;
+ callargs = 0;
+
+ if (val) {
+ vsp = cursp();
+ push();
+ }
+ codegen(s, n->car, VAL); /* receiver */
+ idx = new_msym(s, nsym(n->cdr->car));
+ base = cursp()-1;
+ if (n->cdr->cdr->car) {
+ nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
+ if (nargs >= 0) {
+ callargs = nargs;
+ }
+ else { /* varargs */
+ push();
+ nargs = 1;
+ callargs = CALL_MAXARGS;
+ }
+ }
+ /* copy receiver and arguments */
+ genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+ for (i=0; i<nargs; i++) {
+ genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+ }
+ push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ push();
+ }
+ else {
+ codegen(s, tree->car, VAL);
+ }
+ if (len == 2 &&
+ ((name[0] == '|' && name[1] == '|') ||
+ (name[0] == '&' && name[1] == '&'))) {
+ int pos;
+
+ pop();
+ if (val) {
+ if (vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
+ pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+ }
+ else {
+ pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+ }
+ codegen(s, tree->cdr->cdr->car, VAL);
+ pop();
+ if (val && vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
+ if (nint(tree->car->car) == NODE_CALL) {
+ if (callargs == CALL_MAXARGS) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ else {
+ pop_n(callargs);
+ callargs++;
+ }
+ pop();
+ idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ }
+ else {
+ gen_assignment(s, tree->car, cursp(), val);
+ }
+ dispatch(s, pos);
+ goto exit;
+ }
+ codegen(s, tree->cdr->cdr->car, VAL);
+ push(); pop();
+ pop(); pop();
+
+ idx = new_msym(s, sym);
+ if (len == 1 && name[0] == '+') {
+ genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
+ }
+ else if (len == 1 && name[0] == '-') {
+ genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
+ }
+ else if (len == 1 && name[0] == '*') {
+ genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
+ }
+ else if (len == 1 && name[0] == '/') {
+ genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
+ }
+ else if (len == 1 && name[0] == '<') {
+ genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
+ }
+ else if (len == 2 && name[0] == '<' && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
+ }
+ else if (len == 1 && name[0] == '>') {
+ genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
+ }
+ else if (len == 2 && name[0] == '>' && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
+ }
+ if (callargs < 0) {
+ gen_assignment(s, tree->car, cursp(), val);
+ }
+ else {
+ if (val && vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
+ if (callargs == CALL_MAXARGS) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ else {
+ pop_n(callargs);
+ callargs++;
+ }
+ pop();
+ idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ }
+ }
+ break;
+
+ case NODE_SUPER:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0;
+ int n = 0, noop = 0, sendv = 0;
+
+ push(); /* room for receiver */
+ while (!s2->mscope) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+ push(); push(); /* ARGARY pushes two values */
+ pop(); pop();
+ if (tree) {
+ node *args = tree->car;
+ if (args) {
+ n = gen_values(s, args, VAL, 0);
+ if (n < 0) {
+ n = noop = sendv = 1;
+ push();
+ }
+ }
+ }
+ if (tree && tree->cdr) {
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push(); pop();
+ }
+ pop_n(n+1);
+ if (sendv) n = CALL_MAXARGS;
+ genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+ if (val) push();
+ }
+ break;
+
+ case NODE_ZSUPER:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0, ainfo = 0;
+
+ push(); /* room for receiver */
+ while (!s2->mscope) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ if (s2) ainfo = s2->ainfo;
+ genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+ push(); push(); pop(); /* ARGARY pushes two values */
+ if (tree && tree->cdr) {
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
+ if (val) push();
+ }
+ break;
+
+ case NODE_RETURN:
+ if (tree) {
+ gen_retval(s, tree);
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ if (s->loop) {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+ }
+ else {
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ if (val) push();
+ break;
+
+ case NODE_YIELD:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0, ainfo = 0;
+ int n = 0, sendv = 0;
+
+ while (!s2->mscope) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ if (s2) ainfo = s2->ainfo;
+ push();
+ if (tree) {
+ n = gen_values(s, tree, VAL, 0);
+ if (n < 0) {
+ n = sendv = 1;
+ push();
+ }
+ }
+ push();pop(); /* space for a block */
+ pop_n(n+1);
+ genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+ if (sendv) n = CALL_MAXARGS;
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
+ if (val) push();
+ }
+ break;
+
+ case NODE_BREAK:
+ loop_break(s, tree);
+ if (val) push();
+ break;
+
+ case NODE_NEXT:
+ if (!s->loop) {
+ raise_error(s, "unexpected next");
+ }
+ else if (s->loop->type == LOOP_NORMAL) {
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ codegen(s, tree, NOVAL);
+ genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+ }
+ else {
+ if (tree) {
+ codegen(s, tree, VAL);
+ pop();
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ if (val) push();
+ break;
+
+ case NODE_REDO:
+ if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) {
+ raise_error(s, "unexpected redo");
+ }
+ else {
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+ }
+ if (val) push();
+ break;
+
+ case NODE_RETRY:
+ {
+ const char *msg = "unexpected retry";
+
+ if (!s->loop) {
+ raise_error(s, msg);
+ }
+ else {
+ struct loopinfo *lp = s->loop;
+ int n = 0;
+
+ while (lp && lp->type != LOOP_RESCUE) {
+ if (lp->type == LOOP_BEGIN) {
+ n++;
+ }
+ lp = lp->prev;
+ }
+ if (!lp) {
+ raise_error(s, msg);
+ }
+ else {
+ if (n > 0) {
+ genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ }
+ if (s->ensure_level > lp->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+ }
+ genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ }
+ }
+ if (val) push();
+ }
+ break;
+
+ case NODE_LVAR:
+ if (val) {
+ int idx = lv_idx(s, nsym(tree));
+
+ if (idx > 0) {
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
+ }
+ else {
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ idx = lv_idx(up, nsym(tree));
+ if (idx > 0) {
+ genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
+ break;
+ }
+ lv++;
+ up = up->prev;
+ }
+ }
+ push();
+ }
+ break;
+
+ case NODE_GVAR:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_IVAR:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_CVAR:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_CONST:
+ {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_DEFINED:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_BACK_REF:
+ if (val) {
+ char buf[3];
+ int sym;
+
+ buf[0] = '$';
+ buf[1] = nchar(tree);
+ buf[2] = 0;
+ sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_NTH_REF:
+ if (val) {
+ mrb_state *mrb = s->mrb;
+ mrb_value str;
+ int sym;
+
+ str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
+ sym = new_sym(s, mrb_intern_str(mrb, str));
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_ARG:
+ /* should not happen */
+ break;
+
+ case NODE_BLOCK_ARG:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_INT:
+ if (val) {
+ char *p = (char*)tree->car;
+ int base = nint(tree->cdr->car);
+ mrb_int i;
+ mrb_code co;
+ mrb_bool overflow;
+
+ i = readint_mrb_int(s, p, base, FALSE, &overflow);
+ if (overflow) {
+ double f = readint_float(s, p, base);
+ int off = new_lit(s, mrb_float_value(s->mrb, f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ }
+ else {
+ if (i < MAXARG_sBx && i > -MAXARG_sBx) {
+ co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ }
+ else {
+ int off = new_lit(s, mrb_fixnum_value(i));
+ co = MKOP_ABx(OP_LOADL, cursp(), off);
+ }
+ genop(s, co);
+ }
+ push();
+ }
+ break;
+
+ case NODE_FLOAT:
+ if (val) {
+ char *p = (char*)tree;
+ mrb_float f = mrb_float_read(p, NULL);
+ int off = new_lit(s, mrb_float_value(s->mrb, f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_NEGATE:
+ {
+ nt = nint(tree->car);
+ tree = tree->cdr;
+ switch (nt) {
+ case NODE_FLOAT:
+ if (val) {
+ char *p = (char*)tree;
+ mrb_float f = mrb_float_read(p, NULL);
+ int off = new_lit(s, mrb_float_value(s->mrb, -f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_INT:
+ if (val) {
+ char *p = (char*)tree->car;
+ int base = nint(tree->cdr->car);
+ mrb_int i;
+ mrb_code co;
+ mrb_bool overflow;
+
+ i = readint_mrb_int(s, p, base, TRUE, &overflow);
+ if (overflow) {
+ double f = readint_float(s, p, base);
+ int off = new_lit(s, mrb_float_value(s->mrb, -f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ }
+ else {
+ if (i < MAXARG_sBx && i > -MAXARG_sBx) {
+ co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ }
+ else {
+ int off = new_lit(s, mrb_fixnum_value(i));
+ co = MKOP_ABx(OP_LOADL, cursp(), off);
+ }
+ genop(s, co);
+ }
+ push();
+ }
+ break;
+
+ default:
+ if (val) {
+ int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
+
+ genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
+ push();
+ codegen(s, tree, VAL);
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+ }
+ else {
+ codegen(s, tree, NOVAL);
+ }
+ break;
+ }
+ }
+ break;
+
+ case NODE_STR:
+ if (val) {
+ char *p = (char*)tree->car;
+ size_t len = (intptr_t)tree->cdr;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int off = new_lit(s, mrb_str_new(s->mrb, p, len));
+
+ mrb_gc_arena_restore(s->mrb, ai);
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_HEREDOC:
+ tree = ((struct mrb_parser_heredoc_info *)tree)->doc;
+ /* fall through */
+ case NODE_DSTR:
+ if (val) {
+ node *n = tree;
+
+ if (!n) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ break;
+ }
+ codegen(s, n->car, VAL);
+ n = n->cdr;
+ while (n) {
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ n = n->cdr;
+ }
+ }
+ else {
+ node *n = tree;
+
+ while (n) {
+ if (nint(n->car->car) != NODE_STR) {
+ codegen(s, n->car, NOVAL);
+ }
+ n = n->cdr;
+ }
+ }
+ break;
+
+ case NODE_WORDS:
+ gen_literal_array(s, tree, FALSE, val);
+ break;
+
+ case NODE_SYMBOLS:
+ gen_literal_array(s, tree, TRUE, val);
+ break;
+
+ case NODE_DXSTR:
+ {
+ node *n;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
+
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ codegen(s, tree->car, VAL);
+ n = tree->cdr;
+ while (n) {
+ if (nint(n->car->car) == NODE_XSTR) {
+ n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
+ mrb_assert(!n->cdr); /* must be the end */
+ }
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ n = n->cdr;
+ }
+ push(); /* for block */
+ pop_n(3);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ if (val) push();
+ mrb_gc_arena_restore(s->mrb, ai);
+ }
+ break;
+
+ case NODE_XSTR:
+ {
+ char *p = (char*)tree->car;
+ size_t len = (intptr_t)tree->cdr;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int off = new_lit(s, mrb_str_new(s->mrb, p, len));
+ int sym;
+
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push(); push();
+ pop_n(3);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ if (val) push();
+ mrb_gc_arena_restore(s->mrb, ai);
+ }
+ break;
+
+ case NODE_REGX:
+ if (val) {
+ char *p1 = (char*)tree->car;
+ char *p2 = (char*)tree->cdr->car;
+ char *p3 = (char*)tree->cdr->cdr;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
+ int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1));
+ int argc = 1;
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ push();
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ if (p2 || p3) {
+ if (p2) { /* opt */
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ push();
+ argc++;
+ if (p3) { /* enc */
+ off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ argc++;
+ }
+ }
+ push(); /* space for a block */
+ pop_n(argc+2);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ mrb_gc_arena_restore(s->mrb, ai);
+ push();
+ }
+ break;
+
+ case NODE_DREGX:
+ if (val) {
+ node *n = tree->car;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
+ int argc = 1;
+ int off;
+ char *p;
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ push();
+ codegen(s, n->car, VAL);
+ n = n->cdr;
+ while (n) {
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ n = n->cdr;
+ }
+ n = tree->cdr->cdr;
+ if (n->car) { /* tail */
+ p = (char*)n->car;
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
+ codegen(s, tree->car, VAL);
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ }
+ if (n->cdr->car) { /* opt */
+ char *p2 = (char*)n->cdr->car;
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ argc++;
+ }
+ if (n->cdr->cdr) { /* enc */
+ char *p2 = (char*)n->cdr->cdr;
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ argc++;
+ }
+ push(); /* space for a block */
+ pop_n(argc+2);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ mrb_gc_arena_restore(s->mrb, ai);
+ push();
+ }
+ else {
+ node *n = tree->car;
+
+ while (n) {
+ if (nint(n->car->car) != NODE_STR) {
+ codegen(s, n->car, NOVAL);
+ }
+ n = n->cdr;
+ }
+ }
+ break;
+
+ case NODE_SYM:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_DSYM:
+ codegen(s, tree, val);
+ if (val) {
+ gen_send_intern(s);
+ }
+ break;
+
+ case NODE_SELF:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_NIL:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_TRUE:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADT, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_FALSE:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADF, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_ALIAS:
+ {
+ int a = new_msym(s, nsym(tree->car));
+ int b = new_msym(s, nsym(tree->cdr));
+ int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
+ push();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push(); /* space for a block */
+ pop_n(4);
+ genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_UNDEF:
+ {
+ int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
+ int num = 0;
+ node *t = tree;
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ while (t) {
+ int symbol;
+ if (num >= CALL_MAXARGS - 1) {
+ pop_n(num);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
+ while (t) {
+ symbol = new_msym(s, nsym(t->car));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ t = t->cdr;
+ }
+ num = CALL_MAXARGS;
+ break;
+ }
+ symbol = new_msym(s, nsym(t->car));
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
+ push();
+ t = t->cdr;
+ num++;
+ }
+ push();pop(); /* space for a block */
+ pop();
+ if (num < CALL_MAXARGS) {
+ pop_n(num);
+ }
+ genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_CLASS:
+ {
+ int idx;
+
+ if (tree->car->car == (node*)0) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ else if (tree->car->car == (node*)1) {
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ push();
+ }
+ else {
+ codegen(s, tree->car->car, VAL);
+ }
+ if (tree->cdr->car) {
+ codegen(s, tree->cdr->car, VAL);
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ pop(); pop();
+ idx = new_msym(s, nsym(tree->car->cdr));
+ genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
+ idx = scope_body(s, tree->cdr->cdr->car, val);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_MODULE:
+ {
+ int idx;
+
+ if (tree->car->car == (node*)0) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ else if (tree->car->car == (node*)1) {
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ push();
+ }
+ else {
+ codegen(s, tree->car->car, VAL);
+ }
+ pop();
+ idx = new_msym(s, nsym(tree->car->cdr));
+ genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
+ idx = scope_body(s, tree->cdr->car, val);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_SCLASS:
+ {
+ int idx;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ idx = scope_body(s, tree->cdr->car, val);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_DEF:
+ {
+ int sym = new_msym(s, nsym(tree->car));
+ int idx = lambda_body(s, tree->cdr, 0);
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ push(); pop();
+ pop();
+ genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ if (val) {
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ }
+ break;
+
+ case NODE_SDEF:
+ {
+ node *recv = tree->car;
+ int sym = new_msym(s, nsym(tree->cdr->car));
+ int idx = lambda_body(s, tree->cdr->cdr, 0);
+
+ codegen(s, recv, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ push();
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ pop();
+ genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ if (val) {
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ }
+ break;
+
+ case NODE_POSTEXE:
+ codegen(s, tree, NOVAL);
+ break;
+
+ default:
+ break;
+ }
+ exit:
+ s->rlev = rlev;
+}
+
+static void
+scope_add_irep(codegen_scope *s, mrb_irep *irep)
+{
+ if (s->irep == NULL) {
+ s->irep = irep;
+ return;
+ }
+ if (s->irep->rlen == s->rcapa) {
+ s->rcapa *= 2;
+ s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa);
+ }
+ s->irep->reps[s->irep->rlen] = irep;
+ s->irep->rlen++;
+}
+
+static codegen_scope*
+scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
+{
+ static const codegen_scope codegen_scope_zero = { 0 };
+ mrb_pool *pool = mrb_pool_open(mrb);
+ codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope));
+
+ if (!p) return NULL;
+ *p = codegen_scope_zero;
+ p->mrb = mrb;
+ p->mpool = pool;
+ if (!prev) return p;
+ p->prev = prev;
+ p->ainfo = -1;
+ p->mscope = 0;
+
+ p->irep = mrb_add_irep(mrb);
+ scope_add_irep(prev, p->irep);
+
+ p->rcapa = 8;
+ p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa);
+
+ p->icapa = 1024;
+ p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
+ p->irep->iseq = NULL;
+
+ p->pcapa = 32;
+ p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
+ p->irep->plen = 0;
+
+ p->scapa = MAXMSYMLEN;
+ p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
+ p->irep->slen = 0;
+
+ p->lv = lv;
+ p->sp += node_len(lv)+1; /* add self */
+ p->nlocals = p->sp;
+ if (lv) {
+ node *n = lv;
+ size_t i = 0;
+
+ p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1));
+ for (i=0, n=lv; n; i++,n=n->cdr) {
+ p->irep->lv[i].name = lv_name(n);
+ if (lv_name(n)) {
+ p->irep->lv[i].r = lv_idx(p, lv_name(n));
+ }
+ else {
+ p->irep->lv[i].r = 0;
+ }
+ }
+ mrb_assert(i + 1 == p->nlocals);
+ }
+ p->ai = mrb_gc_arena_save(mrb);
+
+ p->filename = prev->filename;
+ if (p->filename) {
+ p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
+ }
+ p->lineno = prev->lineno;
+
+ /* debug setting */
+ p->debug_start_pos = 0;
+ if (p->filename) {
+ mrb_debug_info_alloc(mrb, p->irep);
+ p->irep->filename = p->filename;
+ p->irep->lines = p->lines;
+ }
+ else {
+ p->irep->debug_info = NULL;
+ }
+ p->parser = prev->parser;
+ p->filename_index = prev->filename_index;
+
+ p->rlev = prev->rlev+1;
+
+ return p;
+}
+
+static void
+scope_finish(codegen_scope *s)
+{
+ mrb_state *mrb = s->mrb;
+ mrb_irep *irep = s->irep;
+ size_t fname_len;
+ char *fname;
+
+ irep->flags = 0;
+ if (s->iseq) {
+ irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
+ irep->ilen = s->pc;
+ if (s->lines) {
+ irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
+ }
+ else {
+ irep->lines = 0;
+ }
+ }
+ irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen);
+ irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
+ irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
+ if (s->filename) {
+ irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
+ mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
+
+ fname_len = strlen(s->filename);
+ fname = (char*)codegen_malloc(s, fname_len + 1);
+ memcpy(fname, s->filename, fname_len);
+ fname[fname_len] = '\0';
+ irep->filename = fname;
+ irep->own_filename = TRUE;
+ }
+
+ irep->nlocals = s->nlocals;
+ irep->nregs = s->nregs;
+
+ mrb_gc_arena_restore(mrb, s->ai);
+ mrb_pool_close(s->mpool);
+}
+
+static struct loopinfo*
+loop_push(codegen_scope *s, enum looptype t)
+{
+ struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo));
+
+ p->type = t;
+ p->pc1 = p->pc2 = p->pc3 = 0;
+ p->prev = s->loop;
+ p->ensure_level = s->ensure_level;
+ p->acc = cursp();
+ s->loop = p;
+
+ return p;
+}
+
+static void
+loop_break(codegen_scope *s, node *tree)
+{
+ if (!s->loop) {
+ codegen(s, tree, NOVAL);
+ raise_error(s, "unexpected break");
+ }
+ else {
+ struct loopinfo *loop;
+ int n = 0;
+
+ if (tree) {
+ gen_retval(s, tree);
+ }
+
+ loop = s->loop;
+ while (loop) {
+ if (loop->type == LOOP_BEGIN) {
+ n++;
+ loop = loop->prev;
+ }
+ else if (loop->type == LOOP_RESCUE) {
+ loop = loop->prev;
+ }
+ else{
+ break;
+ }
+ }
+ if (!loop) {
+ raise_error(s, "unexpected break");
+ return;
+ }
+ if (n > 0) {
+ genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ }
+
+ if (loop->type == LOOP_NORMAL) {
+ int tmp;
+
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ if (tree) {
+ genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
+ }
+ tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
+ loop->pc3 = tmp;
+ }
+ else {
+ if (!tree) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
+ }
+ }
+}
+
+static void
+loop_pop(codegen_scope *s, int val)
+{
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ dispatch_linked(s, s->loop->pc3);
+ s->loop = s->loop->prev;
+ if (val) push();
+}
+
+MRB_API struct RProc*
+mrb_generate_code(mrb_state *mrb, parser_state *p)
+{
+ codegen_scope *scope = scope_new(mrb, 0, 0);
+ struct RProc *proc;
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+
+ if (!scope) {
+ return NULL;
+ }
+ scope->mrb = mrb;
+ scope->parser = p;
+ scope->filename = p->filename;
+ scope->filename_index = p->current_filename_index;
+
+ MRB_TRY(&scope->jmp) {
+ mrb->jmp = &scope->jmp;
+ /* prepare irep */
+ codegen(scope, p->tree, NOVAL);
+ proc = mrb_proc_new(mrb, scope->irep);
+ mrb_irep_decref(mrb, scope->irep);
+ mrb_pool_close(scope->mpool);
+ proc->c = NULL;
+ mrb->jmp = prev_jmp;
+ return proc;
+ }
+ MRB_CATCH(&scope->jmp) {
+ mrb_irep_decref(mrb, scope->irep);
+ mrb_pool_close(scope->mpool);
+ mrb->jmp = prev_jmp;
+ return NULL;
+ }
+ MRB_END_EXC(&scope->jmp);
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords
new file mode 100644
index 00000000..9cb86608
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/keywords
@@ -0,0 +1,50 @@
+%{
+struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
+const struct kwtable *mrb_reserved_word(const char *, unsigned int);
+static const struct kwtable *reserved_word(const char *, unsigned int);
+#define mrb_reserved_word(str, len) reserved_word(str, len)
+%}
+
+struct kwtable;
+%%
+__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END
+__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END
+__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END
+BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END
+END, {keyword_END, keyword_END}, EXPR_END
+alias, {keyword_alias, keyword_alias}, EXPR_FNAME
+and, {keyword_and, keyword_and}, EXPR_VALUE
+begin, {keyword_begin, keyword_begin}, EXPR_BEG
+break, {keyword_break, keyword_break}, EXPR_MID
+case, {keyword_case, keyword_case}, EXPR_VALUE
+class, {keyword_class, keyword_class}, EXPR_CLASS
+def, {keyword_def, keyword_def}, EXPR_FNAME
+do, {keyword_do, keyword_do}, EXPR_BEG
+else, {keyword_else, keyword_else}, EXPR_BEG
+elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE
+end, {keyword_end, keyword_end}, EXPR_END
+ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG
+false, {keyword_false, keyword_false}, EXPR_END
+for, {keyword_for, keyword_for}, EXPR_VALUE
+if, {keyword_if, modifier_if}, EXPR_VALUE
+in, {keyword_in, keyword_in}, EXPR_VALUE
+module, {keyword_module, keyword_module}, EXPR_VALUE
+next, {keyword_next, keyword_next}, EXPR_MID
+nil, {keyword_nil, keyword_nil}, EXPR_END
+not, {keyword_not, keyword_not}, EXPR_ARG
+or, {keyword_or, keyword_or}, EXPR_VALUE
+redo, {keyword_redo, keyword_redo}, EXPR_END
+rescue, {keyword_rescue, modifier_rescue}, EXPR_MID
+retry, {keyword_retry, keyword_retry}, EXPR_END
+return, {keyword_return, keyword_return}, EXPR_MID
+self, {keyword_self, keyword_self}, EXPR_END
+super, {keyword_super, keyword_super}, EXPR_ARG
+then, {keyword_then, keyword_then}, EXPR_BEG
+true, {keyword_true, keyword_true}, EXPR_END
+undef, {keyword_undef, keyword_undef}, EXPR_FNAME
+unless, {keyword_unless, modifier_unless}, EXPR_VALUE
+until, {keyword_until, modifier_until}, EXPR_VALUE
+when, {keyword_when, keyword_when}, EXPR_VALUE
+while, {keyword_while, modifier_while}, EXPR_VALUE
+yield, {keyword_yield, keyword_yield}, EXPR_ARG
+%%
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def
new file mode 100644
index 00000000..58e30296
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/lex.def
@@ -0,0 +1,212 @@
+/* ANSI-C code produced by gperf version 3.0.4 */
+/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' /home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+#line 1 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+
+struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
+const struct kwtable *mrb_reserved_word(const char *, unsigned int);
+static const struct kwtable *reserved_word(const char *, unsigned int);
+#define mrb_reserved_word(str, len) reserved_word(str, len)
+#line 8 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+struct kwtable;
+
+#define TOTAL_KEYWORDS 40
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 12
+#define MIN_HASH_VALUE 8
+#define MAX_HASH_VALUE 50
+/* maximum key range = 43, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+hash (register const char *str, register unsigned int len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 14, 51, 16, 8,
+ 11, 13, 51, 51, 51, 51, 10, 51, 13, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 11, 51, 13, 1, 26,
+ 4, 1, 8, 28, 51, 23, 51, 1, 1, 27,
+ 5, 19, 21, 51, 8, 3, 3, 11, 51, 21,
+ 24, 16, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[2]];
+ /*FALLTHROUGH*/
+ case 2:
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval + asso_values[(unsigned char)str[len - 1]];
+}
+
+#ifdef __GNUC__
+__inline
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
+__attribute__ ((__gnu_inline__))
+#endif
+#endif
+const struct kwtable *
+mrb_reserved_word (register const char *str, register unsigned int len)
+{
+ static const struct kwtable wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"break", {keyword_break, keyword_break}, EXPR_MID},
+#line 23 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"else", {keyword_else, keyword_else}, EXPR_BEG},
+#line 33 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"nil", {keyword_nil, keyword_nil}, EXPR_END},
+#line 26 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG},
+#line 25 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"end", {keyword_end, keyword_end}, EXPR_END},
+#line 42 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"then", {keyword_then, keyword_then}, EXPR_BEG},
+#line 34 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"not", {keyword_not, keyword_not}, EXPR_ARG},
+#line 27 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"false", {keyword_false, keyword_false}, EXPR_END},
+#line 40 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"self", {keyword_self, keyword_self}, EXPR_END},
+#line 24 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE},
+#line 37 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID},
+#line 43 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"true", {keyword_true, keyword_true}, EXPR_END},
+#line 46 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"until", {keyword_until, modifier_until}, EXPR_VALUE},
+#line 45 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE},
+#line 39 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"return", {keyword_return, keyword_return}, EXPR_MID},
+#line 21 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"def", {keyword_def, keyword_def}, EXPR_FNAME},
+#line 16 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"and", {keyword_and, keyword_and}, EXPR_VALUE},
+#line 22 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"do", {keyword_do, keyword_do}, EXPR_BEG},
+#line 49 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"yield", {keyword_yield, keyword_yield}, EXPR_ARG},
+#line 28 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"for", {keyword_for, keyword_for}, EXPR_VALUE},
+#line 44 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME},
+#line 35 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"or", {keyword_or, keyword_or}, EXPR_VALUE},
+#line 30 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"in", {keyword_in, keyword_in}, EXPR_VALUE},
+#line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"when", {keyword_when, keyword_when}, EXPR_VALUE},
+#line 38 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"retry", {keyword_retry, keyword_retry}, EXPR_END},
+#line 29 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"if", {keyword_if, modifier_if}, EXPR_VALUE},
+#line 19 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"case", {keyword_case, keyword_case}, EXPR_VALUE},
+#line 36 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"redo", {keyword_redo, keyword_redo}, EXPR_END},
+#line 32 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"next", {keyword_next, keyword_next}, EXPR_MID},
+#line 41 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"super", {keyword_super, keyword_super}, EXPR_ARG},
+#line 31 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"module", {keyword_module, keyword_module}, EXPR_VALUE},
+#line 17 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"begin", {keyword_begin, keyword_begin}, EXPR_BEG},
+#line 12 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END},
+#line 11 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END},
+#line 10 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
+#line 14 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"END", {keyword_END, keyword_END}, EXPR_END},
+#line 15 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME},
+#line 13 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
+ {""},
+#line 20 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"class", {keyword_class, keyword_class}, EXPR_CLASS},
+ {""}, {""},
+#line 48 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+ {"while", {keyword_while, modifier_while}, EXPR_VALUE}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register const char *s = wordlist[key].name;
+
+ if (*str == *s && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
+#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h
new file mode 100644
index 00000000..9636dd75
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/node.h
@@ -0,0 +1,118 @@
+/*
+** node.h - nodes of abstract syntax tree
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_COMPILER_NODE_H
+#define MRUBY_COMPILER_NODE_H
+
+enum node_type {
+ NODE_METHOD,
+ NODE_FBODY,
+ NODE_CFUNC,
+ NODE_SCOPE,
+ NODE_BLOCK,
+ NODE_IF,
+ NODE_CASE,
+ NODE_WHEN,
+ NODE_OPT_N,
+ NODE_WHILE,
+ NODE_UNTIL,
+ NODE_ITER,
+ NODE_FOR,
+ NODE_BREAK,
+ NODE_NEXT,
+ NODE_REDO,
+ NODE_RETRY,
+ NODE_BEGIN,
+ NODE_RESCUE,
+ NODE_ENSURE,
+ NODE_AND,
+ NODE_OR,
+ NODE_NOT,
+ NODE_MASGN,
+ NODE_ASGN,
+ NODE_CDECL,
+ NODE_CVASGN,
+ NODE_CVDECL,
+ NODE_OP_ASGN,
+ NODE_CALL,
+ NODE_SCALL,
+ NODE_FCALL,
+ NODE_VCALL,
+ NODE_SUPER,
+ NODE_ZSUPER,
+ NODE_ARRAY,
+ NODE_ZARRAY,
+ NODE_HASH,
+ NODE_RETURN,
+ NODE_YIELD,
+ NODE_LVAR,
+ NODE_DVAR,
+ NODE_GVAR,
+ NODE_IVAR,
+ NODE_CONST,
+ NODE_CVAR,
+ NODE_NTH_REF,
+ NODE_BACK_REF,
+ NODE_MATCH,
+ NODE_MATCH2,
+ NODE_MATCH3,
+ NODE_INT,
+ NODE_FLOAT,
+ NODE_NEGATE,
+ NODE_LAMBDA,
+ NODE_SYM,
+ NODE_STR,
+ NODE_DSTR,
+ NODE_XSTR,
+ NODE_DXSTR,
+ NODE_REGX,
+ NODE_DREGX,
+ NODE_DREGX_ONCE,
+ NODE_LIST,
+ NODE_ARG,
+ NODE_ARGSCAT,
+ NODE_ARGSPUSH,
+ NODE_SPLAT,
+ NODE_TO_ARY,
+ NODE_SVALUE,
+ NODE_BLOCK_ARG,
+ NODE_DEF,
+ NODE_SDEF,
+ NODE_ALIAS,
+ NODE_UNDEF,
+ NODE_CLASS,
+ NODE_MODULE,
+ NODE_SCLASS,
+ NODE_COLON2,
+ NODE_COLON3,
+ NODE_CREF,
+ NODE_DOT2,
+ NODE_DOT3,
+ NODE_FLIP2,
+ NODE_FLIP3,
+ NODE_ATTRSET,
+ NODE_SELF,
+ NODE_NIL,
+ NODE_TRUE,
+ NODE_FALSE,
+ NODE_DEFINED,
+ NODE_NEWLINE,
+ NODE_POSTEXE,
+ NODE_ALLOCA,
+ NODE_DMETHOD,
+ NODE_BMETHOD,
+ NODE_MEMO,
+ NODE_IFUNC,
+ NODE_DSYM,
+ NODE_ATTRASGN,
+ NODE_HEREDOC,
+ NODE_LITERAL_DELIM,
+ NODE_WORDS,
+ NODE_SYMBOLS,
+ NODE_LAST
+};
+
+#endif /* MRUBY_COMPILER_NODE_H */
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y
new file mode 100644
index 00000000..0e425b18
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/parse.y
@@ -0,0 +1,6652 @@
+/*
+** parse.y - mruby parser
+**
+** See Copyright Notice in mruby.h
+*/
+
+%{
+#undef PARSER_DEBUG
+#ifdef PARSER_DEBUG
+# define YYDEBUG 1
+#endif
+#define YYERROR_VERBOSE 1
+/*
+ * Force yacc to use our memory management. This is a little evil because
+ * the macros assume that "parser_state *p" is in scope
+ */
+#define YYMALLOC(n) mrb_malloc(p->mrb, (n))
+#define YYFREE(o) mrb_free(p->mrb, (o))
+#define YYSTACK_USE_ALLOCA 0
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/error.h>
+#include <mruby/throw.h>
+#include "node.h"
+
+#define YYLEX_PARAM p
+
+typedef mrb_ast_node node;
+typedef struct mrb_parser_state parser_state;
+typedef struct mrb_parser_heredoc_info parser_heredoc_info;
+
+static int yyparse(parser_state *p);
+static int yylex(void *lval, parser_state *p);
+static void yyerror(parser_state *p, const char *s);
+static void yywarn(parser_state *p, const char *s);
+static void yywarning(parser_state *p, const char *s);
+static void backref_error(parser_state *p, node *n);
+static void void_expr_error(parser_state *p, node *n);
+static void tokadd(parser_state *p, int32_t c);
+
+#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
+
+typedef unsigned int stack_type;
+
+#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1))
+#define BITSTACK_POP(stack) ((stack) = (stack) >> 1)
+#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1))
+#define BITSTACK_SET_P(stack) ((stack)&1)
+
+#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n))
+#define COND_POP() BITSTACK_POP(p->cond_stack)
+#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack)
+#define COND_P() BITSTACK_SET_P(p->cond_stack)
+
+#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n))
+#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack)
+#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack)
+#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack)
+
+#define SET_LINENO(c,n) ((c)->lineno = (n))
+#define NODE_LINENO(c,n) do {\
+ if (n) {\
+ (c)->filename_index = (n)->filename_index;\
+ (c)->lineno = (n)->lineno;\
+ }\
+} while (0)
+
+#define sym(x) ((mrb_sym)(intptr_t)(x))
+#define nsym(x) ((node*)(intptr_t)(x))
+#define nint(x) ((node*)(intptr_t)(x))
+#define intn(x) ((int)(intptr_t)(x))
+
+static inline mrb_sym
+intern_cstr_gen(parser_state *p, const char *s)
+{
+ return mrb_intern_cstr(p->mrb, s);
+}
+#define intern_cstr(s) intern_cstr_gen(p,(s))
+
+static inline mrb_sym
+intern_gen(parser_state *p, const char *s, size_t len)
+{
+ return mrb_intern(p->mrb, s, len);
+}
+#define intern(s,len) intern_gen(p,(s),(len))
+
+static inline mrb_sym
+intern_gen_c(parser_state *p, const char c)
+{
+ return mrb_intern(p->mrb, &c, 1);
+}
+#define intern_c(c) intern_gen_c(p,(c))
+
+static void
+cons_free_gen(parser_state *p, node *cons)
+{
+ cons->cdr = p->cells;
+ p->cells = cons;
+}
+#define cons_free(c) cons_free_gen(p, (c))
+
+static void*
+parser_palloc(parser_state *p, size_t size)
+{
+ void *m = mrb_pool_alloc(p->pool, size);
+
+ if (!m) {
+ MRB_THROW(p->jmp);
+ }
+ return m;
+}
+
+static node*
+cons_gen(parser_state *p, node *car, node *cdr)
+{
+ node *c;
+
+ if (p->cells) {
+ c = p->cells;
+ p->cells = p->cells->cdr;
+ }
+ else {
+ c = (node *)parser_palloc(p, sizeof(mrb_ast_node));
+ }
+
+ c->car = car;
+ c->cdr = cdr;
+ c->lineno = p->lineno;
+ c->filename_index = p->current_filename_index;
+ return c;
+}
+#define cons(a,b) cons_gen(p,(a),(b))
+
+static node*
+list1_gen(parser_state *p, node *a)
+{
+ return cons(a, 0);
+}
+#define list1(a) list1_gen(p, (a))
+
+static node*
+list2_gen(parser_state *p, node *a, node *b)
+{
+ return cons(a, cons(b,0));
+}
+#define list2(a,b) list2_gen(p, (a),(b))
+
+static node*
+list3_gen(parser_state *p, node *a, node *b, node *c)
+{
+ return cons(a, cons(b, cons(c,0)));
+}
+#define list3(a,b,c) list3_gen(p, (a),(b),(c))
+
+static node*
+list4_gen(parser_state *p, node *a, node *b, node *c, node *d)
+{
+ return cons(a, cons(b, cons(c, cons(d, 0))));
+}
+#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d))
+
+static node*
+list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e)
+{
+ return cons(a, cons(b, cons(c, cons(d, cons(e, 0)))));
+}
+#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e))
+
+static node*
+list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f)
+{
+ return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0))))));
+}
+#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f))
+
+static node*
+append_gen(parser_state *p, node *a, node *b)
+{
+ node *c = a;
+
+ if (!a) return b;
+ while (c->cdr) {
+ c = c->cdr;
+ }
+ if (b) {
+ c->cdr = b;
+ }
+ return a;
+}
+#define append(a,b) append_gen(p,(a),(b))
+#define push(a,b) append_gen(p,(a),list1(b))
+
+static char*
+parser_strndup(parser_state *p, const char *s, size_t len)
+{
+ char *b = (char *)parser_palloc(p, len+1);
+
+ memcpy(b, s, len);
+ b[len] = '\0';
+ return b;
+}
+#undef strndup
+#define strndup(s,len) parser_strndup(p, s, len)
+
+static char*
+parser_strdup(parser_state *p, const char *s)
+{
+ return parser_strndup(p, s, strlen(s));
+}
+#undef strdup
+#define strdup(s) parser_strdup(p, s)
+
+/* xxx ----------------------------- */
+
+static node*
+local_switch(parser_state *p)
+{
+ node *prev = p->locals;
+
+ p->locals = cons(0, 0);
+ return prev;
+}
+
+static void
+local_resume(parser_state *p, node *prev)
+{
+ p->locals = prev;
+}
+
+static void
+local_nest(parser_state *p)
+{
+ p->locals = cons(0, p->locals);
+}
+
+static void
+local_unnest(parser_state *p)
+{
+ if (p->locals) {
+ p->locals = p->locals->cdr;
+ }
+}
+
+static mrb_bool
+local_var_p(parser_state *p, mrb_sym sym)
+{
+ node *l = p->locals;
+
+ while (l) {
+ node *n = l->car;
+ while (n) {
+ if (sym(n->car) == sym) return TRUE;
+ n = n->cdr;
+ }
+ l = l->cdr;
+ }
+ return FALSE;
+}
+
+static void
+local_add_f(parser_state *p, mrb_sym sym)
+{
+ if (p->locals) {
+ p->locals->car = push(p->locals->car, nsym(sym));
+ }
+}
+
+static void
+local_add(parser_state *p, mrb_sym sym)
+{
+ if (!local_var_p(p, sym)) {
+ local_add_f(p, sym);
+ }
+}
+
+static node*
+locals_node(parser_state *p)
+{
+ return p->locals ? p->locals->car : NULL;
+}
+
+/* (:scope (vars..) (prog...)) */
+static node*
+new_scope(parser_state *p, node *body)
+{
+ return cons((node*)NODE_SCOPE, cons(locals_node(p), body));
+}
+
+/* (:begin prog...) */
+static node*
+new_begin(parser_state *p, node *body)
+{
+ if (body) {
+ return list2((node*)NODE_BEGIN, body);
+ }
+ return cons((node*)NODE_BEGIN, 0);
+}
+
+#define newline_node(n) (n)
+
+/* (:rescue body rescue else) */
+static node*
+new_rescue(parser_state *p, node *body, node *resq, node *els)
+{
+ return list4((node*)NODE_RESCUE, body, resq, els);
+}
+
+static node*
+new_mod_rescue(parser_state *p, node *body, node *resq)
+{
+ return new_rescue(p, body, list1(list3(0, 0, resq)), 0);
+}
+
+/* (:ensure body ensure) */
+static node*
+new_ensure(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_ENSURE, cons(a, cons(0, b)));
+}
+
+/* (:nil) */
+static node*
+new_nil(parser_state *p)
+{
+ return list1((node*)NODE_NIL);
+}
+
+/* (:true) */
+static node*
+new_true(parser_state *p)
+{
+ return list1((node*)NODE_TRUE);
+}
+
+/* (:false) */
+static node*
+new_false(parser_state *p)
+{
+ return list1((node*)NODE_FALSE);
+}
+
+/* (:alias new old) */
+static node*
+new_alias(parser_state *p, mrb_sym a, mrb_sym b)
+{
+ return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b)));
+}
+
+/* (:if cond then else) */
+static node*
+new_if(parser_state *p, node *a, node *b, node *c)
+{
+ void_expr_error(p, a);
+ return list4((node*)NODE_IF, a, b, c);
+}
+
+/* (:unless cond then else) */
+static node*
+new_unless(parser_state *p, node *a, node *b, node *c)
+{
+ void_expr_error(p, a);
+ return list4((node*)NODE_IF, a, c, b);
+}
+
+/* (:while cond body) */
+static node*
+new_while(parser_state *p, node *a, node *b)
+{
+ void_expr_error(p, a);
+ return cons((node*)NODE_WHILE, cons(a, b));
+}
+
+/* (:until cond body) */
+static node*
+new_until(parser_state *p, node *a, node *b)
+{
+ void_expr_error(p, a);
+ return cons((node*)NODE_UNTIL, cons(a, b));
+}
+
+/* (:for var obj body) */
+static node*
+new_for(parser_state *p, node *v, node *o, node *b)
+{
+ void_expr_error(p, o);
+ return list4((node*)NODE_FOR, v, o, b);
+}
+
+/* (:case a ((when ...) body) ((when...) body)) */
+static node*
+new_case(parser_state *p, node *a, node *b)
+{
+ node *n = list2((node*)NODE_CASE, a);
+ node *n2 = n;
+
+ void_expr_error(p, a);
+ while (n2->cdr) {
+ n2 = n2->cdr;
+ }
+ n2->cdr = b;
+ return n;
+}
+
+/* (:postexe a) */
+static node*
+new_postexe(parser_state *p, node *a)
+{
+ return cons((node*)NODE_POSTEXE, a);
+}
+
+/* (:self) */
+static node*
+new_self(parser_state *p)
+{
+ return list1((node*)NODE_SELF);
+}
+
+/* (:call a b c) */
+static node*
+new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass)
+{
+ node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c);
+ void_expr_error(p, a);
+ NODE_LINENO(n, a);
+ return n;
+}
+
+/* (:fcall self mid args) */
+static node*
+new_fcall(parser_state *p, mrb_sym b, node *c)
+{
+ node *n = new_self(p);
+ NODE_LINENO(n, c);
+ n = list4((node*)NODE_FCALL, n, nsym(b), c);
+ NODE_LINENO(n, c);
+ return n;
+}
+
+/* (:super . c) */
+static node*
+new_super(parser_state *p, node *c)
+{
+ return cons((node*)NODE_SUPER, c);
+}
+
+/* (:zsuper) */
+static node*
+new_zsuper(parser_state *p)
+{
+ return list1((node*)NODE_ZSUPER);
+}
+
+/* (:yield . c) */
+static node*
+new_yield(parser_state *p, node *c)
+{
+ if (c) {
+ if (c->cdr) {
+ yyerror(p, "both block arg and actual block given");
+ }
+ return cons((node*)NODE_YIELD, c->car);
+ }
+ return cons((node*)NODE_YIELD, 0);
+}
+
+/* (:return . c) */
+static node*
+new_return(parser_state *p, node *c)
+{
+ return cons((node*)NODE_RETURN, c);
+}
+
+/* (:break . c) */
+static node*
+new_break(parser_state *p, node *c)
+{
+ return cons((node*)NODE_BREAK, c);
+}
+
+/* (:next . c) */
+static node*
+new_next(parser_state *p, node *c)
+{
+ return cons((node*)NODE_NEXT, c);
+}
+
+/* (:redo) */
+static node*
+new_redo(parser_state *p)
+{
+ return list1((node*)NODE_REDO);
+}
+
+/* (:retry) */
+static node*
+new_retry(parser_state *p)
+{
+ return list1((node*)NODE_RETRY);
+}
+
+/* (:dot2 a b) */
+static node*
+new_dot2(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_DOT2, cons(a, b));
+}
+
+/* (:dot3 a b) */
+static node*
+new_dot3(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_DOT3, cons(a, b));
+}
+
+/* (:colon2 b c) */
+static node*
+new_colon2(parser_state *p, node *b, mrb_sym c)
+{
+ void_expr_error(p, b);
+ return cons((node*)NODE_COLON2, cons(b, nsym(c)));
+}
+
+/* (:colon3 . c) */
+static node*
+new_colon3(parser_state *p, mrb_sym c)
+{
+ return cons((node*)NODE_COLON3, nsym(c));
+}
+
+/* (:and a b) */
+static node*
+new_and(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_AND, cons(a, b));
+}
+
+/* (:or a b) */
+static node*
+new_or(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_OR, cons(a, b));
+}
+
+/* (:array a...) */
+static node*
+new_array(parser_state *p, node *a)
+{
+ return cons((node*)NODE_ARRAY, a);
+}
+
+/* (:splat . a) */
+static node*
+new_splat(parser_state *p, node *a)
+{
+ return cons((node*)NODE_SPLAT, a);
+}
+
+/* (:hash (k . v) (k . v)...) */
+static node*
+new_hash(parser_state *p, node *a)
+{
+ return cons((node*)NODE_HASH, a);
+}
+
+/* (:sym . a) */
+static node*
+new_sym(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_SYM, nsym(sym));
+}
+
+static mrb_sym
+new_strsym(parser_state *p, node* str)
+{
+ const char *s = (const char*)str->cdr->car;
+ size_t len = (size_t)str->cdr->cdr;
+
+ return mrb_intern(p->mrb, s, len);
+}
+
+/* (:lvar . a) */
+static node*
+new_lvar(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_LVAR, nsym(sym));
+}
+
+/* (:gvar . a) */
+static node*
+new_gvar(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_GVAR, nsym(sym));
+}
+
+/* (:ivar . a) */
+static node*
+new_ivar(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_IVAR, nsym(sym));
+}
+
+/* (:cvar . a) */
+static node*
+new_cvar(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_CVAR, nsym(sym));
+}
+
+/* (:const . a) */
+static node*
+new_const(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_CONST, nsym(sym));
+}
+
+/* (:undef a...) */
+static node*
+new_undef(parser_state *p, mrb_sym sym)
+{
+ return list2((node*)NODE_UNDEF, nsym(sym));
+}
+
+/* (:class class super body) */
+static node*
+new_class(parser_state *p, node *c, node *s, node *b)
+{
+ void_expr_error(p, s);
+ return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b));
+}
+
+/* (:sclass obj body) */
+static node*
+new_sclass(parser_state *p, node *o, node *b)
+{
+ void_expr_error(p, o);
+ return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b));
+}
+
+/* (:module module body) */
+static node*
+new_module(parser_state *p, node *m, node *b)
+{
+ return list3((node*)NODE_MODULE, m, cons(locals_node(p), b));
+}
+
+/* (:def m lv (arg . body)) */
+static node*
+new_def(parser_state *p, mrb_sym m, node *a, node *b)
+{
+ return list5((node*)NODE_DEF, nsym(m), locals_node(p), a, b);
+}
+
+/* (:sdef obj m lv (arg . body)) */
+static node*
+new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b)
+{
+ void_expr_error(p, o);
+ return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b);
+}
+
+/* (:arg . sym) */
+static node*
+new_arg(parser_state *p, mrb_sym sym)
+{
+ return cons((node*)NODE_ARG, nsym(sym));
+}
+
+/* (m o r m2 b) */
+/* m: (a b c) */
+/* o: ((a . e1) (b . e2)) */
+/* r: a */
+/* m2: (a b c) */
+/* b: a */
+static node*
+new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk)
+{
+ node *n;
+
+ n = cons(m2, nsym(blk));
+ n = cons(nsym(rest), n);
+ n = cons(opt, n);
+ return cons(m, n);
+}
+
+/* (:block_arg . a) */
+static node*
+new_block_arg(parser_state *p, node *a)
+{
+ return cons((node*)NODE_BLOCK_ARG, a);
+}
+
+/* (:block arg body) */
+static node*
+new_block(parser_state *p, node *a, node *b)
+{
+ return list4((node*)NODE_BLOCK, locals_node(p), a, b);
+}
+
+/* (:lambda arg body) */
+static node*
+new_lambda(parser_state *p, node *a, node *b)
+{
+ return list4((node*)NODE_LAMBDA, locals_node(p), a, b);
+}
+
+/* (:asgn lhs rhs) */
+static node*
+new_asgn(parser_state *p, node *a, node *b)
+{
+ void_expr_error(p, b);
+ return cons((node*)NODE_ASGN, cons(a, b));
+}
+
+/* (:masgn mlhs=(pre rest post) mrhs) */
+static node*
+new_masgn(parser_state *p, node *a, node *b)
+{
+ void_expr_error(p, b);
+ return cons((node*)NODE_MASGN, cons(a, b));
+}
+
+/* (:asgn lhs rhs) */
+static node*
+new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
+{
+ void_expr_error(p, b);
+ return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
+}
+
+/* (:int . i) */
+static node*
+new_int(parser_state *p, const char *s, int base)
+{
+ return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+}
+
+/* (:float . i) */
+static node*
+new_float(parser_state *p, const char *s)
+{
+ return cons((node*)NODE_FLOAT, (node*)strdup(s));
+}
+
+/* (:str . (s . len)) */
+static node*
+new_str(parser_state *p, const char *s, size_t len)
+{
+ return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len)));
+}
+
+/* (:dstr . a) */
+static node*
+new_dstr(parser_state *p, node *a)
+{
+ return cons((node*)NODE_DSTR, a);
+}
+
+/* (:str . (s . len)) */
+static node*
+new_xstr(parser_state *p, const char *s, int len)
+{
+ return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len)));
+}
+
+/* (:xstr . a) */
+static node*
+new_dxstr(parser_state *p, node *a)
+{
+ return cons((node*)NODE_DXSTR, a);
+}
+
+/* (:dsym . a) */
+static node*
+new_dsym(parser_state *p, node *a)
+{
+ return cons((node*)NODE_DSYM, new_dstr(p, a));
+}
+
+/* (:regx . (s . (opt . enc))) */
+static node*
+new_regx(parser_state *p, const char *p1, const char* p2, const char* p3)
+{
+ return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3)));
+}
+
+/* (:dregx . (a . b)) */
+static node*
+new_dregx(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_DREGX, cons(a, b));
+}
+
+/* (:backref . n) */
+static node*
+new_back_ref(parser_state *p, int n)
+{
+ return cons((node*)NODE_BACK_REF, nint(n));
+}
+
+/* (:nthref . n) */
+static node*
+new_nth_ref(parser_state *p, int n)
+{
+ return cons((node*)NODE_NTH_REF, nint(n));
+}
+
+/* (:heredoc . a) */
+static node*
+new_heredoc(parser_state *p)
+{
+ parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info));
+ return cons((node*)NODE_HEREDOC, (node*)inf);
+}
+
+static void
+new_bv(parser_state *p, mrb_sym id)
+{
+}
+
+static node*
+new_literal_delim(parser_state *p)
+{
+ return cons((node*)NODE_LITERAL_DELIM, 0);
+}
+
+/* (:words . a) */
+static node*
+new_words(parser_state *p, node *a)
+{
+ return cons((node*)NODE_WORDS, a);
+}
+
+/* (:symbols . a) */
+static node*
+new_symbols(parser_state *p, node *a)
+{
+ return cons((node*)NODE_SYMBOLS, a);
+}
+
+/* xxx ----------------------------- */
+
+/* (:call a op) */
+static node*
+call_uni_op(parser_state *p, node *recv, const char *m)
+{
+ void_expr_error(p, recv);
+ return new_call(p, recv, intern_cstr(m), 0, 1);
+}
+
+/* (:call a op b) */
+static node*
+call_bin_op(parser_state *p, node *recv, const char *m, node *arg1)
+{
+ return new_call(p, recv, intern_cstr(m), list1(list1(arg1)), 1);
+}
+
+static void
+args_with_block(parser_state *p, node *a, node *b)
+{
+ if (b) {
+ if (a->cdr) {
+ yyerror(p, "both block arg and actual block given");
+ }
+ a->cdr = b;
+ }
+}
+
+static void
+call_with_block(parser_state *p, node *a, node *b)
+{
+ node *n;
+
+ switch ((enum node_type)intn(a->car)) {
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ if (!a->cdr) a->cdr = cons(0, b);
+ else {
+ args_with_block(p, a->cdr, b);
+ }
+ break;
+ case NODE_CALL:
+ case NODE_FCALL:
+ case NODE_SCALL:
+ n = a->cdr->cdr->cdr;
+ if (!n->car) n->car = cons(0, b);
+ else {
+ args_with_block(p, n->car, b);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static node*
+negate_lit(parser_state *p, node *n)
+{
+ return cons((node*)NODE_NEGATE, n);
+}
+
+static node*
+cond(node *n)
+{
+ return n;
+}
+
+static node*
+ret_args(parser_state *p, node *n)
+{
+ if (n->cdr) {
+ yyerror(p, "block argument should not be given");
+ return NULL;
+ }
+ if (!n->car->cdr) return n->car->car;
+ return new_array(p, n->car);
+}
+
+static void
+assignable(parser_state *p, node *lhs)
+{
+ if (intn(lhs->car) == NODE_LVAR) {
+ local_add(p, sym(lhs->cdr));
+ }
+}
+
+static node*
+var_reference(parser_state *p, node *lhs)
+{
+ node *n;
+
+ if (intn(lhs->car) == NODE_LVAR) {
+ if (!local_var_p(p, sym(lhs->cdr))) {
+ n = new_fcall(p, sym(lhs->cdr), 0);
+ cons_free(lhs);
+ return n;
+ }
+ }
+
+ return lhs;
+}
+
+typedef enum mrb_string_type string_type;
+
+static node*
+new_strterm(parser_state *p, string_type type, int term, int paren)
+{
+ return cons(nint(type), cons((node*)0, cons(nint(paren), nint(term))));
+}
+
+static void
+end_strterm(parser_state *p)
+{
+ cons_free(p->lex_strterm->cdr->cdr);
+ cons_free(p->lex_strterm->cdr);
+ cons_free(p->lex_strterm);
+ p->lex_strterm = NULL;
+}
+
+static parser_heredoc_info *
+parsing_heredoc_inf(parser_state *p)
+{
+ node *nd = p->parsing_heredoc;
+ if (nd == NULL)
+ return NULL;
+ /* mrb_assert(nd->car->car == NODE_HEREDOC); */
+ return (parser_heredoc_info*)nd->car->cdr;
+}
+
+static void
+heredoc_treat_nextline(parser_state *p)
+{
+ if (p->heredocs_from_nextline == NULL)
+ return;
+ if (p->parsing_heredoc == NULL) {
+ node *n;
+ p->parsing_heredoc = p->heredocs_from_nextline;
+ p->lex_strterm_before_heredoc = p->lex_strterm;
+ p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0);
+ n = p->all_heredocs;
+ if (n) {
+ while (n->cdr)
+ n = n->cdr;
+ n->cdr = p->parsing_heredoc;
+ }
+ else {
+ p->all_heredocs = p->parsing_heredoc;
+ }
+ }
+ else {
+ node *n, *m;
+ m = p->heredocs_from_nextline;
+ while (m->cdr)
+ m = m->cdr;
+ n = p->all_heredocs;
+ mrb_assert(n != NULL);
+ if (n == p->parsing_heredoc) {
+ m->cdr = n;
+ p->all_heredocs = p->heredocs_from_nextline;
+ p->parsing_heredoc = p->heredocs_from_nextline;
+ }
+ else {
+ while (n->cdr != p->parsing_heredoc) {
+ n = n->cdr;
+ mrb_assert(n != NULL);
+ }
+ m->cdr = n->cdr;
+ n->cdr = p->heredocs_from_nextline;
+ p->parsing_heredoc = p->heredocs_from_nextline;
+ }
+ }
+ p->heredocs_from_nextline = NULL;
+}
+
+static void
+heredoc_end(parser_state *p)
+{
+ p->parsing_heredoc = p->parsing_heredoc->cdr;
+ if (p->parsing_heredoc == NULL) {
+ p->lstate = EXPR_BEG;
+ p->cmd_start = TRUE;
+ end_strterm(p);
+ p->lex_strterm = p->lex_strterm_before_heredoc;
+ p->lex_strterm_before_heredoc = NULL;
+ p->heredoc_end_now = TRUE;
+ }
+ else {
+ /* next heredoc */
+ p->lex_strterm->car = nint(parsing_heredoc_inf(p)->type);
+ }
+}
+#define is_strterm_type(p,str_func) (intn((p)->lex_strterm->car) & (str_func))
+
+/* xxx ----------------------------- */
+
+%}
+
+%pure-parser
+%parse-param {parser_state *p}
+%lex-param {parser_state *p}
+
+%union {
+ node *nd;
+ mrb_sym id;
+ int num;
+ stack_type stack;
+ const struct vtable *vars;
+}
+
+%token <num>
+ keyword_class
+ keyword_module
+ keyword_def
+ keyword_begin
+ keyword_if
+ keyword_unless
+ keyword_while
+ keyword_until
+ keyword_for
+
+%token
+ keyword_undef
+ keyword_rescue
+ keyword_ensure
+ keyword_end
+ keyword_then
+ keyword_elsif
+ keyword_else
+ keyword_case
+ keyword_when
+ keyword_break
+ keyword_next
+ keyword_redo
+ keyword_retry
+ keyword_in
+ keyword_do
+ keyword_do_cond
+ keyword_do_block
+ keyword_do_LAMBDA
+ keyword_return
+ keyword_yield
+ keyword_super
+ keyword_self
+ keyword_nil
+ keyword_true
+ keyword_false
+ keyword_and
+ keyword_or
+ keyword_not
+ modifier_if
+ modifier_unless
+ modifier_while
+ modifier_until
+ modifier_rescue
+ keyword_alias
+ keyword_BEGIN
+ keyword_END
+ keyword__LINE__
+ keyword__FILE__
+ keyword__ENCODING__
+
+%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
+%token <nd> tINTEGER tFLOAT tCHAR tXSTRING tREGEXP
+%token <nd> tSTRING tSTRING_PART tSTRING_MID tLABEL_END
+%token <nd> tNTH_REF tBACK_REF
+%token <num> tREGEXP_END
+
+%type <nd> singleton string string_rep string_interp xstring regexp
+%type <nd> literal numeric cpath symbol
+%type <nd> top_compstmt top_stmts top_stmt
+%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
+%type <nd> expr_value arg_rhs primary_value
+%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
+%type <nd> args call_args opt_call_args
+%type <nd> paren_args opt_paren_args variable
+%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
+%type <nd> command_asgn command_rhs mrhs superclass block_call block_command
+%type <nd> f_block_optarg f_block_opt
+%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
+%type <nd> assoc_list assocs assoc undef_list backref for_var
+%type <nd> block_param opt_block_param block_param_def f_opt
+%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body
+%type <nd> brace_block cmd_brace_block do_block lhs none f_bad_arg
+%type <nd> mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner
+%type <id> fsym sym basic_symbol operation operation2 operation3
+%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn
+%type <nd> heredoc words symbols
+%type <num> call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */
+
+%token tUPLUS /* unary+ */
+%token tUMINUS /* unary- */
+%token tPOW /* ** */
+%token tCMP /* <=> */
+%token tEQ /* == */
+%token tEQQ /* === */
+%token tNEQ /* != */
+%token tGEQ /* >= */
+%token tLEQ /* <= */
+%token tANDOP tOROP /* && and || */
+%token tMATCH tNMATCH /* =~ and !~ */
+%token tDOT2 tDOT3 /* .. and ... */
+%token tAREF tASET /* [] and []= */
+%token tLSHFT tRSHFT /* << and >> */
+%token tCOLON2 /* :: */
+%token tCOLON3 /* :: at EXPR_BEG */
+%token <id> tOP_ASGN /* +=, -= etc. */
+%token tASSOC /* => */
+%token tLPAREN /* ( */
+%token tLPAREN_ARG /* ( */
+%token tRPAREN /* ) */
+%token tLBRACK /* [ */
+%token tLBRACE /* { */
+%token tLBRACE_ARG /* { */
+%token tSTAR /* * */
+%token tAMPER /* & */
+%token tLAMBDA /* -> */
+%token tANDDOT /* &. */
+%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG
+%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG
+%token <nd> tHEREDOC_BEG /* <<, <<- */
+%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM
+%token <nd> tHD_STRING_PART tHD_STRING_MID
+
+/*
+ * precedence table
+ */
+
+%nonassoc tLOWEST
+%nonassoc tLBRACE_ARG
+
+%nonassoc modifier_if modifier_unless modifier_while modifier_until
+%left keyword_or keyword_and
+%right keyword_not
+%right '=' tOP_ASGN
+%left modifier_rescue
+%right '?' ':'
+%nonassoc tDOT2 tDOT3
+%left tOROP
+%left tANDOP
+%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
+%left '>' tGEQ '<' tLEQ
+%left '|' '^'
+%left '&'
+%left tLSHFT tRSHFT
+%left '+' '-'
+%left '*' '/' '%'
+%right tUMINUS_NUM tUMINUS
+%right tPOW
+%right '!' '~' tUPLUS
+
+%token tLAST_TOKEN
+
+%%
+program : {
+ p->lstate = EXPR_BEG;
+ if (!p->locals) p->locals = cons(0,0);
+ }
+ top_compstmt
+ {
+ p->tree = new_scope(p, $2);
+ NODE_LINENO(p->tree, $2);
+ }
+ ;
+
+top_compstmt : top_stmts opt_terms
+ {
+ $$ = $1;
+ }
+ ;
+
+top_stmts : none
+ {
+ $$ = new_begin(p, 0);
+ }
+ | top_stmt
+ {
+ $$ = new_begin(p, $1);
+ NODE_LINENO($$, $1);
+ }
+ | top_stmts terms top_stmt
+ {
+ $$ = push($1, newline_node($3));
+ }
+ | error top_stmt
+ {
+ $$ = new_begin(p, 0);
+ }
+ ;
+
+top_stmt : stmt
+ | keyword_BEGIN
+ {
+ $<nd>$ = local_switch(p);
+ }
+ '{' top_compstmt '}'
+ {
+ yyerror(p, "BEGIN not supported");
+ local_resume(p, $<nd>2);
+ $$ = 0;
+ }
+ ;
+
+bodystmt : compstmt
+ opt_rescue
+ opt_else
+ opt_ensure
+ {
+ if ($2) {
+ $$ = new_rescue(p, $1, $2, $3);
+ NODE_LINENO($$, $1);
+ }
+ else if ($3) {
+ yywarn(p, "else without rescue is useless");
+ $$ = push($1, $3);
+ }
+ else {
+ $$ = $1;
+ }
+ if ($4) {
+ if ($$) {
+ $$ = new_ensure(p, $$, $4);
+ }
+ else {
+ $$ = push($4, new_nil(p));
+ }
+ }
+ }
+ ;
+
+compstmt : stmts opt_terms
+ {
+ $$ = $1;
+ }
+ ;
+
+stmts : none
+ {
+ $$ = new_begin(p, 0);
+ }
+ | stmt
+ {
+ $$ = new_begin(p, $1);
+ NODE_LINENO($$, $1);
+ }
+ | stmts terms stmt
+ {
+ $$ = push($1, newline_node($3));
+ }
+ | error stmt
+ {
+ $$ = new_begin(p, $2);
+ }
+ ;
+
+stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
+ {
+ $$ = new_alias(p, $2, $4);
+ }
+ | keyword_undef undef_list
+ {
+ $$ = $2;
+ }
+ | stmt modifier_if expr_value
+ {
+ $$ = new_if(p, cond($3), $1, 0);
+ }
+ | stmt modifier_unless expr_value
+ {
+ $$ = new_unless(p, cond($3), $1, 0);
+ }
+ | stmt modifier_while expr_value
+ {
+ $$ = new_while(p, cond($3), $1);
+ }
+ | stmt modifier_until expr_value
+ {
+ $$ = new_until(p, cond($3), $1);
+ }
+ | stmt modifier_rescue stmt
+ {
+ $$ = new_mod_rescue(p, $1, $3);
+ }
+ | keyword_END '{' compstmt '}'
+ {
+ yyerror(p, "END not supported");
+ $$ = new_postexe(p, $3);
+ }
+ | command_asgn
+ | mlhs '=' command_call
+ {
+ $$ = new_masgn(p, $1, $3);
+ }
+ | lhs '=' mrhs
+ {
+ $$ = new_asgn(p, $1, new_array(p, $3));
+ }
+ | mlhs '=' arg
+ {
+ $$ = new_masgn(p, $1, $3);
+ }
+ | mlhs '=' mrhs
+ {
+ $$ = new_masgn(p, $1, new_array(p, $3));
+ }
+ | expr
+ ;
+
+command_asgn : lhs '=' command_rhs
+ {
+ $$ = new_asgn(p, $1, $3);
+ }
+ | var_lhs tOP_ASGN command_rhs
+ {
+ $$ = new_op_asgn(p, $1, $2, $3);
+ }
+ | primary_value '[' opt_call_args rbracket tOP_ASGN command_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
+ }
+ | primary_value call_op tIDENTIFIER tOP_ASGN command_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
+ }
+ | primary_value call_op tCONSTANT tOP_ASGN command_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call
+ {
+ yyerror(p, "constant re-assignment");
+ $$ = 0;
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
+ }
+ | backref tOP_ASGN command_rhs
+ {
+ backref_error(p, $1);
+ $$ = new_begin(p, 0);
+ }
+ ;
+
+command_rhs : command_call %prec tOP_ASGN
+ | command_call modifier_rescue stmt
+ {
+ $$ = new_mod_rescue(p, $1, $3);
+ }
+ | command_asgn
+ ;
+
+
+expr : command_call
+ | expr keyword_and expr
+ {
+ $$ = new_and(p, $1, $3);
+ }
+ | expr keyword_or expr
+ {
+ $$ = new_or(p, $1, $3);
+ }
+ | keyword_not opt_nl expr
+ {
+ $$ = call_uni_op(p, cond($3), "!");
+ }
+ | '!' command_call
+ {
+ $$ = call_uni_op(p, cond($2), "!");
+ }
+ | arg
+ ;
+
+expr_value : expr
+ {
+ if (!$1) $$ = new_nil(p);
+ else {
+ $$ = $1;
+ }
+ }
+ ;
+
+command_call : command
+ | block_command
+ ;
+
+block_command : block_call
+ | block_call call_op2 operation2 command_args
+ {
+ $$ = new_call(p, $1, $3, $4, $2);
+ }
+ ;
+
+cmd_brace_block : tLBRACE_ARG
+ {
+ local_nest(p);
+ }
+ opt_block_param
+ compstmt
+ '}'
+ {
+ $$ = new_block(p, $3, $4);
+ local_unnest(p);
+ }
+ ;
+
+command : operation command_args %prec tLOWEST
+ {
+ $$ = new_fcall(p, $1, $2);
+ }
+ | operation command_args cmd_brace_block
+ {
+ args_with_block(p, $2, $3);
+ $$ = new_fcall(p, $1, $2);
+ }
+ | primary_value call_op operation2 command_args %prec tLOWEST
+ {
+ $$ = new_call(p, $1, $3, $4, $2);
+ }
+ | primary_value call_op operation2 command_args cmd_brace_block
+ {
+ args_with_block(p, $4, $5);
+ $$ = new_call(p, $1, $3, $4, $2);
+ }
+ | primary_value tCOLON2 operation2 command_args %prec tLOWEST
+ {
+ $$ = new_call(p, $1, $3, $4, tCOLON2);
+ }
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ args_with_block(p, $4, $5);
+ $$ = new_call(p, $1, $3, $4, tCOLON2);
+ }
+ | keyword_super command_args
+ {
+ $$ = new_super(p, $2);
+ }
+ | keyword_yield command_args
+ {
+ $$ = new_yield(p, $2);
+ }
+ | keyword_return call_args
+ {
+ $$ = new_return(p, ret_args(p, $2));
+ }
+ | keyword_break call_args
+ {
+ $$ = new_break(p, ret_args(p, $2));
+ }
+ | keyword_next call_args
+ {
+ $$ = new_next(p, ret_args(p, $2));
+ }
+ ;
+
+mlhs : mlhs_basic
+ {
+ $$ = $1;
+ }
+ | tLPAREN mlhs_inner rparen
+ {
+ $$ = $2;
+ }
+ ;
+
+mlhs_inner : mlhs_basic
+ | tLPAREN mlhs_inner rparen
+ {
+ $$ = $2;
+ }
+ ;
+
+mlhs_basic : mlhs_list
+ {
+ $$ = list1($1);
+ }
+ | mlhs_list mlhs_item
+ {
+ $$ = list1(push($1,$2));
+ }
+ | mlhs_list tSTAR mlhs_node
+ {
+ $$ = list2($1, $3);
+ }
+ | mlhs_list tSTAR mlhs_node ',' mlhs_post
+ {
+ $$ = list3($1, $3, $5);
+ }
+ | mlhs_list tSTAR
+ {
+ $$ = list2($1, new_nil(p));
+ }
+ | mlhs_list tSTAR ',' mlhs_post
+ {
+ $$ = list3($1, new_nil(p), $4);
+ }
+ | tSTAR mlhs_node
+ {
+ $$ = list2(0, $2);
+ }
+ | tSTAR mlhs_node ',' mlhs_post
+ {
+ $$ = list3(0, $2, $4);
+ }
+ | tSTAR
+ {
+ $$ = list2(0, new_nil(p));
+ }
+ | tSTAR ',' mlhs_post
+ {
+ $$ = list3(0, new_nil(p), $3);
+ }
+ ;
+
+mlhs_item : mlhs_node
+ | tLPAREN mlhs_inner rparen
+ {
+ $$ = new_masgn(p, $2, NULL);
+ }
+ ;
+
+mlhs_list : mlhs_item ','
+ {
+ $$ = list1($1);
+ }
+ | mlhs_list mlhs_item ','
+ {
+ $$ = push($1, $2);
+ }
+ ;
+
+mlhs_post : mlhs_item
+ {
+ $$ = list1($1);
+ }
+ | mlhs_list mlhs_item
+ {
+ $$ = push($1, $2);
+ }
+ ;
+
+mlhs_node : variable
+ {
+ assignable(p, $1);
+ }
+ | primary_value '[' opt_call_args rbracket
+ {
+ $$ = new_call(p, $1, intern("[]",2), $3, '.');
+ }
+ | primary_value call_op tIDENTIFIER
+ {
+ $$ = new_call(p, $1, $3, 0, $2);
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ $$ = new_call(p, $1, $3, 0, tCOLON2);
+ }
+ | primary_value call_op tCONSTANT
+ {
+ $$ = new_call(p, $1, $3, 0, $2);
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ $$ = new_colon2(p, $1, $3);
+ }
+ | tCOLON3 tCONSTANT
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ $$ = new_colon3(p, $2);
+ }
+ | backref
+ {
+ backref_error(p, $1);
+ $$ = 0;
+ }
+ ;
+
+lhs : variable
+ {
+ assignable(p, $1);
+ }
+ | primary_value '[' opt_call_args rbracket
+ {
+ $$ = new_call(p, $1, intern("[]",2), $3, '.');
+ }
+ | primary_value call_op tIDENTIFIER
+ {
+ $$ = new_call(p, $1, $3, 0, $2);
+ }
+ | primary_value tCOLON2 tIDENTIFIER
+ {
+ $$ = new_call(p, $1, $3, 0, tCOLON2);
+ }
+ | primary_value call_op tCONSTANT
+ {
+ $$ = new_call(p, $1, $3, 0, $2);
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ $$ = new_colon2(p, $1, $3);
+ }
+ | tCOLON3 tCONSTANT
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "dynamic constant assignment");
+ $$ = new_colon3(p, $2);
+ }
+ | backref
+ {
+ backref_error(p, $1);
+ $$ = 0;
+ }
+ ;
+
+cname : tIDENTIFIER
+ {
+ yyerror(p, "class/module name must be CONSTANT");
+ }
+ | tCONSTANT
+ ;
+
+cpath : tCOLON3 cname
+ {
+ $$ = cons((node*)1, nsym($2));
+ }
+ | cname
+ {
+ $$ = cons((node*)0, nsym($1));
+ }
+ | primary_value tCOLON2 cname
+ {
+ void_expr_error(p, $1);
+ $$ = cons($1, nsym($3));
+ }
+ ;
+
+fname : tIDENTIFIER
+ | tCONSTANT
+ | tFID
+ | op
+ {
+ p->lstate = EXPR_ENDFN;
+ $$ = $1;
+ }
+ | reswords
+ {
+ p->lstate = EXPR_ENDFN;
+ $$ = $<id>1;
+ }
+ ;
+
+fsym : fname
+ | basic_symbol
+ ;
+
+undef_list : fsym
+ {
+ $$ = new_undef(p, $1);
+ }
+ | undef_list ',' {p->lstate = EXPR_FNAME;} fsym
+ {
+ $$ = push($1, nsym($4));
+ }
+ ;
+
+op : '|' { $$ = intern_c('|'); }
+ | '^' { $$ = intern_c('^'); }
+ | '&' { $$ = intern_c('&'); }
+ | tCMP { $$ = intern("<=>",3); }
+ | tEQ { $$ = intern("==",2); }
+ | tEQQ { $$ = intern("===",3); }
+ | tMATCH { $$ = intern("=~",2); }
+ | tNMATCH { $$ = intern("!~",2); }
+ | '>' { $$ = intern_c('>'); }
+ | tGEQ { $$ = intern(">=",2); }
+ | '<' { $$ = intern_c('<'); }
+ | tLEQ { $$ = intern("<=",2); }
+ | tNEQ { $$ = intern("!=",2); }
+ | tLSHFT { $$ = intern("<<",2); }
+ | tRSHFT { $$ = intern(">>",2); }
+ | '+' { $$ = intern_c('+'); }
+ | '-' { $$ = intern_c('-'); }
+ | '*' { $$ = intern_c('*'); }
+ | tSTAR { $$ = intern_c('*'); }
+ | '/' { $$ = intern_c('/'); }
+ | '%' { $$ = intern_c('%'); }
+ | tPOW { $$ = intern("**",2); }
+ | '!' { $$ = intern_c('!'); }
+ | '~' { $$ = intern_c('~'); }
+ | tUPLUS { $$ = intern("+@",2); }
+ | tUMINUS { $$ = intern("-@",2); }
+ | tAREF { $$ = intern("[]",2); }
+ | tASET { $$ = intern("[]=",3); }
+ | '`' { $$ = intern_c('`'); }
+ ;
+
+reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__
+ | keyword_BEGIN | keyword_END
+ | keyword_alias | keyword_and | keyword_begin
+ | keyword_break | keyword_case | keyword_class | keyword_def
+ | keyword_do | keyword_else | keyword_elsif
+ | keyword_end | keyword_ensure | keyword_false
+ | keyword_for | keyword_in | keyword_module | keyword_next
+ | keyword_nil | keyword_not | keyword_or | keyword_redo
+ | keyword_rescue | keyword_retry | keyword_return | keyword_self
+ | keyword_super | keyword_then | keyword_true | keyword_undef
+ | keyword_when | keyword_yield | keyword_if | keyword_unless
+ | keyword_while | keyword_until
+ ;
+
+arg : lhs '=' arg_rhs
+ {
+ $$ = new_asgn(p, $1, $3);
+ }
+ | var_lhs tOP_ASGN arg_rhs
+ {
+ $$ = new_op_asgn(p, $1, $2, $3);
+ }
+ | primary_value '[' opt_call_args rbracket tOP_ASGN arg_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6);
+ }
+ | primary_value call_op tIDENTIFIER tOP_ASGN arg_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
+ }
+ | primary_value call_op tCONSTANT tOP_ASGN arg_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5);
+ }
+ | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg_rhs
+ {
+ $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5);
+ }
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg_rhs
+ {
+ yyerror(p, "constant re-assignment");
+ $$ = new_begin(p, 0);
+ }
+ | tCOLON3 tCONSTANT tOP_ASGN arg_rhs
+ {
+ yyerror(p, "constant re-assignment");
+ $$ = new_begin(p, 0);
+ }
+ | backref tOP_ASGN arg_rhs
+ {
+ backref_error(p, $1);
+ $$ = new_begin(p, 0);
+ }
+ | arg tDOT2 arg
+ {
+ $$ = new_dot2(p, $1, $3);
+ }
+ | arg tDOT3 arg
+ {
+ $$ = new_dot3(p, $1, $3);
+ }
+ | arg '+' arg
+ {
+ $$ = call_bin_op(p, $1, "+", $3);
+ }
+ | arg '-' arg
+ {
+ $$ = call_bin_op(p, $1, "-", $3);
+ }
+ | arg '*' arg
+ {
+ $$ = call_bin_op(p, $1, "*", $3);
+ }
+ | arg '/' arg
+ {
+ $$ = call_bin_op(p, $1, "/", $3);
+ }
+ | arg '%' arg
+ {
+ $$ = call_bin_op(p, $1, "%", $3);
+ }
+ | arg tPOW arg
+ {
+ $$ = call_bin_op(p, $1, "**", $3);
+ }
+ | tUMINUS_NUM tINTEGER tPOW arg
+ {
+ $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@");
+ }
+ | tUMINUS_NUM tFLOAT tPOW arg
+ {
+ $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@");
+ }
+ | tUPLUS arg
+ {
+ $$ = call_uni_op(p, $2, "+@");
+ }
+ | tUMINUS arg
+ {
+ $$ = call_uni_op(p, $2, "-@");
+ }
+ | arg '|' arg
+ {
+ $$ = call_bin_op(p, $1, "|", $3);
+ }
+ | arg '^' arg
+ {
+ $$ = call_bin_op(p, $1, "^", $3);
+ }
+ | arg '&' arg
+ {
+ $$ = call_bin_op(p, $1, "&", $3);
+ }
+ | arg tCMP arg
+ {
+ $$ = call_bin_op(p, $1, "<=>", $3);
+ }
+ | arg '>' arg
+ {
+ $$ = call_bin_op(p, $1, ">", $3);
+ }
+ | arg tGEQ arg
+ {
+ $$ = call_bin_op(p, $1, ">=", $3);
+ }
+ | arg '<' arg
+ {
+ $$ = call_bin_op(p, $1, "<", $3);
+ }
+ | arg tLEQ arg
+ {
+ $$ = call_bin_op(p, $1, "<=", $3);
+ }
+ | arg tEQ arg
+ {
+ $$ = call_bin_op(p, $1, "==", $3);
+ }
+ | arg tEQQ arg
+ {
+ $$ = call_bin_op(p, $1, "===", $3);
+ }
+ | arg tNEQ arg
+ {
+ $$ = call_bin_op(p, $1, "!=", $3);
+ }
+ | arg tMATCH arg
+ {
+ $$ = call_bin_op(p, $1, "=~", $3);
+ }
+ | arg tNMATCH arg
+ {
+ $$ = call_bin_op(p, $1, "!~", $3);
+ }
+ | '!' arg
+ {
+ $$ = call_uni_op(p, cond($2), "!");
+ }
+ | '~' arg
+ {
+ $$ = call_uni_op(p, cond($2), "~");
+ }
+ | arg tLSHFT arg
+ {
+ $$ = call_bin_op(p, $1, "<<", $3);
+ }
+ | arg tRSHFT arg
+ {
+ $$ = call_bin_op(p, $1, ">>", $3);
+ }
+ | arg tANDOP arg
+ {
+ $$ = new_and(p, $1, $3);
+ }
+ | arg tOROP arg
+ {
+ $$ = new_or(p, $1, $3);
+ }
+ | arg '?' arg opt_nl ':' arg
+ {
+ $$ = new_if(p, cond($1), $3, $6);
+ }
+ | primary
+ {
+ $$ = $1;
+ }
+ ;
+
+aref_args : none
+ | args trailer
+ {
+ $$ = $1;
+ NODE_LINENO($$, $1);
+ }
+ | args comma assocs trailer
+ {
+ $$ = push($1, new_hash(p, $3));
+ }
+ | assocs trailer
+ {
+ $$ = cons(new_hash(p, $1), 0);
+ NODE_LINENO($$, $1);
+ }
+ ;
+
+arg_rhs : arg %prec tOP_ASGN
+ {
+ $$ = $1;
+ }
+ | arg modifier_rescue arg
+ {
+ void_expr_error(p, $1);
+ void_expr_error(p, $3);
+ $$ = new_mod_rescue(p, $1, $3);
+ }
+ ;
+
+paren_args : '(' opt_call_args rparen
+ {
+ $$ = $2;
+ }
+ ;
+
+opt_paren_args : none
+ | paren_args
+ ;
+
+opt_call_args : none
+ | call_args
+ | args ','
+ {
+ $$ = cons($1,0);
+ NODE_LINENO($$, $1);
+ }
+ | args comma assocs ','
+ {
+ $$ = cons(push($1, new_hash(p, $3)), 0);
+ NODE_LINENO($$, $1);
+ }
+ | assocs ','
+ {
+ $$ = cons(list1(new_hash(p, $1)), 0);
+ NODE_LINENO($$, $1);
+ }
+ ;
+
+call_args : command
+ {
+ void_expr_error(p, $1);
+ $$ = cons(list1($1), 0);
+ NODE_LINENO($$, $1);
+ }
+ | args opt_block_arg
+ {
+ $$ = cons($1, $2);
+ NODE_LINENO($$, $1);
+ }
+ | assocs opt_block_arg
+ {
+ $$ = cons(list1(new_hash(p, $1)), $2);
+ NODE_LINENO($$, $1);
+ }
+ | args comma assocs opt_block_arg
+ {
+ $$ = cons(push($1, new_hash(p, $3)), $4);
+ NODE_LINENO($$, $1);
+ }
+ | block_arg
+ {
+ $$ = cons(0, $1);
+ NODE_LINENO($$, $1);
+ }
+ ;
+
+command_args : {
+ $<stack>$ = p->cmdarg_stack;
+ CMDARG_PUSH(1);
+ }
+ call_args
+ {
+ p->cmdarg_stack = $<stack>1;
+ $$ = $2;
+ }
+ ;
+
+block_arg : tAMPER arg
+ {
+ $$ = new_block_arg(p, $2);
+ }
+ ;
+
+opt_block_arg : comma block_arg
+ {
+ $$ = $2;
+ }
+ | none
+ {
+ $$ = 0;
+ }
+ ;
+
+comma : ','
+ | ',' heredoc_bodies
+ ;
+
+args : arg
+ {
+ void_expr_error(p, $1);
+ $$ = cons($1, 0);
+ NODE_LINENO($$, $1);
+ }
+ | tSTAR arg
+ {
+ void_expr_error(p, $2);
+ $$ = cons(new_splat(p, $2), 0);
+ NODE_LINENO($$, $2);
+ }
+ | args comma arg
+ {
+ void_expr_error(p, $3);
+ $$ = push($1, $3);
+ }
+ | args comma tSTAR arg
+ {
+ void_expr_error(p, $4);
+ $$ = push($1, new_splat(p, $4));
+ }
+ ;
+
+mrhs : args comma arg
+ {
+ void_expr_error(p, $3);
+ $$ = push($1, $3);
+ }
+ | args comma tSTAR arg
+ {
+ void_expr_error(p, $4);
+ $$ = push($1, new_splat(p, $4));
+ }
+ | tSTAR arg
+ {
+ void_expr_error(p, $2);
+ $$ = list1(new_splat(p, $2));
+ }
+ ;
+
+primary : literal
+ | string
+ | xstring
+ | regexp
+ | heredoc
+ | var_ref
+ | backref
+ | tFID
+ {
+ $$ = new_fcall(p, $1, 0);
+ }
+ | keyword_begin
+ {
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ bodystmt
+ keyword_end
+ {
+ p->cmdarg_stack = $<stack>2;
+ $$ = $3;
+ }
+ | tLPAREN_ARG
+ {
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ stmt {p->lstate = EXPR_ENDARG;} rparen
+ {
+ p->cmdarg_stack = $<stack>2;
+ $$ = $3;
+ }
+ | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen
+ {
+ $$ = new_nil(p);
+ }
+ | tLPAREN compstmt ')'
+ {
+ $$ = $2;
+ }
+ | primary_value tCOLON2 tCONSTANT
+ {
+ $$ = new_colon2(p, $1, $3);
+ }
+ | tCOLON3 tCONSTANT
+ {
+ $$ = new_colon3(p, $2);
+ }
+ | tLBRACK aref_args ']'
+ {
+ $$ = new_array(p, $2);
+ NODE_LINENO($$, $2);
+ }
+ | tLBRACE assoc_list '}'
+ {
+ $$ = new_hash(p, $2);
+ NODE_LINENO($$, $2);
+ }
+ | keyword_return
+ {
+ $$ = new_return(p, 0);
+ }
+ | keyword_yield opt_paren_args
+ {
+ $$ = new_yield(p, $2);
+ }
+ | keyword_not '(' expr rparen
+ {
+ $$ = call_uni_op(p, cond($3), "!");
+ }
+ | keyword_not '(' rparen
+ {
+ $$ = call_uni_op(p, new_nil(p), "!");
+ }
+ | operation brace_block
+ {
+ $$ = new_fcall(p, $1, cons(0, $2));
+ }
+ | method_call
+ | method_call brace_block
+ {
+ call_with_block(p, $1, $2);
+ $$ = $1;
+ }
+ | tLAMBDA
+ {
+ local_nest(p);
+ $<num>$ = p->lpar_beg;
+ p->lpar_beg = ++p->paren_nest;
+ }
+ f_larglist
+ {
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ lambda_body
+ {
+ p->lpar_beg = $<num>2;
+ $$ = new_lambda(p, $3, $5);
+ local_unnest(p);
+ p->cmdarg_stack = $<stack>4;
+ CMDARG_LEXPOP();
+ }
+ | keyword_if expr_value then
+ compstmt
+ if_tail
+ keyword_end
+ {
+ $$ = new_if(p, cond($2), $4, $5);
+ SET_LINENO($$, $1);
+ }
+ | keyword_unless expr_value then
+ compstmt
+ opt_else
+ keyword_end
+ {
+ $$ = new_unless(p, cond($2), $4, $5);
+ SET_LINENO($$, $1);
+ }
+ | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();}
+ compstmt
+ keyword_end
+ {
+ $$ = new_while(p, cond($3), $6);
+ SET_LINENO($$, $1);
+ }
+ | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();}
+ compstmt
+ keyword_end
+ {
+ $$ = new_until(p, cond($3), $6);
+ SET_LINENO($$, $1);
+ }
+ | keyword_case expr_value opt_terms
+ case_body
+ keyword_end
+ {
+ $$ = new_case(p, $2, $4);
+ }
+ | keyword_case opt_terms case_body keyword_end
+ {
+ $$ = new_case(p, 0, $3);
+ }
+ | keyword_for for_var keyword_in
+ {COND_PUSH(1);}
+ expr_value do
+ {COND_POP();}
+ compstmt
+ keyword_end
+ {
+ $$ = new_for(p, $2, $5, $8);
+ SET_LINENO($$, $1);
+ }
+ | keyword_class
+ cpath superclass
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "class definition in method body");
+ $<nd>$ = local_switch(p);
+ }
+ bodystmt
+ keyword_end
+ {
+ $$ = new_class(p, $2, $3, $5);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>4);
+ }
+ | keyword_class
+ tLSHFT expr
+ {
+ $<num>$ = p->in_def;
+ p->in_def = 0;
+ }
+ term
+ {
+ $<nd>$ = cons(local_switch(p), nint(p->in_single));
+ p->in_single = 0;
+ }
+ bodystmt
+ keyword_end
+ {
+ $$ = new_sclass(p, $3, $7);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>6->car);
+ p->in_def = $<num>4;
+ p->in_single = intn($<nd>6->cdr);
+ }
+ | keyword_module
+ cpath
+ {
+ if (p->in_def || p->in_single)
+ yyerror(p, "module definition in method body");
+ $<nd>$ = local_switch(p);
+ }
+ bodystmt
+ keyword_end
+ {
+ $$ = new_module(p, $2, $4);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>3);
+ }
+ | keyword_def fname
+ {
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ {
+ p->in_def++;
+ $<nd>$ = local_switch(p);
+ }
+ f_arglist
+ bodystmt
+ keyword_end
+ {
+ $$ = new_def(p, $2, $5, $6);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>4);
+ p->in_def--;
+ p->cmdarg_stack = $<stack>3;
+ }
+ | keyword_def singleton dot_or_colon
+ {
+ p->lstate = EXPR_FNAME;
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ fname
+ {
+ p->in_single++;
+ p->lstate = EXPR_ENDFN; /* force for args */
+ $<nd>$ = local_switch(p);
+ }
+ f_arglist
+ bodystmt
+ keyword_end
+ {
+ $$ = new_sdef(p, $2, $5, $7, $8);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>6);
+ p->in_single--;
+ p->cmdarg_stack = $<stack>4;
+ }
+ | keyword_break
+ {
+ $$ = new_break(p, 0);
+ }
+ | keyword_next
+ {
+ $$ = new_next(p, 0);
+ }
+ | keyword_redo
+ {
+ $$ = new_redo(p);
+ }
+ | keyword_retry
+ {
+ $$ = new_retry(p);
+ }
+ ;
+
+primary_value : primary
+ {
+ $$ = $1;
+ if (!$$) $$ = new_nil(p);
+ }
+ ;
+
+then : term
+ | keyword_then
+ | term keyword_then
+ ;
+
+do : term
+ | keyword_do_cond
+ ;
+
+if_tail : opt_else
+ | keyword_elsif expr_value then
+ compstmt
+ if_tail
+ {
+ $$ = new_if(p, cond($2), $4, $5);
+ }
+ ;
+
+opt_else : none
+ | keyword_else compstmt
+ {
+ $$ = $2;
+ }
+ ;
+
+for_var : lhs
+ {
+ $$ = list1(list1($1));
+ }
+ | mlhs
+ ;
+
+f_marg : f_norm_arg
+ {
+ $$ = new_arg(p, $1);
+ }
+ | tLPAREN f_margs rparen
+ {
+ $$ = new_masgn(p, $2, 0);
+ }
+ ;
+
+f_marg_list : f_marg
+ {
+ $$ = list1($1);
+ }
+ | f_marg_list ',' f_marg
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+f_margs : f_marg_list
+ {
+ $$ = list3($1,0,0);
+ }
+ | f_marg_list ',' tSTAR f_norm_arg
+ {
+ $$ = list3($1, new_arg(p, $4), 0);
+ }
+ | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
+ {
+ $$ = list3($1, new_arg(p, $4), $6);
+ }
+ | f_marg_list ',' tSTAR
+ {
+ $$ = list3($1, (node*)-1, 0);
+ }
+ | f_marg_list ',' tSTAR ',' f_marg_list
+ {
+ $$ = list3($1, (node*)-1, $5);
+ }
+ | tSTAR f_norm_arg
+ {
+ $$ = list3(0, new_arg(p, $2), 0);
+ }
+ | tSTAR f_norm_arg ',' f_marg_list
+ {
+ $$ = list3(0, new_arg(p, $2), $4);
+ }
+ | tSTAR
+ {
+ $$ = list3(0, (node*)-1, 0);
+ }
+ | tSTAR ',' f_marg_list
+ {
+ $$ = list3(0, (node*)-1, $3);
+ }
+ ;
+
+block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, $5, 0, $6);
+ }
+ | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, $5, $7, $8);
+ }
+ | f_arg ',' f_block_optarg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, 0, 0, $4);
+ }
+ | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, 0, $5, $6);
+ }
+ | f_arg ',' f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, 0, $3, 0, $4);
+ }
+ | f_arg ','
+ {
+ $$ = new_args(p, $1, 0, 0, 0, 0);
+ }
+ | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, 0, $3, $5, $6);
+ }
+ | f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, 0, 0, 0, $2);
+ }
+ | f_block_optarg ',' f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, $3, 0, $4);
+ }
+ | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, $3, $5, $6);
+ }
+ | f_block_optarg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, 0, 0, $2);
+ }
+ | f_block_optarg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, 0, $3, $4);
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, 0, $1, 0, $2);
+ }
+ | f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, 0, $1, $3, $4);
+ }
+ | f_block_arg
+ {
+ $$ = new_args(p, 0, 0, 0, 0, $1);
+ }
+ ;
+
+opt_block_param : none
+ | block_param_def
+ {
+ p->cmd_start = TRUE;
+ $$ = $1;
+ }
+ ;
+
+block_param_def : '|' opt_bv_decl '|'
+ {
+ $$ = 0;
+ }
+ | tOROP
+ {
+ $$ = 0;
+ }
+ | '|' block_param opt_bv_decl '|'
+ {
+ $$ = $2;
+ }
+ ;
+
+
+opt_bv_decl : opt_nl
+ {
+ $$ = 0;
+ }
+ | opt_nl ';' bv_decls opt_nl
+ {
+ $$ = 0;
+ }
+ ;
+
+bv_decls : bvar
+ | bv_decls ',' bvar
+ ;
+
+bvar : tIDENTIFIER
+ {
+ local_add_f(p, $1);
+ new_bv(p, $1);
+ }
+ | f_bad_arg
+ ;
+
+f_larglist : '(' f_args opt_bv_decl ')'
+ {
+ $$ = $2;
+ }
+ | f_args
+ {
+ $$ = $1;
+ }
+ ;
+
+lambda_body : tLAMBEG compstmt '}'
+ {
+ $$ = $2;
+ }
+ | keyword_do_LAMBDA compstmt keyword_end
+ {
+ $$ = $2;
+ }
+ ;
+
+do_block : keyword_do_block
+ {
+ local_nest(p);
+ }
+ opt_block_param
+ compstmt
+ keyword_end
+ {
+ $$ = new_block(p,$3,$4);
+ local_unnest(p);
+ }
+ ;
+
+block_call : command do_block
+ {
+ if ($1->car == (node*)NODE_YIELD) {
+ yyerror(p, "block given to yield");
+ }
+ else {
+ call_with_block(p, $1, $2);
+ }
+ $$ = $1;
+ }
+ | block_call call_op2 operation2 opt_paren_args
+ {
+ $$ = new_call(p, $1, $3, $4, $2);
+ }
+ | block_call call_op2 operation2 opt_paren_args brace_block
+ {
+ $$ = new_call(p, $1, $3, $4, $2);
+ call_with_block(p, $$, $5);
+ }
+ | block_call call_op2 operation2 command_args do_block
+ {
+ $$ = new_call(p, $1, $3, $4, $2);
+ call_with_block(p, $$, $5);
+ }
+ ;
+
+method_call : operation paren_args
+ {
+ $$ = new_fcall(p, $1, $2);
+ }
+ | primary_value call_op operation2 opt_paren_args
+ {
+ $$ = new_call(p, $1, $3, $4, $2);
+ }
+ | primary_value tCOLON2 operation2 paren_args
+ {
+ $$ = new_call(p, $1, $3, $4, tCOLON2);
+ }
+ | primary_value tCOLON2 operation3
+ {
+ $$ = new_call(p, $1, $3, 0, tCOLON2);
+ }
+ | primary_value call_op paren_args
+ {
+ $$ = new_call(p, $1, intern("call",4), $3, $2);
+ }
+ | primary_value tCOLON2 paren_args
+ {
+ $$ = new_call(p, $1, intern("call",4), $3, tCOLON2);
+ }
+ | keyword_super paren_args
+ {
+ $$ = new_super(p, $2);
+ }
+ | keyword_super
+ {
+ $$ = new_zsuper(p);
+ }
+ | primary_value '[' opt_call_args rbracket
+ {
+ $$ = new_call(p, $1, intern("[]",2), $3, '.');
+ }
+ ;
+
+brace_block : '{'
+ {
+ local_nest(p);
+ $<num>$ = p->lineno;
+ }
+ opt_block_param
+ compstmt '}'
+ {
+ $$ = new_block(p,$3,$4);
+ SET_LINENO($$, $<num>2);
+ local_unnest(p);
+ }
+ | keyword_do
+ {
+ local_nest(p);
+ $<num>$ = p->lineno;
+ }
+ opt_block_param
+ compstmt keyword_end
+ {
+ $$ = new_block(p,$3,$4);
+ SET_LINENO($$, $<num>2);
+ local_unnest(p);
+ }
+ ;
+
+case_body : keyword_when args then
+ compstmt
+ cases
+ {
+ $$ = cons(cons($2, $4), $5);
+ }
+ ;
+
+cases : opt_else
+ {
+ if ($1) {
+ $$ = cons(cons(0, $1), 0);
+ }
+ else {
+ $$ = 0;
+ }
+ }
+ | case_body
+ ;
+
+opt_rescue : keyword_rescue exc_list exc_var then
+ compstmt
+ opt_rescue
+ {
+ $$ = list1(list3($2, $3, $5));
+ if ($6) $$ = append($$, $6);
+ }
+ | none
+ ;
+
+exc_list : arg
+ {
+ $$ = list1($1);
+ }
+ | mrhs
+ | none
+ ;
+
+exc_var : tASSOC lhs
+ {
+ $$ = $2;
+ }
+ | none
+ ;
+
+opt_ensure : keyword_ensure compstmt
+ {
+ $$ = $2;
+ }
+ | none
+ ;
+
+literal : numeric
+ | symbol
+ | words
+ | symbols
+ ;
+
+string : tCHAR
+ | tSTRING
+ | tSTRING_BEG tSTRING
+ {
+ $$ = $2;
+ }
+ | tSTRING_BEG string_rep tSTRING
+ {
+ $$ = new_dstr(p, push($2, $3));
+ }
+ ;
+
+string_rep : string_interp
+ | string_rep string_interp
+ {
+ $$ = append($1, $2);
+ }
+ ;
+
+string_interp : tSTRING_MID
+ {
+ $$ = list1($1);
+ }
+ | tSTRING_PART
+ {
+ $<nd>$ = p->lex_strterm;
+ p->lex_strterm = NULL;
+ }
+ compstmt
+ '}'
+ {
+ p->lex_strterm = $<nd>2;
+ $$ = list2($1, $3);
+ }
+ | tLITERAL_DELIM
+ {
+ $$ = list1(new_literal_delim(p));
+ }
+ | tHD_LITERAL_DELIM heredoc_bodies
+ {
+ $$ = list1(new_literal_delim(p));
+ }
+ ;
+
+xstring : tXSTRING_BEG tXSTRING
+ {
+ $$ = $2;
+ }
+ | tXSTRING_BEG string_rep tXSTRING
+ {
+ $$ = new_dxstr(p, push($2, $3));
+ }
+ ;
+
+regexp : tREGEXP_BEG tREGEXP
+ {
+ $$ = $2;
+ }
+ | tREGEXP_BEG string_rep tREGEXP
+ {
+ $$ = new_dregx(p, $2, $3);
+ }
+ ;
+
+heredoc : tHEREDOC_BEG
+ ;
+
+heredoc_bodies : heredoc_body
+ | heredoc_bodies heredoc_body
+ ;
+
+heredoc_body : tHEREDOC_END
+ {
+ parser_heredoc_info * inf = parsing_heredoc_inf(p);
+ inf->doc = push(inf->doc, new_str(p, "", 0));
+ heredoc_end(p);
+ }
+ | heredoc_string_rep tHEREDOC_END
+ {
+ heredoc_end(p);
+ }
+ ;
+
+heredoc_string_rep : heredoc_string_interp
+ | heredoc_string_rep heredoc_string_interp
+ ;
+
+heredoc_string_interp : tHD_STRING_MID
+ {
+ parser_heredoc_info * inf = parsing_heredoc_inf(p);
+ inf->doc = push(inf->doc, $1);
+ heredoc_treat_nextline(p);
+ }
+ | tHD_STRING_PART
+ {
+ $<nd>$ = p->lex_strterm;
+ p->lex_strterm = NULL;
+ }
+ compstmt
+ '}'
+ {
+ parser_heredoc_info * inf = parsing_heredoc_inf(p);
+ p->lex_strterm = $<nd>2;
+ inf->doc = push(push(inf->doc, $1), $3);
+ }
+ ;
+
+words : tWORDS_BEG tSTRING
+ {
+ $$ = new_words(p, list1($2));
+ }
+ | tWORDS_BEG string_rep tSTRING
+ {
+ $$ = new_words(p, push($2, $3));
+ }
+ ;
+
+
+symbol : basic_symbol
+ {
+ $$ = new_sym(p, $1);
+ }
+ | tSYMBEG tSTRING_BEG string_rep tSTRING
+ {
+ p->lstate = EXPR_END;
+ $$ = new_dsym(p, push($3, $4));
+ }
+ ;
+
+basic_symbol : tSYMBEG sym
+ {
+ p->lstate = EXPR_END;
+ $$ = $2;
+ }
+ ;
+
+sym : fname
+ | tIVAR
+ | tGVAR
+ | tCVAR
+ | tSTRING
+ {
+ $$ = new_strsym(p, $1);
+ }
+ | tSTRING_BEG tSTRING
+ {
+ $$ = new_strsym(p, $2);
+ }
+ ;
+
+symbols : tSYMBOLS_BEG tSTRING
+ {
+ $$ = new_symbols(p, list1($2));
+ }
+ | tSYMBOLS_BEG string_rep tSTRING
+ {
+ $$ = new_symbols(p, push($2, $3));
+ }
+ ;
+
+numeric : tINTEGER
+ | tFLOAT
+ | tUMINUS_NUM tINTEGER %prec tLOWEST
+ {
+ $$ = negate_lit(p, $2);
+ }
+ | tUMINUS_NUM tFLOAT %prec tLOWEST
+ {
+ $$ = negate_lit(p, $2);
+ }
+ ;
+
+variable : tIDENTIFIER
+ {
+ $$ = new_lvar(p, $1);
+ }
+ | tIVAR
+ {
+ $$ = new_ivar(p, $1);
+ }
+ | tGVAR
+ {
+ $$ = new_gvar(p, $1);
+ }
+ | tCVAR
+ {
+ $$ = new_cvar(p, $1);
+ }
+ | tCONSTANT
+ {
+ $$ = new_const(p, $1);
+ }
+ ;
+
+var_lhs : variable
+ {
+ assignable(p, $1);
+ }
+ ;
+
+var_ref : variable
+ {
+ $$ = var_reference(p, $1);
+ }
+ | keyword_nil
+ {
+ $$ = new_nil(p);
+ }
+ | keyword_self
+ {
+ $$ = new_self(p);
+ }
+ | keyword_true
+ {
+ $$ = new_true(p);
+ }
+ | keyword_false
+ {
+ $$ = new_false(p);
+ }
+ | keyword__FILE__
+ {
+ const char *fn = p->filename;
+ if (!fn) {
+ fn = "(null)";
+ }
+ $$ = new_str(p, fn, strlen(fn));
+ }
+ | keyword__LINE__
+ {
+ char buf[16];
+
+ snprintf(buf, sizeof(buf), "%d", p->lineno);
+ $$ = new_int(p, buf, 10);
+ }
+ | keyword__ENCODING__
+ {
+#ifdef MRB_UTF8_STRING
+ const char *enc = "UTF-8";
+#else
+ const char *enc = "ASCII-8BIT";
+#endif
+ $$ = new_str(p, enc, strlen(enc));
+ }
+ ;
+
+backref : tNTH_REF
+ | tBACK_REF
+ ;
+
+superclass : /* term */
+ {
+ $$ = 0;
+ }
+ | '<'
+ {
+ p->lstate = EXPR_BEG;
+ p->cmd_start = TRUE;
+ }
+ expr_value term
+ {
+ $$ = $3;
+ } /*
+ | error term
+ {
+ yyerrok;
+ $$ = 0;
+ } */
+ ;
+
+f_arglist : '(' f_args rparen
+ {
+ $$ = $2;
+ p->lstate = EXPR_BEG;
+ p->cmd_start = TRUE;
+ }
+ | f_args term
+ {
+ $$ = $1;
+ }
+ ;
+
+f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, $5, 0, $6);
+ }
+ | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, $5, $7, $8);
+ }
+ | f_arg ',' f_optarg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, 0, 0, $4);
+ }
+ | f_arg ',' f_optarg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, $3, 0, $5, $6);
+ }
+ | f_arg ',' f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, 0, $3, 0, $4);
+ }
+ | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, 0, $3, $5, $6);
+ }
+ | f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, $1, 0, 0, 0, $2);
+ }
+ | f_optarg ',' f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, $3, 0, $4);
+ }
+ | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, $3, $5, $6);
+ }
+ | f_optarg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, 0, 0, $2);
+ }
+ | f_optarg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, $1, 0, $3, $4);
+ }
+ | f_rest_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, 0, $1, 0, $2);
+ }
+ | f_rest_arg ',' f_arg opt_f_block_arg
+ {
+ $$ = new_args(p, 0, 0, $1, $3, $4);
+ }
+ | f_block_arg
+ {
+ $$ = new_args(p, 0, 0, 0, 0, $1);
+ }
+ | /* none */
+ {
+ local_add_f(p, 0);
+ $$ = new_args(p, 0, 0, 0, 0, 0);
+ }
+ ;
+
+f_bad_arg : tCONSTANT
+ {
+ yyerror(p, "formal argument cannot be a constant");
+ $$ = 0;
+ }
+ | tIVAR
+ {
+ yyerror(p, "formal argument cannot be an instance variable");
+ $$ = 0;
+ }
+ | tGVAR
+ {
+ yyerror(p, "formal argument cannot be a global variable");
+ $$ = 0;
+ }
+ | tCVAR
+ {
+ yyerror(p, "formal argument cannot be a class variable");
+ $$ = 0;
+ }
+ ;
+
+f_norm_arg : f_bad_arg
+ {
+ $$ = 0;
+ }
+ | tIDENTIFIER
+ {
+ local_add_f(p, $1);
+ $$ = $1;
+ }
+ ;
+
+f_arg_item : f_norm_arg
+ {
+ $$ = new_arg(p, $1);
+ }
+ | tLPAREN f_margs rparen
+ {
+ $$ = new_masgn(p, $2, 0);
+ }
+ ;
+
+f_arg : f_arg_item
+ {
+ $$ = list1($1);
+ }
+ | f_arg ',' f_arg_item
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+f_opt_asgn : tIDENTIFIER '='
+ {
+ local_add_f(p, $1);
+ $$ = $1;
+ }
+ ;
+
+f_opt : f_opt_asgn arg
+ {
+ void_expr_error(p, $2);
+ $$ = cons(nsym($1), $2);
+ }
+ ;
+
+f_block_opt : f_opt_asgn primary_value
+ {
+ void_expr_error(p, $2);
+ $$ = cons(nsym($1), $2);
+ }
+ ;
+
+f_block_optarg : f_block_opt
+ {
+ $$ = list1($1);
+ }
+ | f_block_optarg ',' f_block_opt
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+f_optarg : f_opt
+ {
+ $$ = list1($1);
+ }
+ | f_optarg ',' f_opt
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+restarg_mark : '*'
+ | tSTAR
+ ;
+
+f_rest_arg : restarg_mark tIDENTIFIER
+ {
+ local_add_f(p, $2);
+ $$ = $2;
+ }
+ | restarg_mark
+ {
+ local_add_f(p, 0);
+ $$ = -1;
+ }
+ ;
+
+blkarg_mark : '&'
+ | tAMPER
+ ;
+
+f_block_arg : blkarg_mark tIDENTIFIER
+ {
+ local_add_f(p, $2);
+ $$ = $2;
+ }
+ ;
+
+opt_f_block_arg : ',' f_block_arg
+ {
+ $$ = $2;
+ }
+ | none
+ {
+ local_add_f(p, 0);
+ $$ = 0;
+ }
+ ;
+
+singleton : var_ref
+ {
+ $$ = $1;
+ if (!$$) $$ = new_nil(p);
+ }
+ | '(' {p->lstate = EXPR_BEG;} expr rparen
+ {
+ if ($3 == 0) {
+ yyerror(p, "can't define singleton method for ().");
+ }
+ else {
+ switch ((enum node_type)intn($3->car)) {
+ case NODE_STR:
+ case NODE_DSTR:
+ case NODE_XSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_MATCH:
+ case NODE_FLOAT:
+ case NODE_ARRAY:
+ case NODE_HEREDOC:
+ yyerror(p, "can't define singleton method for literals");
+ default:
+ break;
+ }
+ }
+ $$ = $3;
+ }
+ ;
+
+assoc_list : none
+ | assocs trailer
+ {
+ $$ = $1;
+ }
+ ;
+
+assocs : assoc
+ {
+ $$ = list1($1);
+ NODE_LINENO($$, $1);
+ }
+ | assocs ',' assoc
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+assoc : arg tASSOC arg
+ {
+ void_expr_error(p, $1);
+ void_expr_error(p, $3);
+ $$ = cons($1, $3);
+ }
+ | tLABEL arg
+ {
+ void_expr_error(p, $2);
+ $$ = cons(new_sym(p, $1), $2);
+ }
+ | tLABEL_END arg
+ {
+ void_expr_error(p, $2);
+ $$ = cons(new_sym(p, new_strsym(p, $1)), $2);
+ }
+ | tSTRING_BEG tLABEL_END arg
+ {
+ void_expr_error(p, $3);
+ $$ = cons(new_sym(p, new_strsym(p, $2)), $3);
+ }
+ | tSTRING_BEG string_rep tLABEL_END arg
+ {
+ void_expr_error(p, $4);
+ $$ = cons(new_dsym(p, push($2, $3)), $4);
+ }
+ ;
+
+operation : tIDENTIFIER
+ | tCONSTANT
+ | tFID
+ ;
+
+operation2 : tIDENTIFIER
+ | tCONSTANT
+ | tFID
+ | op
+ ;
+
+operation3 : tIDENTIFIER
+ | tFID
+ | op
+ ;
+
+dot_or_colon : '.'
+ | tCOLON2
+ ;
+
+call_op : '.'
+ {
+ $$ = '.';
+ }
+ | tANDDOT
+ {
+ $$ = 0;
+ }
+ ;
+
+call_op2 : call_op
+ | tCOLON2
+ {
+ $$ = tCOLON2;
+ }
+ ;
+
+opt_terms : /* none */
+ | terms
+ ;
+
+opt_nl : /* none */
+ | nl
+ ;
+
+rparen : opt_nl ')'
+ ;
+
+rbracket : opt_nl ']'
+ ;
+
+trailer : /* none */
+ | nl
+ | comma
+ ;
+
+term : ';' {yyerrok;}
+ | nl
+ | heredoc_body
+ ;
+
+nl : '\n'
+ {
+ p->lineno++;
+ p->column = 0;
+ }
+ ;
+
+terms : term
+ | terms term
+ ;
+
+none : /* none */
+ {
+ $$ = 0;
+ }
+ ;
+%%
+#define pylval (*((YYSTYPE*)(p->ylval)))
+
+static void
+yyerror(parser_state *p, const char *s)
+{
+ char* c;
+ size_t n;
+
+ if (! p->capture_errors) {
+#ifndef MRB_DISABLE_STDIO
+ if (p->filename) {
+ fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
+ }
+ else {
+ fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
+ }
+#endif
+ }
+ else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) {
+ n = strlen(s);
+ c = (char *)parser_palloc(p, n + 1);
+ memcpy(c, s, n + 1);
+ p->error_buffer[p->nerr].message = c;
+ p->error_buffer[p->nerr].lineno = p->lineno;
+ p->error_buffer[p->nerr].column = p->column;
+ }
+ p->nerr++;
+}
+
+static void
+yyerror_i(parser_state *p, const char *fmt, int i)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), fmt, i);
+ yyerror(p, buf);
+}
+
+static void
+yywarn(parser_state *p, const char *s)
+{
+ char* c;
+ size_t n;
+
+ if (! p->capture_errors) {
+#ifndef MRB_DISABLE_STDIO
+ if (p->filename) {
+ fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
+ }
+ else {
+ fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
+ }
+#endif
+ }
+ else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) {
+ n = strlen(s);
+ c = (char *)parser_palloc(p, n + 1);
+ memcpy(c, s, n + 1);
+ p->warn_buffer[p->nwarn].message = c;
+ p->warn_buffer[p->nwarn].lineno = p->lineno;
+ p->warn_buffer[p->nwarn].column = p->column;
+ }
+ p->nwarn++;
+}
+
+static void
+yywarning(parser_state *p, const char *s)
+{
+ yywarn(p, s);
+}
+
+static void
+yywarning_s(parser_state *p, const char *fmt, const char *s)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), fmt, s);
+ yywarning(p, buf);
+}
+
+static void
+backref_error(parser_state *p, node *n)
+{
+ int c;
+
+ c = (int)(intptr_t)n->car;
+
+ if (c == NODE_NTH_REF) {
+ yyerror_i(p, "can't set variable $%" MRB_PRId, (mrb_int)(intptr_t)n->cdr);
+ }
+ else if (c == NODE_BACK_REF) {
+ yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
+ }
+ else {
+ mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
+ }
+}
+
+static void
+void_expr_error(parser_state *p, node *n)
+{
+ int c;
+
+ if (n == NULL) return;
+ c = (int)(intptr_t)n->car;
+ switch (c) {
+ case NODE_BREAK:
+ case NODE_RETURN:
+ case NODE_NEXT:
+ case NODE_REDO:
+ case NODE_RETRY:
+ yyerror(p, "void value expression");
+ break;
+ case NODE_AND:
+ case NODE_OR:
+ void_expr_error(p, n->cdr->car);
+ void_expr_error(p, n->cdr->cdr);
+ break;
+ case NODE_BEGIN:
+ if (n->cdr) {
+ while (n->cdr) {
+ n = n->cdr;
+ }
+ void_expr_error(p, n->car);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void pushback(parser_state *p, int c);
+static mrb_bool peeks(parser_state *p, const char *s);
+static mrb_bool skips(parser_state *p, const char *s);
+
+static inline int
+nextc(parser_state *p)
+{
+ int c;
+
+ if (p->pb) {
+ node *tmp;
+
+ c = (int)(intptr_t)p->pb->car;
+ tmp = p->pb;
+ p->pb = p->pb->cdr;
+ cons_free(tmp);
+ }
+ else {
+#ifndef MRB_DISABLE_STDIO
+ if (p->f) {
+ if (feof(p->f)) goto eof;
+ c = fgetc(p->f);
+ if (c == EOF) goto eof;
+ }
+ else
+#endif
+ if (!p->s || p->s >= p->send) {
+ goto eof;
+ }
+ else {
+ c = (unsigned char)*p->s++;
+ }
+ }
+ if (c >= 0) {
+ p->column++;
+ }
+ if (c == '\r') {
+ c = nextc(p);
+ if (c != '\n') {
+ pushback(p, c);
+ return '\r';
+ }
+ return c;
+ }
+ return c;
+
+ eof:
+ if (!p->cxt) return -1;
+ else {
+ if (p->cxt->partial_hook(p) < 0)
+ return -1; /* end of program(s) */
+ return -2; /* end of a file in the program files */
+ }
+}
+
+static void
+pushback(parser_state *p, int c)
+{
+ if (c >= 0) {
+ p->column--;
+ }
+ p->pb = cons((node*)(intptr_t)c, p->pb);
+}
+
+static void
+skip(parser_state *p, char term)
+{
+ int c;
+
+ for (;;) {
+ c = nextc(p);
+ if (c < 0) break;
+ if (c == term) break;
+ }
+}
+
+static int
+peekc_n(parser_state *p, int n)
+{
+ node *list = 0;
+ int c0;
+
+ do {
+ c0 = nextc(p);
+ if (c0 == -1) return c0; /* do not skip partial EOF */
+ if (c0 >= 0) --p->column;
+ list = push(list, (node*)(intptr_t)c0);
+ } while(n--);
+ if (p->pb) {
+ p->pb = append((node*)list, p->pb);
+ }
+ else {
+ p->pb = list;
+ }
+ return c0;
+}
+
+static mrb_bool
+peek_n(parser_state *p, int c, int n)
+{
+ return peekc_n(p, n) == c && c >= 0;
+}
+#define peek(p,c) peek_n((p), (c), 0)
+
+static mrb_bool
+peeks(parser_state *p, const char *s)
+{
+ size_t len = strlen(s);
+
+#ifndef MRB_DISABLE_STDIO
+ if (p->f) {
+ int n = 0;
+ while (*s) {
+ if (!peek_n(p, *s++, n++)) return FALSE;
+ }
+ return TRUE;
+ }
+ else
+#endif
+ if (p->s && p->s + len <= p->send) {
+ if (memcmp(p->s, s, len) == 0) return TRUE;
+ }
+ return FALSE;
+}
+
+static mrb_bool
+skips(parser_state *p, const char *s)
+{
+ int c;
+
+ for (;;) {
+ /* skip until first char */
+ for (;;) {
+ c = nextc(p);
+ if (c < 0) return FALSE;
+ if (c == '\n') {
+ p->lineno++;
+ p->column = 0;
+ }
+ if (c == *s) break;
+ }
+ s++;
+ if (peeks(p, s)) {
+ size_t len = strlen(s);
+
+ while (len--) {
+ if (nextc(p) == '\n') {
+ p->lineno++;
+ p->column = 0;
+ }
+ }
+ return TRUE;
+ }
+ else{
+ s--;
+ }
+ }
+ return FALSE;
+}
+
+
+static int
+newtok(parser_state *p)
+{
+ if (p->tokbuf != p->buf) {
+ mrb_free(p->mrb, p->tokbuf);
+ p->tokbuf = p->buf;
+ p->tsiz = MRB_PARSER_TOKBUF_SIZE;
+ }
+ p->tidx = 0;
+ return p->column - 1;
+}
+
+static void
+tokadd(parser_state *p, int32_t c)
+{
+ char utf8[4];
+ int i, len;
+
+ /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */
+ if (c >= 0) {
+ /* Single byte from source or non-Unicode escape */
+ utf8[0] = (char)c;
+ len = 1;
+ }
+ else {
+ /* Unicode character */
+ c = -c;
+ if (c < 0x80) {
+ utf8[0] = (char)c;
+ len = 1;
+ }
+ else if (c < 0x800) {
+ utf8[0] = (char)(0xC0 | (c >> 6));
+ utf8[1] = (char)(0x80 | (c & 0x3F));
+ len = 2;
+ }
+ else if (c < 0x10000) {
+ utf8[0] = (char)(0xE0 | (c >> 12) );
+ utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | ( c & 0x3F));
+ len = 3;
+ }
+ else {
+ utf8[0] = (char)(0xF0 | (c >> 18) );
+ utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
+ utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F));
+ utf8[3] = (char)(0x80 | ( c & 0x3F));
+ len = 4;
+ }
+ }
+ if (p->tidx+len >= p->tsiz) {
+ if (p->tsiz >= MRB_PARSER_TOKBUF_MAX) {
+ p->tidx += len;
+ return;
+ }
+ p->tsiz *= 2;
+ if (p->tokbuf == p->buf) {
+ p->tokbuf = (char*)mrb_malloc(p->mrb, p->tsiz);
+ memcpy(p->tokbuf, p->buf, MRB_PARSER_TOKBUF_SIZE);
+ }
+ else {
+ p->tokbuf = (char*)mrb_realloc(p->mrb, p->tokbuf, p->tsiz);
+ }
+ }
+ for (i = 0; i < len; i++) {
+ p->tokbuf[p->tidx++] = utf8[i];
+ }
+}
+
+static int
+toklast(parser_state *p)
+{
+ return p->tokbuf[p->tidx-1];
+}
+
+static void
+tokfix(parser_state *p)
+{
+ if (p->tidx >= MRB_PARSER_TOKBUF_MAX) {
+ p->tidx = MRB_PARSER_TOKBUF_MAX-1;
+ yyerror(p, "string too long (truncated)");
+ }
+ p->tokbuf[p->tidx] = '\0';
+}
+
+static const char*
+tok(parser_state *p)
+{
+ return p->tokbuf;
+}
+
+static int
+toklen(parser_state *p)
+{
+ return p->tidx;
+}
+
+#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG)
+#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN)
+#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS)
+#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c))
+#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG())
+#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1))
+
+static int32_t
+scan_oct(const int *start, int len, int *retlen)
+{
+ const int *s = start;
+ int32_t retval = 0;
+
+ /* mrb_assert(len <= 3) */
+ while (len-- && *s >= '0' && *s <= '7') {
+ retval <<= 3;
+ retval |= *s++ - '0';
+ }
+ *retlen = (int)(s - start);
+
+ return retval;
+}
+
+static int32_t
+scan_hex(parser_state *p, const int *start, int len, int *retlen)
+{
+ static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
+ const int *s = start;
+ uint32_t retval = 0;
+ char *tmp;
+
+ /* mrb_assert(len <= 8) */
+ while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) {
+ retval <<= 4;
+ retval |= (tmp - hexdigit) & 15;
+ s++;
+ }
+ *retlen = (int)(s - start);
+
+ return (int32_t)retval;
+}
+
+static int32_t
+read_escape_unicode(parser_state *p, int limit)
+{
+ int buf[9];
+ int i;
+ int32_t hex;
+
+ /* Look for opening brace */
+ i = 0;
+ buf[0] = nextc(p);
+ if (buf[0] < 0) {
+ eof:
+ yyerror(p, "invalid escape character syntax");
+ return -1;
+ }
+ if (ISXDIGIT(buf[0])) {
+ /* \uxxxx form */
+ for (i=1; i<limit; i++) {
+ buf[i] = nextc(p);
+ if (buf[i] < 0) goto eof;
+ if (!ISXDIGIT(buf[i])) {
+ pushback(p, buf[i]);
+ break;
+ }
+ }
+ }
+ else {
+ pushback(p, buf[0]);
+ }
+ hex = scan_hex(p, buf, i, &i);
+ if (i == 0 || hex > 0x10FFFF || (hex & 0xFFFFF800) == 0xD800) {
+ yyerror(p, "invalid Unicode code point");
+ return -1;
+ }
+ return hex;
+}
+
+/* Return negative to indicate Unicode code point */
+static int32_t
+read_escape(parser_state *p)
+{
+ int32_t c;
+
+ switch (c = nextc(p)) {
+ case '\\':/* Backslash */
+ return c;
+
+ case 'n':/* newline */
+ return '\n';
+
+ case 't':/* horizontal tab */
+ return '\t';
+
+ case 'r':/* carriage-return */
+ return '\r';
+
+ case 'f':/* form-feed */
+ return '\f';
+
+ case 'v':/* vertical tab */
+ return '\13';
+
+ case 'a':/* alarm(bell) */
+ return '\007';
+
+ case 'e':/* escape */
+ return 033;
+
+ case '0': case '1': case '2': case '3': /* octal constant */
+ case '4': case '5': case '6': case '7':
+ {
+ int buf[3];
+ int i;
+
+ buf[0] = c;
+ for (i=1; i<3; i++) {
+ buf[i] = nextc(p);
+ if (buf[i] < 0) goto eof;
+ if (buf[i] < '0' || '7' < buf[i]) {
+ pushback(p, buf[i]);
+ break;
+ }
+ }
+ c = scan_oct(buf, i, &i);
+ }
+ return c;
+
+ case 'x': /* hex constant */
+ {
+ int buf[2];
+ int i;
+
+ for (i=0; i<2; i++) {
+ buf[i] = nextc(p);
+ if (buf[i] < 0) goto eof;
+ if (!ISXDIGIT(buf[i])) {
+ pushback(p, buf[i]);
+ break;
+ }
+ }
+ if (i == 0) {
+ yyerror(p, "invalid hex escape");
+ return -1;
+ }
+ return scan_hex(p, buf, i, &i);
+ }
+
+ case 'u': /* Unicode */
+ if (peek(p, '{')) {
+ /* \u{xxxxxxxx} form */
+ nextc(p);
+ c = read_escape_unicode(p, 8);
+ if (c < 0) return 0;
+ if (nextc(p) != '}') goto eof;
+ }
+ else {
+ c = read_escape_unicode(p, 4);
+ if (c < 0) return 0;
+ }
+ return -c;
+
+ case 'b':/* backspace */
+ return '\010';
+
+ case 's':/* space */
+ return ' ';
+
+ case 'M':
+ if ((c = nextc(p)) != '-') {
+ yyerror(p, "Invalid escape character syntax");
+ pushback(p, c);
+ return '\0';
+ }
+ if ((c = nextc(p)) == '\\') {
+ return read_escape(p) | 0x80;
+ }
+ else if (c < 0) goto eof;
+ else {
+ return ((c & 0xff) | 0x80);
+ }
+
+ case 'C':
+ if ((c = nextc(p)) != '-') {
+ yyerror(p, "Invalid escape character syntax");
+ pushback(p, c);
+ return '\0';
+ }
+ case 'c':
+ if ((c = nextc(p))== '\\') {
+ c = read_escape(p);
+ }
+ else if (c == '?')
+ return 0177;
+ else if (c < 0) goto eof;
+ return c & 0x9f;
+
+ eof:
+ case -1:
+ case -2: /* end of a file */
+ yyerror(p, "Invalid escape character syntax");
+ return '\0';
+
+ default:
+ return c;
+ }
+}
+
+static int
+parse_string(parser_state *p)
+{
+ int c;
+ string_type type = (string_type)(intptr_t)p->lex_strterm->car;
+ int nest_level = intn(p->lex_strterm->cdr->car);
+ int beg = intn(p->lex_strterm->cdr->cdr->car);
+ int end = intn(p->lex_strterm->cdr->cdr->cdr);
+ parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
+ int cmd_state = p->cmd_start;
+
+ if (beg == 0) beg = -3; /* should never happen */
+ if (end == 0) end = -3;
+ newtok(p);
+ while ((c = nextc(p)) != end || nest_level != 0) {
+ if (hinf && (c == '\n' || c < 0)) {
+ mrb_bool line_head;
+ tokadd(p, '\n');
+ tokfix(p);
+ p->lineno++;
+ p->column = 0;
+ line_head = hinf->line_head;
+ hinf->line_head = TRUE;
+ if (line_head) {
+ /* check whether end of heredoc */
+ const char *s = tok(p);
+ int len = toklen(p);
+ if (hinf->allow_indent) {
+ while (ISSPACE(*s) && len > 0) {
+ ++s;
+ --len;
+ }
+ }
+ if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) {
+ if (c < 0) {
+ p->parsing_heredoc = NULL;
+ }
+ else {
+ return tHEREDOC_END;
+ }
+ }
+ }
+ if (c < 0) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term);
+ yyerror(p, buf);
+ return 0;
+ }
+ pylval.nd = new_str(p, tok(p), toklen(p));
+ return tHD_STRING_MID;
+ }
+ if (c < 0) {
+ yyerror(p, "unterminated string meets end of file");
+ return 0;
+ }
+ else if (c == beg) {
+ nest_level++;
+ p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+ }
+ else if (c == end) {
+ nest_level--;
+ p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+ }
+ else if (c == '\\') {
+ c = nextc(p);
+ if (type & STR_FUNC_EXPAND) {
+ if (c == end || c == beg) {
+ tokadd(p, c);
+ }
+ else if (c == '\n') {
+ p->lineno++;
+ p->column = 0;
+ if (type & STR_FUNC_ARRAY) {
+ tokadd(p, '\n');
+ }
+ }
+ else if (type & STR_FUNC_REGEXP) {
+ tokadd(p, '\\');
+ tokadd(p, c);
+ }
+ else if (c == 'u' && peek(p, '{')) {
+ /* \u{xxxx xxxx xxxx} form */
+ nextc(p);
+ while (1) {
+ do c = nextc(p); while (ISSPACE(c));
+ if (c == '}') break;
+ pushback(p, c);
+ c = read_escape_unicode(p, 8);
+ if (c < 0) break;
+ tokadd(p, -c);
+ }
+ if (hinf)
+ hinf->line_head = FALSE;
+ }
+ else {
+ pushback(p, c);
+ tokadd(p, read_escape(p));
+ if (hinf)
+ hinf->line_head = FALSE;
+ }
+ }
+ else {
+ if (c != beg && c != end) {
+ if (c == '\n') {
+ p->lineno++;
+ p->column = 0;
+ }
+ if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) {
+ tokadd(p, '\\');
+ }
+ }
+ tokadd(p, c);
+ }
+ continue;
+ }
+ else if ((c == '#') && (type & STR_FUNC_EXPAND)) {
+ c = nextc(p);
+ if (c == '{') {
+ tokfix(p);
+ p->lstate = EXPR_BEG;
+ p->cmd_start = TRUE;
+ pylval.nd = new_str(p, tok(p), toklen(p));
+ if (hinf) {
+ hinf->line_head = FALSE;
+ return tHD_STRING_PART;
+ }
+ return tSTRING_PART;
+ }
+ tokadd(p, '#');
+ pushback(p, c);
+ continue;
+ }
+ if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) {
+ if (toklen(p) == 0) {
+ do {
+ if (c == '\n') {
+ p->lineno++;
+ p->column = 0;
+ heredoc_treat_nextline(p);
+ if (p->parsing_heredoc != NULL) {
+ return tHD_LITERAL_DELIM;
+ }
+ }
+ c = nextc(p);
+ } while (ISSPACE(c));
+ pushback(p, c);
+ return tLITERAL_DELIM;
+ }
+ else {
+ pushback(p, c);
+ tokfix(p);
+ pylval.nd = new_str(p, tok(p), toklen(p));
+ return tSTRING_MID;
+ }
+ }
+ if (c == '\n') {
+ p->lineno++;
+ p->column = 0;
+ }
+ tokadd(p, c);
+ }
+
+ tokfix(p);
+ p->lstate = EXPR_END;
+ end_strterm(p);
+
+ if (type & STR_FUNC_XQUOTE) {
+ pylval.nd = new_xstr(p, tok(p), toklen(p));
+ return tXSTRING;
+ }
+
+ if (type & STR_FUNC_REGEXP) {
+ int f = 0;
+ int re_opt;
+ char *s = strndup(tok(p), toklen(p));
+ char flags[3];
+ char *flag = flags;
+ char enc = '\0';
+ char *encp;
+ char *dup;
+
+ newtok(p);
+ while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) {
+ switch (re_opt) {
+ case 'i': f |= 1; break;
+ case 'x': f |= 2; break;
+ case 'm': f |= 4; break;
+ case 'u': f |= 16; break;
+ case 'n': f |= 32; break;
+ default: tokadd(p, re_opt); break;
+ }
+ }
+ pushback(p, re_opt);
+ if (toklen(p)) {
+ char msg[128];
+ tokfix(p);
+ snprintf(msg, sizeof(msg), "unknown regexp option%s - %s",
+ toklen(p) > 1 ? "s" : "", tok(p));
+ yyerror(p, msg);
+ }
+ if (f != 0) {
+ if (f & 1) *flag++ = 'i';
+ if (f & 2) *flag++ = 'x';
+ if (f & 4) *flag++ = 'm';
+ if (f & 16) enc = 'u';
+ if (f & 32) enc = 'n';
+ }
+ if (flag > flags) {
+ dup = strndup(flags, (size_t)(flag - flags));
+ }
+ else {
+ dup = NULL;
+ }
+ if (enc) {
+ encp = strndup(&enc, 1);
+ }
+ else {
+ encp = NULL;
+ }
+ pylval.nd = new_regx(p, s, dup, encp);
+
+ return tREGEXP;
+ }
+ pylval.nd = new_str(p, tok(p), toklen(p));
+ if (IS_LABEL_POSSIBLE()) {
+ if (IS_LABEL_SUFFIX(0)) {
+ p->lstate = EXPR_BEG;
+ nextc(p);
+ return tLABEL_END;
+ }
+ }
+
+ return tSTRING;
+}
+
+
+static int
+heredoc_identifier(parser_state *p)
+{
+ int c;
+ int type = str_heredoc;
+ mrb_bool indent = FALSE;
+ mrb_bool quote = FALSE;
+ node *newnode;
+ parser_heredoc_info *info;
+
+ c = nextc(p);
+ if (ISSPACE(c) || c == '=') {
+ pushback(p, c);
+ return 0;
+ }
+ if (c == '-') {
+ indent = TRUE;
+ c = nextc(p);
+ }
+ if (c == '\'' || c == '"') {
+ int term = c;
+ if (c == '\'')
+ quote = TRUE;
+ newtok(p);
+ while ((c = nextc(p)) >= 0 && c != term) {
+ if (c == '\n') {
+ c = -1;
+ break;
+ }
+ tokadd(p, c);
+ }
+ if (c < 0) {
+ yyerror(p, "unterminated here document identifier");
+ return 0;
+ }
+ }
+ else {
+ if (c < 0) {
+ return 0; /* missing here document identifier */
+ }
+ if (! identchar(c)) {
+ pushback(p, c);
+ if (indent) pushback(p, '-');
+ return 0;
+ }
+ newtok(p);
+ do {
+ tokadd(p, c);
+ } while ((c = nextc(p)) >= 0 && identchar(c));
+ pushback(p, c);
+ }
+ tokfix(p);
+ newnode = new_heredoc(p);
+ info = (parser_heredoc_info*)newnode->cdr;
+ info->term = strndup(tok(p), toklen(p));
+ info->term_len = toklen(p);
+ if (! quote)
+ type |= STR_FUNC_EXPAND;
+ info->type = (string_type)type;
+ info->allow_indent = indent;
+ info->line_head = TRUE;
+ info->doc = NULL;
+ p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode);
+ p->lstate = EXPR_END;
+
+ pylval.nd = newnode;
+ return tHEREDOC_BEG;
+}
+
+static int
+arg_ambiguous(parser_state *p)
+{
+ yywarning(p, "ambiguous first argument; put parentheses or even spaces");
+ return 1;
+}
+
+#include "lex.def"
+
+static int
+parser_yylex(parser_state *p)
+{
+ int32_t c;
+ int space_seen = 0;
+ int cmd_state;
+ enum mrb_lex_state_enum last_state;
+ int token_column;
+
+ if (p->lex_strterm) {
+ if (is_strterm_type(p, STR_FUNC_HEREDOC)) {
+ if (p->parsing_heredoc != NULL)
+ return parse_string(p);
+ }
+ else
+ return parse_string(p);
+ }
+ cmd_state = p->cmd_start;
+ p->cmd_start = FALSE;
+ retry:
+ last_state = p->lstate;
+ switch (c = nextc(p)) {
+ case '\004': /* ^D */
+ case '\032': /* ^Z */
+ case '\0': /* NUL */
+ case -1: /* end of script. */
+ if (p->heredocs_from_nextline)
+ goto maybe_heredoc;
+ return 0;
+
+ /* white spaces */
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen = 1;
+ goto retry;
+
+ case '#': /* it's a comment */
+ skip(p, '\n');
+ /* fall through */
+ case -2: /* end of a file */
+ case '\n':
+ maybe_heredoc:
+ heredoc_treat_nextline(p);
+ switch (p->lstate) {
+ case EXPR_BEG:
+ case EXPR_FNAME:
+ case EXPR_DOT:
+ case EXPR_CLASS:
+ case EXPR_VALUE:
+ p->lineno++;
+ p->column = 0;
+ if (p->parsing_heredoc != NULL) {
+ if (p->lex_strterm) {
+ return parse_string(p);
+ }
+ }
+ goto retry;
+ default:
+ break;
+ }
+ if (p->parsing_heredoc != NULL) {
+ return '\n';
+ }
+ while ((c = nextc(p))) {
+ switch (c) {
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen = 1;
+ break;
+ case '.':
+ if ((c = nextc(p)) != '.') {
+ pushback(p, c);
+ pushback(p, '.');
+ goto retry;
+ }
+ case -1: /* EOF */
+ case -2: /* end of a file */
+ goto normal_newline;
+ default:
+ pushback(p, c);
+ goto normal_newline;
+ }
+ }
+ normal_newline:
+ p->cmd_start = TRUE;
+ p->lstate = EXPR_BEG;
+ return '\n';
+
+ case '*':
+ if ((c = nextc(p)) == '*') {
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern("**",2);
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ c = tPOW;
+ }
+ else {
+ if (c == '=') {
+ pylval.id = intern_c('*');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ if (IS_SPCARG(c)) {
+ yywarning(p, "'*' interpreted as argument prefix");
+ c = tSTAR;
+ }
+ else if (IS_BEG()) {
+ c = tSTAR;
+ }
+ else {
+ c = '*';
+ }
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ return c;
+
+ case '!':
+ c = nextc(p);
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ if (c == '@') {
+ return '!';
+ }
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ if (c == '=') {
+ return tNEQ;
+ }
+ if (c == '~') {
+ return tNMATCH;
+ }
+ pushback(p, c);
+ return '!';
+
+ case '=':
+ if (p->column == 1) {
+ static const char begin[] = "begin";
+ static const char end[] = "\n=end";
+ if (peeks(p, begin)) {
+ c = peekc_n(p, sizeof(begin)-1);
+ if (c < 0 || ISSPACE(c)) {
+ do {
+ if (!skips(p, end)) {
+ yyerror(p, "embedded document meets end of file");
+ return 0;
+ }
+ c = nextc(p);
+ } while (!(c < 0 || ISSPACE(c)));
+ if (c != '\n') skip(p, '\n');
+ p->lineno++;
+ p->column = 0;
+ goto retry;
+ }
+ }
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ if ((c = nextc(p)) == '=') {
+ if ((c = nextc(p)) == '=') {
+ return tEQQ;
+ }
+ pushback(p, c);
+ return tEQ;
+ }
+ if (c == '~') {
+ return tMATCH;
+ }
+ else if (c == '>') {
+ return tASSOC;
+ }
+ pushback(p, c);
+ return '=';
+
+ case '<':
+ c = nextc(p);
+ if (c == '<' &&
+ p->lstate != EXPR_DOT &&
+ p->lstate != EXPR_CLASS &&
+ !IS_END() &&
+ (!IS_ARG() || space_seen)) {
+ int token = heredoc_identifier(p);
+ if (token)
+ return token;
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ if (p->lstate == EXPR_CLASS) {
+ p->cmd_start = TRUE;
+ }
+ }
+ if (c == '=') {
+ if ((c = nextc(p)) == '>') {
+ return tCMP;
+ }
+ pushback(p, c);
+ return tLEQ;
+ }
+ if (c == '<') {
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern("<<",2);
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ return tLSHFT;
+ }
+ pushback(p, c);
+ return '<';
+
+ case '>':
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ if ((c = nextc(p)) == '=') {
+ return tGEQ;
+ }
+ if (c == '>') {
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern(">>",2);
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ return tRSHFT;
+ }
+ pushback(p, c);
+ return '>';
+
+ case '"':
+ p->lex_strterm = new_strterm(p, str_dquote, '"', 0);
+ return tSTRING_BEG;
+
+ case '\'':
+ p->lex_strterm = new_strterm(p, str_squote, '\'', 0);
+ return parse_string(p);
+
+ case '`':
+ if (p->lstate == EXPR_FNAME) {
+ p->lstate = EXPR_ENDFN;
+ return '`';
+ }
+ if (p->lstate == EXPR_DOT) {
+ if (cmd_state)
+ p->lstate = EXPR_CMDARG;
+ else
+ p->lstate = EXPR_ARG;
+ return '`';
+ }
+ p->lex_strterm = new_strterm(p, str_xquote, '`', 0);
+ return tXSTRING_BEG;
+
+ case '?':
+ if (IS_END()) {
+ p->lstate = EXPR_VALUE;
+ return '?';
+ }
+ c = nextc(p);
+ if (c < 0) {
+ yyerror(p, "incomplete character syntax");
+ return 0;
+ }
+ if (ISSPACE(c)) {
+ if (!IS_ARG()) {
+ int c2;
+ switch (c) {
+ case ' ':
+ c2 = 's';
+ break;
+ case '\n':
+ c2 = 'n';
+ break;
+ case '\t':
+ c2 = 't';
+ break;
+ case '\v':
+ c2 = 'v';
+ break;
+ case '\r':
+ c2 = 'r';
+ break;
+ case '\f':
+ c2 = 'f';
+ break;
+ default:
+ c2 = 0;
+ break;
+ }
+ if (c2) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2);
+ yyerror(p, buf);
+ }
+ }
+ ternary:
+ pushback(p, c);
+ p->lstate = EXPR_VALUE;
+ return '?';
+ }
+ newtok(p);
+ /* need support UTF-8 if configured */
+ if ((isalnum(c) || c == '_')) {
+ int c2 = nextc(p);
+ pushback(p, c2);
+ if ((isalnum(c2) || c2 == '_')) {
+ goto ternary;
+ }
+ }
+ if (c == '\\') {
+ c = read_escape(p);
+ tokadd(p, c);
+ }
+ else {
+ tokadd(p, c);
+ }
+ tokfix(p);
+ pylval.nd = new_str(p, tok(p), toklen(p));
+ p->lstate = EXPR_END;
+ return tCHAR;
+
+ case '&':
+ if ((c = nextc(p)) == '&') {
+ p->lstate = EXPR_BEG;
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern("&&",2);
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ return tANDOP;
+ }
+ else if (c == '.') {
+ p->lstate = EXPR_DOT;
+ return tANDDOT;
+ }
+ else if (c == '=') {
+ pylval.id = intern_c('&');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ if (IS_SPCARG(c)) {
+ yywarning(p, "'&' interpreted as argument prefix");
+ c = tAMPER;
+ }
+ else if (IS_BEG()) {
+ c = tAMPER;
+ }
+ else {
+ c = '&';
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ return c;
+
+ case '|':
+ if ((c = nextc(p)) == '|') {
+ p->lstate = EXPR_BEG;
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern("||",2);
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ return tOROP;
+ }
+ if (c == '=') {
+ pylval.id = intern_c('|');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ pushback(p, c);
+ return '|';
+
+ case '+':
+ c = nextc(p);
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ if (c == '@') {
+ return tUPLUS;
+ }
+ pushback(p, c);
+ return '+';
+ }
+ if (c == '=') {
+ pylval.id = intern_c('+');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) {
+ p->lstate = EXPR_BEG;
+ pushback(p, c);
+ if (c >= 0 && ISDIGIT(c)) {
+ c = '+';
+ goto start_num;
+ }
+ return tUPLUS;
+ }
+ p->lstate = EXPR_BEG;
+ pushback(p, c);
+ return '+';
+
+ case '-':
+ c = nextc(p);
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ if (c == '@') {
+ return tUMINUS;
+ }
+ pushback(p, c);
+ return '-';
+ }
+ if (c == '=') {
+ pylval.id = intern_c('-');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (c == '>') {
+ p->lstate = EXPR_ENDFN;
+ return tLAMBDA;
+ }
+ if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) {
+ p->lstate = EXPR_BEG;
+ pushback(p, c);
+ if (c >= 0 && ISDIGIT(c)) {
+ return tUMINUS_NUM;
+ }
+ return tUMINUS;
+ }
+ p->lstate = EXPR_BEG;
+ pushback(p, c);
+ return '-';
+
+ case '.':
+ p->lstate = EXPR_BEG;
+ if ((c = nextc(p)) == '.') {
+ if ((c = nextc(p)) == '.') {
+ return tDOT3;
+ }
+ pushback(p, c);
+ return tDOT2;
+ }
+ pushback(p, c);
+ if (c >= 0 && ISDIGIT(c)) {
+ yyerror(p, "no .<digit> floating literal anymore; put 0 before dot");
+ }
+ p->lstate = EXPR_DOT;
+ return '.';
+
+ start_num:
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int is_float, seen_point, seen_e, nondigit;
+
+ is_float = seen_point = seen_e = nondigit = 0;
+ p->lstate = EXPR_END;
+ newtok(p);
+ if (c == '-' || c == '+') {
+ tokadd(p, c);
+ c = nextc(p);
+ }
+ if (c == '0') {
+#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0)
+ int start = toklen(p);
+ c = nextc(p);
+ if (c == 'x' || c == 'X') {
+ /* hexadecimal */
+ c = nextc(p);
+ if (c >= 0 && ISXDIGIT(c)) {
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (!ISXDIGIT(c)) break;
+ nondigit = 0;
+ tokadd(p, tolower(c));
+ } while ((c = nextc(p)) >= 0);
+ }
+ pushback(p, c);
+ tokfix(p);
+ if (toklen(p) == start) {
+ no_digits();
+ }
+ else if (nondigit) goto trailing_uc;
+ pylval.nd = new_int(p, tok(p), 16);
+ return tINTEGER;
+ }
+ if (c == 'b' || c == 'B') {
+ /* binary */
+ c = nextc(p);
+ if (c == '0' || c == '1') {
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (c != '0' && c != '1') break;
+ nondigit = 0;
+ tokadd(p, c);
+ } while ((c = nextc(p)) >= 0);
+ }
+ pushback(p, c);
+ tokfix(p);
+ if (toklen(p) == start) {
+ no_digits();
+ }
+ else if (nondigit) goto trailing_uc;
+ pylval.nd = new_int(p, tok(p), 2);
+ return tINTEGER;
+ }
+ if (c == 'd' || c == 'D') {
+ /* decimal */
+ c = nextc(p);
+ if (c >= 0 && ISDIGIT(c)) {
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (!ISDIGIT(c)) break;
+ nondigit = 0;
+ tokadd(p, c);
+ } while ((c = nextc(p)) >= 0);
+ }
+ pushback(p, c);
+ tokfix(p);
+ if (toklen(p) == start) {
+ no_digits();
+ }
+ else if (nondigit) goto trailing_uc;
+ pylval.nd = new_int(p, tok(p), 10);
+ return tINTEGER;
+ }
+ if (c == '_') {
+ /* 0_0 */
+ goto octal_number;
+ }
+ if (c == 'o' || c == 'O') {
+ /* prefixed octal */
+ c = nextc(p);
+ if (c < 0 || c == '_' || !ISDIGIT(c)) {
+ no_digits();
+ }
+ }
+ if (c >= '0' && c <= '7') {
+ /* octal */
+ octal_number:
+ do {
+ if (c == '_') {
+ if (nondigit) break;
+ nondigit = c;
+ continue;
+ }
+ if (c < '0' || c > '9') break;
+ if (c > '7') goto invalid_octal;
+ nondigit = 0;
+ tokadd(p, c);
+ } while ((c = nextc(p)) >= 0);
+
+ if (toklen(p) > start) {
+ pushback(p, c);
+ tokfix(p);
+ if (nondigit) goto trailing_uc;
+ pylval.nd = new_int(p, tok(p), 8);
+ return tINTEGER;
+ }
+ if (nondigit) {
+ pushback(p, c);
+ goto trailing_uc;
+ }
+ }
+ if (c > '7' && c <= '9') {
+ invalid_octal:
+ yyerror(p, "Invalid octal digit");
+ }
+ else if (c == '.' || c == 'e' || c == 'E') {
+ tokadd(p, '0');
+ }
+ else {
+ pushback(p, c);
+ pylval.nd = new_int(p, "0", 10);
+ return tINTEGER;
+ }
+ }
+
+ for (;;) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ nondigit = 0;
+ tokadd(p, c);
+ break;
+
+ case '.':
+ if (nondigit) goto trailing_uc;
+ if (seen_point || seen_e) {
+ goto decode_num;
+ }
+ else {
+ int c0 = nextc(p);
+ if (c0 < 0 || !ISDIGIT(c0)) {
+ pushback(p, c0);
+ goto decode_num;
+ }
+ c = c0;
+ }
+ tokadd(p, '.');
+ tokadd(p, c);
+ is_float++;
+ seen_point++;
+ nondigit = 0;
+ break;
+
+ case 'e':
+ case 'E':
+ if (nondigit) {
+ pushback(p, c);
+ c = nondigit;
+ goto decode_num;
+ }
+ if (seen_e) {
+ goto decode_num;
+ }
+ tokadd(p, c);
+ seen_e++;
+ is_float++;
+ nondigit = c;
+ c = nextc(p);
+ if (c != '-' && c != '+') continue;
+ tokadd(p, c);
+ nondigit = c;
+ break;
+
+ case '_': /* '_' in number just ignored */
+ if (nondigit) goto decode_num;
+ nondigit = c;
+ break;
+
+ default:
+ goto decode_num;
+ }
+ c = nextc(p);
+ }
+
+ decode_num:
+ pushback(p, c);
+ if (nondigit) {
+ trailing_uc:
+ yyerror_i(p, "trailing '%c' in number", nondigit);
+ }
+ tokfix(p);
+ if (is_float) {
+ double d;
+ char *endp;
+
+ errno = 0;
+ d = mrb_float_read(tok(p), &endp);
+ if (d == 0 && endp == tok(p)) {
+ yywarning_s(p, "corrupted float value %s", tok(p));
+ }
+ else if (errno == ERANGE) {
+ yywarning_s(p, "float %s out of range", tok(p));
+ errno = 0;
+ }
+ pylval.nd = new_float(p, tok(p));
+ return tFLOAT;
+ }
+ pylval.nd = new_int(p, tok(p), 10);
+ return tINTEGER;
+ }
+
+ case ')':
+ case ']':
+ p->paren_nest--;
+ /* fall through */
+ case '}':
+ COND_LEXPOP();
+ CMDARG_LEXPOP();
+ if (c == ')')
+ p->lstate = EXPR_ENDFN;
+ else
+ p->lstate = EXPR_ENDARG;
+ return c;
+
+ case ':':
+ c = nextc(p);
+ if (c == ':') {
+ if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) {
+ p->lstate = EXPR_BEG;
+ return tCOLON3;
+ }
+ p->lstate = EXPR_DOT;
+ return tCOLON2;
+ }
+ if (IS_END() || ISSPACE(c)) {
+ pushback(p, c);
+ p->lstate = EXPR_BEG;
+ return ':';
+ }
+ pushback(p, c);
+ p->lstate = EXPR_FNAME;
+ return tSYMBEG;
+
+ case '/':
+ if (IS_BEG()) {
+ p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
+ return tREGEXP_BEG;
+ }
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern_c('/');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ pushback(p, c);
+ if (IS_SPCARG(c)) {
+ p->lex_strterm = new_strterm(p, str_regexp, '/', 0);
+ return tREGEXP_BEG;
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ return '/';
+
+ case '^':
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern_c('^');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ pushback(p, c);
+ return '^';
+
+ case ';':
+ p->lstate = EXPR_BEG;
+ return ';';
+
+ case ',':
+ p->lstate = EXPR_BEG;
+ return ',';
+
+ case '~':
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ if ((c = nextc(p)) != '@') {
+ pushback(p, c);
+ }
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ return '~';
+
+ case '(':
+ if (IS_BEG()) {
+ c = tLPAREN;
+ }
+ else if (IS_SPCARG(-1)) {
+ c = tLPAREN_ARG;
+ }
+ p->paren_nest++;
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ p->lstate = EXPR_BEG;
+ return c;
+
+ case '[':
+ p->paren_nest++;
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ if ((c = nextc(p)) == ']') {
+ if ((c = nextc(p)) == '=') {
+ return tASET;
+ }
+ pushback(p, c);
+ return tAREF;
+ }
+ pushback(p, c);
+ return '[';
+ }
+ else if (IS_BEG()) {
+ c = tLBRACK;
+ }
+ else if (IS_ARG() && space_seen) {
+ c = tLBRACK;
+ }
+ p->lstate = EXPR_BEG;
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ return c;
+
+ case '{':
+ if (p->lpar_beg && p->lpar_beg == p->paren_nest) {
+ p->lstate = EXPR_BEG;
+ p->lpar_beg = 0;
+ p->paren_nest--;
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ return tLAMBEG;
+ }
+ if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN)
+ c = '{'; /* block (primary) */
+ else if (p->lstate == EXPR_ENDARG)
+ c = tLBRACE_ARG; /* block (expr) */
+ else
+ c = tLBRACE; /* hash */
+ COND_PUSH(0);
+ CMDARG_PUSH(0);
+ p->lstate = EXPR_BEG;
+ return c;
+
+ case '\\':
+ c = nextc(p);
+ if (c == '\n') {
+ p->lineno++;
+ p->column = 0;
+ space_seen = 1;
+ goto retry; /* skip \\n */
+ }
+ pushback(p, c);
+ return '\\';
+
+ case '%':
+ if (IS_BEG()) {
+ int term;
+ int paren;
+
+ c = nextc(p);
+ quotation:
+ if (c < 0 || !ISALNUM(c)) {
+ term = c;
+ c = 'Q';
+ }
+ else {
+ term = nextc(p);
+ if (isalnum(term)) {
+ yyerror(p, "unknown type of %string");
+ return 0;
+ }
+ }
+ if (c < 0 || term < 0) {
+ yyerror(p, "unterminated quoted string meets end of file");
+ return 0;
+ }
+ paren = term;
+ if (term == '(') term = ')';
+ else if (term == '[') term = ']';
+ else if (term == '{') term = '}';
+ else if (term == '<') term = '>';
+ else paren = 0;
+
+ switch (c) {
+ case 'Q':
+ p->lex_strterm = new_strterm(p, str_dquote, term, paren);
+ return tSTRING_BEG;
+
+ case 'q':
+ p->lex_strterm = new_strterm(p, str_squote, term, paren);
+ return parse_string(p);
+
+ case 'W':
+ p->lex_strterm = new_strterm(p, str_dword, term, paren);
+ return tWORDS_BEG;
+
+ case 'w':
+ p->lex_strterm = new_strterm(p, str_sword, term, paren);
+ return tWORDS_BEG;
+
+ case 'x':
+ p->lex_strterm = new_strterm(p, str_xquote, term, paren);
+ return tXSTRING_BEG;
+
+ case 'r':
+ p->lex_strterm = new_strterm(p, str_regexp, term, paren);
+ return tREGEXP_BEG;
+
+ case 's':
+ p->lex_strterm = new_strterm(p, str_ssym, term, paren);
+ return tSYMBEG;
+
+ case 'I':
+ p->lex_strterm = new_strterm(p, str_dsymbols, term, paren);
+ return tSYMBOLS_BEG;
+
+ case 'i':
+ p->lex_strterm = new_strterm(p, str_ssymbols, term, paren);
+ return tSYMBOLS_BEG;
+
+ default:
+ yyerror(p, "unknown type of %string");
+ return 0;
+ }
+ }
+ if ((c = nextc(p)) == '=') {
+ pylval.id = intern_c('%');
+ p->lstate = EXPR_BEG;
+ return tOP_ASGN;
+ }
+ if (IS_SPCARG(c)) {
+ goto quotation;
+ }
+ if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) {
+ p->lstate = EXPR_ARG;
+ }
+ else {
+ p->lstate = EXPR_BEG;
+ }
+ pushback(p, c);
+ return '%';
+
+ case '$':
+ p->lstate = EXPR_END;
+ token_column = newtok(p);
+ c = nextc(p);
+ if (c < 0) {
+ yyerror(p, "incomplete global variable syntax");
+ return 0;
+ }
+ switch (c) {
+ case '_': /* $_: last read line string */
+ c = nextc(p);
+ if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */
+ tokadd(p, '$');
+ tokadd(p, c);
+ break;
+ }
+ pushback(p, c);
+ c = '_';
+ /* fall through */
+ case '~': /* $~: match-data */
+ case '*': /* $*: argv */
+ case '$': /* $$: pid */
+ case '?': /* $?: last status */
+ case '!': /* $!: error string */
+ case '@': /* $@: error position */
+ case '/': /* $/: input record separator */
+ case '\\': /* $\: output record separator */
+ case ';': /* $;: field separator */
+ case ',': /* $,: output field separator */
+ case '.': /* $.: last read line number */
+ case '=': /* $=: ignorecase */
+ case ':': /* $:: load path */
+ case '<': /* $<: reading filename */
+ case '>': /* $>: default output handle */
+ case '\"': /* $": already loaded files */
+ tokadd(p, '$');
+ tokadd(p, c);
+ tokfix(p);
+ pylval.id = intern_cstr(tok(p));
+ return tGVAR;
+
+ case '-':
+ tokadd(p, '$');
+ tokadd(p, c);
+ c = nextc(p);
+ pushback(p, c);
+ gvar:
+ tokfix(p);
+ pylval.id = intern_cstr(tok(p));
+ return tGVAR;
+
+ case '&': /* $&: last match */
+ case '`': /* $`: string before last match */
+ case '\'': /* $': string after last match */
+ case '+': /* $+: string matches last pattern */
+ if (last_state == EXPR_FNAME) {
+ tokadd(p, '$');
+ tokadd(p, c);
+ goto gvar;
+ }
+ pylval.nd = new_back_ref(p, c);
+ return tBACK_REF;
+
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ do {
+ tokadd(p, c);
+ c = nextc(p);
+ } while (c >= 0 && isdigit(c));
+ pushback(p, c);
+ if (last_state == EXPR_FNAME) goto gvar;
+ tokfix(p);
+ {
+ unsigned long n = strtoul(tok(p), NULL, 10);
+ if (n > INT_MAX) {
+ yyerror_i(p, "capture group index must be <= %d", INT_MAX);
+ return 0;
+ }
+ pylval.nd = new_nth_ref(p, (int)n);
+ }
+ return tNTH_REF;
+
+ default:
+ if (!identchar(c)) {
+ pushback(p, c);
+ return '$';
+ }
+ /* fall through */
+ case '0':
+ tokadd(p, '$');
+ }
+ break;
+
+ case '@':
+ c = nextc(p);
+ token_column = newtok(p);
+ tokadd(p, '@');
+ if (c == '@') {
+ tokadd(p, '@');
+ c = nextc(p);
+ }
+ if (c < 0) {
+ if (p->tidx == 1) {
+ yyerror(p, "incomplete instance variable syntax");
+ }
+ else {
+ yyerror(p, "incomplete class variable syntax");
+ }
+ return 0;
+ }
+ else if (isdigit(c)) {
+ if (p->tidx == 1) {
+ yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
+ }
+ else {
+ yyerror_i(p, "'@@%c' is not allowed as a class variable name", c);
+ }
+ return 0;
+ }
+ if (!identchar(c)) {
+ pushback(p, c);
+ return '@';
+ }
+ break;
+
+ case '_':
+ token_column = newtok(p);
+ break;
+
+ default:
+ if (!identchar(c)) {
+ yyerror_i(p, "Invalid char '\\x%02X' in expression", c);
+ goto retry;
+ }
+
+ token_column = newtok(p);
+ break;
+ }
+
+ do {
+ tokadd(p, c);
+ c = nextc(p);
+ if (c < 0) break;
+ } while (identchar(c));
+ if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') &&
+ strncmp(tok(p), "__END__", toklen(p)) == 0)
+ return -1;
+
+ switch (tok(p)[0]) {
+ case '@': case '$':
+ pushback(p, c);
+ break;
+ default:
+ if ((c == '!' || c == '?') && !peek(p, '=')) {
+ tokadd(p, c);
+ }
+ else {
+ pushback(p, c);
+ }
+ }
+ tokfix(p);
+ {
+ int result = 0;
+
+ switch (tok(p)[0]) {
+ case '$':
+ p->lstate = EXPR_END;
+ result = tGVAR;
+ break;
+ case '@':
+ p->lstate = EXPR_END;
+ if (tok(p)[1] == '@')
+ result = tCVAR;
+ else
+ result = tIVAR;
+ break;
+
+ default:
+ if (toklast(p) == '!' || toklast(p) == '?') {
+ result = tFID;
+ }
+ else {
+ if (p->lstate == EXPR_FNAME) {
+ if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') &&
+ (!peek(p, '=') || (peek_n(p, '>', 1)))) {
+ result = tIDENTIFIER;
+ tokadd(p, c);
+ tokfix(p);
+ }
+ else {
+ pushback(p, c);
+ }
+ }
+ if (result == 0 && ISUPPER(tok(p)[0])) {
+ result = tCONSTANT;
+ }
+ else {
+ result = tIDENTIFIER;
+ }
+ }
+
+ if (IS_LABEL_POSSIBLE()) {
+ if (IS_LABEL_SUFFIX(0)) {
+ p->lstate = EXPR_BEG;
+ nextc(p);
+ tokfix(p);
+ pylval.id = intern_cstr(tok(p));
+ return tLABEL;
+ }
+ }
+ if (p->lstate != EXPR_DOT) {
+ const struct kwtable *kw;
+
+ /* See if it is a reserved word. */
+ kw = mrb_reserved_word(tok(p), toklen(p));
+ if (kw) {
+ enum mrb_lex_state_enum state = p->lstate;
+ pylval.num = p->lineno;
+ p->lstate = kw->state;
+ if (state == EXPR_FNAME) {
+ pylval.id = intern_cstr(kw->name);
+ return kw->id[0];
+ }
+ if (p->lstate == EXPR_BEG) {
+ p->cmd_start = TRUE;
+ }
+ if (kw->id[0] == keyword_do) {
+ if (p->lpar_beg && p->lpar_beg == p->paren_nest) {
+ p->lpar_beg = 0;
+ p->paren_nest--;
+ return keyword_do_LAMBDA;
+ }
+ if (COND_P()) return keyword_do_cond;
+ if (CMDARG_P() && state != EXPR_CMDARG)
+ return keyword_do_block;
+ if (state == EXPR_ENDARG || state == EXPR_BEG)
+ return keyword_do_block;
+ return keyword_do;
+ }
+ if (state == EXPR_BEG || state == EXPR_VALUE)
+ return kw->id[0];
+ else {
+ if (kw->id[0] != kw->id[1])
+ p->lstate = EXPR_BEG;
+ return kw->id[1];
+ }
+ }
+ }
+
+ if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) {
+ if (cmd_state) {
+ p->lstate = EXPR_CMDARG;
+ }
+ else {
+ p->lstate = EXPR_ARG;
+ }
+ }
+ else if (p->lstate == EXPR_FNAME) {
+ p->lstate = EXPR_ENDFN;
+ }
+ else {
+ p->lstate = EXPR_END;
+ }
+ }
+ {
+ mrb_sym ident = intern_cstr(tok(p));
+
+ pylval.id = ident;
+#if 0
+ if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) {
+ p->lstate = EXPR_END;
+ }
+#endif
+ }
+ return result;
+ }
+}
+
+static int
+yylex(void *lval, parser_state *p)
+{
+ p->ylval = lval;
+ return parser_yylex(p);
+}
+
+static void
+parser_init_cxt(parser_state *p, mrbc_context *cxt)
+{
+ if (!cxt) return;
+ if (cxt->filename) mrb_parser_set_filename(p, cxt->filename);
+ if (cxt->lineno) p->lineno = cxt->lineno;
+ if (cxt->syms) {
+ int i;
+
+ p->locals = cons(0,0);
+ for (i=0; i<cxt->slen; i++) {
+ local_add_f(p, cxt->syms[i]);
+ }
+ }
+ p->capture_errors = cxt->capture_errors;
+ p->no_optimize = cxt->no_optimize;
+ if (cxt->partial_hook) {
+ p->cxt = cxt;
+ }
+}
+
+static void
+parser_update_cxt(parser_state *p, mrbc_context *cxt)
+{
+ node *n, *n0;
+ int i = 0;
+
+ if (!cxt) return;
+ if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return;
+ n0 = n = p->tree->cdr->car;
+ while (n) {
+ i++;
+ n = n->cdr;
+ }
+ cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym));
+ cxt->slen = i;
+ for (i=0, n=n0; n; i++,n=n->cdr) {
+ cxt->syms[i] = sym(n->car);
+ }
+}
+
+void mrb_codedump_all(mrb_state*, struct RProc*);
+void mrb_parser_dump(mrb_state *mrb, node *tree, int offset);
+
+MRB_API void
+mrb_parser_parse(parser_state *p, mrbc_context *c)
+{
+ struct mrb_jmpbuf buf1;
+ p->jmp = &buf1;
+
+ MRB_TRY(p->jmp) {
+ int n = 1;
+
+ p->cmd_start = TRUE;
+ p->in_def = p->in_single = 0;
+ p->nerr = p->nwarn = 0;
+ p->lex_strterm = NULL;
+
+ parser_init_cxt(p, c);
+
+ if (p->mrb->jmp) {
+ n = yyparse(p);
+ }
+ else {
+ struct mrb_jmpbuf buf2;
+
+ p->mrb->jmp = &buf2;
+ MRB_TRY(p->mrb->jmp) {
+ n = yyparse(p);
+ }
+ MRB_CATCH(p->mrb->jmp) {
+ p->nerr++;
+ }
+ MRB_END_EXC(p->mrb->jmp);
+ p->mrb->jmp = 0;
+ }
+ if (n != 0 || p->nerr > 0) {
+ p->tree = 0;
+ return;
+ }
+ if (!p->tree) {
+ p->tree = new_nil(p);
+ }
+ parser_update_cxt(p, c);
+ if (c && c->dump_result) {
+ mrb_parser_dump(p->mrb, p->tree, 0);
+ }
+ }
+ MRB_CATCH(p->jmp) {
+ yyerror(p, "memory allocation error");
+ p->nerr++;
+ p->tree = 0;
+ return;
+ }
+ MRB_END_EXC(p->jmp);
+}
+
+MRB_API parser_state*
+mrb_parser_new(mrb_state *mrb)
+{
+ mrb_pool *pool;
+ parser_state *p;
+ static const parser_state parser_state_zero = { 0 };
+
+ pool = mrb_pool_open(mrb);
+ if (!pool) return NULL;
+ p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state));
+ if (!p) return NULL;
+
+ *p = parser_state_zero;
+ p->mrb = mrb;
+ p->pool = pool;
+
+ p->s = p->send = NULL;
+#ifndef MRB_DISABLE_STDIO
+ p->f = NULL;
+#endif
+
+ p->cmd_start = TRUE;
+ p->in_def = p->in_single = 0;
+
+ p->capture_errors = FALSE;
+ p->lineno = 1;
+ p->column = 0;
+#if defined(PARSER_TEST) || defined(PARSER_DEBUG)
+ yydebug = 1;
+#endif
+ p->tsiz = MRB_PARSER_TOKBUF_SIZE;
+ p->tokbuf = p->buf;
+
+ p->lex_strterm = NULL;
+ p->all_heredocs = p->parsing_heredoc = NULL;
+ p->lex_strterm_before_heredoc = NULL;
+
+ p->current_filename_index = -1;
+ p->filename_table = NULL;
+ p->filename_table_length = 0;
+
+ return p;
+}
+
+MRB_API void
+mrb_parser_free(parser_state *p) {
+ if (p->tokbuf != p->buf) {
+ mrb_free(p->mrb, p->tokbuf);
+ }
+ mrb_pool_close(p->pool);
+}
+
+MRB_API mrbc_context*
+mrbc_context_new(mrb_state *mrb)
+{
+ return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context));
+}
+
+MRB_API void
+mrbc_context_free(mrb_state *mrb, mrbc_context *cxt)
+{
+ mrb_free(mrb, cxt->filename);
+ mrb_free(mrb, cxt->syms);
+ mrb_free(mrb, cxt);
+}
+
+MRB_API const char*
+mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
+{
+ if (s) {
+ size_t len = strlen(s);
+ char *p = (char *)mrb_malloc(mrb, len + 1);
+
+ memcpy(p, s, len + 1);
+ if (c->filename) {
+ mrb_free(mrb, c->filename);
+ }
+ c->filename = p;
+ }
+ return c->filename;
+}
+
+MRB_API void
+mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data)
+{
+ c->partial_hook = func;
+ c->partial_data = data;
+}
+
+MRB_API void
+mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
+{
+ mrb_sym sym;
+ size_t i;
+ mrb_sym* new_table;
+
+ sym = mrb_intern_cstr(p->mrb, f);
+ p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
+ p->lineno = (p->filename_table_length > 0)? 0 : 1;
+
+ for (i = 0; i < p->filename_table_length; ++i) {
+ if (p->filename_table[i] == sym) {
+ p->current_filename_index = (int)i;
+ return;
+ }
+ }
+
+ p->current_filename_index = (int)p->filename_table_length++;
+
+ new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length);
+ if (p->filename_table) {
+ memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length);
+ }
+ p->filename_table = new_table;
+ p->filename_table[p->filename_table_length - 1] = sym;
+}
+
+MRB_API char const*
+mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
+ if (idx >= p->filename_table_length) { return NULL; }
+ else {
+ return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
+ }
+}
+
+#ifndef MRB_DISABLE_STDIO
+MRB_API parser_state*
+mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
+{
+ parser_state *p;
+
+ p = mrb_parser_new(mrb);
+ if (!p) return NULL;
+ p->s = p->send = NULL;
+ p->f = f;
+
+ mrb_parser_parse(p, c);
+ return p;
+}
+#endif
+
+MRB_API parser_state*
+mrb_parse_nstring(mrb_state *mrb, const char *s, size_t len, mrbc_context *c)
+{
+ parser_state *p;
+
+ p = mrb_parser_new(mrb);
+ if (!p) return NULL;
+ p->s = s;
+ p->send = s + len;
+
+ mrb_parser_parse(p, c);
+ return p;
+}
+
+MRB_API parser_state*
+mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c)
+{
+ return mrb_parse_nstring(mrb, s, strlen(s), c);
+}
+
+MRB_API mrb_value
+mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
+{
+ struct RClass *target = mrb->object_class;
+ struct RProc *proc;
+ mrb_value v;
+ unsigned int keep = 0;
+
+ if (!p) {
+ return mrb_undef_value();
+ }
+ if (!p->tree || p->nerr) {
+ if (c) c->parser_nerr = p->nerr;
+ if (p->capture_errors) {
+ char buf[256];
+ int n;
+
+ n = snprintf(buf, sizeof(buf), "line %d: %s\n",
+ p->error_buffer[0].lineno, p->error_buffer[0].message);
+ mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n));
+ mrb_parser_free(p);
+ return mrb_undef_value();
+ }
+ else {
+ if (mrb->exc == NULL) {
+ mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error"));
+ }
+ mrb_parser_free(p);
+ return mrb_undef_value();
+ }
+ }
+ proc = mrb_generate_code(mrb, p);
+ mrb_parser_free(p);
+ if (proc == NULL) {
+ if (mrb->exc == NULL) {
+ mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error"));
+ }
+ return mrb_undef_value();
+ }
+ if (c) {
+ if (c->dump_result) mrb_codedump_all(mrb, proc);
+ if (c->no_exec) return mrb_obj_value(proc);
+ if (c->target_class) {
+ target = c->target_class;
+ }
+ if (c->keep_lv) {
+ keep = c->slen + 1;
+ }
+ else {
+ c->keep_lv = TRUE;
+ }
+ }
+ proc->target_class = target;
+ if (mrb->c->ci) {
+ mrb->c->ci->target_class = target;
+ }
+ v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep);
+ if (mrb->exc) return mrb_nil_value();
+ return v;
+}
+
+#ifndef MRB_DISABLE_STDIO
+MRB_API mrb_value
+mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
+{
+ return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c);
+}
+
+MRB_API mrb_value
+mrb_load_file(mrb_state *mrb, FILE *f)
+{
+ return mrb_load_file_cxt(mrb, f, NULL);
+}
+#endif
+
+MRB_API mrb_value
+mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrbc_context *c)
+{
+ return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
+}
+
+MRB_API mrb_value
+mrb_load_nstring(mrb_state *mrb, const char *s, size_t len)
+{
+ return mrb_load_nstring_cxt(mrb, s, len, NULL);
+}
+
+MRB_API mrb_value
+mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c)
+{
+ return mrb_load_nstring_cxt(mrb, s, strlen(s), c);
+}
+
+MRB_API mrb_value
+mrb_load_string(mrb_state *mrb, const char *s)
+{
+ return mrb_load_string_cxt(mrb, s, NULL);
+}
+
+#ifndef MRB_DISABLE_STDIO
+
+static void
+dump_prefix(node *tree, int offset)
+{
+ printf("%05d ", tree->lineno);
+ while (offset--) {
+ putc(' ', stdout);
+ putc(' ', stdout);
+ }
+}
+
+static void
+dump_recur(mrb_state *mrb, node *tree, int offset)
+{
+ while (tree) {
+ mrb_parser_dump(mrb, tree->car, offset);
+ tree = tree->cdr;
+ }
+}
+
+#endif
+
+void
+mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
+{
+#ifndef MRB_DISABLE_STDIO
+ int nodetype;
+
+ if (!tree) return;
+ again:
+ dump_prefix(tree, offset);
+ nodetype = (int)(intptr_t)tree->car;
+ tree = tree->cdr;
+ switch (nodetype) {
+ case NODE_BEGIN:
+ printf("NODE_BEGIN:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_RESCUE:
+ printf("NODE_RESCUE:\n");
+ if (tree->car) {
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ node *n2 = tree->car;
+
+ dump_prefix(n2, offset+1);
+ printf("rescue:\n");
+ while (n2) {
+ node *n3 = n2->car;
+ if (n3->car) {
+ dump_prefix(n2, offset+2);
+ printf("handle classes:\n");
+ dump_recur(mrb, n3->car, offset+3);
+ }
+ if (n3->cdr->car) {
+ dump_prefix(n3, offset+2);
+ printf("exc_var:\n");
+ mrb_parser_dump(mrb, n3->cdr->car, offset+3);
+ }
+ if (n3->cdr->cdr->car) {
+ dump_prefix(n3, offset+2);
+ printf("rescue body:\n");
+ mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3);
+ }
+ n2 = n2->cdr;
+ }
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ dump_prefix(tree, offset+1);
+ printf("else:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ }
+ break;
+
+ case NODE_ENSURE:
+ printf("NODE_ENSURE:\n");
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("ensure:\n");
+ mrb_parser_dump(mrb, tree->cdr->cdr, offset+2);
+ break;
+
+ case NODE_LAMBDA:
+ printf("NODE_BLOCK:\n");
+ goto block;
+
+ case NODE_BLOCK:
+ block:
+ printf("NODE_BLOCK:\n");
+ tree = tree->cdr;
+ if (tree->car) {
+ node *n = tree->car;
+
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("optional args:\n");
+ {
+ node *n2 = n->car;
+
+ while (n2) {
+ dump_prefix(n2, offset+2);
+ printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
+ mrb_parser_dump(mrb, n2->car->cdr, 0);
+ n2 = n2->cdr;
+ }
+ }
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("post mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ if (n->cdr) {
+ dump_prefix(n, offset+1);
+ printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
+ }
+ }
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr->car, offset+2);
+ break;
+
+ case NODE_IF:
+ printf("NODE_IF:\n");
+ dump_prefix(tree, offset+1);
+ printf("cond:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("then:\n");
+ mrb_parser_dump(mrb, tree->cdr->car, offset+2);
+ if (tree->cdr->cdr->car) {
+ dump_prefix(tree, offset+1);
+ printf("else:\n");
+ mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2);
+ }
+ break;
+
+ case NODE_AND:
+ printf("NODE_AND:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ mrb_parser_dump(mrb, tree->cdr, offset+1);
+ break;
+
+ case NODE_OR:
+ printf("NODE_OR:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ mrb_parser_dump(mrb, tree->cdr, offset+1);
+ break;
+
+ case NODE_CASE:
+ printf("NODE_CASE:\n");
+ if (tree->car) {
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ }
+ tree = tree->cdr;
+ while (tree) {
+ dump_prefix(tree, offset+1);
+ printf("case:\n");
+ dump_recur(mrb, tree->car->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->car->cdr, offset+2);
+ tree = tree->cdr;
+ }
+ break;
+
+ case NODE_WHILE:
+ printf("NODE_WHILE:\n");
+ dump_prefix(tree, offset+1);
+ printf("cond:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset+2);
+ break;
+
+ case NODE_UNTIL:
+ printf("NODE_UNTIL:\n");
+ dump_prefix(tree, offset+1);
+ printf("cond:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset+2);
+ break;
+
+ case NODE_FOR:
+ printf("NODE_FOR:\n");
+ dump_prefix(tree, offset+1);
+ printf("var:\n");
+ {
+ node *n2 = tree->car;
+
+ if (n2->car) {
+ dump_prefix(n2, offset+2);
+ printf("pre:\n");
+ dump_recur(mrb, n2->car, offset+3);
+ }
+ n2 = n2->cdr;
+ if (n2) {
+ if (n2->car) {
+ dump_prefix(n2, offset+2);
+ printf("rest:\n");
+ mrb_parser_dump(mrb, n2->car, offset+3);
+ }
+ n2 = n2->cdr;
+ if (n2) {
+ if (n2->car) {
+ dump_prefix(n2, offset+2);
+ printf("post:\n");
+ dump_recur(mrb, n2->car, offset+3);
+ }
+ }
+ }
+ }
+ tree = tree->cdr;
+ dump_prefix(tree, offset+1);
+ printf("in:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ tree = tree->cdr;
+ dump_prefix(tree, offset+1);
+ printf("do:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ break;
+
+ case NODE_SCOPE:
+ printf("NODE_SCOPE:\n");
+ {
+ node *n2 = tree->car;
+ mrb_bool first_lval = TRUE;
+
+ if (n2 && (n2->car || n2->cdr)) {
+ dump_prefix(n2, offset+1);
+ printf("local variables:\n");
+ dump_prefix(n2, offset+2);
+ while (n2) {
+ if (n2->car) {
+ if (!first_lval) printf(", ");
+ printf("%s", mrb_sym2name(mrb, sym(n2->car)));
+ first_lval = FALSE;
+ }
+ n2 = n2->cdr;
+ }
+ printf("\n");
+ }
+ }
+ tree = tree->cdr;
+ offset++;
+ goto again;
+
+ case NODE_FCALL:
+ case NODE_CALL:
+ case NODE_SCALL:
+ switch (nodetype) {
+ case NODE_FCALL:
+ printf("NODE_FCALL:\n"); break;
+ case NODE_CALL:
+ printf("NODE_CALL(.):\n"); break;
+ case NODE_SCALL:
+ printf("NODE_SCALL(&.):\n"); break;
+ default:
+ break;
+ }
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("method='%s' (%d)\n",
+ mrb_sym2name(mrb, sym(tree->cdr->car)),
+ (int)(intptr_t)tree->cdr->car);
+ tree = tree->cdr->cdr->car;
+ if (tree) {
+ dump_prefix(tree, offset+1);
+ printf("args:\n");
+ dump_recur(mrb, tree->car, offset+2);
+ if (tree->cdr) {
+ dump_prefix(tree, offset+1);
+ printf("block:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset+2);
+ }
+ }
+ break;
+
+ case NODE_DOT2:
+ printf("NODE_DOT2:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ mrb_parser_dump(mrb, tree->cdr, offset+1);
+ break;
+
+ case NODE_DOT3:
+ printf("NODE_DOT3:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ mrb_parser_dump(mrb, tree->cdr, offset+1);
+ break;
+
+ case NODE_COLON2:
+ printf("NODE_COLON2:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr)));
+ break;
+
+ case NODE_COLON3:
+ printf("NODE_COLON3: ::%s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_ARRAY:
+ printf("NODE_ARRAY:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_HASH:
+ printf("NODE_HASH:\n");
+ while (tree) {
+ dump_prefix(tree, offset+1);
+ printf("key:\n");
+ mrb_parser_dump(mrb, tree->car->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("value:\n");
+ mrb_parser_dump(mrb, tree->car->cdr, offset+2);
+ tree = tree->cdr;
+ }
+ break;
+
+ case NODE_SPLAT:
+ printf("NODE_SPLAT:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_ASGN:
+ printf("NODE_ASGN:\n");
+ dump_prefix(tree, offset+1);
+ printf("lhs:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("rhs:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset+2);
+ break;
+
+ case NODE_MASGN:
+ printf("NODE_MASGN:\n");
+ dump_prefix(tree, offset+1);
+ printf("mlhs:\n");
+ {
+ node *n2 = tree->car;
+
+ if (n2->car) {
+ dump_prefix(tree, offset+2);
+ printf("pre:\n");
+ dump_recur(mrb, n2->car, offset+3);
+ }
+ n2 = n2->cdr;
+ if (n2) {
+ if (n2->car) {
+ dump_prefix(n2, offset+2);
+ printf("rest:\n");
+ if (n2->car == (node*)-1) {
+ dump_prefix(n2, offset+2);
+ printf("(empty)\n");
+ }
+ else {
+ mrb_parser_dump(mrb, n2->car, offset+3);
+ }
+ }
+ n2 = n2->cdr;
+ if (n2) {
+ if (n2->car) {
+ dump_prefix(n2, offset+2);
+ printf("post:\n");
+ dump_recur(mrb, n2->car, offset+3);
+ }
+ }
+ }
+ }
+ dump_prefix(tree, offset+1);
+ printf("rhs:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset+2);
+ break;
+
+ case NODE_OP_ASGN:
+ printf("NODE_OP_ASGN:\n");
+ dump_prefix(tree, offset+1);
+ printf("lhs:\n");
+ mrb_parser_dump(mrb, tree->car, offset+2);
+ tree = tree->cdr;
+ dump_prefix(tree, offset+1);
+ printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car);
+ tree = tree->cdr;
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ break;
+
+ case NODE_SUPER:
+ printf("NODE_SUPER:\n");
+ if (tree) {
+ dump_prefix(tree, offset+1);
+ printf("args:\n");
+ dump_recur(mrb, tree->car, offset+2);
+ if (tree->cdr) {
+ dump_prefix(tree, offset+1);
+ printf("block:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset+2);
+ }
+ }
+ break;
+
+ case NODE_ZSUPER:
+ printf("NODE_ZSUPER\n");
+ break;
+
+ case NODE_RETURN:
+ printf("NODE_RETURN:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_YIELD:
+ printf("NODE_YIELD:\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_BREAK:
+ printf("NODE_BREAK:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_NEXT:
+ printf("NODE_NEXT:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_REDO:
+ printf("NODE_REDO\n");
+ break;
+
+ case NODE_RETRY:
+ printf("NODE_RETRY\n");
+ break;
+
+ case NODE_LVAR:
+ printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_GVAR:
+ printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_IVAR:
+ printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_CVAR:
+ printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_CONST:
+ printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_MATCH:
+ printf("NODE_MATCH:\n");
+ dump_prefix(tree, offset + 1);
+ printf("lhs:\n");
+ mrb_parser_dump(mrb, tree->car, offset + 2);
+ dump_prefix(tree, offset + 1);
+ printf("rhs:\n");
+ mrb_parser_dump(mrb, tree->cdr, offset + 2);
+ break;
+
+ case NODE_BACK_REF:
+ printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree);
+ break;
+
+ case NODE_NTH_REF:
+ printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree);
+ break;
+
+ case NODE_ARG:
+ printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
+ case NODE_BLOCK_ARG:
+ printf("NODE_BLOCK_ARG:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_INT:
+ printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car);
+ break;
+
+ case NODE_FLOAT:
+ printf("NODE_FLOAT %s\n", (char*)tree);
+ break;
+
+ case NODE_NEGATE:
+ printf("NODE_NEGATE\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_STR:
+ printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+ break;
+
+ case NODE_DSTR:
+ printf("NODE_DSTR\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_XSTR:
+ printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+ break;
+
+ case NODE_DXSTR:
+ printf("NODE_DXSTR\n");
+ dump_recur(mrb, tree, offset+1);
+ break;
+
+ case NODE_REGX:
+ printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr);
+ break;
+
+ case NODE_DREGX:
+ printf("NODE_DREGX\n");
+ dump_recur(mrb, tree->car, offset+1);
+ dump_prefix(tree, offset);
+ printf("tail: %s\n", (char*)tree->cdr->cdr->car);
+ if (tree->cdr->cdr->cdr->car) {
+ dump_prefix(tree, offset);
+ printf("opt: %s\n", (char*)tree->cdr->cdr->cdr->car);
+ }
+ if (tree->cdr->cdr->cdr->cdr) {
+ dump_prefix(tree, offset);
+ printf("enc: %s\n", (char*)tree->cdr->cdr->cdr->cdr);
+ }
+ break;
+
+ case NODE_SYM:
+ printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
+ (int)(intptr_t)tree);
+ break;
+
+ case NODE_SELF:
+ printf("NODE_SELF\n");
+ break;
+
+ case NODE_NIL:
+ printf("NODE_NIL\n");
+ break;
+
+ case NODE_TRUE:
+ printf("NODE_TRUE\n");
+ break;
+
+ case NODE_FALSE:
+ printf("NODE_FALSE\n");
+ break;
+
+ case NODE_ALIAS:
+ printf("NODE_ALIAS %s %s:\n",
+ mrb_sym2name(mrb, sym(tree->car)),
+ mrb_sym2name(mrb, sym(tree->cdr)));
+ break;
+
+ case NODE_UNDEF:
+ printf("NODE_UNDEF");
+ {
+ node *t = tree;
+ while (t) {
+ printf(" %s", mrb_sym2name(mrb, sym(t->car)));
+ t = t->cdr;
+ }
+ }
+ printf(":\n");
+ break;
+
+ case NODE_CLASS:
+ printf("NODE_CLASS:\n");
+ if (tree->car->car == (node*)0) {
+ dump_prefix(tree, offset+1);
+ printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ }
+ else if (tree->car->car == (node*)1) {
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ }
+ else {
+ mrb_parser_dump(mrb, tree->car->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ }
+ if (tree->cdr->car) {
+ dump_prefix(tree, offset+1);
+ printf("super:\n");
+ mrb_parser_dump(mrb, tree->cdr->car, offset+2);
+ }
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2);
+ break;
+
+ case NODE_MODULE:
+ printf("NODE_MODULE:\n");
+ if (tree->car->car == (node*)0) {
+ dump_prefix(tree, offset+1);
+ printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ }
+ else if (tree->car->car == (node*)1) {
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ }
+ else {
+ mrb_parser_dump(mrb, tree->car->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
+ }
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
+ break;
+
+ case NODE_SCLASS:
+ printf("NODE_SCLASS:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
+ break;
+
+ case NODE_DEF:
+ printf("NODE_DEF:\n");
+ dump_prefix(tree, offset+1);
+ printf("%s\n", mrb_sym2name(mrb, sym(tree->car)));
+ tree = tree->cdr;
+ {
+ node *n2 = tree->car;
+ mrb_bool first_lval = TRUE;
+
+ if (n2 && (n2->car || n2->cdr)) {
+ dump_prefix(n2, offset+1);
+ printf("local variables:\n");
+ dump_prefix(n2, offset+2);
+ while (n2) {
+ if (n2->car) {
+ if (!first_lval) printf(", ");
+ printf("%s", mrb_sym2name(mrb, sym(n2->car)));
+ first_lval = FALSE;
+ }
+ n2 = n2->cdr;
+ }
+ printf("\n");
+ }
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ node *n = tree->car;
+
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("optional args:\n");
+ {
+ node *n2 = n->car;
+
+ while (n2) {
+ dump_prefix(n2, offset+2);
+ printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
+ mrb_parser_dump(mrb, n2->car->cdr, 0);
+ n2 = n2->cdr;
+ }
+ }
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("post mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ if (n->cdr) {
+ dump_prefix(n, offset+1);
+ printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
+ }
+ }
+ mrb_parser_dump(mrb, tree->cdr->car, offset+1);
+ break;
+
+ case NODE_SDEF:
+ printf("NODE_SDEF:\n");
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ tree = tree->cdr;
+ dump_prefix(tree, offset+1);
+ printf(":%s\n", mrb_sym2name(mrb, sym(tree->car)));
+ tree = tree->cdr->cdr;
+ if (tree->car) {
+ node *n = tree->car;
+
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("optional args:\n");
+ {
+ node *n2 = n->car;
+
+ while (n2) {
+ dump_prefix(n2, offset+2);
+ printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
+ mrb_parser_dump(mrb, n2->car->cdr, 0);
+ n2 = n2->cdr;
+ }
+ }
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("post mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n) {
+ dump_prefix(n, offset+1);
+ printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
+ }
+ }
+ tree = tree->cdr;
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ break;
+
+ case NODE_POSTEXE:
+ printf("NODE_POSTEXE:\n");
+ mrb_parser_dump(mrb, tree, offset+1);
+ break;
+
+ case NODE_HEREDOC:
+ printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term);
+ dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
+ break;
+
+ default:
+ printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
+ break;
+ }
+#endif
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake
new file mode 100644
index 00000000..3bf6d6ae
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/mrbgem.rake
@@ -0,0 +1,40 @@
+MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby compiler library'
+
+ current_dir = spec.dir
+ current_build_dir = spec.build_dir
+
+ lex_def = "#{current_dir}/core/lex.def"
+ core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f|
+ next nil if build.cxx_exception_enabled? and f =~ /(codegen).c$/
+ objfile(f.pathmap("#{current_build_dir}/core/%n"))
+ }.compact
+
+ if build.cxx_exception_enabled?
+ core_objs <<
+ build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx",
+ objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) <<
+ build.compile_as_cxx("#{current_dir}/core/codegen.c", "#{current_build_dir}/core/codegen.cxx")
+ else
+ core_objs << objfile("#{current_build_dir}/core/y.tab")
+ file objfile("#{current_build_dir}/core/y.tab") => "#{current_build_dir}/core/y.tab.c" do |t|
+ cc.run t.name, t.prerequisites.first, [], ["#{current_dir}/core"]
+ end
+ end
+ file objfile("#{current_build_dir}/core/y.tab") => lex_def
+
+ # Parser
+ file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y"] do |t|
+ yacc.run t.name, t.prerequisites.first
+ end
+
+ # Lexical analyzer
+ file lex_def => "#{current_dir}/core/keywords" do |t|
+ gperf.run t.name, t.prerequisites.first
+ end
+
+ file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
+ build.libmruby << core_objs
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake
new file mode 100644
index 00000000..d5816b80
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-enum-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Enumerable module extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb
new file mode 100644
index 00000000..7741e515
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -0,0 +1,711 @@
+##
+# Enumerable
+#
+module Enumerable
+ ##
+ # call-seq:
+ # enum.drop(n) -> array
+ #
+ # Drops first n elements from <i>enum</i>, and returns rest elements
+ # in an array.
+ #
+ # a = [1, 2, 3, 4, 5, 0]
+ # a.drop(3) #=> [4, 5, 0]
+
+ def drop(n)
+ raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ raise ArgumentError, "attempt to drop negative size" if n < 0
+
+ n = n.to_int
+ ary = []
+ self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
+ ary
+ end
+
+ ##
+ # call-seq:
+ # enum.drop_while {|arr| block } -> array
+ # enum.drop_while -> an_enumerator
+ #
+ # Drops elements up to, but not including, the first element for
+ # which the block returns +nil+ or +false+ and returns an array
+ # containing the remaining elements.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # a = [1, 2, 3, 4, 5, 0]
+ # a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0]
+
+ def drop_while(&block)
+ return to_enum :drop_while unless block
+
+ ary, state = [], false
+ self.each do |*val|
+ state = true if !state and !block.call(*val)
+ ary << val.__svalue if state
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
+ # enum.take(n) -> array
+ #
+ # Returns first n elements from <i>enum</i>.
+ #
+ # a = [1, 2, 3, 4, 5, 0]
+ # a.take(3) #=> [1, 2, 3]
+
+ def take(n)
+ raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ i = n.to_int
+ raise ArgumentError, "attempt to take negative size" if i < 0
+ ary = []
+ return ary if i == 0
+ self.each do |*val|
+ ary << val.__svalue
+ i -= 1
+ break if i == 0
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
+ # enum.take_while {|arr| block } -> array
+ # enum.take_while -> an_enumerator
+ #
+ # Passes elements to the block until the block returns +nil+ or +false+,
+ # then stops iterating and returns an array of all prior elements.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # a = [1, 2, 3, 4, 5, 0]
+ # a.take_while {|i| i < 3 } #=> [1, 2]
+ #
+ def take_while(&block)
+ return to_enum :take_while unless block
+
+ ary = []
+ self.each do |*val|
+ return ary unless block.call(*val)
+ ary << val.__svalue
+ end
+ ary
+ end
+
+ ##
+ # Iterates the given block for each array of consecutive <n>
+ # elements.
+ #
+ # @return [nil]
+ #
+ # @example
+ # (1..10).each_cons(3) {|a| p a}
+ # # outputs below
+ # [1, 2, 3]
+ # [2, 3, 4]
+ # [3, 4, 5]
+ # [4, 5, 6]
+ # [5, 6, 7]
+ # [6, 7, 8]
+ # [7, 8, 9]
+ # [8, 9, 10]
+
+ def each_cons(n, &block)
+ raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ raise ArgumentError, "invalid size" if n <= 0
+
+ return to_enum(:each_cons,n) unless block
+ ary = []
+ n = n.to_int
+ self.each do |*val|
+ ary.shift if ary.size == n
+ ary << val.__svalue
+ block.call(ary.dup) if ary.size == n
+ end
+ nil
+ end
+
+ ##
+ # Iterates the given block for each slice of <n> elements.
+ #
+ # @return [nil]
+ #
+ # @example
+ # (1..10).each_slice(3) {|a| p a}
+ # # outputs below
+ # [1, 2, 3]
+ # [4, 5, 6]
+ # [7, 8, 9]
+ # [10]
+
+ def each_slice(n, &block)
+ raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ raise ArgumentError, "invalid slice size" if n <= 0
+
+ return to_enum(:each_slice,n) unless block
+ ary = []
+ n = n.to_int
+ self.each do |*val|
+ ary << val.__svalue
+ if ary.size == n
+ block.call(ary)
+ ary = []
+ end
+ end
+ block.call(ary) unless ary.empty?
+ nil
+ end
+
+ ##
+ # call-seq:
+ # enum.group_by {| obj | block } -> a_hash
+ # enum.group_by -> an_enumerator
+ #
+ # Returns a hash, which keys are evaluated result from the
+ # block, and values are arrays of elements in <i>enum</i>
+ # corresponding to the key.
+ #
+ # (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
+ #
+ def group_by(&block)
+ return to_enum :group_by unless block
+
+ h = {}
+ self.each do |*val|
+ key = block.call(*val)
+ sv = val.__svalue
+ h.key?(key) ? (h[key] << sv) : (h[key] = [sv])
+ end
+ h
+ end
+
+ ##
+ # call-seq:
+ # enum.sort_by { |obj| block } -> array
+ # enum.sort_by -> an_enumerator
+ #
+ # Sorts <i>enum</i> using a set of keys generated by mapping the
+ # values in <i>enum</i> through the given block.
+ #
+ # If no block is given, an enumerator is returned instead.
+
+ def sort_by(&block)
+ return to_enum :sort_by unless block
+
+ ary = []
+ orig = []
+ self.each_with_index{|e, i|
+ orig.push(e)
+ ary.push([block.call(e), i])
+ }
+ if ary.size > 1
+ __sort_sub__(ary, 0, ary.size - 1) do |a,b|
+ a <=> b
+ end
+ end
+ ary.collect{|e,i| orig[i]}
+ end
+
+ NONE = Object.new
+ ##
+ # call-seq:
+ # enum.first -> obj or nil
+ # enum.first(n) -> an_array
+ #
+ # Returns the first element, or the first +n+ elements, of the enumerable.
+ # If the enumerable is empty, the first form returns <code>nil</code>, and the
+ # second form returns an empty array.
+ def first(*args)
+ case args.length
+ when 0
+ self.each do |*val|
+ return val.__svalue
+ end
+ return nil
+ when 1
+ n = args[0]
+ raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ i = n.to_int
+ raise ArgumentError, "attempt to take negative size" if i < 0
+ ary = []
+ return ary if i == 0
+ self.each do |*val|
+ ary << val.__svalue
+ i -= 1
+ break if i == 0
+ end
+ ary
+ else
+ raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)"
+ end
+ end
+
+ ##
+ # call-seq:
+ # enum.count -> int
+ # enum.count(item) -> int
+ # enum.count { |obj| block } -> int
+ #
+ # Returns the number of items in +enum+ through enumeration.
+ # If an argument is given, the number of items in +enum+ that
+ # are equal to +item+ are counted. If a block is given, it
+ # counts the number of elements yielding a true value.
+ def count(v=NONE, &block)
+ count = 0
+ if block
+ self.each do |*val|
+ count += 1 if block.call(*val)
+ end
+ else
+ if v == NONE
+ self.each { count += 1 }
+ else
+ self.each do |*val|
+ count += 1 if val.__svalue == v
+ end
+ end
+ end
+ count
+ end
+
+ ##
+ # call-seq:
+ # enum.flat_map { |obj| block } -> array
+ # enum.collect_concat { |obj| block } -> array
+ # enum.flat_map -> an_enumerator
+ # enum.collect_concat -> an_enumerator
+ #
+ # Returns a new array with the concatenated results of running
+ # <em>block</em> once for every element in <i>enum</i>.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
+ # [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
+ def flat_map(&block)
+ return to_enum :flat_map unless block
+
+ ary = []
+ self.each do |*e|
+ e2 = block.call(*e)
+ if e2.respond_to? :each
+ e2.each {|e3| ary.push(e3) }
+ else
+ ary.push(e2)
+ end
+ end
+ ary
+ end
+ alias collect_concat flat_map
+
+ ##
+ # call-seq:
+ # enum.max_by {|obj| block } -> obj
+ # enum.max_by -> an_enumerator
+ #
+ # Returns the object in <i>enum</i> that gives the maximum
+ # value from the given block.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # %w[albatross dog horse].max_by {|x| x.length } #=> "albatross"
+
+ def max_by(&block)
+ return to_enum :max_by unless block
+
+ first = true
+ max = nil
+ max_cmp = nil
+
+ self.each do |*val|
+ if first
+ max = val.__svalue
+ max_cmp = block.call(*val)
+ first = false
+ else
+ if (cmp = block.call(*val)) > max_cmp
+ max = val.__svalue
+ max_cmp = cmp
+ end
+ end
+ end
+ max
+ end
+
+ ##
+ # call-seq:
+ # enum.min_by {|obj| block } -> obj
+ # enum.min_by -> an_enumerator
+ #
+ # Returns the object in <i>enum</i> that gives the minimum
+ # value from the given block.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # %w[albatross dog horse].min_by {|x| x.length } #=> "dog"
+
+ def min_by(&block)
+ return to_enum :min_by unless block
+
+ first = true
+ min = nil
+ min_cmp = nil
+
+ self.each do |*val|
+ if first
+ min = val.__svalue
+ min_cmp = block.call(*val)
+ first = false
+ else
+ if (cmp = block.call(*val)) < min_cmp
+ min = val.__svalue
+ min_cmp = cmp
+ end
+ end
+ end
+ min
+ end
+
+ ##
+ # call-seq:
+ # enum.minmax -> [min, max]
+ # enum.minmax { |a, b| block } -> [min, max]
+ #
+ # Returns two elements array which contains the minimum and the
+ # maximum value in the enumerable. The first form assumes all
+ # objects implement <code>Comparable</code>; the second uses the
+ # block to return <em>a <=> b</em>.
+ #
+ # a = %w(albatross dog horse)
+ # a.minmax #=> ["albatross", "horse"]
+ # a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"]
+
+ def minmax(&block)
+ max = nil
+ min = nil
+ first = true
+
+ self.each do |*val|
+ if first
+ val = val.__svalue
+ max = val
+ min = val
+ first = false
+ else
+ val = val.__svalue
+ if block
+ max = val if block.call(val, max) > 0
+ min = val if block.call(val, min) < 0
+ else
+ max = val if (val <=> max) > 0
+ min = val if (val <=> min) < 0
+ end
+ end
+ end
+ [min, max]
+ end
+
+ ##
+ # call-seq:
+ # enum.minmax_by { |obj| block } -> [min, max]
+ # enum.minmax_by -> an_enumerator
+ #
+ # Returns a two element array containing the objects in
+ # <i>enum</i> that correspond to the minimum and maximum values respectively
+ # from the given block.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # %w(albatross dog horse).minmax_by { |x| x.length } #=> ["dog", "albatross"]
+
+ def minmax_by(&block)
+ return to_enum :minmax_by unless block
+
+ max = nil
+ max_cmp = nil
+ min = nil
+ min_cmp = nil
+ first = true
+
+ self.each do |*val|
+ if first
+ max = min = val.__svalue
+ max_cmp = min_cmp = block.call(*val)
+ first = false
+ else
+ if (cmp = block.call(*val)) > max_cmp
+ max = val.__svalue
+ max_cmp = cmp
+ end
+ if (cmp = block.call(*val)) < min_cmp
+ min = val.__svalue
+ min_cmp = cmp
+ end
+ end
+ end
+ [min, max]
+ end
+
+ ##
+ # call-seq:
+ # enum.none? [{ |obj| block }] -> true or false
+ #
+ # Passes each element of the collection to the given block. The method
+ # returns <code>true</code> if the block never returns <code>true</code>
+ # for all elements. If the block is not given, <code>none?</code> will return
+ # <code>true</code> only if none of the collection members is true.
+ #
+ # %w(ant bear cat).none? { |word| word.length == 5 } #=> true
+ # %w(ant bear cat).none? { |word| word.length >= 4 } #=> false
+ # [].none? #=> true
+ # [nil, false].none? #=> true
+ # [nil, true].none? #=> false
+
+ def none?(&block)
+ if block
+ self.each do |*val|
+ return false if block.call(*val)
+ end
+ else
+ self.each do |*val|
+ return false if val.__svalue
+ end
+ end
+ true
+ end
+
+ ##
+ # call-seq:
+ # enum.one? [{ |obj| block }] -> true or false
+ #
+ # Passes each element of the collection to the given block. The method
+ # returns <code>true</code> if the block returns <code>true</code>
+ # exactly once. If the block is not given, <code>one?</code> will return
+ # <code>true</code> only if exactly one of the collection members is
+ # true.
+ #
+ # %w(ant bear cat).one? { |word| word.length == 4 } #=> true
+ # %w(ant bear cat).one? { |word| word.length > 4 } #=> false
+ # %w(ant bear cat).one? { |word| word.length < 4 } #=> false
+ # [nil, true, 99].one? #=> false
+ # [nil, true, false].one? #=> true
+ #
+
+ def one?(&block)
+ count = 0
+ if block
+ self.each do |*val|
+ count += 1 if block.call(*val)
+ return false if count > 1
+ end
+ else
+ self.each do |*val|
+ count += 1 if val.__svalue
+ return false if count > 1
+ end
+ end
+
+ count == 1 ? true : false
+ end
+
+ ##
+ # call-seq:
+ # enum.each_with_object(obj) { |(*args), memo_obj| ... } -> obj
+ # enum.each_with_object(obj) -> an_enumerator
+ #
+ # Iterates the given block for each element with an arbitrary
+ # object given, and returns the initially given object.
+ #
+ # If no block is given, returns an enumerator.
+ #
+ # (1..10).each_with_object([]) { |i, a| a << i*2 }
+ # #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
+ #
+
+ def each_with_object(obj=nil, &block)
+ raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil?
+
+ return to_enum(:each_with_object, obj) unless block
+
+ self.each {|*val| block.call(val.__svalue, obj) }
+ obj
+ end
+
+ ##
+ # call-seq:
+ # enum.reverse_each { |item| block } -> enum
+ # enum.reverse_each -> an_enumerator
+ #
+ # Builds a temporary array and traverses that array in reverse order.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # (1..3).reverse_each { |v| p v }
+ #
+ # produces:
+ #
+ # 3
+ # 2
+ # 1
+ #
+
+ def reverse_each(&block)
+ return to_enum :reverse_each unless block
+
+ ary = self.to_a
+ i = ary.size - 1
+ while i>=0
+ block.call(ary[i])
+ i -= 1
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # enum.cycle(n=nil) { |obj| block } -> nil
+ # enum.cycle(n=nil) -> an_enumerator
+ #
+ # Calls <i>block</i> for each element of <i>enum</i> repeatedly _n_
+ # times or forever if none or +nil+ is given. If a non-positive
+ # number is given or the collection is empty, does nothing. Returns
+ # +nil+ if the loop has finished without getting interrupted.
+ #
+ # Enumerable#cycle saves elements in an internal array so changes
+ # to <i>enum</i> after the first pass have no effect.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # a = ["a", "b", "c"]
+ # a.cycle { |x| puts x } # print, a, b, c, a, b, c,.. forever.
+ # a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c.
+ #
+
+ def cycle(nv = nil, &block)
+ return to_enum(:cycle, nv) unless block
+
+ n = nil
+
+ if nv.nil?
+ n = -1
+ else
+ unless nv.respond_to?(:to_int)
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer"
+ end
+ n = nv.to_int
+ unless n.kind_of?(Integer)
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer"
+ end
+ return nil if n <= 0
+ end
+
+ ary = []
+ each do |*i|
+ ary.push(i)
+ yield(*i)
+ end
+ return nil if ary.empty?
+
+ while n < 0 || 0 < (n -= 1)
+ ary.each do |i|
+ yield(*i)
+ end
+ end
+
+ nil
+ end
+
+ ##
+ # call-seq:
+ # enum.find_index(value) -> int or nil
+ # enum.find_index { |obj| block } -> int or nil
+ # enum.find_index -> an_enumerator
+ #
+ # Compares each entry in <i>enum</i> with <em>value</em> or passes
+ # to <em>block</em>. Returns the index for the first for which the
+ # evaluated value is non-false. If no object matches, returns
+ # <code>nil</code>
+ #
+ # If neither block nor argument is given, an enumerator is returned instead.
+ #
+ # (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
+ # (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 } #=> 34
+ # (1..100).find_index(50) #=> 49
+ #
+
+ def find_index(val=NONE, &block)
+ return to_enum(:find_index, val) if !block && val == NONE
+
+ idx = 0
+ if block
+ self.each do |*e|
+ return idx if block.call(*e)
+ idx += 1
+ end
+ else
+ self.each do |*e|
+ return idx if e.__svalue == val
+ idx += 1
+ end
+ end
+ nil
+ end
+
+ ##
+ # call-seq:
+ # enum.zip(arg, ...) -> an_array_of_array
+ #
+ # Takes one element from <i>enum</i> and merges corresponding
+ # elements from each <i>args</i>. This generates a sequence of
+ # <em>n</em>-element arrays, where <em>n</em> is one more than the
+ # count of arguments. The length of the resulting sequence will be
+ # <code>enum#size</code>. If the size of any argument is less than
+ # <code>enum#size</code>, <code>nil</code> values are supplied.
+ #
+
+ def zip(*arg)
+ ary = []
+ arg = arg.map{|a|a.to_a}
+ i = 0
+ self.each do |*val|
+ a = []
+ a.push(val.__svalue)
+ idx = 0
+ while idx < arg.size
+ a.push(arg[idx][i])
+ idx += 1
+ end
+ ary.push(a)
+ i += 1
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
+ # enum.to_h -> hash
+ #
+ # Returns the result of interpreting <i>enum</i> as a list of
+ # <tt>[key, value]</tt> pairs.
+ #
+ # %i[hello world].each_with_index.to_h
+ # # => {:hello => 0, :world => 1}
+ #
+
+ def to_h
+ h = {}
+ self.each do |*v|
+ v = v.__svalue
+ raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
+ raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
+ h[v[0]] = v[1]
+ end
+ h
+ end
+
+ def nil.to_h
+ {}
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb
new file mode 100644
index 00000000..e772f85b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-ext/test/enum.rb
@@ -0,0 +1,171 @@
+##
+# Enumerable(Ext) Test
+
+assert("Enumerable#drop") do
+ a = [1, 2, 3, 4, 5, 0]
+
+ assert_equal [4, 5, 0], a.drop(3)
+ assert_equal [], a.drop(6)
+end
+
+assert("Enumerable#drop_while") do
+ a = [1, 2, 3, 4, 5, 0]
+ assert_equal [3, 4, 5, 0], a.drop_while {|i| i < 3 }
+end
+
+assert("Enumerable#take") do
+ a = [1, 2, 3, 4, 5, 0]
+ assert_equal [1, 2, 3], a.take(3)
+end
+
+assert("Enumerable#take_while") do
+ a = [1, 2, 3, 4, 5, 0]
+ assert_equal [1, 2], a.take_while {|i| i < 3}
+end
+
+assert("Enumerable#each_cons") do
+ a = []
+ b = (1..5).each_cons(3){|e| a << e}
+ assert_equal [[1, 2, 3], [2, 3, 4], [3, 4, 5]], a
+ assert_equal nil, b
+end
+
+assert("Enumerable#each_slice") do
+ a = []
+ b = (1..10).each_slice(3){|e| a << e}
+ assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], a
+ assert_equal nil, b
+end
+
+assert("Enumerable#group_by") do
+ r = (1..6).group_by {|i| i % 3 }
+ assert_equal [3, 6], r[0]
+ assert_equal [1, 4], r[1]
+ assert_equal [2, 5], r[2]
+end
+
+assert("Enumerable#sort_by") do
+ assert_equal ["car", "train", "bicycle"], %w{car bicycle train}.sort_by {|e| e.length}
+end
+
+assert("Enumerable#first") do
+ a = Object.new
+ a.extend Enumerable
+ def a.each
+ yield 1
+ yield 2
+ yield 3
+ end
+ assert_equal 1, a.first
+ assert_equal [1, 2], a.first(2)
+ assert_equal [1, 2, 3], a.first(10)
+ a = Object.new
+ a.extend Enumerable
+ def a.each
+ end
+ assert_nil a.first
+end
+
+assert("Enumerable#count") do
+ a = [1, 2, 4, 2]
+ assert_equal 4, a.count
+ assert_equal 2, a.count(2)
+ assert_equal 3, a.count{|x| x % 2 == 0}
+end
+
+assert("Enumerable#flat_map") do
+ assert_equal [1, 2, 3, 4], [1, 2, 3, 4].flat_map { |e| e }
+ assert_equal [1, -1, 2, -2, 3, -3, 4, -4], [1, 2, 3, 4].flat_map { |e| [e, -e] }
+ assert_equal [1, 2, 100, 3, 4, 100], [[1, 2], [3, 4]].flat_map { |e| e + [100] }
+end
+
+assert("Enumerable#max_by") do
+ assert_equal "albatross", %w[albatross dog horse].max_by { |x| x.length }
+end
+
+assert("Enumerable#min_by") do
+ assert_equal "dog", %w[albatross dog horse].min_by { |x| x.length }
+end
+
+assert("Enumerable#minmax") do
+ a = %w(albatross dog horse)
+ assert_equal ["albatross", "horse"], a.minmax
+ assert_equal ["dog", "albatross"], a.minmax { |a, b| a.length <=> b.length }
+end
+
+assert("Enumerable#minmax_by") do
+ assert_equal ["dog", "albatross"], %w(albatross dog horse).minmax_by { |x| x.length }
+end
+
+assert("Enumerable#none?") do
+ assert_true %w(ant bear cat).none? { |word| word.length == 5 }
+ assert_false %w(ant bear cat).none? { |word| word.length >= 4 }
+ assert_true [].none?
+ assert_true [nil, false].none?
+ assert_false [nil, true].none?
+end
+
+assert("Enumerable#one?") do
+ assert_true %w(ant bear cat).one? { |word| word.length == 4 }
+ assert_false %w(ant bear cat).one? { |word| word.length > 4 }
+ assert_false %w(ant bear cat).one? { |word| word.length < 4 }
+ assert_false [nil, true, 99].one?
+ assert_true [nil, true, false].one?
+end
+
+assert("Enumerable#each_with_object") do
+ assert_true [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 }
+ assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } }
+end
+
+assert("Enumerable#reverse_each") do
+ r = (1..3)
+ a = []
+ assert_equal (1..3), r.reverse_each { |v| a << v }
+ assert_equal [3, 2, 1], a
+end
+
+assert("Enumerable#cycle") do
+ a = []
+ ["a", "b", "c"].cycle(2) { |v| a << v }
+ assert_equal ["a", "b", "c", "a", "b", "c"], a
+ assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } }
+
+ empty = Class.new do
+ include Enumerable
+ def each
+ end
+ end
+ assert_nil empty.new.cycle { break :nope }
+end
+
+assert("Enumerable#find_index") do
+ assert_nil (1..10).find_index { |i| i % 5 == 0 and i % 7 == 0 }
+ assert_equal 34, (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 }
+ assert_equal 49 ,(1..100).find_index(50)
+end
+
+assert("Enumerable#zip") do
+ a = [ 4, 5, 6 ]
+ b = [ 7, 8, 9 ]
+ assert_equal [[4, 7], [5, 8], [6, 9]], a.zip(b)
+ assert_equal [[1, 4, 7], [2, 5, 8], [3, 6, 9]], [1, 2, 3].zip(a, b)
+ assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b)
+ assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8])
+end
+
+assert("Enumerable#to_h") do
+ c = Class.new {
+ include Enumerable
+ def each
+ yield [1,2]
+ yield [3,4]
+ end
+ }
+ h0 = {1=>2, 3=>4}
+ h = c.new.to_h
+ assert_equal Hash, h.class
+ assert_equal h0, h
+ # mruby-enum-ext also provides nil.to_h
+ assert_equal Hash.new, nil.to_h
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake
new file mode 100644
index 00000000..682134c4
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Enumerator::Lazy class'
+ spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
+ spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext')
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
new file mode 100644
index 00000000..c98681ed
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
@@ -0,0 +1,163 @@
+module Enumerable
+
+ # = Enumerable#lazy implementation
+ #
+ # Enumerable#lazy returns an instance of Enumerator::Lazy.
+ # You can use it just like as normal Enumerable object,
+ # except these methods act as 'lazy':
+ #
+ # - map collect
+ # - select find_all
+ # - reject
+ # - grep
+ # - drop
+ # - drop_while
+ # - take_while
+ # - flat_map collect_concat
+ # - zip
+ def lazy
+ Enumerator::Lazy.new(self)
+ end
+end
+
+class Enumerator
+ # == Acknowledgements
+ #
+ # Based on https://github.com/yhara/enumerable-lazy
+ # Inspired by https://github.com/antimon2/enumerable_lz
+ # http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja)
+ class Lazy < Enumerator
+ def initialize(obj, &block)
+ super(){|yielder|
+ begin
+ obj.each{|x|
+ if block
+ block.call(yielder, x)
+ else
+ yielder << x
+ end
+ }
+ rescue StopIteration
+ end
+ }
+ end
+
+ def to_enum(meth=:each, *args, &block)
+ unless self.respond_to?(meth)
+ raise NoMethodError, "undefined method #{meth}"
+ end
+ lz = Lazy.new(self, &block)
+ lz.obj = self
+ lz.meth = meth
+ lz.args = args
+ lz
+ end
+ alias enum_for to_enum
+
+ def map(&block)
+ Lazy.new(self){|yielder, val|
+ yielder << block.call(val)
+ }
+ end
+ alias collect map
+
+ def select(&block)
+ Lazy.new(self){|yielder, val|
+ if block.call(val)
+ yielder << val
+ end
+ }
+ end
+ alias find_all select
+
+ def reject(&block)
+ Lazy.new(self){|yielder, val|
+ unless block.call(val)
+ yielder << val
+ end
+ }
+ end
+
+ def grep(pattern)
+ Lazy.new(self){|yielder, val|
+ if pattern === val
+ yielder << val
+ end
+ }
+ end
+
+ def drop(n)
+ dropped = 0
+ Lazy.new(self){|yielder, val|
+ if dropped < n
+ dropped += 1
+ else
+ yielder << val
+ end
+ }
+ end
+
+ def drop_while(&block)
+ dropping = true
+ Lazy.new(self){|yielder, val|
+ if dropping
+ if not block.call(val)
+ yielder << val
+ dropping = false
+ end
+ else
+ yielder << val
+ end
+ }
+ end
+
+ def take(n)
+ if n == 0
+ return Lazy.new(self){raise StopIteration}
+ end
+ taken = 0
+ Lazy.new(self){|yielder, val|
+ yielder << val
+ taken += 1
+ if taken >= n
+ raise StopIteration
+ end
+ }
+ end
+
+ def take_while(&block)
+ Lazy.new(self){|yielder, val|
+ if block.call(val)
+ yielder << val
+ else
+ raise StopIteration
+ end
+ }
+ end
+
+ def flat_map(&block)
+ Lazy.new(self){|yielder, val|
+ ary = block.call(val)
+ # TODO: check ary is an Array
+ ary.each{|x|
+ yielder << x
+ }
+ }
+ end
+ alias collect_concat flat_map
+
+ def zip(*args, &block)
+ enums = [self] + args
+ Lazy.new(self){|yielder, val|
+ ary = enums.map{|e| e.next}
+ if block
+ yielder << block.call(ary)
+ else
+ yielder << ary
+ end
+ }
+ end
+
+ alias force to_a
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb
new file mode 100644
index 00000000..940d070e
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb
@@ -0,0 +1,53 @@
+assert("Enumerator::Lazy") do
+ a = [1, 2]
+ assert_equal Enumerator::Lazy, a.lazy.class
+end
+
+assert("Enumerator::Lazy laziness") do
+ a = Object.new
+ def a.each
+ return to_enum :each unless block_given?
+ self.b << 10
+ yield 1
+ self.b << 20
+ yield 2
+ self.b << 30
+ yield 3
+ self.b << 40
+ yield 4
+ self.b << 50
+ yield 5
+ end
+ def a.b(b=nil)
+ @b = b if b
+ @b
+ end
+
+ a.b([])
+ assert_equal [1,2], a.each.lazy.take(2).force
+ assert_equal [10,20], a.b
+
+ a.b([])
+ assert_equal [2,4], a.each.lazy.select{|x|x%2==0}.take(2).force
+ assert_equal [10,20,30,40], a.b
+
+ a.b([])
+ assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(1).force
+ assert_equal [10], a.b
+
+ a.b([])
+ assert_equal [1], a.each.lazy.take_while{|x|x<2}.take(4).force
+ assert_equal [10,20], a.b
+end
+
+assert("Enumrator::Lazy#to_enum") do
+ lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2)
+ assert_kind_of Enumerator::Lazy, lazy_enum
+ assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4)
+end
+
+assert("Enumerator::Lazy#zip with cycle") do
+ e1 = [1, 2, 3].cycle
+ e2 = [:a, :b].cycle
+ assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake
new file mode 100644
index 00000000..8757a15e
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-enumerator') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.add_dependency('mruby-fiber', :core => 'mruby-fiber')
+ spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext'
+ spec.summary = 'Enumerator class'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb
new file mode 100644
index 00000000..1e77af36
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -0,0 +1,645 @@
+##
+# enumerator.rb Enumerator class
+# See Copyright Notice in mruby.h
+
+##
+# A class which allows both internal and external iteration.
+#
+# An Enumerator can be created by the following methods.
+# - {Kernel#to_enum}
+# - {Kernel#enum_for}
+# - {Enumerator#initialize Enumerator.new}
+#
+# Most methods have two forms: a block form where the contents
+# are evaluated for each item in the enumeration, and a non-block form
+# which returns a new Enumerator wrapping the iteration.
+#
+# enumerator = %w(one two three).each
+# puts enumerator.class # => Enumerator
+#
+# enumerator.each_with_object("foo") do |item, obj|
+# puts "#{obj}: #{item}"
+# end
+#
+# # foo: one
+# # foo: two
+# # foo: three
+#
+# enum_with_obj = enumerator.each_with_object("foo")
+# puts enum_with_obj.class # => Enumerator
+#
+# enum_with_obj.each do |item, obj|
+# puts "#{obj}: #{item}"
+# end
+#
+# # foo: one
+# # foo: two
+# # foo: three
+#
+# This allows you to chain Enumerators together. For example, you
+# can map a list's elements to strings containing the index
+# and the element as a string via:
+#
+# puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
+# # => ["0:foo", "1:bar", "2:baz"]
+#
+# An Enumerator can also be used as an external iterator.
+# For example, Enumerator#next returns the next value of the iterator
+# or raises StopIteration if the Enumerator is at the end.
+#
+# e = [1,2,3].each # returns an enumerator object.
+# puts e.next # => 1
+# puts e.next # => 2
+# puts e.next # => 3
+# puts e.next # raises StopIteration
+#
+# You can use this to implement an internal iterator as follows:
+#
+# def ext_each(e)
+# while true
+# begin
+# vs = e.next_values
+# rescue StopIteration
+# return $!.result
+# end
+# y = yield(*vs)
+# e.feed y
+# end
+# end
+#
+# o = Object.new
+#
+# def o.each
+# puts yield
+# puts yield(1)
+# puts yield(1, 2)
+# 3
+# end
+#
+# # use o.each as an internal iterator directly.
+# puts o.each {|*x| puts x; [:b, *x] }
+# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
+#
+# # convert o.each to an external iterator for
+# # implementing an internal iterator.
+# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
+# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
+#
+class Enumerator
+ include Enumerable
+
+ ##
+ # @overload initialize(size = nil, &block)
+ # @overload initialize(obj, method = :each, *args)
+ #
+ # Creates a new Enumerator object, which can be used as an
+ # Enumerable.
+ #
+ # In the first form, iteration is defined by the given block, in
+ # which a "yielder" object, given as block parameter, can be used to
+ # yield a value by calling the +yield+ method (aliased as +<<+):
+ #
+ # fib = Enumerator.new do |y|
+ # a = b = 1
+ # loop do
+ # y << a
+ # a, b = b, a + b
+ # end
+ # end
+ #
+ # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+ #
+ def initialize(obj=nil, meth=:each, *args, &block)
+ if block
+ obj = Generator.new(&block)
+ else
+ raise ArgumentError unless obj
+ end
+ if @obj and !self.respond_to?(meth)
+ raise NoMethodError, "undefined method #{meth}"
+ end
+
+ @obj = obj
+ @meth = meth
+ @args = args.dup
+ @fib = nil
+ @dst = nil
+ @lookahead = nil
+ @feedvalue = nil
+ @stop_exc = false
+ end
+ attr_accessor :obj, :meth, :args, :fib
+ private :obj, :meth, :args, :fib
+
+ def initialize_copy(obj)
+ raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
+ raise TypeError, "can't copy execution context" if obj.fib
+ @obj = obj.obj
+ @meth = obj.meth
+ @args = obj.args
+ @fib = nil
+ @lookahead = nil
+ @feedvalue = nil
+ self
+ end
+
+ ##
+ # call-seq:
+ # e.with_index(offset = 0) {|(*args), idx| ... }
+ # e.with_index(offset = 0)
+ #
+ # Iterates the given block for each element with an index, which
+ # starts from +offset+. If no block is given, returns a new Enumerator
+ # that includes the index, starting from +offset+
+ #
+ # +offset+:: the starting index to use
+ #
+ def with_index(offset=0, &block)
+ return to_enum :with_index, offset unless block
+
+ offset = if offset.nil?
+ 0
+ elsif offset.respond_to?(:to_int)
+ offset.to_int
+ else
+ raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+ end
+
+ n = offset - 1
+ enumerator_block_call do |*i|
+ n += 1
+ block.call i.__svalue, n
+ end
+ end
+
+ ##
+ # call-seq:
+ # e.each_with_index {|(*args), idx| ... }
+ # e.each_with_index
+ #
+ # Same as Enumerator#with_index(0), i.e. there is no starting offset.
+ #
+ # If no block is given, a new Enumerator is returned that includes the index.
+ #
+ def each_with_index(&block)
+ with_index(0, &block)
+ end
+
+ ##
+ # call-seq:
+ # e.each_with_object(obj) {|(*args), obj| ... }
+ # e.each_with_object(obj)
+ # e.with_object(obj) {|(*args), obj| ... }
+ # e.with_object(obj)
+ #
+ # Iterates the given block for each element with an arbitrary object, +obj+,
+ # and returns +obj+
+ #
+ # If no block is given, returns a new Enumerator.
+ #
+ # @example
+ # to_three = Enumerator.new do |y|
+ # 3.times do |x|
+ # y << x
+ # end
+ # end
+ #
+ # to_three_with_string = to_three.with_object("foo")
+ # to_three_with_string.each do |x,string|
+ # puts "#{string}: #{x}"
+ # end
+ #
+ # # => foo:0
+ # # => foo:1
+ # # => foo:2
+ #
+ def with_object(object, &block)
+ return to_enum(:with_object, object) unless block
+
+ enumerator_block_call do |i|
+ block.call [i,object]
+ end
+ object
+ end
+
+ def inspect
+ return "#<#{self.class}: uninitialized>" unless @obj
+
+ if @args && @args.size > 0
+ args = @args.join(", ")
+ "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
+ else
+ "#<#{self.class}: #{@obj}:#{@meth}>"
+ end
+ end
+
+ ##
+ # call-seq:
+ # enum.each { |elm| block } -> obj
+ # enum.each -> enum
+ # enum.each(*appending_args) { |elm| block } -> obj
+ # enum.each(*appending_args) -> an_enumerator
+ #
+ # Iterates over the block according to how this Enumerator was constructed.
+ # If no block and no arguments are given, returns self.
+ #
+ # === Examples
+ #
+ # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"]
+ # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"]
+ # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
+ #
+ # obj = Object.new
+ #
+ # def obj.each_arg(a, b=:b, *rest)
+ # yield a
+ # yield b
+ # yield rest
+ # :method_returned
+ # end
+ #
+ # enum = obj.to_enum :each_arg, :a, :x
+ #
+ # enum.each.to_a #=> [:a, :x, []]
+ # enum.each.equal?(enum) #=> true
+ # enum.each { |elm| elm } #=> :method_returned
+ #
+ # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]]
+ # enum.each(:y, :z).equal?(enum) #=> false
+ # enum.each(:y, :z) { |elm| elm } #=> :method_returned
+ #
+ def each(*argv, &block)
+ obj = self
+ if 0 < argv.length
+ obj = self.dup
+ args = obj.args
+ if !args.empty?
+ args = args.dup
+ args.concat argv
+ else
+ args = argv.dup
+ end
+ obj.args = args
+ end
+ return obj unless block
+ enumerator_block_call(&block)
+ end
+
+ def enumerator_block_call(&block)
+ @obj.__send__ @meth, *@args, &block
+ end
+ private :enumerator_block_call
+
+ ##
+ # call-seq:
+ # e.next -> object
+ #
+ # Returns the next object in the enumerator, and move the internal position
+ # forward. When the position reached at the end, StopIteration is raised.
+ #
+ # === Example
+ #
+ # a = [1,2,3]
+ # e = a.to_enum
+ # p e.next #=> 1
+ # p e.next #=> 2
+ # p e.next #=> 3
+ # p e.next #raises StopIteration
+ #
+ # Note that enumeration sequence by +next+ does not affect other non-external
+ # enumeration methods, unless the underlying iteration methods itself has
+ # side-effect
+ #
+ def next
+ next_values.__svalue
+ end
+
+ ##
+ # call-seq:
+ # e.next_values -> array
+ #
+ # Returns the next object as an array in the enumerator, and move the
+ # internal position forward. When the position reached at the end,
+ # StopIteration is raised.
+ #
+ # This method can be used to distinguish <code>yield</code> and <code>yield
+ # nil</code>.
+ #
+ # === Example
+ #
+ # o = Object.new
+ # def o.each
+ # yield
+ # yield 1
+ # yield 1, 2
+ # yield nil
+ # yield [1, 2]
+ # end
+ # e = o.to_enum
+ # p e.next_values
+ # p e.next_values
+ # p e.next_values
+ # p e.next_values
+ # p e.next_values
+ # e = o.to_enum
+ # p e.next
+ # p e.next
+ # p e.next
+ # p e.next
+ # p e.next
+ #
+ # ## yield args next_values next
+ # # yield [] nil
+ # # yield 1 [1] 1
+ # # yield 1, 2 [1, 2] [1, 2]
+ # # yield nil [nil] nil
+ # # yield [1, 2] [[1, 2]] [1, 2]
+ #
+ # Note that +next_values+ does not affect other non-external enumeration
+ # methods unless underlying iteration method itself has side-effect
+ #
+ def next_values
+ if @lookahead
+ vs = @lookahead
+ @lookahead = nil
+ return vs
+ end
+ raise @stop_exc if @stop_exc
+
+ curr = Fiber.current
+
+ if !@fib || !@fib.alive?
+ @dst = curr
+ @fib = Fiber.new do
+ result = each do |*args|
+ feedvalue = nil
+ Fiber.yield args
+ if @feedvalue
+ feedvalue = @feedvalue
+ @feedvalue = nil
+ end
+ feedvalue
+ end
+ @stop_exc = StopIteration.new "iteration reached an end"
+ @stop_exc.result = result
+ Fiber.yield nil
+ end
+ @lookahead = nil
+ end
+
+ vs = @fib.resume curr
+ if @stop_exc
+ @fib = nil
+ @dst = nil
+ @lookahead = nil
+ @feedvalue = nil
+ raise @stop_exc
+ end
+ vs
+ end
+
+ ##
+ # call-seq:
+ # e.peek -> object
+ #
+ # Returns the next object in the enumerator, but doesn't move the internal
+ # position forward. If the position is already at the end, StopIteration
+ # is raised.
+ #
+ # === Example
+ #
+ # a = [1,2,3]
+ # e = a.to_enum
+ # p e.next #=> 1
+ # p e.peek #=> 2
+ # p e.peek #=> 2
+ # p e.peek #=> 2
+ # p e.next #=> 2
+ # p e.next #=> 3
+ # p e.next #raises StopIteration
+ #
+ def peek
+ peek_values.__svalue
+ end
+
+ ##
+ # call-seq:
+ # e.peek_values -> array
+ #
+ # Returns the next object as an array, similar to Enumerator#next_values, but
+ # doesn't move the internal position forward. If the position is already at
+ # the end, StopIteration is raised.
+ #
+ # === Example
+ #
+ # o = Object.new
+ # def o.each
+ # yield
+ # yield 1
+ # yield 1, 2
+ # end
+ # e = o.to_enum
+ # p e.peek_values #=> []
+ # e.next
+ # p e.peek_values #=> [1]
+ # p e.peek_values #=> [1]
+ # e.next
+ # p e.peek_values #=> [1, 2]
+ # e.next
+ # p e.peek_values # raises StopIteration
+ #
+ def peek_values
+ if @lookahead.nil?
+ @lookahead = next_values
+ end
+ @lookahead.dup
+ end
+
+ ##
+ # call-seq:
+ # e.rewind -> e
+ #
+ # Rewinds the enumeration sequence to the beginning.
+ #
+ # If the enclosed object responds to a "rewind" method, it is called.
+ #
+ def rewind
+ @obj.rewind if @obj.respond_to? :rewind
+ @fib = nil
+ @dst = nil
+ @lookahead = nil
+ @feedvalue = nil
+ @stop_exc = false
+ self
+ end
+
+ ##
+ # call-seq:
+ # e.feed obj -> nil
+ #
+ # Sets the value to be returned by the next yield inside +e+.
+ #
+ # If the value is not set, the yield returns nil.
+ #
+ # This value is cleared after being yielded.
+ #
+ # # Array#map passes the array's elements to "yield" and collects the
+ # # results of "yield" as an array.
+ # # Following example shows that "next" returns the passed elements and
+ # # values passed to "feed" are collected as an array which can be
+ # # obtained by StopIteration#result.
+ # e = [1,2,3].map
+ # p e.next #=> 1
+ # e.feed "a"
+ # p e.next #=> 2
+ # e.feed "b"
+ # p e.next #=> 3
+ # e.feed "c"
+ # begin
+ # e.next
+ # rescue StopIteration
+ # p $!.result #=> ["a", "b", "c"]
+ # end
+ #
+ # o = Object.new
+ # def o.each
+ # x = yield # (2) blocks
+ # p x # (5) => "foo"
+ # x = yield # (6) blocks
+ # p x # (8) => nil
+ # x = yield # (9) blocks
+ # p x # not reached w/o another e.next
+ # end
+ #
+ # e = o.to_enum
+ # e.next # (1)
+ # e.feed "foo" # (3)
+ # e.next # (4)
+ # e.next # (7)
+ # # (10)
+ #
+ def feed(value)
+ raise TypeError, "feed value already set" if @feedvalue
+ @feedvalue = value
+ nil
+ end
+
+ # just for internal
+ class Generator
+ include Enumerable
+ def initialize(&block)
+ raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
+
+ @proc = block
+ end
+
+ def each(*args, &block)
+ args.unshift Yielder.new(&block)
+ @proc.call(*args)
+ end
+ end
+
+ # just for internal
+ class Yielder
+ def initialize(&block)
+ raise LocalJumpError, "no block given" unless block
+
+ @proc = block
+ end
+
+ def yield(*args)
+ @proc.call(*args)
+ end
+
+ def << *args
+ self.yield(*args)
+ self
+ end
+ end
+end
+
+module Kernel
+ ##
+ # call-seq:
+ # obj.to_enum(method = :each, *args) -> enum
+ # obj.enum_for(method = :each, *args) -> enum
+ # obj.to_enum(method = :each, *args) {|*args| block} -> enum
+ # obj.enum_for(method = :each, *args){|*args| block} -> enum
+ #
+ # Creates a new Enumerator which will enumerate by calling +method+ on
+ # +obj+, passing +args+ if any.
+ #
+ # If a block is given, it will be used to calculate the size of
+ # the enumerator without the need to iterate it (see Enumerator#size).
+ #
+ # === Examples
+ #
+ # str = "xyz"
+ #
+ # enum = str.enum_for(:each_byte)
+ # enum.each { |b| puts b }
+ # # => 120
+ # # => 121
+ # # => 122
+ #
+ # # protect an array from being modified by some_method
+ # a = [1, 2, 3]
+ # some_method(a.to_enum)
+ #
+ # It is typical to call to_enum when defining methods for
+ # a generic Enumerable, in case no block is passed.
+ #
+ # Here is such an example, with parameter passing and a sizing block:
+ #
+ # module Enumerable
+ # # a generic method to repeat the values of any enumerable
+ # def repeat(n)
+ # raise ArgumentError, "#{n} is negative!" if n < 0
+ # unless block_given?
+ # return to_enum(__method__, n) do # __method__ is :repeat here
+ # sz = size # Call size and multiply by n...
+ # sz * n if sz # but return nil if size itself is nil
+ # end
+ # end
+ # each do |*val|
+ # n.times { yield *val }
+ # end
+ # end
+ # end
+ #
+ # %i[hello world].repeat(2) { |w| puts w }
+ # # => Prints 'hello', 'hello', 'world', 'world'
+ # enum = (1..14).repeat(3)
+ # # => returns an Enumerator when called without a block
+ # enum.first(4) # => [1, 1, 1, 2]
+ #
+ def to_enum(meth=:each, *args)
+ Enumerator.new self, meth, *args
+ end
+ alias enum_for to_enum
+end
+
+module Enumerable
+ # use Enumerator to use infinite sequence
+ def zip(*arg)
+ ary = []
+ arg = arg.map{|a|a.each}
+ i = 0
+ self.each do |*val|
+ a = []
+ a.push(val.__svalue)
+ idx = 0
+ while idx < arg.size
+ begin
+ a.push(arg[idx].next)
+ rescue StopIteration
+ a.push(nil)
+ end
+ idx += 1
+ end
+ ary.push(a)
+ i += 1
+ end
+ ary
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb
new file mode 100644
index 00000000..763cd36e
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -0,0 +1,546 @@
+@obj = Object.new
+class << @obj
+ include Enumerable
+ def foo *a
+ a.each { |x| yield x }
+ end
+end
+
+assert 'Enumerator' do
+ assert_equal Class, Enumerator.class
+end
+
+assert 'Enumerator' do
+ assert_equal Object, Enumerator.superclass
+end
+
+assert 'Enumerator.new' do
+ assert_equal [0,1,2], 3.times.map{|i| i}.sort
+ assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort
+ assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort
+ assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a
+ assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a
+ assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3)
+ assert_raise(ArgumentError) { Enumerator.new }
+ enum = @obj.to_enum
+ assert_raise(NoMethodError) { enum.each {} }
+
+ # examples
+ fib = Enumerator.new do |y|
+ a = b = 1
+ loop do
+ y << a
+ a, b = b, a + b
+ end
+ end
+ assert_equal fib.take(10), [1,1,2,3,5,8,13,21,34,55]
+end
+
+assert 'Enumerator#initialize_copy' do
+ assert_equal [1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a
+ e = @obj.to_enum :foo, 1, 2, 3
+ assert_nothing_raised { assert_equal(1, e.next) }
+ assert_raise(TypeError) { e.dup }
+
+ e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup
+ assert_nothing_raised { assert_equal(1, e.next) }
+ assert_raise(TypeError) { e.dup }
+end
+
+assert 'Enumerator#with_index' do
+ assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
+ assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+ a = []
+ @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i }
+ assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
+end
+
+assert 'Enumerator#with_index nonnum offset' do
+ s = Object.new
+ def s.to_int; 1 end
+ assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
+end
+
+assert 'Enumerator#with_index string offset' do
+ assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
+end
+
+assert 'Enumerator#each_with_index' do
+ assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).each_with_index.to_a)
+ a = []
+ @obj.to_enum(:foo, 1, 2, 3).each_with_index {|*i| a << i}
+ assert_equal([[1, 0], [2, 1], [3, 2]], a)
+end
+
+assert 'Enumerator#with_object' do
+ obj = [0, 1]
+ ret = (1..10).each.with_object(obj) {|i, memo|
+ memo[0] += i
+ memo[1] *= i
+ }
+ assert_true(obj.equal?(ret))
+ assert_equal([55, 3628800], ret)
+end
+
+assert 'Enumerator#with_object arguments' do
+ to_three = Enumerator.new do |y|
+ 3.times do |x|
+ y << x
+ end
+ end
+
+ a = []
+ to_three_with_string = to_three.with_object("foo")
+ to_three_with_string.each do |x,string|
+ a << "#{string}:#{x}"
+ end
+ assert_equal ["foo:0","foo:1","foo:2"], a
+end
+
+assert 'Enumerator#inspect' do
+ e = (0..10).each
+ assert_equal("#<Enumerator: 0..10:each>", e.inspect)
+ e = Enumerator.new("FooObject", :foo, 1)
+ assert_equal("#<Enumerator: FooObject:foo(1)>", e.inspect)
+ e = Enumerator.new("FooObject", :foo, 1, 2, 3)
+ assert_equal("#<Enumerator: FooObject:foo(1, 2, 3)>", e.inspect)
+end
+
+assert 'Enumerator#each' do
+ o = Object.new
+ def o.each(ary)
+ ary << 1
+ yield
+ end
+ ary = []
+ e = o.to_enum.each(ary)
+ e.next
+ assert_equal([1], ary)
+end
+
+assert 'Enumerator#each arguments' do
+ obj = Object.new
+
+ def obj.each_arg(a, b=:b, *rest)
+ yield a
+ yield b
+ yield rest
+ :method_returned
+ end
+
+ enum = obj.to_enum :each_arg, :a, :x
+
+ assert_equal [:a, :x, []], enum.each.to_a
+ assert_true enum.each.equal?(enum)
+ assert_equal :method_returned, enum.each { |elm| elm }
+
+ assert_equal [:a, :x, [:y, :z]], enum.each(:y, :z).to_a
+ assert_false enum.each(:y, :z).equal?(enum)
+ assert_equal :method_returned, enum.each(:y, :z) { |elm| elm }
+end
+
+assert 'Enumerator#next' do
+ e = 3.times
+ 3.times { |i|
+ assert_equal i, e.next
+ }
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'Enumerator#next_values' do
+ o = Object.new
+ def o.each
+ yield
+ yield 1
+ yield 1, 2
+ end
+ e = o.to_enum
+ assert_equal nil, e.next
+ assert_equal 1, e.next
+ assert_equal [1,2], e.next
+ e = o.to_enum
+ assert_equal [], e.next_values
+ assert_equal [1], e.next_values
+ assert_equal [1,2], e.next_values
+end
+
+assert 'Enumerator#peek' do
+ a = [1]
+ e = a.each
+ assert_equal 1, e.peek
+ assert_equal 1, e.peek
+ assert_equal 1, e.next
+ assert_raise(StopIteration) { e.peek }
+ assert_raise(StopIteration) { e.peek }
+end
+
+assert 'Enumerator#peek modify' do
+ o = Object.new
+ def o.each
+ yield 1,2
+ end
+ e = o.to_enum
+ a = e.peek
+ a << 3
+ assert_equal([1,2], e.peek)
+end
+
+assert 'Enumerator#peek_values' do
+ o = Object.new
+ def o.each
+ yield
+ yield 1
+ yield 1, 2
+ end
+ e = o.to_enum
+ assert_equal nil, e.peek
+ assert_equal nil, e.next
+ assert_equal 1, e.peek
+ assert_equal 1, e.next
+ assert_equal [1,2], e.peek
+ assert_equal [1,2], e.next
+ e = o.to_enum
+ assert_equal [], e.peek_values
+ assert_equal [], e.next_values
+ assert_equal [1], e.peek_values
+ assert_equal [1], e.next_values
+ assert_equal [1,2], e.peek_values
+ assert_equal [1,2], e.next_values
+ e = o.to_enum
+ assert_equal [], e.peek_values
+ assert_equal nil, e.next
+ assert_equal [1], e.peek_values
+ assert_equal 1, e.next
+ assert_equal [1,2], e.peek_values
+ assert_equal [1,2], e.next
+ e = o.to_enum
+ assert_equal nil, e.peek
+ assert_equal [], e.next_values
+ assert_equal 1, e.peek
+ assert_equal [1], e.next_values
+ assert_equal [1,2], e.peek
+ assert_equal [1,2], e.next_values
+end
+
+assert 'Enumerator#peek_values modify' do
+ o = Object.new
+ def o.each
+ yield 1,2
+ end
+ e = o.to_enum
+ a = e.peek_values
+ a << 3
+ assert_equal [1,2], e.peek
+end
+
+assert 'Enumerator#feed' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.next
+ e.feed 3
+ assert_raise(StopIteration) { e.next }
+ assert_equal [1,2,3], ary
+end
+
+assert 'Enumerator#feed mixed' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.next
+ e.feed 1
+ e.next
+ e.next
+ e.feed 3
+ assert_raise(StopIteration) { e.next }
+ assert_equal [1,nil,3], ary
+end
+
+assert 'Enumerator#feed twice' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.feed 1
+ assert_raise(TypeError) { e.feed 2 }
+end
+
+assert 'Enumerator#feed before first next' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.feed 1
+ e.next
+ e.next
+ assert_equal [1], ary
+end
+
+assert 'Enumerator#feed yielder' do
+ x = nil
+ e = Enumerator.new {|y| x = y.yield; 10 }
+ e.next
+ e.feed 100
+ assert_raise(StopIteration) { e.next }
+ assert_equal 100, x
+end
+
+assert 'Enumerator#rewind' do
+ e = @obj.to_enum(:foo, 1, 2, 3)
+ assert_equal 1, e.next
+ assert_equal 2, e.next
+ e.rewind
+ assert_equal 1, e.next
+ assert_equal 2, e.next
+ assert_equal 3, e.next
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'Enumerator#rewind clear feed' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.rewind
+ e.next
+ e.next
+ assert_equal([1,nil], ary)
+end
+
+assert 'Enumerator#rewind clear' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.rewind
+ e.next
+ e.next
+ assert_equal [1,nil], ary
+end
+
+assert 'Enumerator::Generator' do
+ # note: Enumerator::Generator is a class just for internal
+ g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo }
+ g2 = g.dup
+ a = []
+ assert_equal(:foo, g.each {|x| a << x })
+ assert_equal([1, 2, 3], a)
+ a = []
+ assert_equal(:foo, g2.each {|x| a << x })
+ assert_equal([1, 2, 3], a)
+end
+
+assert 'Enumerator::Generator args' do
+ g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x }
+ a = []
+ assert_equal(:bar, g.each(:bar) {|x| a << x })
+ assert_equal([1, 2, 3], a)
+end
+
+assert 'Enumerator::Yielder' do
+ # note: Enumerator::Yielder is a class just for internal
+ a = []
+ y = Enumerator::Yielder.new {|x| a << x }
+ assert_equal(y, y << 1 << 2 << 3)
+ assert_equal([1, 2, 3], a)
+
+ a = []
+ y = Enumerator::Yielder.new {|x| a << x }
+ assert_equal([1], y.yield(1))
+ assert_equal([1, 2], y.yield(2))
+ assert_equal([1, 2, 3], y.yield(3))
+
+ assert_raise(LocalJumpError) { Enumerator::Yielder.new }
+end
+
+assert 'next after StopIteration' do
+ a = [1]
+ e = a.each
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.next }
+ assert_raise(StopIteration) { e.next }
+ e.rewind
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.next }
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'gc' do
+ assert_nothing_raised do
+ 1.times do
+ foo = [1,2,3].to_enum
+ GC.start
+ end
+ GC.start
+ end
+end
+
+assert 'nested iteration' do
+ def (o = Object.new).each
+ yield :ok1
+ yield [:ok2, :x].each.next
+ end
+ e = o.to_enum
+ assert_equal :ok1, e.next
+ assert_equal :ok2, e.next
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'Kernel#to_enum' do
+ assert_equal Enumerator, [].to_enum.class
+ assert_raise(ArgumentError){ nil.to_enum }
+end
+
+assert 'modifying existing methods' do
+ assert_equal Enumerator, loop.class
+ e = 3.times
+ i = 0
+ loop_ret = loop {
+ assert_equal i, e.next
+ i += 1
+ }
+end
+
+assert 'Integral#times' do
+ a = 3
+ b = a.times
+ c = []
+ b.with_object(c) do |i, obj|
+ obj << i
+ end
+ assert_equal 3, a
+ assert_equal Enumerator, b.class
+ assert_equal [0,1,2], c
+end
+
+assert 'Enumerable#each_with_index' do
+ assert_equal [['a',0],['b',1],['c',2]], ['a','b','c'].each_with_index.to_a
+end
+
+assert 'Enumerable#map' do
+ a = [1,2,3]
+ b = a.map
+ c = b.with_index do |i, index|
+ [i*i, index*index]
+ end
+ assert_equal [1,2,3], a
+ assert_equal [[1,0],[4,1],[9,4]], c
+end
+
+assert 'Enumerable#find_all' do
+ assert_equal [[3,4]], [[1,2],[3,4],[5,6]].find_all.each{ |i| i[1] == 4 }
+end
+
+assert 'Array#each_index' do
+ a = [1,2,3]
+ b = a.each_index
+ c = []
+ b.with_index do |index1,index2|
+ c << [index1+2,index2+5]
+ end
+ assert_equal [1,2,3], a
+ assert_equal [[2,5],[3,6],[4,7]], c
+end
+
+assert 'Array#map!' do
+ a = [1,2,3]
+ b = a.map!
+ b.with_index do |i, index|
+ [i*i, index*index]
+ end
+ assert_equal [[1,0],[4,1],[9,4]], a
+end
+
+assert 'Hash#each' do
+ a = {a:1,b:2}
+ b = a.each
+ c = []
+ b.each do |k,v|
+ c << [k,v]
+ end
+ assert_equal [[:a,1], [:b,2]], c.sort
+end
+
+assert 'Hash#each_key' do
+ assert_equal [:a,:b], {a:1,b:2}.each_key.to_a.sort
+end
+
+assert 'Hash#each_value' do
+ assert_equal [1,2], {a:1,b:2}.each_value.to_a.sort
+end
+
+assert 'Hash#select' do
+ h = {1=>2,3=>4,5=>6}
+ hret = h.select.with_index {|a,b| a[1] == 4}
+ assert_equal({3=>4}, hret)
+ assert_equal({1=>2,3=>4,5=>6}, h)
+end
+
+assert 'Hash#select!' do
+ h = {1=>2,3=>4,5=>6}
+ hret = h.select!.with_index {|a,b| a[1] == 4}
+ assert_equal h, hret
+ assert_equal({3=>4}, h)
+end
+
+assert 'Hash#reject' do
+ h = {1=>2,3=>4,5=>6}
+ hret = h.reject.with_index {|a,b| a[1] == 4}
+ assert_equal({1=>2,5=>6}, hret)
+ assert_equal({1=>2,3=>4,5=>6}, h)
+end
+
+assert 'Hash#reject!' do
+ h = {1=>2,3=>4,5=>6}
+ hret = h.reject!.with_index {|a,b| a[1] == 4}
+ assert_equal h, hret
+ assert_equal({1=>2,5=>6}, h)
+end
+
+assert 'Range#each' do
+ a = (1..5)
+ b = a.each
+ c = []
+ b.each do |i|
+ c << i
+ end
+ assert_equal [1,2,3,4,5], c
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake
new file mode 100644
index 00000000..30a4259a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/mrbgem.rake
@@ -0,0 +1,10 @@
+MRuby::Gem::Specification.new('mruby-error') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'extensional error handling'
+
+ if build.cxx_exception_enabled?
+ @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx")
+ @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") }
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c
new file mode 100644
index 00000000..170abb69
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/src/exception.c
@@ -0,0 +1,100 @@
+#include <mruby.h>
+#include <mruby/throw.h>
+#include <mruby/error.h>
+
+MRB_API mrb_value
+mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result = mrb_nil_value();
+
+ if (state) { *state = FALSE; }
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, data);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+ result = mrb_obj_value(mrb->exc);
+ mrb->exc = NULL;
+ if (state) { *state = TRUE; }
+ } MRB_END_EXC(&c_jmp);
+
+ mrb_gc_protect(mrb, result);
+ return result;
+}
+
+MRB_API mrb_value
+mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, b_data);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+ ensure(mrb, e_data);
+ MRB_THROW(mrb->jmp); /* rethrow catched exceptions */
+ } MRB_END_EXC(&c_jmp);
+
+ ensure(mrb, e_data);
+ mrb_gc_protect(mrb, result);
+ return result;
+}
+
+MRB_API mrb_value
+mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
+ mrb_func_t rescue, mrb_value r_data)
+{
+ return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, 1, &mrb->eStandardError_class);
+}
+
+MRB_API mrb_value
+mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data,
+ mrb_int len, struct RClass **classes)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result;
+ mrb_bool error_matched = FALSE;
+ mrb_int i;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, b_data);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+
+ for (i = 0; i < len; ++i) {
+ if (mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), classes[i])) {
+ error_matched = TRUE;
+ break;
+ }
+ }
+
+ if (!error_matched) { MRB_THROW(mrb->jmp); }
+
+ mrb->exc = NULL;
+ result = rescue(mrb, r_data);
+ } MRB_END_EXC(&c_jmp);
+
+ mrb_gc_protect(mrb, result);
+ return result;
+}
+
+void
+mrb_mruby_error_gem_init(mrb_state *mrb)
+{
+}
+
+void
+mrb_mruby_error_gem_final(mrb_state *mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c
new file mode 100644
index 00000000..4de0e960
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.c
@@ -0,0 +1,59 @@
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/array.h>
+
+static mrb_value
+protect_cb(mrb_state *mrb, mrb_value b)
+{
+ return mrb_yield_argv(mrb, b, 0, NULL);
+}
+
+static mrb_value
+run_protect(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b;
+ mrb_value ret[2];
+ mrb_bool state;
+ mrb_get_args(mrb, "&", &b);
+ ret[0] = mrb_protect(mrb, protect_cb, b, &state);
+ ret[1] = mrb_bool_value(state);
+ return mrb_ary_new_from_values(mrb, 2, ret);
+}
+
+static mrb_value
+run_ensure(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b, e;
+ mrb_get_args(mrb, "oo", &b, &e);
+ return mrb_ensure(mrb, protect_cb, b, protect_cb, e);
+}
+
+static mrb_value
+run_rescue(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b, r;
+ mrb_get_args(mrb, "oo", &b, &r);
+ return mrb_rescue(mrb, protect_cb, b, protect_cb, r);
+}
+
+static mrb_value
+run_rescue_exceptions(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b, r;
+ struct RClass *cls[1];
+ mrb_get_args(mrb, "oo", &b, &r);
+ cls[0] = E_TYPE_ERROR;
+ return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, 1, cls);
+}
+
+void
+mrb_mruby_error_gem_test(mrb_state *mrb)
+{
+ struct RClass *cls;
+
+ cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class);
+ mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK());
+ mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2));
+ mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2));
+ mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2));
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb
new file mode 100644
index 00000000..90846504
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-error/test/exception.rb
@@ -0,0 +1,55 @@
+assert 'mrb_protect' do
+ # no failure in protect returns [result, false]
+ assert_equal ['test', false] do
+ ExceptionTest.mrb_protect { 'test' }
+ end
+ # failure in protect returns [exception, true]
+ result = ExceptionTest.mrb_protect { raise 'test' }
+ assert_kind_of RuntimeError, result[0]
+ assert_true result[1]
+end
+
+assert 'mrb_ensure' do
+ a = false
+ assert_equal 'test' do
+ ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true }
+ end
+ assert_true a
+
+ a = false
+ assert_raise RuntimeError do
+ ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true }
+ end
+ assert_true a
+end
+
+assert 'mrb_rescue' do
+ assert_equal 'test' do
+ ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {}
+ end
+
+ class CustomExp < Exception
+ end
+
+ assert_raise CustomExp do
+ ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' }
+ end
+
+ assert_equal 'rescue' do
+ ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' }
+ end
+end
+
+assert 'mrb_rescue_exceptions' do
+ assert_equal 'test' do
+ ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {}
+ end
+
+ assert_raise RangeError do
+ ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' }
+ end
+
+ assert_equal 'rescue' do
+ ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' }
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake
new file mode 100644
index 00000000..cb8835b3
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-eval') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard Kernel#eval method'
+
+ add_dependency 'mruby-compiler', :core => 'mruby-compiler'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c
new file mode 100644
index 00000000..5c0ea82f
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/src/eval.c
@@ -0,0 +1,346 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/error.h>
+
+mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
+mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
+
+static struct mrb_irep *
+get_closure_irep(mrb_state *mrb, int level)
+{
+ struct mrb_context *c = mrb->c;
+ struct REnv *e = c->ci[-1].proc->env;
+ struct RProc *proc;
+
+ if (level == 0) {
+ proc = c->ci[-1].proc;
+ if (MRB_PROC_CFUNC_P(proc)) {
+ return NULL;
+ }
+ return proc->body.irep;
+ }
+
+ while (--level) {
+ e = (struct REnv*)e->c;
+ if (!e) return NULL;
+ }
+
+ if (!e) return NULL;
+ if (!MRB_ENV_STACK_SHARED_P(e)) return NULL;
+ c = e->cxt.c;
+ proc = c->cibase[e->cioff].proc;
+
+ if (!proc || MRB_PROC_CFUNC_P(proc)) {
+ return NULL;
+ }
+ return proc->body.irep;
+}
+
+/* search for irep lev above the bottom */
+static mrb_irep*
+search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
+{
+ int i;
+
+ for (i=0; i<top->rlen; i++) {
+ mrb_irep* tmp = top->reps[i];
+
+ if (tmp == bottom) return top;
+ tmp = search_irep(tmp, bnest-1, lev, bottom);
+ if (tmp) {
+ if (bnest == lev) return top;
+ return tmp;
+ }
+ }
+ return NULL;
+}
+
+static inline mrb_code
+search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
+{
+ mrb_irep *virep;
+ int level;
+ int pos;
+
+ for (level = 0; (virep = get_closure_irep(mrb, level)); level++) {
+ if (!virep || virep->lv == NULL) {
+ continue;
+ }
+ for (pos = 0; pos < virep->nlocals - 1; pos++) {
+ if (vsym == virep->lv[pos].name) {
+ return (MKARG_B(pos + 1) | MKARG_C(level + bnest));
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+irep_argc(mrb_irep *irep)
+{
+ mrb_code c;
+
+ c = irep->iseq[0];
+ if (GET_OPCODE(c) == OP_ENTER) {
+ mrb_aspec ax = GETARG_Ax(c);
+ /* extra 1 means a slot for block */
+ return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
+ }
+ return 0;
+}
+
+static mrb_bool
+potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
+{
+ if (v >= nlocals) return FALSE;
+ /* skip arguments */
+ if (v < argc+1) return FALSE;
+ return TRUE;
+}
+
+static void
+patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
+{
+ int i;
+ mrb_code c;
+ int argc = irep_argc(irep);
+
+ for (i = 0; i < irep->ilen; i++) {
+ c = irep->iseq[i];
+ switch(GET_OPCODE(c)){
+ case OP_EPUSH:
+ patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
+ break;
+
+ case OP_LAMBDA:
+ {
+ int arg_c = GETARG_c(c);
+ if (arg_c & OP_L_CAPTURE) {
+ patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top);
+ }
+ }
+ break;
+
+ case OP_SEND:
+ if (GETARG_C(c) != 0) {
+ break;
+ }
+ {
+ mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
+ if (arg != 0) {
+ /* must replace */
+ irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ }
+ }
+ break;
+
+ case OP_MOVE:
+ /* src part */
+ if (potential_upvar_p(irep->lv, GETARG_B(c), argc, irep->nlocals)) {
+ mrb_code arg = search_variable(mrb, irep->lv[GETARG_B(c) - 1].name, bnest);
+ if (arg != 0) {
+ /* must replace */
+ irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ }
+ }
+ /* dst part */
+ if (potential_upvar_p(irep->lv, GETARG_A(c), argc, irep->nlocals)) {
+ mrb_code arg = search_variable(mrb, irep->lv[GETARG_A(c) - 1].name, bnest);
+ if (arg != 0) {
+ /* must replace */
+ irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg;
+ }
+ }
+ break;
+
+ case OP_GETUPVAR:
+ {
+ int lev = GETARG_C(c)+1;
+ mrb_irep *tmp = search_irep(top, bnest, lev, irep);
+ if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
+ mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+ if (arg != 0) {
+ /* must replace */
+ irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ }
+ }
+ }
+ break;
+
+ case OP_SETUPVAR:
+ {
+ int lev = GETARG_C(c)+1;
+ mrb_irep *tmp = search_irep(top, bnest, lev, irep);
+ if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
+ mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+ if (arg != 0) {
+ /* must replace */
+ irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ }
+ }
+ }
+ break;
+
+ case OP_STOP:
+ if (mrb->c->ci->acc >= 0) {
+ irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
+ }
+ break;
+ }
+ }
+}
+
+void mrb_codedump_all(mrb_state*, struct RProc*);
+
+static struct RProc*
+create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, const char *file, mrb_int line)
+{
+ mrbc_context *cxt;
+ struct mrb_parser_state *p;
+ struct RProc *proc;
+ struct REnv *e;
+ struct mrb_context *c = mrb->c;
+
+ if (!mrb_nil_p(binding)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil.");
+ }
+
+ cxt = mrbc_context_new(mrb);
+ cxt->lineno = line;
+
+ mrbc_filename(mrb, cxt, file ? file : "(eval)");
+ cxt->capture_errors = TRUE;
+ cxt->no_optimize = TRUE;
+
+ p = mrb_parse_nstring(mrb, s, len, cxt);
+
+ /* only occur when memory ran out */
+ if (!p) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state.");
+ }
+
+ if (0 < p->nerr) {
+ /* parse error */
+ mrb_value str;
+
+ if (file) {
+ str = mrb_format(mrb, " file %S line %S: %S",
+ mrb_str_new_cstr(mrb, file),
+ mrb_fixnum_value(p->error_buffer[0].lineno),
+ mrb_str_new_cstr(mrb, p->error_buffer[0].message));
+ }
+ else {
+ str = mrb_format(mrb, " line %S: %S",
+ mrb_fixnum_value(p->error_buffer[0].lineno),
+ mrb_str_new_cstr(mrb, p->error_buffer[0].message));
+ }
+ mrb_parser_free(p);
+ mrbc_context_free(mrb, cxt);
+ mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str));
+ }
+
+ proc = mrb_generate_code(mrb, p);
+ if (proc == NULL) {
+ /* codegen error */
+ mrb_parser_free(p);
+ mrbc_context_free(mrb, cxt);
+ mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
+ }
+ if (c->ci[-1].proc->target_class) {
+ proc->target_class = c->ci[-1].proc->target_class;
+ }
+ e = c->ci[-1].proc->env;
+ if (!e) e = c->ci[-1].env;
+ e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e);
+ e->cxt.c = c;
+ e->cioff = c->ci - c->cibase;
+ e->stack = c->ci->stackent;
+ MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals);
+ c->ci->target_class = proc->target_class;
+ c->ci->env = 0;
+ proc->env = e;
+ patch_irep(mrb, proc->body.irep, 0, proc->body.irep);
+ /* mrb_codedump_all(mrb, proc); */
+
+ mrb_parser_free(p);
+ mrbc_context_free(mrb, cxt);
+
+ return proc;
+}
+
+static mrb_value
+exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
+{
+ if (mrb->c->ci->acc < 0) {
+ mrb_value ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0);
+ if (mrb->exc) {
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
+ }
+ return ret;
+ }
+ return mrb_exec_irep(mrb, self, proc);
+}
+
+static mrb_value
+f_eval(mrb_state *mrb, mrb_value self)
+{
+ char *s;
+ mrb_int len;
+ mrb_value binding = mrb_nil_value();
+ char *file = NULL;
+ mrb_int line = 1;
+ struct RProc *proc;
+
+ mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
+
+ proc = create_proc_from_string(mrb, s, len, binding, file, line);
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ return exec_irep(mrb, self, proc);
+}
+
+static mrb_value
+f_instance_eval(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b;
+ mrb_int argc; mrb_value *argv;
+
+ mrb_get_args(mrb, "*!&", &argv, &argc, &b);
+
+ if (mrb_nil_p(b)) {
+ char *s;
+ mrb_int len;
+ char *file = NULL;
+ mrb_int line = 1;
+ mrb_value cv;
+ struct RProc *proc;
+
+ mrb_get_args(mrb, "s|zi", &s, &len, &file, &line);
+ cv = mrb_singleton_class(mrb, self);
+ proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
+ proc->target_class = mrb_class_ptr(cv);
+ mrb->c->ci->env = NULL;
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ return exec_irep(mrb, self, proc);
+ }
+ else {
+ mrb_get_args(mrb, "&", &b);
+ return mrb_obj_instance_eval(mrb, self);
+ }
+}
+
+void
+mrb_mruby_eval_gem_init(mrb_state* mrb)
+{
+ mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3));
+ mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2));
+}
+
+void
+mrb_mruby_eval_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb
new file mode 100644
index 00000000..66ca1fcd
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-eval/test/eval.rb
@@ -0,0 +1,101 @@
+assert('Kernel.eval', '15.3.1.2.3') do
+ assert_equal(10) { Kernel.eval '1 * 10' }
+ assert_equal('aaa') { Kernel.eval "'a' * 3" }
+ assert_equal(10) {
+ a = 10
+ Kernel.eval "a"
+ }
+ assert_equal(20) {
+ a = 10
+ Kernel.eval "a = 20"
+ a
+ }
+ assert_equal(15) {
+ c = 5
+ lambda {
+ a = 10
+ Kernel.eval "c = a + c"
+ }.call
+ c
+ }
+ assert_equal(5) {
+ c = 5
+ lambda {
+ Kernel.eval 'lambda { c }.call'
+ }.call
+ }
+ assert_equal(15) {
+ c = 5
+ lambda {
+ a = 10
+ Kernel.eval 'lambda { c = a + c }.call'
+ }.call
+ c
+ }
+ assert_equal(2) {
+ a = 10
+ Kernel.eval 'def f(a); b=a.send(:+, 1); end'
+ f(1)
+ }
+end
+
+assert('Kernel#eval', '15.3.1.3.12') do
+ assert_equal(10) { eval '1 * 10' }
+end
+
+assert('rest arguments of eval') do
+ assert_raise(ArgumentError) { Kernel.eval('0', 0, 'test', 0) }
+ assert_equal ['test', 'test.rb', 10] do
+ Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10)
+ end
+end
+
+assert 'eval syntax error' do
+ assert_raise(SyntaxError) do
+ eval 'p "test'
+ end
+end
+
+assert('String instance_eval') do
+ obj = Object.new
+ obj.instance_variable_set :@test, 'test'
+ assert_raise(ArgumentError) { obj.instance_eval(0) { } }
+ assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') }
+ assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)}
+ assert_equal('test') { obj.instance_eval('@test') }
+ assert_equal('test') { obj.instance_eval { @test } }
+ o = Object.new
+ assert_equal ['', o, o], o.instance_eval("[''].each { |s| break [s, o, self] }")
+end
+
+assert('Kernel.#eval(string) context') do
+ class TestEvalConstScope
+ EVAL_CONST_CLASS = 'class'
+ def const_string
+ eval 'EVAL_CONST_CLASS'
+ end
+ end
+ obj = TestEvalConstScope.new
+ assert_raise(NameError) { eval 'EVAL_CONST_CLASS' }
+ assert_equal('class') { obj.const_string }
+end
+
+assert('Object#instance_eval with begin-rescue-ensure execution order') do
+ class HellRaiser
+ def raise_hell
+ order = [:enter_raise_hell]
+ begin
+ order.push :begin
+ self.instance_eval("raise 'error'")
+ rescue
+ order.push :rescue
+ ensure
+ order.push :ensure
+ end
+ order
+ end
+ end
+
+ hell_raiser = HellRaiser.new
+ assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake
new file mode 100644
index 00000000..d193528d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-exit') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Kernel#exit method'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c
new file mode 100644
index 00000000..3e147f80
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-exit/src/mruby-exit.c
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+#include <mruby.h>
+
+static mrb_value
+f_exit(mrb_state *mrb, mrb_value self)
+{
+ mrb_int i = EXIT_SUCCESS;
+
+ mrb_get_args(mrb, "|i", &i);
+ exit(i);
+ /* not reached */
+ return mrb_nil_value();
+}
+
+void
+mrb_mruby_exit_gem_init(mrb_state* mrb)
+{
+ mrb_define_method(mrb, mrb->kernel_module, "exit", f_exit, MRB_ARGS_OPT(1));
+}
+
+void
+mrb_mruby_exit_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake
new file mode 100644
index 00000000..815cd3c4
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-fiber') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Fiber class'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c
new file mode 100644
index 00000000..9de175f3
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/src/fiber.c
@@ -0,0 +1,420 @@
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+
+#define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o))
+
+#define FIBER_STACK_INIT_SIZE 64
+#define FIBER_CI_INIT_SIZE 8
+#define CI_ACC_RESUMED -3
+
+/*
+ * call-seq:
+ * Fiber.new{...} -> obj
+ *
+ * Creates a fiber, whose execution is suspend until it is explicitly
+ * resumed using <code>Fiber#resume</code> method.
+ * The code running inside the fiber can give up control by calling
+ * <code>Fiber.yield</code> in which case it yields control back to caller
+ * (the caller of the <code>Fiber#resume</code>).
+ *
+ * Upon yielding or termination the Fiber returns the value of the last
+ * executed expression
+ *
+ * For instance:
+ *
+ * fiber = Fiber.new do
+ * Fiber.yield 1
+ * 2
+ * end
+ *
+ * puts fiber.resume
+ * puts fiber.resume
+ * puts fiber.resume
+ *
+ * <em>produces</em>
+ *
+ * 1
+ * 2
+ * resuming dead fiber (FiberError)
+ *
+ * The <code>Fiber#resume</code> method accepts an arbitrary number of
+ * parameters, if it is the first call to <code>resume</code> then they
+ * will be passed as block arguments. Otherwise they will be the return
+ * value of the call to <code>Fiber.yield</code>
+ *
+ * Example:
+ *
+ * fiber = Fiber.new do |first|
+ * second = Fiber.yield first + 2
+ * end
+ *
+ * puts fiber.resume 10
+ * puts fiber.resume 14
+ * puts fiber.resume 18
+ *
+ * <em>produces</em>
+ *
+ * 12
+ * 14
+ * resuming dead fiber (FiberError)
+ *
+ */
+static mrb_value
+fiber_init(mrb_state *mrb, mrb_value self)
+{
+ static const struct mrb_context mrb_context_zero = { 0 };
+ struct RFiber *f = fiber_ptr(self);
+ struct mrb_context *c;
+ struct RProc *p;
+ mrb_callinfo *ci;
+ mrb_value blk;
+ size_t slen;
+
+ mrb_get_args(mrb, "&", &blk);
+
+ if (f->cxt) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice");
+ }
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block");
+ }
+ p = mrb_proc_ptr(blk);
+ if (MRB_PROC_CFUNC_P(p)) {
+ mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method");
+ }
+
+ c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
+ *c = mrb_context_zero;
+ f->cxt = c;
+
+ /* initialize VM stack */
+ slen = FIBER_STACK_INIT_SIZE;
+ if (p->body.irep->nregs > slen) {
+ slen += p->body.irep->nregs;
+ }
+ c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value));
+ c->stend = c->stbase + slen;
+ c->stack = c->stbase;
+
+#ifdef MRB_NAN_BOXING
+ {
+ mrb_value *p = c->stbase;
+ mrb_value *pend = c->stend;
+
+ while (p < pend) {
+ SET_NIL_VALUE(*p);
+ p++;
+ }
+ }
+#else
+ memset(c->stbase, 0, slen * sizeof(mrb_value));
+#endif
+
+ /* copy receiver from a block */
+ c->stack[0] = mrb->c->stack[0];
+
+ /* initialize callinfo stack */
+ c->cibase = (mrb_callinfo *)mrb_calloc(mrb, FIBER_CI_INIT_SIZE, sizeof(mrb_callinfo));
+ c->ciend = c->cibase + FIBER_CI_INIT_SIZE;
+ c->ci = c->cibase;
+ c->ci->stackent = c->stack;
+
+ /* adjust return callinfo */
+ ci = c->ci;
+ ci->target_class = p->target_class;
+ ci->proc = p;
+ mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
+ ci->pc = p->body.irep->iseq;
+ ci->nregs = p->body.irep->nregs;
+ ci[1] = ci[0];
+ c->ci++; /* push dummy callinfo */
+
+ c->fib = f;
+ c->status = MRB_FIBER_CREATED;
+
+ return self;
+}
+
+static struct mrb_context*
+fiber_check(mrb_state *mrb, mrb_value fib)
+{
+ struct RFiber *f = fiber_ptr(fib);
+
+ mrb_assert(f->tt == MRB_TT_FIBER);
+ if (!f->cxt) {
+ mrb_raise(mrb, E_FIBER_ERROR, "uninitialized Fiber");
+ }
+ return f->cxt;
+}
+
+static mrb_value
+fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len)
+{
+ if (len == 0) return mrb_nil_value();
+ if (len == 1) return a[0];
+ return mrb_ary_new_from_values(mrb, len, a);
+}
+
+/* mark return from context modifying method */
+#define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL
+
+static void
+fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
+{
+ mrb_callinfo *ci;
+
+ for (ci = c->ci; ci >= c->cibase; ci--) {
+ if (ci->acc < 0) {
+ mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary");
+ }
+ }
+}
+
+static void
+fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
+{
+ if (mrb->c->fib) {
+ mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+ }
+ c->status = MRB_FIBER_RUNNING;
+ mrb->c = c;
+}
+
+static mrb_value
+fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec)
+{
+ struct mrb_context *c = fiber_check(mrb, self);
+ struct mrb_context *old_c = mrb->c;
+ mrb_value value;
+
+ fiber_check_cfunc(mrb, c);
+ if (resume && c->status == MRB_FIBER_TRANSFERRED) {
+ mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
+ }
+ if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
+ mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
+ }
+ if (c->status == MRB_FIBER_TERMINATED) {
+ mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
+ }
+ mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
+ c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
+ if (c->status == MRB_FIBER_CREATED) {
+ mrb_value *b, *e;
+
+ if (len >= c->stend - c->stack) {
+ mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
+ }
+ b = c->stack+1;
+ e = b + len;
+ while (b<e) {
+ *b++ = *a++;
+ }
+ c->cibase->argc = len;
+ value = c->stack[0] = c->ci->proc->env->stack[0];
+ }
+ else {
+ value = fiber_result(mrb, a, len);
+ }
+ fiber_switch_context(mrb, c);
+
+ if (vmexec) {
+ c->vmexec = TRUE;
+ value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc);
+ mrb->c = old_c;
+ }
+ else {
+ MARK_CONTEXT_MODIFY(c);
+ }
+ return value;
+}
+
+/*
+ * call-seq:
+ * fiber.resume(args, ...) -> obj
+ *
+ * Resumes the fiber from the point at which the last <code>Fiber.yield</code>
+ * was called, or starts running it if it is the first call to
+ * <code>resume</code>. Arguments passed to resume will be the value of
+ * the <code>Fiber.yield</code> expression or will be passed as block
+ * parameters to the fiber's block if this is the first <code>resume</code>.
+ *
+ * Alternatively, when resume is called it evaluates to the arguments passed
+ * to the next <code>Fiber.yield</code> statement inside the fiber's block
+ * or to the block value if it runs to completion without any
+ * <code>Fiber.yield</code>
+ */
+static mrb_value
+fiber_resume(mrb_state *mrb, mrb_value self)
+{
+ mrb_value *a;
+ mrb_int len;
+ mrb_bool vmexec = FALSE;
+
+ mrb_get_args(mrb, "*!", &a, &len);
+ if (mrb->c->ci->acc < 0) {
+ vmexec = TRUE;
+ }
+ return fiber_switch(mrb, self, len, a, TRUE, vmexec);
+}
+
+/* resume thread with given arguments */
+MRB_API mrb_value
+mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a)
+{
+ return fiber_switch(mrb, fib, len, a, TRUE, TRUE);
+}
+
+/*
+ * call-seq:
+ * fiber.alive? -> true or false
+ *
+ * Returns true if the fiber can still be resumed. After finishing
+ * execution of the fiber block this method will always return false.
+ */
+static mrb_value
+fiber_alive_p(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_context *c = fiber_check(mrb, self);
+ return mrb_bool_value(c->status != MRB_FIBER_TERMINATED);
+}
+
+static mrb_value
+fiber_eq(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other;
+ mrb_get_args(mrb, "o", &other);
+
+ if (mrb_type(other) != MRB_TT_FIBER) {
+ return mrb_false_value();
+ }
+ return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other));
+}
+
+/*
+ * call-seq:
+ * fiber.transfer(args, ...) -> obj
+ *
+ * Transfers control to receiver fiber of the method call.
+ * Unlike <code>resume</code> the receiver wouldn't be pushed to call
+ * stack of fibers. Instead it will switch to the call stack of
+ * transferring fiber.
+ * When resuming a fiber that was transferred to another fiber it would
+ * cause double resume error. Though when the fiber is re-transferred
+ * and <code>Fiber.yield</code> is called, the fiber would be resumable.
+ */
+static mrb_value
+fiber_transfer(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_context *c = fiber_check(mrb, self);
+ mrb_value* a;
+ mrb_int len;
+
+ fiber_check_cfunc(mrb, mrb->c);
+ mrb_get_args(mrb, "*!", &a, &len);
+
+ if (c == mrb->root_c) {
+ mrb->c->status = MRB_FIBER_TRANSFERRED;
+ fiber_switch_context(mrb, c);
+ MARK_CONTEXT_MODIFY(c);
+ return fiber_result(mrb, a, len);
+ }
+
+ if (c == mrb->c) {
+ return fiber_result(mrb, a, len);
+ }
+
+ return fiber_switch(mrb, self, len, a, FALSE, FALSE);
+}
+
+/* yield values to the caller fiber */
+/* mrb_fiber_yield() must be called as `return mrb_fiber_yield(...)` */
+MRB_API mrb_value
+mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
+{
+ struct mrb_context *c = mrb->c;
+
+ if (!c->prev) {
+ mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber");
+ }
+
+ fiber_check_cfunc(mrb, c);
+ c->prev->status = MRB_FIBER_RUNNING;
+ c->status = MRB_FIBER_SUSPENDED;
+ fiber_switch_context(mrb, c->prev);
+ c->prev = NULL;
+ if (c->vmexec) {
+ c->vmexec = FALSE;
+ mrb->c->ci->acc = CI_ACC_RESUMED;
+ }
+ MARK_CONTEXT_MODIFY(mrb->c);
+ return fiber_result(mrb, a, len);
+}
+
+/*
+ * call-seq:
+ * Fiber.yield(args, ...) -> obj
+ *
+ * Yields control back to the context that resumed the fiber, passing
+ * along any arguments that were passed to it. The fiber will resume
+ * processing at this point when <code>resume</code> is called next.
+ * Any arguments passed to the next <code>resume</code> will be the
+ *
+ * mruby limitation: Fiber resume/yield cannot cross C function boundary.
+ * thus you cannot yield from #initialize which is called by mrb_funcall().
+ */
+static mrb_value
+fiber_yield(mrb_state *mrb, mrb_value self)
+{
+ mrb_value *a;
+ mrb_int len;
+
+ mrb_get_args(mrb, "*!", &a, &len);
+ return mrb_fiber_yield(mrb, len, a);
+}
+
+/*
+ * call-seq:
+ * Fiber.current() -> fiber
+ *
+ * Returns the current fiber. If you are not running in the context of
+ * a fiber this method will return the root fiber.
+ */
+static mrb_value
+fiber_current(mrb_state *mrb, mrb_value self)
+{
+ if (!mrb->c->fib) {
+ struct RFiber *f = (struct RFiber*)mrb_obj_alloc(mrb, MRB_TT_FIBER, mrb_class_ptr(self));
+
+ f->cxt = mrb->c;
+ mrb->c->fib = f;
+ }
+ return mrb_obj_value(mrb->c->fib);
+}
+
+void
+mrb_mruby_fiber_gem_init(mrb_state* mrb)
+{
+ struct RClass *c;
+
+ c = mrb_define_class(mrb, "Fiber", mrb->object_class);
+ MRB_SET_INSTANCE_TT(c, MRB_TT_FIBER);
+
+ mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE());
+ mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY());
+ mrb_define_method(mrb, c, "transfer", fiber_transfer, MRB_ARGS_ANY());
+ mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, c, "==", fiber_eq, MRB_ARGS_REQ(1));
+
+ mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, c, "current", fiber_current, MRB_ARGS_NONE());
+
+ mrb_define_class(mrb, "FiberError", mrb->eStandardError_class);
+}
+
+void
+mrb_mruby_fiber_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb
new file mode 100644
index 00000000..d063a0a6
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-fiber/test/fiber.rb
@@ -0,0 +1,208 @@
+assert('Fiber.new') do
+ f = Fiber.new{}
+ assert_kind_of Fiber, f
+end
+
+assert('Fiber#resume') do
+ f = Fiber.new{|x| x }
+ assert_equal 2, f.resume(2)
+end
+
+assert('Fiber#transfer') do
+ f2 = nil
+ f1 = Fiber.new do |v|
+ Fiber.yield v
+ f2.transfer
+ end
+ f2 = Fiber.new do
+ f1.transfer(1)
+ f1.transfer(1)
+ Fiber.yield 2
+ end
+ assert_equal 1, f2.resume
+ assert_raise(FiberError) { f2.resume }
+ assert_equal 2, f2.transfer
+ assert_raise(FiberError) { f1.resume }
+ f1.transfer
+ f2.resume
+ assert_false f1.alive?
+ assert_false f2.alive?
+end
+
+assert('Fiber#alive?') do
+ f = Fiber.new{ Fiber.yield }
+ f.resume
+ assert_true f.alive?
+ f.resume
+ assert_false f.alive?
+end
+
+assert('Fiber#==') do
+ root = Fiber.current
+ assert_equal root, root
+ assert_equal root, Fiber.current
+ assert_false root != Fiber.current
+ f = Fiber.new {
+ assert_false root == Fiber.current
+ }
+ f.resume
+ assert_false f == root
+ assert_true f != root
+end
+
+assert('Fiber.yield') do
+ f = Fiber.new{|x| Fiber.yield x }
+ assert_equal 3, f.resume(3)
+ assert_true f.alive?
+end
+
+assert('FiberError') do
+ assert_equal StandardError, FiberError.superclass
+end
+
+assert('Fiber iteration') do
+ f1 = Fiber.new{
+ [1,2,3].each{|x| Fiber.yield(x)}
+ }
+ f2 = Fiber.new{
+ [9,8,7].each{|x| Fiber.yield(x)}
+ }
+ a = []
+ 3.times {
+ a << f1.resume
+ a << f2.resume
+ }
+ assert_equal [1,9,2,8,3,7], a
+end
+
+assert('Fiber with splat in the block argument list') {
+ Fiber.new{|*x|x}.resume(1) == [1]
+}
+
+assert('Fiber raises on resume when dead') do
+ assert_raise(FiberError) do
+ f = Fiber.new{}
+ f.resume
+ assert_false f.alive?
+ f.resume
+ end
+end
+
+assert('Yield raises when called on root fiber') do
+ assert_raise(FiberError) { Fiber.yield }
+end
+
+assert('Double resume of Fiber') do
+ f1 = Fiber.new {}
+ f2 = Fiber.new {
+ f1.resume
+ assert_raise(FiberError) { f2.resume }
+ Fiber.yield 0
+ }
+ assert_equal 0, f2.resume
+ f2.resume
+ assert_false f1.alive?
+ assert_false f2.alive?
+end
+
+assert('Recursive resume of Fiber') do
+ f1, f2 = nil, nil
+ f1 = Fiber.new { assert_raise(FiberError) { f2.resume } }
+ f2 = Fiber.new {
+ f1.resume
+ Fiber.yield 0
+ }
+ f3 = Fiber.new {
+ f2.resume
+ }
+ assert_equal 0, f3.resume
+ f2.resume
+ assert_false f1.alive?
+ assert_false f2.alive?
+ assert_false f3.alive?
+end
+
+assert('Root fiber resume') do
+ root = Fiber.current
+ assert_raise(FiberError) { root.resume }
+ f = Fiber.new {
+ assert_raise(FiberError) { root.resume }
+ }
+ f.resume
+ assert_false f.alive?
+end
+
+assert('Fiber without block') do
+ assert_raise(ArgumentError) { Fiber.new }
+end
+
+
+assert('Transfer to self.') do
+ result = []
+ f = Fiber.new { result << :start; f.transfer; result << :end }
+ f.transfer
+ assert_equal [:start, :end], result
+
+ result = []
+ f = Fiber.new { result << :start; f.transfer; result << :end }
+ f.resume
+ assert_equal [:start, :end], result
+end
+
+assert('Resume transferred fiber') do
+ f = Fiber.new {
+ assert_raise(FiberError) { f.resume }
+ }
+ f.transfer
+end
+
+assert('Root fiber transfer.') do
+ result = nil
+ root = Fiber.current
+ f = Fiber.new {
+ result = :ok
+ root.transfer
+ }
+ f.resume
+ assert_true f.alive?
+ assert_equal :ok, result
+end
+
+assert('Break nested fiber with root fiber transfer') do
+ root = Fiber.current
+
+ result = nil
+ f2 = nil
+ f1 = Fiber.new {
+ Fiber.yield f2.resume
+ result = :f1
+ }
+ f2 = Fiber.new {
+ result = :to_root
+ root.transfer :from_f2
+ result = :f2
+ }
+ assert_equal :from_f2, f1.resume
+ assert_equal :to_root, result
+ assert_equal :f2, f2.transfer
+ assert_equal :f2, result
+ assert_false f2.alive?
+ assert_equal :f1, f1.resume
+ assert_equal :f1, result
+ assert_false f1.alive?
+end
+
+assert('CRuby Fiber#transfer test.') do
+ ary = []
+ f2 = nil
+ f1 = Fiber.new{
+ ary << f2.transfer(:foo)
+ :ok
+ }
+ f2 = Fiber.new{
+ ary << f1.transfer(:baz)
+ :ng
+ }
+ assert_equal :ok, f1.transfer
+ assert_equal [:baz], ary
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake
new file mode 100644
index 00000000..103410ab
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrbgem.rake
@@ -0,0 +1,8 @@
+MRuby::Gem::Specification.new('mruby-hash-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Hash class extension'
+ spec.add_dependency 'mruby-enum-ext', core: 'mruby-enum-ext'
+ spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext'
+ spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb
new file mode 100644
index 00000000..73d1fbe6
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb
@@ -0,0 +1,477 @@
+class Hash
+
+ # ISO does not define Hash#each_pair, so each_pair is defined in gem.
+ alias each_pair each
+
+ ##
+ # call-seq:
+ # Hash[ key, value, ... ] -> new_hash
+ # Hash[ [ [key, value], ... ] ] -> new_hash
+ # Hash[ object ] -> new_hash
+ #
+ # Creates a new hash populated with the given objects.
+ #
+ # Similar to the literal `{ _key_ => _value_, ... }`. In the first
+ # form, keys and values occur in pairs, so there must be an even number of
+ # arguments.
+ #
+ # The second and third form take a single argument which is either an array
+ # of key-value pairs or an object convertible to a hash.
+ #
+ # Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
+ # Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
+ # Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
+ #
+
+ def self.[](*object)
+ length = object.length
+ if length == 1
+ o = object[0]
+ if o.respond_to?(:to_hash)
+ h = self.new
+ object[0].to_hash.each { |k, v| h[k] = v }
+ return h
+ elsif o.respond_to?(:to_a)
+ h = self.new
+ o.to_a.each do |i|
+ raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a)
+ k, v = nil
+ case i.size
+ when 2
+ k = i[0]
+ v = i[1]
+ when 1
+ k = i[0]
+ else
+ raise ArgumentError, "invalid number of elements (#{i.size} for 1..2)"
+ end
+ h[k] = v
+ end
+ return h
+ end
+ end
+ unless length % 2 == 0
+ raise ArgumentError, 'odd number of arguments for Hash'
+ end
+ h = self.new
+ 0.step(length - 2, 2) do |i|
+ h[object[i]] = object[i + 1]
+ end
+ h
+ end
+
+ ##
+ # call-seq:
+ # Hash.try_convert(obj) -> hash or nil
+ #
+ # Try to convert <i>obj</i> into a hash, using to_hash method.
+ # Returns converted hash or nil if <i>obj</i> cannot be converted
+ # for any reason.
+ #
+ # Hash.try_convert({1=>2}) # => {1=>2}
+ # Hash.try_convert("1=>2") # => nil
+ #
+ def self.try_convert(obj)
+ if obj.respond_to?(:to_hash)
+ obj.to_hash
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
+ # hsh.merge!(other_hash) -> hsh
+ # hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh
+ #
+ # Adds the contents of _other_hash_ to _hsh_. If no block is specified,
+ # entries with duplicate keys are overwritten with the values from
+ # _other_hash_, otherwise the value of each duplicate key is determined by
+ # calling the block with the key, its value in _hsh_ and its value in
+ # _other_hash_.
+ #
+ # h1 = { "a" => 100, "b" => 200 }
+ # h2 = { "b" => 254, "c" => 300 }
+ # h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
+ #
+ # h1 = { "a" => 100, "b" => 200 }
+ # h2 = { "b" => 254, "c" => 300 }
+ # h1.merge!(h2) { |key, v1, v2| v1 }
+ # #=> {"a"=>100, "b"=>200, "c"=>300}
+ #
+
+ def merge!(other, &block)
+ raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
+ if block
+ other.each_key{|k|
+ self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
+ }
+ else
+ other.each_key{|k| self[k] = other[k]}
+ end
+ self
+ end
+
+ alias update merge!
+
+ ##
+ # call-seq:
+ # hsh.compact -> new_hsh
+ #
+ # Returns a new hash with the nil values/key pairs removed
+ #
+ # h = { a: 1, b: false, c: nil }
+ # h.compact #=> { a: 1, b: false }
+ # h #=> { a: 1, b: false, c: nil }
+ #
+ def compact
+ result = self.dup
+ result.compact!
+ result
+ end
+
+ ##
+ # call-seq:
+ # hsh.fetch(key [, default] ) -> obj
+ # hsh.fetch(key) {| key | block } -> obj
+ #
+ # Returns a value from the hash for the given key. If the key can't be
+ # found, there are several options: With no other arguments, it will
+ # raise an <code>KeyError</code> exception; if <i>default</i> is
+ # given, then that will be returned; if the optional code block is
+ # specified, then that will be run and its result returned.
+ #
+ # h = { "a" => 100, "b" => 200 }
+ # h.fetch("a") #=> 100
+ # h.fetch("z", "go fish") #=> "go fish"
+ # h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z"
+ #
+ # The following example shows that an exception is raised if the key
+ # is not found and a default value is not supplied.
+ #
+ # h = { "a" => 100, "b" => 200 }
+ # h.fetch("z")
+ #
+ # <em>produces:</em>
+ #
+ # prog.rb:2:in 'fetch': key not found (KeyError)
+ # from prog.rb:2
+ #
+
+ def fetch(key, none=NONE, &block)
+ unless self.key?(key)
+ if block
+ block.call(key)
+ elsif none != NONE
+ none
+ else
+ raise KeyError, "Key not found: #{key}"
+ end
+ else
+ self[key]
+ end
+ end
+
+ ##
+ # call-seq:
+ # hsh.delete_if {| key, value | block } -> hsh
+ # hsh.delete_if -> an_enumerator
+ #
+ # Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
+ # evaluates to <code>true</code>.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # h = { "a" => 100, "b" => 200, "c" => 300 }
+ # h.delete_if {|key, value| key >= "b" } #=> {"a"=>100}
+ #
+
+ def delete_if(&block)
+ return to_enum :delete_if unless block
+
+ self.each do |k, v|
+ self.delete(k) if block.call(k, v)
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # hash.flatten -> an_array
+ # hash.flatten(level) -> an_array
+ #
+ # Returns a new array that is a one-dimensional flattening of this
+ # hash. That is, for every key or value that is an array, extract
+ # its elements into the new array. Unlike Array#flatten, this
+ # method does not flatten recursively by default. The optional
+ # <i>level</i> argument determines the level of recursion to flatten.
+ #
+ # a = {1=> "one", 2 => [2,"two"], 3 => "three"}
+ # a.flatten # => [1, "one", 2, [2, "two"], 3, "three"]
+ # a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"]
+ #
+
+ def flatten(level=1)
+ self.to_a.flatten(level)
+ end
+
+ ##
+ # call-seq:
+ # hsh.invert -> new_hash
+ #
+ # Returns a new hash created by using <i>hsh</i>'s values as keys, and
+ # the keys as values.
+ #
+ # h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 }
+ # h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"}
+ #
+
+ def invert
+ h = self.class.new
+ self.each {|k, v| h[v] = k }
+ h
+ end
+
+ ##
+ # call-seq:
+ # hsh.keep_if {| key, value | block } -> hsh
+ # hsh.keep_if -> an_enumerator
+ #
+ # Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
+ # evaluates to false.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+
+ def keep_if(&block)
+ return to_enum :keep_if unless block
+
+ keys = []
+ self.each do |k, v|
+ unless block.call([k, v])
+ self.delete(k)
+ end
+ end
+ self
+ end
+
+ ##
+ # call-seq:
+ # hsh.key(value) -> key
+ #
+ # Returns the key of an occurrence of a given value. If the value is
+ # not found, returns <code>nil</code>.
+ #
+ # h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 }
+ # h.key(200) #=> "b"
+ # h.key(300) #=> "c"
+ # h.key(999) #=> nil
+ #
+
+ def key(val)
+ self.each do |k, v|
+ return k if v == val
+ end
+ nil
+ end
+
+ ##
+ # call-seq:
+ # hsh.to_h -> hsh or new_hash
+ #
+ # Returns +self+. If called on a subclass of Hash, converts
+ # the receiver to a Hash object.
+ #
+ def to_h
+ self
+ end
+
+ ##
+ # call-seq:
+ # hash < other -> true or false
+ #
+ # Returns <code>true</code> if <i>hash</i> is subset of
+ # <i>other</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 < h2 #=> true
+ # h2 < h1 #=> false
+ # h1 < h1 #=> false
+ #
+ def <(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size < hash.size and all? {|key, val|
+ hash.key?(key) and hash[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hash <= other -> true or false
+ #
+ # Returns <code>true</code> if <i>hash</i> is subset of
+ # <i>other</i> or equals to <i>other</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 <= h2 #=> true
+ # h2 <= h1 #=> false
+ # h1 <= h1 #=> true
+ #
+ def <=(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size <= hash.size and all? {|key, val|
+ hash.key?(key) and hash[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hash > other -> true or false
+ #
+ # Returns <code>true</code> if <i>other</i> is subset of
+ # <i>hash</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 > h2 #=> false
+ # h2 > h1 #=> true
+ # h1 > h1 #=> false
+ #
+ def >(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size > hash.size and hash.all? {|key, val|
+ key?(key) and self[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hash >= other -> true or false
+ #
+ # Returns <code>true</code> if <i>other</i> is subset of
+ # <i>hash</i> or equals to <i>hash</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 >= h2 #=> false
+ # h2 >= h1 #=> true
+ # h1 >= h1 #=> true
+ #
+ def >=(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size >= hash.size and hash.all? {|key, val|
+ key?(key) and self[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hsh.dig(key,...) -> object
+ #
+ # Extracts the nested value specified by the sequence of <i>key</i>
+ # objects by calling +dig+ at each step, returning +nil+ if any
+ # intermediate step is +nil+.
+ #
+ def dig(idx,*args)
+ n = self[idx]
+ if args.size > 0
+ n&.dig(*args)
+ else
+ n
+ end
+ end
+
+ ##
+ # call-seq:
+ # hsh.transform_keys {|key| block } -> new_hash
+ # hsh.transform_keys -> an_enumerator
+ #
+ # Returns a new hash, with the keys computed from running the block
+ # once for each key in the hash, and the values unchanged.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ def transform_keys(&block)
+ return to_enum :transform_keys unless block
+ hash = {}
+ self.keys.each do |k|
+ new_key = block.call(k)
+ hash[new_key] = self[k]
+ end
+ hash
+ end
+ ##
+ # call-seq:
+ # hsh.transform_keys! {|key| block } -> hsh
+ # hsh.transform_keys! -> an_enumerator
+ #
+ # Invokes the given block once for each key in <i>hsh</i>, replacing it
+ # with the new key returned by the block, and then returns <i>hsh</i>.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ def transform_keys!(&block)
+ return to_enum :transform_keys! unless block
+ self.keys.each do |k|
+ value = self[k]
+ new_key = block.call(k)
+ self.__delete(k)
+ self[new_key] = value
+ end
+ self
+ end
+ ##
+ # call-seq:
+ # hsh.transform_values {|value| block } -> new_hash
+ # hsh.transform_values -> an_enumerator
+ #
+ # Returns a new hash with the results of running the block once for
+ # every value.
+ # This method does not change the keys.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ def transform_values(&b)
+ return to_enum :transform_values unless block_given?
+ hash = {}
+ self.keys.each do |k|
+ hash[k] = yield(self[k])
+ end
+ hash
+ end
+ ##
+ # call-seq:
+ # hsh.transform_values! {|key| block } -> hsh
+ # hsh.transform_values! -> an_enumerator
+ #
+ # Invokes the given block once for each value in the hash, replacing
+ # with the new value returned by the block, and then returns <i>hsh</i>.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ def transform_values!(&b)
+ return to_enum :transform_values! unless block_given?
+ self.keys.each do |k|
+ self[k] = yield(self[k])
+ end
+ self
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c
new file mode 100644
index 00000000..952f2eb6
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c
@@ -0,0 +1,88 @@
+/*
+** hash.c - Hash class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+
+/*
+ * call-seq:
+ * hsh.values_at(key, ...) -> array
+ *
+ * Return an array containing the values associated with the given keys.
+ * Also see <code>Hash.select</code>.
+ *
+ * h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
+ * h.values_at("cow", "cat") #=> ["bovine", "feline"]
+ */
+
+static mrb_value
+hash_values_at(mrb_state *mrb, mrb_value hash)
+{
+ mrb_value *argv, result;
+ mrb_int argc, i;
+ int ai;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ result = mrb_ary_new_capa(mrb, argc);
+ ai = mrb_gc_arena_save(mrb);
+ for (i = 0; i < argc; i++) {
+ mrb_ary_push(mrb, result, mrb_hash_get(mrb, hash, argv[i]));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * hsh.compact! -> hsh
+ *
+ * Removes all nil values from the hash. Returns the hash.
+ *
+ * h = { a: 1, b: false, c: nil }
+ * h.compact! #=> { a: 1, b: false }
+ */
+static mrb_value
+hash_compact_bang(mrb_state *mrb, mrb_value hash)
+{
+ khiter_t k;
+ khash_t(ht) *h = RHASH_TBL(hash);
+ mrb_int n = -1;
+
+ if (!h) return mrb_nil_value();
+ for (k = kh_begin(h); k != kh_end(h); k++) {
+ if (kh_exist(h, k)) {
+ mrb_value val = kh_value(h, k).v;
+ khiter_t k2;
+
+ if (mrb_nil_p(val)) {
+ kh_del(ht, mrb, h, k);
+ n = kh_value(h, k).n;
+ for (k2 = kh_begin(h); k2 != kh_end(h); k2++) {
+ if (!kh_exist(h, k2)) continue;
+ if (kh_value(h, k2).n > n) kh_value(h, k2).n--;
+ }
+ }
+ }
+ }
+ if (n < 0) return mrb_nil_value();
+ return hash;
+}
+
+void
+mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
+{
+ struct RClass *h;
+
+ h = mrb->hash_class;
+ mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY());
+ mrb_define_method(mrb, h, "compact!", hash_compact_bang, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_hash_ext_gem_final(mrb_state *mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb
new file mode 100644
index 00000000..ca4e346f
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-hash-ext/test/hash.rb
@@ -0,0 +1,294 @@
+##
+# Hash(Ext) Test
+
+assert('Hash.[] Hash') do
+ a = Hash['a_key' => 'a_value']
+
+ assert_equal({'a_key' => 'a_value'}, a)
+end
+
+assert('Hash.[] [ [ ["b_key", "b_value" ] ] ]') do
+ a = Hash[ [ ['b_key', 'b_value'] ] ]
+
+ assert_equal({'b_key' => 'b_value'}, a)
+
+ a = Hash[ [ ] ]
+
+ assert_equal({}, a)
+
+ assert_raise(ArgumentError) do
+ Hash[ [ ['b_key', 'b_value', 'b_over'] ] ]
+ end
+
+ assert_raise(ArgumentError) do
+ Hash[ [ [] ] ]
+ end
+end
+
+assert('Hash.[] "c_key", "c_value"') do
+ a = Hash['c_key', 'c_value', 'd_key', 1]
+
+ assert_equal({'c_key' => 'c_value', 'd_key' => 1}, a)
+
+ a = Hash[]
+
+ assert_equal({}, a)
+
+ assert_raise(ArgumentError) do
+ Hash['d_key']
+ end
+end
+
+assert('Hash.[] for sub class') do
+ sub_hash_class = Class.new(Hash)
+ sub_hash = sub_hash_class[]
+ assert_equal(sub_hash_class, sub_hash.class)
+end
+
+assert('Hash.try_convert') do
+ assert_nil Hash.try_convert(nil)
+ assert_nil Hash.try_convert("{1=>2}")
+ assert_equal({1=>2}, Hash.try_convert({1=>2}))
+end
+
+assert('Hash#merge!') do
+ a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
+ b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }
+
+ result_1 = a.merge! b
+
+ a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
+ result_2 = a.merge!(b) do |key, original, new|
+ original
+ end
+
+ assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'XXX',
+ 'xyz_key' => 'xyz_value' }, result_1)
+ assert_equal({'abc_key' => 'abc_value', 'cba_key' => 'cba_value',
+ 'xyz_key' => 'xyz_value' }, result_2)
+
+ assert_raise(TypeError) do
+ { 'abc_key' => 'abc_value' }.merge! "a"
+ end
+end
+
+assert('Hash#values_at') do
+ h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
+ assert_equal ["bovine", "feline"], h.values_at("cow", "cat")
+
+ keys = []
+ (0...1000).each { |v| keys.push "#{v}" }
+ h = Hash.new { |hash,k| hash[k] = k }
+ assert_equal keys, h.values_at(*keys)
+end
+
+assert('Hash#compact') do
+ h = { "cat" => "feline", "dog" => nil, "cow" => false }
+
+ assert_equal({ "cat" => "feline", "cow" => false }, h.compact)
+ assert_equal({ "cat" => "feline", "dog" => nil, "cow" => false }, h)
+end
+
+assert('Hash#compact!') do
+ h = { "cat" => "feline", "dog" => nil, "cow" => false }
+
+ h.compact!
+ assert_equal({ "cat" => "feline", "cow" => false }, h)
+end
+
+assert('Hash#fetch') do
+ h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
+ assert_equal "feline", h.fetch("cat")
+ assert_equal "mickey", h.fetch("mouse", "mickey")
+ assert_equal "minny", h.fetch("mouse"){"minny"}
+ assert_equal "mouse", h.fetch("mouse"){|k| k}
+ assert_raise(KeyError) do
+ h.fetch("gnu")
+ end
+end
+
+assert("Hash#delete_if") do
+ base = { 1 => 'one', 2 => false, true => 'true', 'cat' => 99 }
+ h1 = { 1 => 'one', 2 => false, true => 'true' }
+ h2 = { 2 => false, 'cat' => 99 }
+ h3 = { 2 => false }
+
+ h = base.dup
+ assert_equal(h, h.delete_if { false })
+ assert_equal({}, h.delete_if { true })
+
+ h = base.dup
+ assert_equal(h1, h.delete_if {|k,v| k.instance_of?(String) })
+ assert_equal(h1, h)
+
+ h = base.dup
+ assert_equal(h2, h.delete_if {|k,v| v.instance_of?(String) })
+ assert_equal(h2, h)
+
+ h = base.dup
+ assert_equal(h3, h.delete_if {|k,v| v })
+ assert_equal(h3, h)
+
+ h = base.dup
+ n = 0
+ h.delete_if {|*a|
+ n += 1
+ assert_equal(2, a.size)
+ assert_equal(base[a[0]], a[1])
+ h.shift
+ true
+ }
+ assert_equal(base.size, n)
+end
+
+assert("Hash#flatten") do
+ a = {1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]}
+ assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten
+ assert_equal [[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0)
+ assert_equal [1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1)
+ assert_equal [1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2)
+ assert_equal [1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3)
+end
+
+assert("Hash#invert") do
+ h = { 1 => 'one', 2 => 'two', 3 => 'three',
+ true => 'true', nil => 'nil' }.invert
+ assert_equal 1, h['one']
+ assert_equal true, h['true']
+ assert_equal nil, h['nil']
+
+ h = { 'a' => 1, 'b' => 2, 'c' => 1 }.invert
+ assert_equal(2, h.length)
+ assert_include(%w[a c], h[1])
+ assert_equal('b', h[2])
+end
+
+assert("Hash#invert with sub class") do
+ sub_hash_class = Class.new(Hash)
+ sub_hash = sub_hash_class.new
+ assert_equal(sub_hash_class, sub_hash.invert.class)
+end
+
+assert("Hash#keep_if") do
+ h = { 1 => 2, 3 => 4, 5 => 6 }
+ assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
+ h = { 1 => 2, 3 => 4, 5 => 6 }
+ assert_equal({ 1 => 2, 3=> 4, 5 =>6} , h.keep_if { true })
+end
+
+assert("Hash#key") do
+ h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300, nil => 'nil', 'nil' => nil }
+ assert_equal "b", h.key(200)
+ assert_equal "c", h.key(300)
+ assert_nil h.key(999)
+ assert_nil h.key('nil')
+ assert_equal 'nil', h.key(nil)
+end
+
+assert("Hash#to_h") do
+ h = { "a" => 100, "b" => 200 }
+ assert_equal Hash, h.to_h.class
+ assert_equal h, h.to_h
+end
+
+assert('Hash#<') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_false(h1 < h1)
+ assert_true(h1 < h2)
+ assert_false(h2 < h1)
+ assert_false(h2 < h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_false(h1 < h1)
+ assert_false(h1 < h2)
+ assert_false(h2 < h1)
+ assert_false(h2 < h2)
+end
+
+assert('Hash#<=') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_true(h1 <= h1)
+ assert_true(h1 <= h2)
+ assert_false(h2 <= h1)
+ assert_true(h2 <= h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_true(h1 <= h1)
+ assert_false(h1 <= h2)
+ assert_false(h2 <= h1)
+ assert_true(h2 <= h2)
+end
+
+assert('Hash#>=') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_true(h1 >= h1)
+ assert_false(h1 >= h2)
+ assert_true(h2 >= h1)
+ assert_true(h2 >= h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_true(h1 >= h1)
+ assert_false(h1 >= h2)
+ assert_false(h2 >= h1)
+ assert_true(h2 >= h2)
+end
+
+assert('Hash#>') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_false(h1 > h1)
+ assert_false(h1 > h2)
+ assert_true(h2 > h1)
+ assert_false(h2 > h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_false(h1 > h1)
+ assert_false(h1 > h2)
+ assert_false(h2 > h1)
+ assert_false(h2 > h2)
+end
+
+assert("Hash#dig") do
+ h = {a:{b:{c:1}}}
+ assert_equal(1, h.dig(:a, :b, :c))
+ assert_nil(h.dig(:d))
+end
+
+assert("Hash#transform_keys") do
+ h = {"1" => 100, "2" => 200}
+ assert_equal(h.transform_keys{|k| k+"!"},
+ {"1!" => 100, "2!" => 200})
+ assert_equal(h.transform_keys{|k|k.to_i},
+ {1 => 100, 2 => 200})
+ assert_equal(h.transform_keys.with_index{|k, i| "#{k}.#{i}"},
+ {"1.0" => 100, "2.1" => 200})
+ assert_equal(h.transform_keys!{|k|k.to_i}, h)
+ assert_equal(h, {1 => 100, 2 => 200})
+end
+
+assert("Hash#transform_values") do
+ h = {a: 1, b: 2, c: 3}
+ assert_equal(h.transform_values{|v| v * v + 1},
+ {a: 2, b: 5, c: 10})
+ assert_equal(h.transform_values{|v|v.to_s},
+ {a: "1", b: "2", c: "3"})
+ assert_equal(h.transform_values.with_index{|v, i| "#{v}.#{i}"},
+ {a: "1.0", b: "2.1", c: "3.2"})
+ assert_equal(h.transform_values!{|v|v.to_s}, h)
+ assert_equal(h, {a: "1", b: "2", c: "3"})
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake
new file mode 100644
index 00000000..91ad9f44
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-inline-struct') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'inline structure'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c
new file mode 100644
index 00000000..0baaab61
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.c
@@ -0,0 +1,83 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/istruct.h>
+
+static mrb_value
+istruct_test_initialize(mrb_state *mrb, mrb_value self)
+{
+ char *string = (char*)mrb_istruct_ptr(self);
+ mrb_int size = mrb_istruct_size();
+ mrb_value object;
+ mrb_get_args(mrb, "o", &object);
+
+ if (mrb_float_p(object))
+ {
+ snprintf(string, size, "float(%.3f)", mrb_float(object));
+ }
+ else if (mrb_fixnum_p(object))
+ {
+ snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
+ }
+ else if (mrb_string_p(object))
+ {
+ snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+ }
+
+ string[size - 1] = 0; // force NULL at the end
+ return self;
+}
+
+static mrb_value
+istruct_test_to_s(mrb_state *mrb, mrb_value self)
+{
+ return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self));
+}
+
+static mrb_value
+istruct_test_length(mrb_state *mrb, mrb_value self)
+{
+ return mrb_fixnum_value(mrb_istruct_size());
+}
+
+static mrb_value
+istruct_test_test_receive(mrb_state *mrb, mrb_value self)
+{
+ mrb_value object;
+ mrb_get_args(mrb, "o", &object);
+ if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
+ {
+ mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
+ }
+ return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
+}
+
+static mrb_value
+istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self)
+{
+ char *ptr;
+ mrb_get_args(mrb, "I", &ptr);
+ return mrb_bool_value(ptr[0] == 's');
+}
+
+static mrb_value
+istruct_test_mutate(mrb_state *mrb, mrb_value self)
+{
+ char *ptr = (char*)mrb_istruct_ptr(self);
+ memcpy(ptr, "mutate", 6);
+ return mrb_nil_value();
+}
+
+void mrb_mruby_inline_struct_gem_test(mrb_state *mrb)
+{
+ struct RClass *cls;
+
+ cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT);
+ mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1));
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb
new file mode 100644
index 00000000..49585923
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-inline-struct/test/inline.rb
@@ -0,0 +1,151 @@
+##
+# InlineStruct Test
+
+class InlineStructTest
+ def extra_method
+ :ok
+ end
+
+ def test_ivar_set
+ @var = :ivar
+ end
+
+ def test_ivar_get
+ @vat
+ end
+end
+
+assert('InlineStructTest#dup') do
+ obj = InlineStructTest.new(1)
+ assert_equal obj.to_s, 'fixnum(1)'
+ assert_equal obj.dup.to_s, 'fixnum(1)'
+end
+
+assert('InlineStructTest#clone') do
+ obj = InlineStructTest.new(1)
+ assert_equal obj.to_s, 'fixnum(1)'
+ assert_equal obj.clone.to_s, 'fixnum(1)'
+end
+
+assert('InlineStruct#object_id') do
+ obj1 = InlineStructTest.new(1)
+ obj2 = InlineStructTest.new(1)
+ assert_not_equal obj1, obj2
+ assert_not_equal obj1.object_id, obj2.object_id
+ assert_not_equal obj1.object_id, obj1.dup.object_id
+ assert_not_equal obj1.object_id, obj1.clone.object_id
+end
+
+assert('InlineStructTest#mutate (dup)') do
+ obj1 = InlineStructTest.new("foo")
+ assert_equal obj1.to_s, "string(foo)"
+ obj2 = obj1.dup
+ assert_equal obj2.to_s, "string(foo)"
+ obj1.mutate
+ assert_equal obj1.to_s, "mutate(foo)"
+ assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#mutate (clone)') do
+ obj1 = InlineStructTest.new("foo")
+ assert_equal obj1.to_s, "string(foo)"
+ obj2 = obj1.clone
+ assert_equal obj2.to_s, "string(foo)"
+ obj1.mutate
+ assert_equal obj1.to_s, "mutate(foo)"
+ assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#test_receive(string)') do
+ assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+ assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+ assert_raise(TypeError) do
+ InlineStructTest.test_receive([])
+ end
+end
+
+assert('InlineStructTest#test_receive(string)') do
+ assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+ assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+ assert_raise(TypeError) do
+ InlineStructTest.test_receive_direct([])
+ end
+end
+
+assert('InlineStructTest#extra_method') do
+ assert_equal InlineStructTest.new(1).extra_method, :ok
+end
+
+assert('InlineStructTest instance variable') do
+ obj = InlineStructTest.new(1)
+ assert_raise(ArgumentError) do
+ obj.test_ivar_set
+ end
+ assert_equal obj.test_ivar_get, nil
+end
+
+# 64-bit mode
+if InlineStructTest.length == 24
+ assert('InlineStructTest length [64 bit]') do
+ assert_equal InlineStructTest.length, 3 * 8
+ end
+
+ assert('InlineStructTest w/float [64 bit]') do
+ obj = InlineStructTest.new(1.25)
+ assert_equal obj.to_s, "float(1.250)"
+ end
+
+ assert('InlineStructTest w/fixnum [64 bit]') do
+ obj = InlineStructTest.new(42)
+ assert_equal obj.to_s, "fixnum(42)"
+ end
+
+ assert('InlineStructTest w/string [64 bit]') do
+ obj = InlineStructTest.new("hello")
+ assert_equal obj.to_s, "string(hello)"
+ end
+
+ assert('InlineStructTest w/long string [64 bit]') do
+ obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
+ assert_equal obj.to_s, "string(this won't fit i"
+ end
+end
+
+# 32-bit mode
+if InlineStructTest.length == 12
+ assert('InlineStructTest length [32 bit]') do
+ assert_equal InlineStructTest.length, 3 * 4
+ end
+
+ assert('InlineStructTest w/float [32 bit]') do
+ obj = InlineStructTest.new(1.25)
+ assert_equal obj.to_s, "float(1.250"
+ end
+
+ assert('InlineStructTest w/fixnum [32 bit]') do
+ obj = InlineStructTest.new(42)
+ assert_equal obj.to_s, "fixnum(42)"
+ end
+
+ assert('InlineStructTest w/string [32 bit]') do
+ obj = InlineStructTest.new("hello")
+ assert_equal obj.to_s, "string(hell"
+ end
+
+ assert('InlineStructTest w/long string [32 bit]') do
+ obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
+ assert_equal obj.to_s, "string(this"
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake
new file mode 100644
index 00000000..fcb3a83b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Kernel module extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c
new file mode 100644
index 00000000..7e6fa28b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -0,0 +1,243 @@
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/range.h>
+
+static mrb_value
+mrb_f_caller(mrb_state *mrb, mrb_value self)
+{
+ mrb_value bt, v, length;
+ mrb_int bt_len, argc, lev, n;
+
+ bt = mrb_get_backtrace(mrb);
+ bt_len = RARRAY_LEN(bt);
+ argc = mrb_get_args(mrb, "|oo", &v, &length);
+
+ switch (argc) {
+ case 0:
+ lev = 1;
+ n = bt_len - lev;
+ break;
+ case 1:
+ if (mrb_type(v) == MRB_TT_RANGE) {
+ mrb_int beg, len;
+ if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) {
+ lev = beg;
+ n = len;
+ }
+ else {
+ return mrb_nil_value();
+ }
+ }
+ else {
+ v = mrb_to_int(mrb, v);
+ lev = mrb_fixnum(v);
+ if (lev < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+ }
+ n = bt_len - lev;
+ }
+ break;
+ case 2:
+ lev = mrb_fixnum(mrb_to_int(mrb, v));
+ n = mrb_fixnum(mrb_to_int(mrb, length));
+ if (lev < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+ }
+ if (n < 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length);
+ }
+ break;
+ default:
+ lev = n = 0;
+ break;
+ }
+
+ if (n == 0) {
+ return mrb_ary_new(mrb);
+ }
+
+ return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n));
+}
+
+/*
+ * call-seq:
+ * __method__ -> symbol
+ *
+ * Returns the name at the definition of the current method as a
+ * Symbol.
+ * If called outside of a method, it returns <code>nil</code>.
+ *
+ */
+static mrb_value
+mrb_f_method(mrb_state *mrb, mrb_value self)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+ ci--;
+ if (ci->mid)
+ return mrb_symbol_value(ci->mid);
+ else
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * Integer(arg,base=0) -> integer
+ *
+ * Converts <i>arg</i> to a <code>Fixnum</code>.
+ * Numeric types are converted directly (with floating point numbers
+ * being truncated). <i>base</i> (0, or between 2 and 36) is a base for
+ * integer string representation. If <i>arg</i> is a <code>String</code>,
+ * when <i>base</i> is omitted or equals to zero, radix indicators
+ * (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored.
+ * In any case, strings should be strictly conformed to numeric
+ * representation. This behavior is different from that of
+ * <code>String#to_i</code>. Non string values will be converted using
+ * <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code>
+ * raises a TypeError.
+ *
+ * Integer(123.999) #=> 123
+ * Integer("0x1a") #=> 26
+ * Integer(Time.new) #=> 1204973019
+ * Integer("0930", 10) #=> 930
+ * Integer("111", 2) #=> 7
+ * Integer(nil) #=> TypeError
+ */
+static mrb_value
+mrb_f_integer(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg;
+ mrb_int base = 0;
+
+ mrb_get_args(mrb, "o|i", &arg, &base);
+ return mrb_convert_to_integer(mrb, arg, base);
+}
+
+/*
+ * call-seq:
+ * Float(arg) -> float
+ *
+ * Returns <i>arg</i> converted to a float. Numeric types are converted
+ * directly, the rest are converted using <i>arg</i>.to_f.
+ *
+ * Float(1) #=> 1.0
+ * Float(123.456) #=> 123.456
+ * Float("123.456") #=> 123.456
+ * Float(nil) #=> TypeError
+ */
+static mrb_value
+mrb_f_float(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg;
+
+ mrb_get_args(mrb, "o", &arg);
+ return mrb_Float(mrb, arg);
+}
+
+/*
+ * call-seq:
+ * String(arg) -> string
+ *
+ * Returns <i>arg</i> as an <code>String</code>.
+ *
+ * First tries to call its <code>to_str</code> method, then its to_s method.
+ *
+ * String(self) #=> "main"
+ * String(self.class) #=> "Object"
+ * String(123456) #=> "123456"
+ */
+static mrb_value
+mrb_f_string(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg, tmp;
+
+ mrb_get_args(mrb, "o", &arg);
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_str");
+ if (mrb_nil_p(tmp)) {
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
+ }
+ return tmp;
+}
+
+/*
+ * call-seq:
+ * Array(arg) -> array
+ *
+ * Returns +arg+ as an Array.
+ *
+ * First tries to call Array#to_ary on +arg+, then Array#to_a.
+ *
+ * Array(1..5) #=> [1, 2, 3, 4, 5]
+ *
+ */
+static mrb_value
+mrb_f_array(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg, tmp;
+
+ mrb_get_args(mrb, "o", &arg);
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_ary");
+ if (mrb_nil_p(tmp)) {
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a");
+ }
+ if (mrb_nil_p(tmp)) {
+ return mrb_ary_new_from_values(mrb, 1, &arg);
+ }
+
+ return tmp;
+}
+
+/*
+ * call-seq:
+ * Hash(arg) -> hash
+ *
+ * Converts <i>arg</i> to a <code>Hash</code> by calling
+ * <i>arg</i><code>.to_hash</code>. Returns an empty <code>Hash</code> when
+ * <i>arg</i> is <tt>nil</tt> or <tt>[]</tt>.
+ *
+ * Hash([]) #=> {}
+ * Hash(nil) #=> {}
+ * Hash(key: :value) #=> {:key => :value}
+ * Hash([1, 2, 3]) #=> TypeError
+ *
+ */
+static mrb_value
+mrb_f_hash(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg, tmp;
+
+ mrb_get_args(mrb, "o", &arg);
+ if (mrb_nil_p(arg)) {
+ return mrb_hash_new(mrb);
+ }
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_HASH, "Hash", "to_hash");
+ if (mrb_nil_p(tmp)) {
+ if (mrb_array_p(arg) && RARRAY_LEN(arg) == 0) {
+ return mrb_hash_new(mrb);
+ }
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into Hash",
+ mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, arg)));
+ }
+ return tmp;
+}
+
+void
+mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
+{
+ struct RClass *krn = mrb->kernel_module;
+
+ mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2));
+ mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2));
+ mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE());
+ mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY());
+ mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_kernel_ext_gem_final(mrb_state *mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb
new file mode 100644
index 00000000..89cedaf2
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -0,0 +1,86 @@
+assert('Kernel.fail, Kernel#fail') do
+ assert_raise(RuntimeError) { fail }
+ assert_raise(RuntimeError) { Kernel.fail }
+end
+
+assert('Kernel.caller, Kernel#caller') do
+ skip "backtrace isn't available" if caller(0).empty?
+
+ caller_lineno = __LINE__ + 3
+ c = Class.new do
+ def foo(*args)
+ caller(*args)
+ end
+
+ def bar(*args)
+ foo(*args)
+ end
+
+ def baz(*args)
+ bar(*args)
+ end
+ end
+ assert_equal "kernel.rb:#{caller_lineno}:in foo", c.new.baz(0)[0][-19..-1]
+ assert_equal "bar", c.new.baz[0][-3..-1]
+ assert_equal "foo", c.new.baz(0)[0][-3..-1]
+ assert_equal "bar", c.new.baz(1)[0][-3..-1]
+ assert_equal "baz", c.new.baz(2)[0][-3..-1]
+ assert_equal ["foo", "bar"], c.new.baz(0, 2).map { |i| i[-3..-1] }
+ assert_equal ["bar", "baz"], c.new.baz(1..2).map { |i| i[-3..-1] }
+ assert_nil c.new.baz(10..20)
+ assert_raise(ArgumentError) { c.new.baz(-1) }
+ assert_raise(ArgumentError) { c.new.baz(-1, 1) }
+ assert_raise(ArgumentError) { c.new.baz(1, -1) }
+ assert_raise(TypeError) { c.new.baz(nil) }
+end
+
+assert('Kernel#__method__') do
+ assert_equal(:m, Class.new {def m; __method__; end}.new.m)
+ assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m)
+ c = Class.new do
+ [:m1, :m2].each do |m|
+ define_method(m) do
+ __method__
+ end
+ end
+ end
+ assert_equal(:m1, c.new.m1)
+ assert_equal(:m2, c.new.m2)
+end
+
+assert('Kernel#Integer') do
+ assert_equal(123, Integer(123.999))
+ assert_equal(26, Integer("0x1a"))
+ assert_equal(930, Integer("0930", 10))
+ assert_equal(7, Integer("111", 2))
+ assert_equal(0, Integer("0"))
+ assert_equal(0, Integer("00000"))
+ assert_raise(TypeError) { Integer(nil) }
+end
+
+assert('Kernel#Float') do
+ assert_equal(1.0, Float(1))
+ assert_equal(123.456, Float(123.456))
+ assert_equal(123.456, Float("123.456"))
+ assert_raise(TypeError) { Float(nil) }
+end
+
+assert('Kernel#String') do
+ assert_equal("main", String(self))
+ assert_equal("Object", String(self.class))
+ assert_equal("123456", String(123456))
+end
+
+assert('Kernel#Array') do
+ assert_equal([1], Kernel.Array(1))
+ assert_equal([1, 2, 3, 4, 5], Kernel.Array([1, 2, 3, 4, 5]))
+ assert_equal([1, 2, 3, 4, 5], Kernel.Array(1..5))
+ assert_equal([[:a, 1], [:b, 2], [:c, 3]], Kernel.Array({a:1, b:2, c:3}))
+end
+
+assert('Kernel#Hash') do
+ assert_equal({}, Hash([]))
+ assert_equal({}, Hash(nil))
+ assert_equal({:key => :value}, Hash(key: :value))
+ assert_raise(TypeError) { Hash([1, 2, 3]) }
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake
new file mode 100644
index 00000000..66eb5c8d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-math') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard Math module'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c
new file mode 100644
index 00000000..7302b92d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/src/math.c
@@ -0,0 +1,783 @@
+/*
+** math.c - Math module
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+#include <mruby/array.h>
+
+#include <math.h>
+
+static void
+domain_error(mrb_state *mrb, const char *func)
+{
+ struct RClass *math = mrb_module_get(mrb, "Math");
+ struct RClass *domainerror = mrb_class_get_under(mrb, math, "DomainError");
+ mrb_value str = mrb_str_new_cstr(mrb, func);
+ mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %S", str);
+}
+
+/* math functions not provided by Microsoft Visual C++ 2012 or older */
+#if defined _MSC_VER && _MSC_VER <= 1700
+
+#include <float.h>
+
+#define MATH_TOLERANCE 1E-12
+
+double
+asinh(double x)
+{
+ double xa, ya, y;
+
+ /* Basic formula loses precision for x < 0, but asinh is an odd function */
+ xa = fabs(x);
+ if (xa > 3.16227E+18) {
+ /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */
+ ya = log(xa) + 0.69314718055994530942;
+ }
+ else {
+ /* Basic formula for asinh */
+ ya = log(xa + sqrt(xa*xa + 1.0));
+ }
+
+ y = _copysign(ya, x);
+ return y;
+}
+
+double
+acosh(double x)
+{
+ double y;
+
+ if (x > 3.16227E+18) {
+ /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */
+ y = log(x) + 0.69314718055994530942;
+ }
+ else {
+ /* Basic formula for acosh */
+ y = log(x + sqrt(x*x - 1.0));
+ }
+
+ return y;
+}
+
+double
+atanh(double x)
+{
+ double y;
+
+ if (fabs(x) < 1E-2) {
+ /* The sums 1+x and 1-x lose precision for small x. Use the polynomial
+ instead. */
+ double x2 = x * x;
+ y = x*(1.0 + x2*(1.0/3.0 + x2*(1.0/5.0 + x2*(1.0/7.0))));
+ }
+ else {
+ /* Basic formula for atanh */
+ y = 0.5 * (log(1.0+x) - log(1.0-x));
+ }
+
+ return y;
+}
+
+double
+cbrt(double x)
+{
+ double xa, ya, y;
+
+ /* pow(x, y) is undefined for x < 0 and y not an integer, but cbrt is an
+ odd function */
+ xa = fabs(x);
+ ya = pow(xa, 1.0/3.0);
+ y = _copysign(ya, x);
+ return y;
+}
+
+/* Declaration of complementary Error function */
+double
+erfc(double x);
+
+/*
+** Implementations of error functions
+** credits to http://www.digitalmars.com/archives/cplusplus/3634.html
+*/
+
+/* Implementation of Error function */
+double
+erf(double x)
+{
+ static const double two_sqrtpi = 1.128379167095512574;
+ double sum = x;
+ double term = x;
+ double xsqr = x*x;
+ int j= 1;
+ if (fabs(x) > 2.2) {
+ return 1.0 - erfc(x);
+ }
+ do {
+ term *= xsqr/j;
+ sum -= term/(2*j+1);
+ ++j;
+ term *= xsqr/j;
+ sum += term/(2*j+1);
+ ++j;
+ } while (fabs(term/sum) > MATH_TOLERANCE);
+ return two_sqrtpi*sum;
+}
+
+/* Implementation of complementary Error function */
+double
+erfc(double x)
+{
+ static const double one_sqrtpi= 0.564189583547756287;
+ double a = 1;
+ double b = x;
+ double c = x;
+ double d = x*x+0.5;
+ double q1;
+ double q2 = b/d;
+ double n = 1.0;
+ double t;
+ if (fabs(x) < 2.2) {
+ return 1.0 - erf(x);
+ }
+ if (x < 0.0) { /*signbit(x)*/
+ return 2.0 - erfc(-x);
+ }
+ do {
+ t = a*n+b*x;
+ a = b;
+ b = t;
+ t = c*n+d*x;
+ c = d;
+ d = t;
+ n += 0.5;
+ q1 = q2;
+ q2 = b/d;
+ } while (fabs(q1-q2)/q2 > MATH_TOLERANCE);
+ return one_sqrtpi*exp(-x*x)*q2;
+}
+
+#endif
+
+#if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000)
+
+double
+log2(double x)
+{
+ return log10(x)/log10(2.0);
+}
+
+#endif
+
+/*
+ TRIGONOMETRIC FUNCTIONS
+*/
+
+/*
+ * call-seq:
+ * Math.sin(x) -> float
+ *
+ * Computes the sine of <i>x</i> (expressed in radians). Returns
+ * -1..1.
+ */
+static mrb_value
+math_sin(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = sin(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.cos(x) -> float
+ *
+ * Computes the cosine of <i>x</i> (expressed in radians). Returns
+ * -1..1.
+ */
+static mrb_value
+math_cos(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = cos(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.tan(x) -> float
+ *
+ * Returns the tangent of <i>x</i> (expressed in radians).
+ */
+static mrb_value
+math_tan(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = tan(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ INVERSE TRIGONOMETRIC FUNCTIONS
+*/
+
+/*
+ * call-seq:
+ * Math.asin(x) -> float
+ *
+ * Computes the arc sine of <i>x</i>.
+ * @return computed value between `-(PI/2)` and `(PI/2)`.
+ */
+static mrb_value
+math_asin(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < -1.0 || x > 1.0) {
+ domain_error(mrb, "asin");
+ }
+ x = asin(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.acos(x) -> float
+ *
+ * Computes the arc cosine of <i>x</i>. Returns 0..PI.
+ */
+static mrb_value
+math_acos(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < -1.0 || x > 1.0) {
+ domain_error(mrb, "acos");
+ }
+ x = acos(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.atan(x) -> float
+ *
+ * Computes the arc tangent of <i>x</i>. Returns `-(PI/2) .. (PI/2)`.
+ */
+static mrb_value
+math_atan(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = atan(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.atan2(y, x) -> float
+ *
+ * Computes the arc tangent given <i>y</i> and <i>x</i>. Returns
+ * -PI..PI.
+ *
+ * Math.atan2(-0.0, -1.0) #=> -3.141592653589793
+ * Math.atan2(-1.0, -1.0) #=> -2.356194490192345
+ * Math.atan2(-1.0, 0.0) #=> -1.5707963267948966
+ * Math.atan2(-1.0, 1.0) #=> -0.7853981633974483
+ * Math.atan2(-0.0, 1.0) #=> -0.0
+ * Math.atan2(0.0, 1.0) #=> 0.0
+ * Math.atan2(1.0, 1.0) #=> 0.7853981633974483
+ * Math.atan2(1.0, 0.0) #=> 1.5707963267948966
+ * Math.atan2(1.0, -1.0) #=> 2.356194490192345
+ * Math.atan2(0.0, -1.0) #=> 3.141592653589793
+ *
+ */
+static mrb_value
+math_atan2(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x, y;
+
+ mrb_get_args(mrb, "ff", &x, &y);
+ x = atan2(x, y);
+
+ return mrb_float_value(mrb, x);
+}
+
+
+
+/*
+ HYPERBOLIC TRIG FUNCTIONS
+*/
+/*
+ * call-seq:
+ * Math.sinh(x) -> float
+ *
+ * Computes the hyperbolic sine of <i>x</i> (expressed in
+ * radians).
+ */
+static mrb_value
+math_sinh(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = sinh(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.cosh(x) -> float
+ *
+ * Computes the hyperbolic cosine of <i>x</i> (expressed in radians).
+ */
+static mrb_value
+math_cosh(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = cosh(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.tanh() -> float
+ *
+ * Computes the hyperbolic tangent of <i>x</i> (expressed in
+ * radians).
+ */
+static mrb_value
+math_tanh(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = tanh(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+
+/*
+ INVERSE HYPERBOLIC TRIG FUNCTIONS
+*/
+
+/*
+ * call-seq:
+ * Math.asinh(x) -> float
+ *
+ * Computes the inverse hyperbolic sine of <i>x</i>.
+ */
+static mrb_value
+math_asinh(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+
+ x = asinh(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.acosh(x) -> float
+ *
+ * Computes the inverse hyperbolic cosine of <i>x</i>.
+ */
+static mrb_value
+math_acosh(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < 1.0) {
+ domain_error(mrb, "acosh");
+ }
+ x = acosh(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.atanh(x) -> float
+ *
+ * Computes the inverse hyperbolic tangent of <i>x</i>.
+ */
+static mrb_value
+math_atanh(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < -1.0 || x > 1.0) {
+ domain_error(mrb, "atanh");
+ }
+ x = atanh(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ EXPONENTIALS AND LOGARITHMS
+*/
+
+/*
+ * call-seq:
+ * Math.exp(x) -> float
+ *
+ * Returns e**x.
+ *
+ * Math.exp(0) #=> 1.0
+ * Math.exp(1) #=> 2.718281828459045
+ * Math.exp(1.5) #=> 4.4816890703380645
+ *
+ */
+static mrb_value
+math_exp(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = exp(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.log(numeric) -> float
+ * Math.log(num,base) -> float
+ *
+ * Returns the natural logarithm of <i>numeric</i>.
+ * If additional second argument is given, it will be the base
+ * of logarithm.
+ *
+ * Math.log(1) #=> 0.0
+ * Math.log(Math::E) #=> 1.0
+ * Math.log(Math::E**3) #=> 3.0
+ * Math.log(12,3) #=> 2.2618595071429146
+ *
+ */
+static mrb_value
+math_log(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x, base;
+ int argc;
+
+ argc = mrb_get_args(mrb, "f|f", &x, &base);
+ if (x < 0.0) {
+ domain_error(mrb, "log");
+ }
+ x = log(x);
+ if (argc == 2) {
+ if (base < 0.0) {
+ domain_error(mrb, "log");
+ }
+ x /= log(base);
+ }
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.log2(numeric) -> float
+ *
+ * Returns the base 2 logarithm of <i>numeric</i>.
+ *
+ * Math.log2(1) #=> 0.0
+ * Math.log2(2) #=> 1.0
+ * Math.log2(32768) #=> 15.0
+ * Math.log2(65536) #=> 16.0
+ *
+ */
+static mrb_value
+math_log2(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < 0.0) {
+ domain_error(mrb, "log2");
+ }
+ x = log2(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.log10(numeric) -> float
+ *
+ * Returns the base 10 logarithm of <i>numeric</i>.
+ *
+ * Math.log10(1) #=> 0.0
+ * Math.log10(10) #=> 1.0
+ * Math.log10(10**100) #=> 100.0
+ *
+ */
+static mrb_value
+math_log10(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < 0.0) {
+ domain_error(mrb, "log10");
+ }
+ x = log10(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.sqrt(numeric) -> float
+ *
+ * Returns the square root of <i>numeric</i>.
+ *
+ */
+static mrb_value
+math_sqrt(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ if (x < 0.0) {
+ domain_error(mrb, "sqrt");
+ }
+ x = sqrt(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+
+/*
+ * call-seq:
+ * Math.cbrt(numeric) -> float
+ *
+ * Returns the cube root of <i>numeric</i>.
+ *
+ * -9.upto(9) {|x|
+ * p [x, Math.cbrt(x), Math.cbrt(x)**3]
+ * }
+ * #=>
+ * [-9, -2.0800838230519, -9.0]
+ * [-8, -2.0, -8.0]
+ * [-7, -1.91293118277239, -7.0]
+ * [-6, -1.81712059283214, -6.0]
+ * [-5, -1.7099759466767, -5.0]
+ * [-4, -1.5874010519682, -4.0]
+ * [-3, -1.44224957030741, -3.0]
+ * [-2, -1.25992104989487, -2.0]
+ * [-1, -1.0, -1.0]
+ * [0, 0.0, 0.0]
+ * [1, 1.0, 1.0]
+ * [2, 1.25992104989487, 2.0]
+ * [3, 1.44224957030741, 3.0]
+ * [4, 1.5874010519682, 4.0]
+ * [5, 1.7099759466767, 5.0]
+ * [6, 1.81712059283214, 6.0]
+ * [7, 1.91293118277239, 7.0]
+ * [8, 2.0, 8.0]
+ * [9, 2.0800838230519, 9.0]
+ *
+ */
+static mrb_value
+math_cbrt(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = cbrt(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+
+/*
+ * call-seq:
+ * Math.frexp(numeric) -> [ fraction, exponent ]
+ *
+ * Returns a two-element array containing the normalized fraction (a
+ * <code>Float</code>) and exponent (a <code>Fixnum</code>) of
+ * <i>numeric</i>.
+ *
+ * fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11]
+ * fraction * 2**exponent #=> 1234.0
+ */
+static mrb_value
+math_frexp(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+ int exp;
+
+ mrb_get_args(mrb, "f", &x);
+ x = frexp(x, &exp);
+
+ return mrb_assoc_new(mrb, mrb_float_value(mrb, x), mrb_fixnum_value(exp));
+}
+
+/*
+ * call-seq:
+ * Math.ldexp(flt, int) -> float
+ *
+ * Returns the value of <i>flt</i>*(2**<i>int</i>).
+ *
+ * fraction, exponent = Math.frexp(1234)
+ * Math.ldexp(fraction, exponent) #=> 1234.0
+ */
+static mrb_value
+math_ldexp(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+ mrb_int i;
+
+ mrb_get_args(mrb, "fi", &x, &i);
+ x = ldexp(x, i);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.hypot(x, y) -> float
+ *
+ * Returns sqrt(x**2 + y**2), the hypotenuse of a right-angled triangle
+ * with sides <i>x</i> and <i>y</i>.
+ *
+ * Math.hypot(3, 4) #=> 5.0
+ */
+static mrb_value
+math_hypot(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x, y;
+
+ mrb_get_args(mrb, "ff", &x, &y);
+ x = hypot(x, y);
+
+ return mrb_float_value(mrb, x);
+}
+
+/*
+ * call-seq:
+ * Math.erf(x) -> float
+ *
+ * Calculates the error function of x.
+ */
+static mrb_value
+math_erf(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = erf(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+
+/*
+ * call-seq:
+ * Math.erfc(x) -> float
+ *
+ * Calculates the complementary error function of x.
+ */
+static mrb_value
+math_erfc(mrb_state *mrb, mrb_value obj)
+{
+ mrb_float x;
+
+ mrb_get_args(mrb, "f", &x);
+ x = erfc(x);
+
+ return mrb_float_value(mrb, x);
+}
+
+/* ------------------------------------------------------------------------*/
+void
+mrb_mruby_math_gem_init(mrb_state* mrb)
+{
+ struct RClass *mrb_math;
+ mrb_math = mrb_define_module(mrb, "Math");
+
+ mrb_define_class_under(mrb, mrb_math, "DomainError", mrb->eStandardError_class);
+
+#ifdef M_PI
+ mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, M_PI));
+#else
+ mrb_define_const(mrb, mrb_math, "PI", mrb_float_value(mrb, atan(1.0)*4.0));
+#endif
+
+#ifdef M_E
+ mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, M_E));
+#else
+ mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, exp(1.0)));
+#endif
+
+#ifdef MRB_USE_FLOAT
+ mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-5));
+#else
+ mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12));
+#endif
+
+ mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, mrb_math, "asin", math_asin, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "acos", math_acos, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "atan", math_atan, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "atan2", math_atan2, MRB_ARGS_REQ(2));
+
+ mrb_define_module_function(mrb, mrb_math, "sinh", math_sinh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "cosh", math_cosh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "tanh", math_tanh, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, mrb_math, "asinh", math_asinh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "acosh", math_acosh, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "atanh", math_atanh, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, mrb_math, "exp", math_exp, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "log", math_log, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_module_function(mrb, mrb_math, "log2", math_log2, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "log10", math_log10, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "sqrt", math_sqrt, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "cbrt", math_cbrt, MRB_ARGS_REQ(1));
+
+ mrb_define_module_function(mrb, mrb_math, "frexp", math_frexp, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "ldexp", math_ldexp, MRB_ARGS_REQ(2));
+
+ mrb_define_module_function(mrb, mrb_math, "hypot", math_hypot, MRB_ARGS_REQ(2));
+
+ mrb_define_module_function(mrb, mrb_math, "erf", math_erf, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, mrb_math, "erfc", math_erfc, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_math_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb
new file mode 100644
index 00000000..e9ea07cc
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-math/test/math.rb
@@ -0,0 +1,152 @@
+##
+# Math Test
+
+##
+# Performs fuzzy check for equality on methods returning floats
+# on the basis of the Math::TOLERANCE constant.
+def check_float(a, b)
+ tolerance = Math::TOLERANCE
+ a = a.to_f
+ b = b.to_f
+ if a.finite? and b.finite?
+ (a-b).abs < tolerance
+ else
+ true
+ end
+end
+
+assert('Math.sin 0') do
+ check_float(Math.sin(0), 0)
+end
+
+assert('Math.sin PI/2') do
+ check_float(Math.sin(Math::PI / 2), 1)
+end
+
+assert('Math.cos 0') do
+ check_float(Math.cos(0), 1)
+end
+
+assert('Math.cos PI/2') do
+ check_float(Math.cos(Math::PI / 2), 0)
+end
+
+assert('Math.tan 0') do
+ check_float(Math.tan(0), 0)
+end
+
+assert('Math.tan PI/4') do
+ check_float(Math.tan(Math::PI / 4), 1)
+end
+
+assert('Fundamental trig identities') do
+ result = true
+ N = 13
+ N.times do |i|
+ a = Math::PI / N * i
+ ca = Math::PI / 2 - a
+ s = Math.sin(a)
+ c = Math.cos(a)
+ t = Math.tan(a)
+ result &= check_float(s, Math.cos(ca))
+ result &= check_float(t, 1 / Math.tan(ca))
+ result &= check_float(s ** 2 + c ** 2, 1)
+ result &= check_float(t ** 2 + 1, (1/c) ** 2)
+ result &= check_float((1/t) ** 2 + 1, (1/s) ** 2)
+ end
+ result
+end
+
+assert('Math.erf 0') do
+ check_float(Math.erf(0), 0)
+end
+
+assert('Math.exp 0') do
+ check_float(Math.exp(0), 1.0)
+end
+
+assert('Math.exp 1') do
+ check_float(Math.exp(1), 2.718281828459045)
+end
+
+assert('Math.exp 1.5') do
+ check_float(Math.exp(1.5), 4.4816890703380645)
+end
+
+assert('Math.log 1') do
+ check_float(Math.log(1), 0)
+end
+
+assert('Math.log E') do
+ check_float(Math.log(Math::E), 1.0)
+end
+
+assert('Math.log E**3') do
+ check_float(Math.log(Math::E**3), 3.0)
+end
+
+assert('Math.log2 1') do
+ check_float(Math.log2(1), 0.0)
+end
+
+assert('Math.log2 2') do
+ check_float(Math.log2(2), 1.0)
+end
+
+assert('Math.log10 1') do
+ check_float(Math.log10(1), 0.0)
+end
+
+assert('Math.log10 10') do
+ check_float(Math.log10(10), 1.0)
+end
+
+assert('Math.log10 10**100') do
+ check_float(Math.log10(10**100), 100.0)
+end
+
+assert('Math.sqrt') do
+ num = [0.0, 1.0, 2.0, 3.0, 4.0]
+ sqr = [0, 1, 4, 9, 16]
+ result = true
+ sqr.each_with_index do |v,i|
+ result &= check_float(Math.sqrt(v), num[i])
+ end
+ result
+end
+
+assert('Math.cbrt') do
+ num = [-2.0, -1.0, 0.0, 1.0, 2.0]
+ cub = [-8, -1, 0, 1, 8]
+ result = true
+ cub.each_with_index do |v,i|
+ result &= check_float(Math.cbrt(v), num[i])
+ end
+ result
+end
+
+assert('Math.hypot') do
+ check_float(Math.hypot(3, 4), 5.0)
+end
+
+assert('Math.frexp 1234') do
+ n = 1234
+ fraction, exponent = Math.frexp(n)
+ check_float(Math.ldexp(fraction, exponent), n)
+end
+
+assert('Math.erf 1') do
+ check_float(Math.erf(1), 0.842700792949715)
+end
+
+assert('Math.erfc 1') do
+ check_float(Math.erfc(1), 0.157299207050285)
+end
+
+assert('Math.erf -1') do
+ check_float(Math.erf(-1), -0.8427007929497148)
+end
+
+assert('Math.erfc -1') do
+ check_float(Math.erfc(-1), 1.8427007929497148)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake
new file mode 100644
index 00000000..6db7e589
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-numeric-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Numeric class extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
new file mode 100644
index 00000000..0bf3c6ab
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
@@ -0,0 +1,17 @@
+module Integral
+ def div(other)
+ self.divmod(other)[0]
+ end
+
+ def zero?
+ self == 0
+ end
+
+ def nonzero?
+ if self == 0
+ nil
+ else
+ self
+ end
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
new file mode 100644
index 00000000..f71236a3
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -0,0 +1,30 @@
+#include <limits.h>
+#include <mruby.h>
+
+static mrb_value
+mrb_int_chr(mrb_state *mrb, mrb_value x)
+{
+ mrb_int chr;
+ char c;
+
+ chr = mrb_fixnum(x);
+ if (chr >= (1 << CHAR_BIT)) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
+ }
+ c = (char)chr;
+
+ return mrb_str_new(mrb, &c, 1);
+}
+
+void
+mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass *i = mrb_class_get(mrb, "Integer");
+
+ mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_numeric_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb
new file mode 100644
index 00000000..8bead248
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -0,0 +1,28 @@
+##
+# Numeric(Ext) Test
+
+assert('Integer#chr') do
+ assert_equal("A", 65.chr)
+ assert_equal("B", 0x42.chr)
+
+ # multibyte encoding (not support yet)
+ assert_raise(RangeError) { 256.chr }
+end
+
+assert('Integer#div') do
+ assert_equal 52, 365.div(7)
+end
+
+assert('Float#div') do
+ assert_float 52, 365.2425.div(7)
+end
+
+assert('Integer#zero?') do
+ assert_equal true, 0.zero?
+ assert_equal false, 1.zero?
+end
+
+assert('Integer#nonzero?') do
+ assert_equal nil, 0.nonzero?
+ assert_equal 1000, 1000.nonzero?
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake
new file mode 100644
index 00000000..6d14b4a5
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-object-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Object class extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb
new file mode 100644
index 00000000..581156cb
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/mrblib/object.rb
@@ -0,0 +1,19 @@
+class Object
+ ##
+ # call-seq:
+ # obj.tap{|x|...} -> obj
+ #
+ # Yields <code>x</code> to the block, and then returns <code>x</code>.
+ # The primary purpose of this method is to "tap into" a method chain,
+ # in order to perform operations on intermediate results within the chain.
+ #
+ # (1..10) .tap {|x| puts "original: #{x.inspect}"}
+ # .to_a .tap {|x| puts "array: #{x.inspect}"}
+ # .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"}
+ # .map { |x| x*x } .tap {|x| puts "squares: #{x.inspect}"}
+ #
+ def tap
+ yield self
+ self
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c
new file mode 100644
index 00000000..35a07b58
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/src/object.c
@@ -0,0 +1,106 @@
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+
+/*
+ * call-seq:
+ * nil.to_a -> []
+ *
+ * Always returns an empty array.
+ */
+
+static mrb_value
+nil_to_a(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_ary_new(mrb);
+}
+
+/*
+ * call-seq:
+ * nil.to_f -> 0.0
+ *
+ * Always returns zero.
+ */
+
+static mrb_value
+nil_to_f(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_float_value(mrb, 0.0);
+}
+
+/*
+ * call-seq:
+ * nil.to_i -> 0
+ *
+ * Always returns zero.
+ */
+
+static mrb_value
+nil_to_i(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_fixnum_value(0);
+}
+
+/*
+ * call-seq:
+ * obj.instance_exec(arg...) {|var...| block } -> obj
+ *
+ * Executes the given block within the context of the receiver
+ * (_obj_). In order to set the context, the variable +self+ is set
+ * to _obj_ while the code is executing, giving the code access to
+ * _obj_'s instance variables. Arguments are passed as block parameters.
+ *
+ * class KlassWithSecret
+ * def initialize
+ * @secret = 99
+ * end
+ * end
+ * k = KlassWithSecret.new
+ * k.instance_exec(5) {|x| @secret+x } #=> 104
+ */
+
+static mrb_value
+mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
+{
+ const mrb_value *argv;
+ mrb_int argc;
+ mrb_value blk;
+ struct RClass *c;
+
+ mrb_get_args(mrb, "*&", &argv, &argc, &blk);
+
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ }
+
+ switch (mrb_type(self)) {
+ case MRB_TT_SYMBOL:
+ case MRB_TT_FIXNUM:
+ case MRB_TT_FLOAT:
+ c = NULL;
+ break;
+ default:
+ c = mrb_class_ptr(mrb_singleton_class(mrb, self));
+ break;
+ }
+ mrb->c->ci->target_class = c;
+ return mrb_yield_cont(mrb, blk, self, argc, argv);
+}
+
+void
+mrb_mruby_object_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass * n = mrb->nil_class;
+
+ mrb_define_method(mrb, n, "to_a", nil_to_a, MRB_ARGS_NONE());
+ mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE());
+ mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
+}
+
+void
+mrb_mruby_object_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb
new file mode 100644
index 00000000..5cd1cf4e
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/nil.rb
@@ -0,0 +1,11 @@
+assert('NilClass#to_a') do
+ assert_equal [], nil.to_a
+end
+
+assert('NilClass#to_f') do
+ assert_equal 0.0, nil.to_f
+end
+
+assert('NilClass#to_i') do
+ assert_equal 0, nil.to_i
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb
new file mode 100644
index 00000000..f0742f8c
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-object-ext/test/object.rb
@@ -0,0 +1,53 @@
+assert('Object#instance_exec') do
+ class KlassWithSecret
+ def initialize
+ @secret = 99
+ end
+ end
+ k = KlassWithSecret.new
+ assert_equal 104, k.instance_exec(5) {|x| @secret+x }
+end
+
+assert('Object#tap') do
+ ret = []
+ (1..10) .tap {|x| ret << "original: #{x.inspect}"}
+ .to_a .tap {|x| ret << "array: #{x.inspect}"}
+ .select {|x| x%2==0} .tap {|x| ret << "evens: #{x.inspect}"}
+ .map { |x| x*x } .tap {|x| ret << "squares: #{x.inspect}"}
+
+ assert_equal [
+ "original: 1..10",
+ "array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
+ "evens: [2, 4, 6, 8, 10]",
+ "squares: [4, 16, 36, 64, 100]"
+ ], ret
+ assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m)
+end
+
+assert('instance_exec on primitives with class and module definition') do
+ begin
+ class A
+ 1.instance_exec do
+ class B
+ end
+ end
+ end
+
+ assert_kind_of Class, A::B
+ ensure
+ Object.remove_const :A
+ end
+
+ begin
+ class A
+ 1.instance_exec do
+ module B
+ end
+ end
+ end
+
+ assert_kind_of Module, A::B
+ ensure
+ Object.remove_const :A
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake
new file mode 100644
index 00000000..fa35136a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-objectspace') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'ObjectSpace class'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c
new file mode 100644
index 00000000..3887091d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/src/mruby_objectspace.c
@@ -0,0 +1,187 @@
+#include <mruby.h>
+#include <mruby/gc.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
+
+struct os_count_struct {
+ mrb_int total;
+ mrb_int freed;
+ mrb_int counts[MRB_TT_MAXDEFINE+1];
+};
+
+static int
+os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data)
+{
+ struct os_count_struct *obj_count;
+ obj_count = (struct os_count_struct*)data;
+
+ obj_count->total++;
+
+ if (mrb_object_dead_p(mrb, obj)) {
+ obj_count->freed++;
+ }
+ else {
+ obj_count->counts[obj->tt]++;
+ }
+ return MRB_EACH_OBJ_OK;
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.count_objects([result_hash]) -> hash
+ *
+ * Counts objects for each type.
+ *
+ * It returns a hash, such as:
+ * {
+ * :TOTAL=>10000,
+ * :FREE=>3011,
+ * :T_OBJECT=>6,
+ * :T_CLASS=>404,
+ * # ...
+ * }
+ *
+ * If the optional argument +result_hash+ is given,
+ * it is overwritten and returned. This is intended to avoid probe effect.
+ *
+ */
+
+static mrb_value
+os_count_objects(mrb_state *mrb, mrb_value self)
+{
+ struct os_count_struct obj_count = { 0 };
+ mrb_int i;
+ mrb_value hash;
+
+ if (mrb_get_args(mrb, "|H", &hash) == 0) {
+ hash = mrb_hash_new(mrb);
+ }
+
+ if (!mrb_test(mrb_hash_empty_p(mrb, hash))) {
+ mrb_hash_clear(mrb, hash);
+ }
+
+ mrb_objspace_each_objects(mrb, os_count_object_type, &obj_count);
+
+ mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "TOTAL")), mrb_fixnum_value(obj_count.total));
+ mrb_hash_set(mrb, hash, mrb_symbol_value(mrb_intern_lit(mrb, "FREE")), mrb_fixnum_value(obj_count.freed));
+
+ for (i = MRB_TT_FALSE; i < MRB_TT_MAXDEFINE; i++) {
+ mrb_value type;
+ switch (i) {
+#define COUNT_TYPE(t) case (MRB_T ## t): type = mrb_symbol_value(mrb_intern_lit(mrb, #t)); break;
+ COUNT_TYPE(T_FALSE);
+ COUNT_TYPE(T_FREE);
+ COUNT_TYPE(T_TRUE);
+ COUNT_TYPE(T_FIXNUM);
+ COUNT_TYPE(T_SYMBOL);
+ COUNT_TYPE(T_UNDEF);
+ COUNT_TYPE(T_FLOAT);
+ COUNT_TYPE(T_CPTR);
+ COUNT_TYPE(T_OBJECT);
+ COUNT_TYPE(T_CLASS);
+ COUNT_TYPE(T_MODULE);
+ COUNT_TYPE(T_ICLASS);
+ COUNT_TYPE(T_SCLASS);
+ COUNT_TYPE(T_PROC);
+ COUNT_TYPE(T_ARRAY);
+ COUNT_TYPE(T_HASH);
+ COUNT_TYPE(T_STRING);
+ COUNT_TYPE(T_RANGE);
+ COUNT_TYPE(T_EXCEPTION);
+ COUNT_TYPE(T_FILE);
+ COUNT_TYPE(T_ENV);
+ COUNT_TYPE(T_DATA);
+ COUNT_TYPE(T_FIBER);
+#undef COUNT_TYPE
+ default:
+ type = mrb_fixnum_value(i); break;
+ }
+ if (obj_count.counts[i])
+ mrb_hash_set(mrb, hash, type, mrb_fixnum_value(obj_count.counts[i]));
+ }
+
+ return hash;
+}
+
+struct os_each_object_data {
+ mrb_value block;
+ struct RClass *target_module;
+ mrb_int count;
+};
+
+static int
+os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud)
+{
+ struct os_each_object_data *d = (struct os_each_object_data*)ud;
+
+ /* filter dead objects */
+ if (mrb_object_dead_p(mrb, obj)) {
+ return MRB_EACH_OBJ_OK;
+ }
+
+ /* filter internal objects */
+ switch (obj->tt) {
+ case MRB_TT_ENV:
+ case MRB_TT_ICLASS:
+ return MRB_EACH_OBJ_OK;
+ default:
+ break;
+ }
+
+ /* filter half baked (or internal) objects */
+ if (!obj->c) return MRB_EACH_OBJ_OK;
+
+ /* filter class kind if target module defined */
+ if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) {
+ return MRB_EACH_OBJ_OK;
+ }
+
+ mrb_yield(mrb, d->block, mrb_obj_value(obj));
+ ++d->count;
+ return MRB_EACH_OBJ_OK;
+}
+
+/*
+ * call-seq:
+ * ObjectSpace.each_object([module]) {|obj| ... } -> fixnum
+ *
+ * Calls the block once for each object in this Ruby process.
+ * Returns the number of objects found.
+ * If the optional argument +module+ is given,
+ * calls the block for only those classes or modules
+ * that match (or are a subclass of) +module+.
+ *
+ * If no block is given, ArgumentError is raised.
+ *
+ */
+
+static mrb_value
+os_each_object(mrb_state *mrb, mrb_value self)
+{
+ mrb_value cls = mrb_nil_value();
+ struct os_each_object_data d;
+ mrb_get_args(mrb, "&|C", &d.block, &cls);
+
+ if (mrb_nil_p(d.block)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Expected block in ObjectSpace.each_object.");
+ }
+
+ d.target_module = mrb_nil_p(cls) ? NULL : mrb_class_ptr(cls);
+ d.count = 0;
+ mrb_objspace_each_objects(mrb, os_each_object_cb, &d);
+ return mrb_fixnum_value(d.count);
+}
+
+void
+mrb_mruby_objectspace_gem_init(mrb_state *mrb)
+{
+ struct RClass *os = mrb_define_module(mrb, "ObjectSpace");
+ mrb_define_class_method(mrb, os, "count_objects", os_count_objects, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, os, "each_object", os_each_object, MRB_ARGS_OPT(1));
+}
+
+void
+mrb_mruby_objectspace_gem_final(mrb_state *mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb
new file mode 100644
index 00000000..4731d53b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-objectspace/test/objectspace.rb
@@ -0,0 +1,60 @@
+assert('ObjectSpace.count_objects') do
+ h = {}
+ f = Fiber.new {} if Object.const_defined? :Fiber
+ ObjectSpace.count_objects(h)
+ assert_kind_of(Hash, h)
+ assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
+ assert_true(h.values.all? {|x| x.is_a?(Integer) })
+
+ assert_true(h.has_key?(:TOTAL))
+ assert_true(h.has_key?(:FREE))
+ assert_true(h.has_key?(:T_FIBER)) if Object.const_defined? :Fiber
+
+ assert_equal(h[:TOTAL] * 2, h.values.reduce(:+))
+
+ h = ObjectSpace.count_objects
+ assert_kind_of(Hash, h)
+ assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
+ assert_true(h.values.all? {|x| x.is_a?(Integer) })
+
+ assert_raise(TypeError) { ObjectSpace.count_objects(1) }
+
+ h0 = {:T_FOO=>1000}
+ h = ObjectSpace.count_objects(h0)
+ assert_false(h0.has_key?(:T_FOO))
+
+ GC.start
+ h_after = {}
+ h_before = ObjectSpace.count_objects
+
+ objs = []
+ 1000.times do
+ objs << {}
+ end
+ objs = nil
+ ObjectSpace.count_objects(h)
+ GC.start
+ ObjectSpace.count_objects(h_after)
+
+ assert_equal(h[:T_HASH], h_before[:T_HASH] + 1000)
+ assert_equal(h_after[:T_HASH], h_before[:T_HASH])
+end
+
+assert('ObjectSpace.each_object') do
+ objs = []
+ objs_count = ObjectSpace.each_object { |obj|
+ objs << obj
+ }
+ assert_equal objs.length, objs_count
+
+ arys = []
+ arys_count = ObjectSpace.each_object(Array) { |obj|
+ arys << obj
+ }
+ assert_equal arys.length, arys_count
+ assert_true arys.length < objs.length
+end
+
+assert 'Check class pointer of ObjectSpace.each_object.' do
+ ObjectSpace.each_object { |obj| !obj }
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake
new file mode 100644
index 00000000..2ea6e312
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-print') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard print/puts/p'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb
new file mode 100644
index 00000000..38a10661
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/mrblib/print.rb
@@ -0,0 +1,64 @@
+##
+# Kernel
+#
+# ISO 15.3.1
+module Kernel
+ ##
+ # Invoke method +print+ on STDOUT and passing +*args+
+ #
+ # ISO 15.3.1.2.10
+ def print(*args)
+ i = 0
+ len = args.size
+ while i < len
+ __printstr__ args[i].to_s
+ i += 1
+ end
+ end
+
+ ##
+ # Invoke method +puts+ on STDOUT and passing +*args*+
+ #
+ # ISO 15.3.1.2.11
+ def puts(*args)
+ i = 0
+ len = args.size
+ while i < len
+ s = args[i].to_s
+ __printstr__ s
+ __printstr__ "\n" if (s[-1] != "\n")
+ i += 1
+ end
+ __printstr__ "\n" if len == 0
+ nil
+ end
+
+ ##
+ # Print human readable object description
+ #
+ # ISO 15.3.1.3.34
+ def p(*args)
+ i = 0
+ len = args.size
+ while i < len
+ __printstr__ args[i].inspect
+ __printstr__ "\n"
+ i += 1
+ end
+ args[0]
+ end
+
+ unless Kernel.respond_to?(:sprintf)
+ def printf(*args)
+ raise NotImplementedError.new('printf not available')
+ end
+ def sprintf(*args)
+ raise NotImplementedError.new('sprintf not available')
+ end
+ else
+ def printf(*args)
+ __printstr__(sprintf(*args))
+ nil
+ end
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c
new file mode 100644
index 00000000..d0c522d2
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-print/src/print.c
@@ -0,0 +1,64 @@
+#include <mruby.h>
+#include <mruby/string.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#if defined(_WIN32)
+# include <windows.h>
+# include <io.h>
+#ifdef _MSC_VER
+# define isatty(x) _isatty(x)
+# define fileno(x) _fileno(x)
+#endif
+#endif
+
+static void
+printstr(mrb_state *mrb, mrb_value obj)
+{
+ if (mrb_string_p(obj)) {
+#if defined(_WIN32)
+ if (isatty(fileno(stdout))) {
+ DWORD written;
+ int mlen = RSTRING_LEN(obj);
+ char* utf8 = RSTRING_PTR(obj);
+ int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
+ wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
+ if (utf16 == NULL) return;
+ if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) {
+ utf16[wlen] = 0;
+ WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+ utf16, wlen, &written, NULL);
+ }
+ mrb_free(mrb, utf16);
+ } else
+#endif
+ fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
+ fflush(stdout);
+ }
+}
+
+/* 15.3.1.2.9 */
+/* 15.3.1.3.34 */
+mrb_value
+mrb_printstr(mrb_state *mrb, mrb_value self)
+{
+ mrb_value argv;
+
+ mrb_get_args(mrb, "o", &argv);
+ printstr(mrb, argv);
+
+ return argv;
+}
+
+void
+mrb_mruby_print_gem_init(mrb_state* mrb)
+{
+ struct RClass *krn;
+ krn = mrb->kernel_module;
+ mrb_define_method(mrb, krn, "__printstr__", mrb_printstr, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_print_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake
new file mode 100644
index 00000000..e4d15a14
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-proc-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Proc class extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb
new file mode 100644
index 00000000..b7166393
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/mrblib/proc.rb
@@ -0,0 +1,42 @@
+class Proc
+
+ def ===(*args)
+ call(*args)
+ end
+
+ def yield(*args)
+ call(*args)
+ end
+
+ def to_proc
+ self
+ end
+
+ def curry(arity=self.arity)
+ type = :proc
+ abs = lambda {|a| a < 0 ? -a - 1 : a}
+ arity = abs[arity]
+ if lambda?
+ type = :lambda
+ self_arity = self.arity
+ if (self_arity >= 0 && arity != self_arity) ||
+ (self_arity < 0 && abs[self_arity] > arity)
+ raise ArgumentError, "wrong number of arguments (#{arity} for #{abs[self_arity]})"
+ end
+ end
+
+ pproc = self
+ make_curry = proc do |given_args=[]|
+ send(type) do |*args|
+ new_args = given_args + args
+ if new_args.size >= arity
+ pproc[*new_args]
+ else
+ make_curry[new_args]
+ end
+ end
+ end
+ make_curry.call
+ end
+
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c
new file mode 100644
index 00000000..b13606f5
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/src/proc.c
@@ -0,0 +1,173 @@
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
+
+static mrb_value
+mrb_proc_lambda(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *p = mrb_proc_ptr(self);
+ return mrb_bool_value(MRB_PROC_STRICT_P(p));
+}
+
+static mrb_value
+mrb_proc_source_location(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *p = mrb_proc_ptr(self);
+
+ if (MRB_PROC_CFUNC_P(p)) {
+ return mrb_nil_value();
+ }
+ else {
+ mrb_irep *irep = p->body.irep;
+ int32_t line;
+ const char *filename;
+
+ filename = mrb_debug_get_filename(irep, 0);
+ line = mrb_debug_get_line(irep, 0);
+
+ return (!filename && line == -1)? mrb_nil_value()
+ : mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line));
+ }
+}
+
+static mrb_value
+mrb_proc_inspect(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *p = mrb_proc_ptr(self);
+ mrb_value str = mrb_str_new_lit(mrb, "#<Proc:");
+ mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
+
+ if (!MRB_PROC_CFUNC_P(p)) {
+ mrb_irep *irep = p->body.irep;
+ const char *filename;
+ int32_t line;
+ mrb_str_cat_lit(mrb, str, "@");
+
+ filename = mrb_debug_get_filename(irep, 0);
+ mrb_str_cat_cstr(mrb, str, filename ? filename : "-");
+ mrb_str_cat_lit(mrb, str, ":");
+
+ line = mrb_debug_get_line(irep, 0);
+ if (line != -1) {
+ str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
+ }
+ else {
+ mrb_str_cat_lit(mrb, str, "-");
+ }
+ }
+
+ if (MRB_PROC_STRICT_P(p)) {
+ mrb_str_cat_lit(mrb, str, " (lambda)");
+ }
+
+ mrb_str_cat_lit(mrb, str, ">");
+ return str;
+}
+
+static mrb_value
+mrb_kernel_proc(mrb_state *mrb, mrb_value self)
+{
+ mrb_value blk;
+
+ mrb_get_args(mrb, "&", &blk);
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
+ }
+
+ return blk;
+}
+
+/*
+ * call-seq:
+ * prc.parameters -> array
+ *
+ * Returns the parameter information of this proc.
+ *
+ * prc = lambda{|x, y=42, *other|}
+ * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]]
+ */
+
+static mrb_value
+mrb_proc_parameters(mrb_state *mrb, mrb_value self)
+{
+ struct parameters_type {
+ int size;
+ const char *name;
+ } *p, parameters_list [] = {
+ {0, "req"},
+ {0, "opt"},
+ {0, "rest"},
+ {0, "req"},
+ {0, "block"},
+ {0, NULL}
+ };
+ const struct RProc *proc = mrb_proc_ptr(self);
+ const struct mrb_irep *irep = proc->body.irep;
+ mrb_aspec aspec;
+ mrb_value sname, parameters;
+ int i, j;
+
+ if (MRB_PROC_CFUNC_P(proc)) {
+ // TODO cfunc aspec is not implemented yet
+ return mrb_ary_new(mrb);
+ }
+ if (!irep) {
+ return mrb_ary_new(mrb);
+ }
+ if (!irep->lv) {
+ return mrb_ary_new(mrb);
+ }
+ if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
+ return mrb_ary_new(mrb);
+ }
+
+ if (!MRB_PROC_STRICT_P(proc)) {
+ parameters_list[0].name = "opt";
+ parameters_list[3].name = "opt";
+ }
+
+ aspec = GETARG_Ax(*irep->iseq);
+ parameters_list[0].size = MRB_ASPEC_REQ(aspec);
+ parameters_list[1].size = MRB_ASPEC_OPT(aspec);
+ parameters_list[2].size = MRB_ASPEC_REST(aspec);
+ parameters_list[3].size = MRB_ASPEC_POST(aspec);
+ parameters_list[4].size = MRB_ASPEC_BLOCK(aspec);
+
+ parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
+
+ for (i = 0, p = parameters_list; p->name; p++) {
+ if (p->size <= 0) continue;
+ sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
+ for (j = 0; j < p->size; i++, j++) {
+ mrb_value a = mrb_ary_new(mrb);
+ mrb_ary_push(mrb, a, sname);
+ if (irep->lv[i].name) {
+ mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
+ }
+ mrb_ary_push(mrb, parameters, a);
+ }
+ }
+ return parameters;
+}
+
+void
+mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass *p = mrb->proc_class;
+ mrb_define_method(mrb, p, "lambda?", mrb_proc_lambda, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "to_s", mrb_proc_inspect, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "inspect", mrb_proc_inspect, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "parameters", mrb_proc_parameters, MRB_ARGS_NONE());
+
+ mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
+ mrb_define_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_proc_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c
new file mode 100644
index 00000000..a77b68e9
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.c
@@ -0,0 +1,56 @@
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/class.h>
+
+static mrb_value
+return_func_name(mrb_state *mrb, mrb_value self)
+{
+ return mrb_cfunc_env_get(mrb, 0);
+}
+
+static mrb_value
+proc_new_cfunc_with_env(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym n;
+ mrb_value n_val;
+ mrb_get_args(mrb, "n", &n);
+ n_val = mrb_symbol_value(n);
+ mrb_define_method_raw(mrb, mrb_class_ptr(self), n,
+ mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val));
+ return self;
+}
+
+static mrb_value
+return_env(mrb_state *mrb, mrb_value self)
+{
+ mrb_int idx;
+ mrb_get_args(mrb, "i", &idx);
+ return mrb_cfunc_env_get(mrb, idx);
+}
+
+static mrb_value
+cfunc_env_get(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym n;
+ mrb_value *argv; mrb_int argc;
+ mrb_get_args(mrb, "na", &n, &argv, &argc);
+ mrb_define_method_raw(mrb, mrb_class_ptr(self), n,
+ mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv));
+ return self;
+}
+
+static mrb_value
+cfunc_without_env(mrb_state *mrb, mrb_value self)
+{
+ return mrb_cfunc_env_get(mrb, 0);
+}
+
+void mrb_mruby_proc_ext_gem_test(mrb_state *mrb)
+{
+ struct RClass *cls;
+
+ cls = mrb_define_class(mrb, "ProcExtTest", mrb->object_class);
+ mrb_define_module_function(mrb, cls, "mrb_proc_new_cfunc_with_env", proc_new_cfunc_with_env, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, cls, "mrb_cfunc_env_get", cfunc_env_get, MRB_ARGS_REQ(2));
+ mrb_define_module_function(mrb, cls, "cfunc_without_env", cfunc_without_env, MRB_ARGS_NONE());
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb
new file mode 100644
index 00000000..037d8d12
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-proc-ext/test/proc.rb
@@ -0,0 +1,96 @@
+##
+# Proc(Ext) Test
+
+assert('Proc#source_location') do
+ loc = Proc.new {}.source_location
+ next true if loc.nil?
+ assert_equal loc[0][-7, 7], 'proc.rb'
+ assert_equal loc[1], 5
+end
+
+assert('Proc#inspect') do
+ ins = Proc.new{}.inspect
+ assert_kind_of String, ins
+end
+
+assert('Proc#lambda?') do
+ assert_true lambda{}.lambda?
+ assert_true !Proc.new{}.lambda?
+end
+
+assert('Proc#===') do
+ proc = Proc.new {|a| a * 2}
+ assert_equal 20, (proc === 10)
+end
+
+assert('Proc#yield') do
+ proc = Proc.new {|a| a * 2}
+ assert_equal 20, proc.yield(10)
+end
+
+assert('Proc#curry') do
+ b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
+ assert_equal 6, b.curry[1][2][3]
+ assert_equal 6, b.curry[1, 2][3, 4]
+ assert_equal 6, b.curry(5)[1][2][3][4][5]
+ assert_equal 6, b.curry(5)[1, 2][3, 4][5]
+ assert_equal 1, b.curry(1)[1]
+
+ b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
+ assert_equal 6, b.curry[1][2][3]
+ assert_raise(ArgumentError) { b.curry[1, 2][3, 4] }
+ assert_raise(ArgumentError) { b.curry(5) }
+ assert_raise(ArgumentError) { b.curry(1) }
+
+ assert_false(proc{}.curry.lambda?)
+ assert_true(lambda{}.curry.lambda?)
+end
+
+assert('Proc#parameters') do
+ assert_equal([], Proc.new {}.parameters)
+ assert_equal([], Proc.new {||}.parameters)
+ assert_equal([[:opt, :a]], Proc.new {|a|}.parameters)
+ assert_equal([[:req, :a]], lambda {|a|}.parameters)
+ assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters)
+ assert_equal([[:req, :a]], ->(a){}.parameters)
+ assert_equal([[:rest]], lambda { |*| }.parameters)
+ assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters)
+ assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
+ assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
+end
+
+assert('Proc#to_proc') do
+ proc = Proc.new {}
+ assert_equal proc, proc.to_proc
+end
+
+assert('Kernel#proc') do
+ assert_true !proc{|a|}.lambda?
+
+ assert_raise LocalJumpError do
+ proc{ break }.call
+ end
+end
+
+assert('mrb_proc_new_cfunc_with_env') do
+ ProcExtTest.mrb_proc_new_cfunc_with_env(:test)
+ ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby)
+
+ t = ProcExtTest.new
+
+ assert_equal :test, t.test
+ assert_equal :mruby, t.mruby
+end
+
+assert('mrb_cfunc_env_get') do
+ ProcExtTest.mrb_cfunc_env_get :get_int, [0, 1, 2]
+
+ t = ProcExtTest.new
+
+ assert_raise(TypeError) { t.cfunc_without_env }
+
+ assert_raise(IndexError) { t.get_int(-1) }
+ assert_raise(IndexError) { t.get_int(3) }
+
+ assert_equal 1, t.get_int(1)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake
new file mode 100644
index 00000000..3a3fd2bd
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-random') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Random class'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c
new file mode 100644
index 00000000..405bd5c2
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.c
@@ -0,0 +1,224 @@
+/*
+** mt19937ar.c - MT Random functions
+**
+** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
+** All rights reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+**
+** Any feedback is very welcome.
+** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+**
+** This version is modified by mruby developers. If you see any problem,
+** contact us first at https://github.com/mruby/mruby/issues
+*/
+
+#include <mruby.h>
+#include "mt19937ar.h"
+
+/* Period parameters */
+/* #define N 624 */
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+
+#if 0 /* dead_code */
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+#endif /* dead_code */
+
+void mrb_random_init_genrand(mt_state *t, unsigned long s)
+{
+ t->mt[0]= s & 0xffffffffUL;
+ for (t->mti=1; t->mti<N; t->mti++) {
+ t->mt[t->mti] = (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti);
+ t->mt[t->mti] &= 0xffffffffUL;
+ }
+}
+
+unsigned long mrb_random_genrand_int32(mt_state *t)
+{
+ unsigned long y;
+ static const unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (t->mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (t->mti == N+1) /* if init_genrand() has not been called, */
+ mrb_random_init_genrand(t, 5489UL); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
+ t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
+ t->mt[kk] = t->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (t->mt[N-1]&UPPER_MASK)|(t->mt[0]&LOWER_MASK);
+ t->mt[N-1] = t->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ t->mti = 0;
+ }
+
+ y = t->mt[t->mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ t->gen.int_ = y;
+
+ return y;
+}
+
+double mrb_random_genrand_real1(mt_state *t)
+{
+ mrb_random_genrand_int32(t);
+ t->gen.double_ = t->gen.int_*(1.0/4294967295.0);
+ return t->gen.double_;
+ /* divided by 2^32-1 */
+}
+
+#if 0 /* dead_code */
+/* initializes mt[N] with a seed */
+void init_genrand(unsigned long s)
+{
+ mt[0]= s & 0xffffffffUL;
+ for (mti=1; mti<N; mti++) {
+ mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ mt[mti] &= 0xffffffffUL;
+ /* for >32 bit machines */
+ }
+}
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+/* slight change for C++, 2004/2/26 */
+void init_by_array(unsigned long init_key[], int key_length)
+{
+ int i, j, k;
+ init_genrand(19650218UL);
+ i=1; j=0;
+ k = (N>key_length ? N : key_length);
+ for (; k; k--) {
+ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+ + init_key[j] + j; /* non linear */
+ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++; j++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ if (j>=key_length) j=0;
+ }
+ for (k=N-1; k; k--) {
+ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
+ - i; /* non linear */
+ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ }
+
+ mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long genrand_int32(void)
+{
+ unsigned long y;
+ static const unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if init_genrand() has not been called, */
+ init_genrand(5489UL); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
+
+/* generates a random number on [0,0x7fffffff]-interval */
+long genrand_int31(void)
+{
+ return (long)(genrand_int32()>>1);
+}
+
+/* generates a random number on [0,1]-real-interval */
+double genrand_real1(void)
+{
+ return genrand_int32()*(1.0/4294967295.0);
+ /* divided by 2^32-1 */
+}
+
+/* generates a random number on [0,1)-real-interval */
+double genrand_real2(void)
+{
+ return genrand_int32()*(1.0/4294967296.0);
+ /* divided by 2^32 */
+}
+
+/* generates a random number on (0,1)-real-interval */
+double genrand_real3(void)
+{
+ return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0);
+ /* divided by 2^32 */
+}
+
+/* generates a random number on [0,1) with 53-bit resolution*/
+double genrand_res53(void)
+{
+ unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+ return(a*67108864.0+b)*(1.0/9007199254740992.0);
+}
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+#endif /* dead_code */
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h
new file mode 100644
index 00000000..7d382320
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/mt19937ar.h
@@ -0,0 +1,80 @@
+/*
+** mt19937ar.h - MT Random functions
+**
+** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
+** All rights reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+**
+** Any feedback is very welcome.
+** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+**
+** This version is modified by mruby developers. If you see any problem,
+** contact us first at https://github.com/mruby/mruby/issues
+*/
+
+#define N 624
+
+typedef struct {
+ unsigned long mt[N];
+ int mti;
+ union {
+ unsigned long int_;
+ double double_;
+ } gen;
+
+ mrb_int seed;
+ mrb_bool has_seed : 1;
+} mt_state;
+
+void mrb_random_init_genrand(mt_state *, unsigned long);
+unsigned long mrb_random_genrand_int32(mt_state *);
+double mrb_random_genrand_real1(mt_state *t);
+
+/* initializes mt[N] with a seed */
+void init_genrand(unsigned long s);
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+/* slight change for C++, 2004/2/26 */
+void init_by_array(unsigned long init_key[], int key_length);
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long genrand_int32(void);
+
+/* generates a random number on [0,0x7fffffff]-interval */
+long genrand_int31(void);
+
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+/* generates a random number on [0,1]-real-interval */
+double genrand_real1(void);
+
+/* generates a random number on [0,1)-real-interval */
+double genrand_real2(void);
+
+/* generates a random number on (0,1)-real-interval */
+double genrand_real3(void);
+
+/* generates a random number on [0,1) with 53-bit resolution*/
+double genrand_res53(void);
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c
new file mode 100644
index 00000000..b865244c
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.c
@@ -0,0 +1,349 @@
+/*
+** random.c - Random module
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+#include <mruby/variable.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/array.h>
+#include "mt19937ar.h"
+
+#include <time.h>
+
+static char const MT_STATE_KEY[] = "$mrb_i_mt_state";
+
+static const struct mrb_data_type mt_state_type = {
+ MT_STATE_KEY, mrb_free,
+};
+
+static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self);
+static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self);
+
+static void
+mt_srand(mt_state *t, unsigned long seed)
+{
+ mrb_random_init_genrand(t, seed);
+}
+
+static unsigned long
+mt_rand(mt_state *t)
+{
+ return mrb_random_genrand_int32(t);
+}
+
+static double
+mt_rand_real(mt_state *t)
+{
+ return mrb_random_genrand_real1(t);
+}
+
+static mrb_value
+mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed)
+{
+ if (mrb_nil_p(seed)) {
+ seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t)));
+ if (mrb_fixnum(seed) < 0) {
+ seed = mrb_fixnum_value(0 - mrb_fixnum(seed));
+ }
+ }
+
+ mt_srand(t, (unsigned) mrb_fixnum(seed));
+
+ return seed;
+}
+
+static mrb_value
+mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max)
+{
+ mrb_value value;
+
+ if (mrb_fixnum(max) == 0) {
+ value = mrb_float_value(mrb, mt_rand_real(t));
+ }
+ else {
+ value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max));
+ }
+
+ return value;
+}
+
+static mrb_value
+get_opt(mrb_state* mrb)
+{
+ mrb_value arg;
+
+ arg = mrb_nil_value();
+ mrb_get_args(mrb, "|o", &arg);
+
+ if (!mrb_nil_p(arg)) {
+ arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
+ if (mrb_nil_p(arg)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
+ }
+ if (mrb_fixnum(arg) < 0) {
+ arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
+ }
+ }
+ return arg;
+}
+
+static mrb_value
+get_random(mrb_state *mrb) {
+ return mrb_const_get(mrb,
+ mrb_obj_value(mrb_class_get(mrb, "Random")),
+ mrb_intern_lit(mrb, "DEFAULT"));
+}
+
+static mt_state *
+get_random_state(mrb_state *mrb)
+{
+ mrb_value random_val = get_random(mrb);
+ return DATA_GET_PTR(mrb, random_val, &mt_state_type, mt_state);
+}
+
+static mrb_value
+mrb_random_g_rand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value random = get_random(mrb);
+ return mrb_random_rand(mrb, random);
+}
+
+static mrb_value
+mrb_random_g_srand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value random = get_random(mrb);
+ return mrb_random_srand(mrb, random);
+}
+
+static mrb_value
+mrb_random_init(mrb_state *mrb, mrb_value self)
+{
+ mrb_value seed;
+ mt_state *t;
+
+ seed = get_opt(mrb);
+
+ /* avoid memory leaks */
+ t = (mt_state*)DATA_PTR(self);
+ if (t) {
+ mrb_free(mrb, t);
+ }
+ mrb_data_init(self, NULL, &mt_state_type);
+
+ t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state));
+ t->mti = N + 1;
+
+ seed = mrb_random_mt_srand(mrb, t, seed);
+ if (mrb_nil_p(seed)) {
+ t->has_seed = FALSE;
+ }
+ else {
+ mrb_assert(mrb_fixnum_p(seed));
+ t->has_seed = TRUE;
+ t->seed = mrb_fixnum(seed);
+ }
+
+ mrb_data_init(self, t, &mt_state_type);
+
+ return self;
+}
+
+static void
+mrb_random_rand_seed(mrb_state *mrb, mt_state *t)
+{
+ if (!t->has_seed) {
+ mrb_random_mt_srand(mrb, t, mrb_nil_value());
+ }
+}
+
+static mrb_value
+mrb_random_rand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value max;
+ mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
+
+ max = get_opt(mrb);
+ mrb_random_rand_seed(mrb, t);
+ return mrb_random_mt_rand(mrb, t, max);
+}
+
+static mrb_value
+mrb_random_srand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value seed;
+ mrb_value old_seed;
+ mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
+
+ seed = get_opt(mrb);
+ seed = mrb_random_mt_srand(mrb, t, seed);
+ old_seed = t->has_seed? mrb_fixnum_value(t->seed) : mrb_nil_value();
+ if (mrb_nil_p(seed)) {
+ t->has_seed = FALSE;
+ }
+ else {
+ mrb_assert(mrb_fixnum_p(seed));
+ t->has_seed = TRUE;
+ t->seed = mrb_fixnum(seed);
+ }
+
+ return old_seed;
+}
+
+/*
+ * call-seq:
+ * ary.shuffle! -> ary
+ *
+ * Shuffles elements in self in place.
+ */
+
+static mrb_value
+mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
+{
+ mrb_int i;
+ mt_state *random = NULL;
+
+ if (RARRAY_LEN(ary) > 1) {
+ mrb_get_args(mrb, "|d", &random, &mt_state_type);
+
+ if (random == NULL) {
+ random = get_random_state(mrb);
+ }
+ mrb_random_rand_seed(mrb, random);
+
+ mrb_ary_modify(mrb, mrb_ary_ptr(ary));
+
+ for (i = RARRAY_LEN(ary) - 1; i > 0; i--) {
+ mrb_int j;
+ mrb_value *ptr = RARRAY_PTR(ary);
+ mrb_value tmp;
+
+
+ j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary))));
+
+ tmp = ptr[i];
+ ptr[i] = ptr[j];
+ ptr[j] = tmp;
+ }
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * ary.shuffle -> new_ary
+ *
+ * Returns a new array with elements of self shuffled.
+ */
+
+static mrb_value
+mrb_ary_shuffle(mrb_state *mrb, mrb_value ary)
+{
+ mrb_value new_ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary));
+ mrb_ary_shuffle_bang(mrb, new_ary);
+
+ return new_ary;
+}
+
+/*
+ * call-seq:
+ * ary.sample -> obj
+ * ary.sample(n) -> new_ary
+ *
+ * Choose a random element or +n+ random elements from the array.
+ *
+ * The elements are chosen by using random and unique indices into the array
+ * in order to ensure that an element doesn't repeat itself unless the array
+ * already contained duplicate elements.
+ *
+ * If the array is empty the first form returns +nil+ and the second form
+ * returns an empty array.
+ */
+
+static mrb_value
+mrb_ary_sample(mrb_state *mrb, mrb_value ary)
+{
+ mrb_int n = 0;
+ mrb_bool given;
+ mt_state *random = NULL;
+ mrb_int len;
+
+ mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
+ if (random == NULL) {
+ random = get_random_state(mrb);
+ }
+ mrb_random_rand_seed(mrb, random);
+ mt_rand(random);
+ len = RARRAY_LEN(ary);
+ if (!given) { /* pick one element */
+ switch (len) {
+ case 0:
+ return mrb_nil_value();
+ case 1:
+ return RARRAY_PTR(ary)[0];
+ default:
+ return RARRAY_PTR(ary)[mt_rand(random) % len];
+ }
+ }
+ else {
+ mrb_value result;
+ mrb_int i, j;
+
+ if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative sample number");
+ if (n > len) n = len;
+ result = mrb_ary_new_capa(mrb, n);
+ for (i=0; i<n; i++) {
+ mrb_int r;
+
+ for (;;) {
+ retry:
+ r = mt_rand(random) % len;
+
+ for (j=0; j<i; j++) {
+ if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) {
+ goto retry; /* retry if duplicate */
+ }
+ }
+ break;
+ }
+ mrb_ary_push(mrb, result, mrb_fixnum_value(r));
+ }
+ for (i=0; i<n; i++) {
+ mrb_ary_set(mrb, result, i, RARRAY_PTR(ary)[mrb_fixnum(RARRAY_PTR(result)[i])]);
+ }
+ return result;
+ }
+}
+
+
+void mrb_mruby_random_gem_init(mrb_state *mrb)
+{
+ struct RClass *random;
+ struct RClass *array = mrb->array_class;
+
+ mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1));
+
+ random = mrb_define_class(mrb, "Random", mrb->object_class);
+ MRB_SET_INSTANCE_TT(random, MRB_TT_DATA);
+ mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1));
+
+ mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1));
+
+ mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, array, "sample", mrb_ary_sample, MRB_ARGS_OPT(2));
+
+ mrb_const_set(mrb, mrb_obj_value(random), mrb_intern_lit(mrb, "DEFAULT"),
+ mrb_obj_new(mrb, random, 0, NULL));
+}
+
+void mrb_mruby_random_gem_final(mrb_state *mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h
new file mode 100644
index 00000000..a4785ae5
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/src/random.h
@@ -0,0 +1,12 @@
+/*
+** random.h - Random module
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_RANDOM_H
+#define MRUBY_RANDOM_H
+
+void mrb_mruby_random_gem_init(mrb_state *mrb);
+
+#endif
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb
new file mode 100644
index 00000000..1c59be3a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-random/test/random.rb
@@ -0,0 +1,88 @@
+##
+# Random Test
+
+assert("Random#srand") do
+ r1 = Random.new(123)
+ r2 = Random.new(123)
+ r1.rand == r2.rand
+end
+
+assert("Kernel::srand") do
+ srand(234)
+ r1 = rand
+ srand(234)
+ r2 = rand
+ r1 == r2
+end
+
+assert("Random::srand") do
+ Random.srand(345)
+ r1 = rand
+ srand(345)
+ r2 = Random.rand
+ r1 == r2
+end
+
+assert("fixnum") do
+ rand(3).class == Fixnum
+end
+
+assert("float") do
+ rand.class == Float
+end
+
+assert("Array#shuffle") do
+ ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ shuffled = ary.shuffle
+
+ ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x }
+end
+
+assert('Array#shuffle!') do
+ ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary.shuffle!
+
+ ary != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary.include? x }
+end
+
+assert("Array#shuffle(random)") do
+ assert_raise(TypeError) do
+ # this will cause an exception due to the wrong argument
+ [1, 2].shuffle "Not a Random instance"
+ end
+
+ # verify that the same seed causes the same results
+ ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ shuffle1 = ary1.shuffle Random.new 345
+ ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ shuffle2 = ary2.shuffle Random.new 345
+
+ ary1 != shuffle1 and 10.times { |x| shuffle1.include? x } and shuffle1 == shuffle2
+end
+
+assert('Array#shuffle!(random)') do
+ assert_raise(TypeError) do
+ # this will cause an exception due to the wrong argument
+ [1, 2].shuffle! "Not a Random instance"
+ end
+
+ # verify that the same seed causes the same results
+ ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary1.shuffle! Random.new 345
+ ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary2.shuffle! Random.new 345
+
+ ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
+end
+
+assert('Array#sample checks input length after reading arguments') do
+ $ary = [1, 2, 3]
+ class ArrayChange
+ def to_i
+ $ary << 4
+ 4
+ end
+ end
+
+ assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake
new file mode 100644
index 00000000..e6fa7df5
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-range-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Range class extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb
new file mode 100644
index 00000000..e5d1fb07
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -0,0 +1,31 @@
+class Range
+ ##
+ # call-seq:
+ # rng.first -> obj
+ # rng.first(n) -> an_array
+ #
+ # Returns the first object in the range, or an array of the first +n+
+ # elements.
+ #
+ # (10..20).first #=> 10
+ # (10..20).first(3) #=> [10, 11, 12]
+ #
+ def first(*args)
+ return self.begin if args.empty?
+
+ raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
+ nv = args[0]
+ raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
+ n = nv.to_int
+ raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
+ raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
+ ary = []
+ each do |i|
+ break if n <= 0
+ ary.push(i)
+ n -= 1
+ end
+ ary
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c
new file mode 100644
index 00000000..aca71cc0
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/src/range.c
@@ -0,0 +1,176 @@
+#include <mruby.h>
+#include <mruby/range.h>
+#include <math.h>
+
+static mrb_bool
+r_le(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ if (mrb_fixnum_p(r)) {
+ mrb_int c = mrb_fixnum(r);
+ if (c == 0 || c == -1) return TRUE;
+ }
+
+ return FALSE;
+}
+
+static mrb_bool
+r_lt(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ return mrb_fixnum_p(r) && mrb_fixnum(r) == -1;
+}
+
+/*
+ * call-seq:
+ * rng.cover?(obj) -> true or false
+ *
+ * Returns <code>true</code> if +obj+ is between the begin and end of
+ * the range.
+ *
+ * This tests <code>begin <= obj <= end</code> when #exclude_end? is +false+
+ * and <code>begin <= obj < end</code> when #exclude_end? is +true+.
+ *
+ * ("a".."z").cover?("c") #=> true
+ * ("a".."z").cover?("5") #=> false
+ * ("a".."z").cover?("cc") #=> true
+ */
+static mrb_value
+mrb_range_cover(mrb_state *mrb, mrb_value range)
+{
+ mrb_value val;
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg, end;
+
+ mrb_get_args(mrb, "o", &val);
+
+ beg = r->edges->beg;
+ end = r->edges->end;
+
+ if (r_le(mrb, beg, val)) {
+ if (r->excl) {
+ if (r_lt(mrb, val, end))
+ return mrb_true_value();
+ }
+ else {
+ if (r_le(mrb, val, end))
+ return mrb_true_value();
+ }
+ }
+
+ return mrb_false_value();
+}
+
+/*
+ * call-seq:
+ * rng.last -> obj
+ * rng.last(n) -> an_array
+ *
+ * Returns the last object in the range,
+ * or an array of the last +n+ elements.
+ *
+ * Note that with no arguments +last+ will return the object that defines
+ * the end of the range even if #exclude_end? is +true+.
+ *
+ * (10..20).last #=> 20
+ * (10...20).last #=> 20
+ * (10..20).last(3) #=> [18, 19, 20]
+ * (10...20).last(3) #=> [17, 18, 19]
+ */
+static mrb_value
+mrb_range_last(mrb_state *mrb, mrb_value range)
+{
+ mrb_value num;
+ mrb_value array;
+ struct RRange *r = mrb_range_ptr(mrb, range);
+
+ if (mrb_get_args(mrb, "|o", &num) == 0) {
+ return r->edges->end;
+ }
+
+ array = mrb_funcall(mrb, range, "to_a", 0);
+ return mrb_funcall(mrb, array, "last", 1, mrb_to_int(mrb, num));
+}
+
+/*
+ * call-seq:
+ * rng.size -> num
+ *
+ * Returns the number of elements in the range. Both the begin and the end of
+ * the Range must be Numeric, otherwise nil is returned.
+ *
+ * (10..20).size #=> 11
+ * ('a'..'z').size #=> nil
+ */
+
+static mrb_value
+mrb_range_size(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg, end;
+ mrb_float beg_f, end_f;
+ mrb_bool num_p = TRUE;
+ mrb_bool excl;
+
+ beg = r->edges->beg;
+ end = r->edges->end;
+ excl = r->excl;
+ if (mrb_fixnum_p(beg)) {
+ beg_f = (mrb_float)mrb_fixnum(beg);
+ }
+ else if (mrb_float_p(beg)) {
+ beg_f = mrb_float(beg);
+ }
+ else {
+ num_p = FALSE;
+ }
+ if (mrb_fixnum_p(end)) {
+ end_f = (mrb_float)mrb_fixnum(end);
+ }
+ else if (mrb_float_p(end)) {
+ end_f = mrb_float(end);
+ }
+ else {
+ num_p = FALSE;
+ }
+ if (num_p) {
+ mrb_float n = end_f - beg_f;
+ mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON;
+
+ if (err>0.5) err=0.5;
+ if (excl) {
+ if (n<=0) return mrb_fixnum_value(0);
+ if (n<1)
+ n = 0;
+ else
+ n = floor(n - err);
+ }
+ else {
+ if (n<0) return mrb_fixnum_value(0);
+ n = floor(n + err);
+ }
+ if (isinf(n+1))
+ return mrb_float_value(mrb, INFINITY);
+ return mrb_fixnum_value((mrb_int)n+1);
+ }
+ return mrb_nil_value();
+}
+
+void
+mrb_mruby_range_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass * s = mrb_class_get(mrb, "Range");
+
+ mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_range_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb
new file mode 100644
index 00000000..efcbdabe
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-range-ext/test/range.rb
@@ -0,0 +1,32 @@
+##
+# Range(Ext) Test
+
+assert('Range#cover?') do
+ assert_true ("a".."z").cover?("c")
+ assert_true !("a".."z").cover?("5")
+ assert_true ("a".."z").cover?("cc")
+end
+
+assert('Range#first') do
+ assert_equal 10, (10..20).first
+ assert_equal [10, 11, 12], (10..20).first(3)
+ assert_equal [0, 1, 2], (0..Float::INFINITY).first(3)
+end
+
+assert('Range#last') do
+ assert_equal 20, (10..20).last
+ assert_equal 20, (10...20).last
+ assert_equal [18, 19, 20], (10..20).last(3)
+ assert_equal [17, 18, 19], (10...20).last(3)
+end
+
+assert('Range#size') do
+ assert_equal 42, (1..42).size
+ assert_equal 41, (1...42).size
+ assert_equal 6, (1...6.3).size
+ assert_equal 5, (1...6.0).size
+ assert_equal 5, (1.1...6).size
+ assert_equal 15, (1.0..15.9).size
+ assert_equal Float::INFINITY, (0..Float::INFINITY).size
+ assert_nil ('a'..'z').size
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake
new file mode 100644
index 00000000..bc897243
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-sprintf') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard Kernel#sprintf method'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb
new file mode 100644
index 00000000..d7e55536
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/mrblib/string.rb
@@ -0,0 +1,9 @@
+class String
+ def %(args)
+ if args.is_a? Array
+ sprintf(self, *args)
+ else
+ sprintf(self, args)
+ end
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c
new file mode 100644
index 00000000..946b43a8
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/kernel.c
@@ -0,0 +1,30 @@
+/*
+** kernel.c - Kernel module suppliment
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+
+mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */
+
+void
+mrb_mruby_sprintf_gem_init(mrb_state* mrb)
+{
+ struct RClass *krn;
+
+ if (mrb->kernel_module == NULL) {
+ mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* Might be PARANOID. */
+ }
+ krn = mrb->kernel_module;
+
+ mrb_define_method(mrb, krn, "sprintf", mrb_f_sprintf, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "format", mrb_f_sprintf, MRB_ARGS_ANY());
+}
+
+void
+mrb_mruby_sprintf_gem_final(mrb_state* mrb)
+{
+ /* nothing to do. */
+}
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c
new file mode 100644
index 00000000..d2069924
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/src/sprintf.c
@@ -0,0 +1,1113 @@
+/*
+** sprintf.c - Kernel.#sprintf
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <mruby.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <mruby/string.h>
+#include <mruby/hash.h>
+#include <mruby/numeric.h>
+#include <math.h>
+#include <ctype.h>
+
+#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
+#define BITSPERDIG MRB_INT_BIT
+#define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n)))
+
+mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value);
+static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int);
+
+static char*
+remove_sign_bits(char *str, int base)
+{
+ char *t;
+
+ t = str;
+ if (base == 16) {
+ while (*t == 'f') {
+ t++;
+ }
+ }
+ else if (base == 8) {
+ *t |= EXTENDSIGN(3, strlen(t));
+ while (*t == '7') {
+ t++;
+ }
+ }
+ else if (base == 2) {
+ while (*t == '1') {
+ t++;
+ }
+ }
+
+ return t;
+}
+
+static char
+sign_bits(int base, const char *p)
+{
+ char c;
+
+ switch (base) {
+ case 16:
+ if (*p == 'X') c = 'F';
+ else c = 'f';
+ break;
+ case 8:
+ c = '7'; break;
+ case 2:
+ c = '1'; break;
+ default:
+ c = '.'; break;
+ }
+ return c;
+}
+
+static mrb_value
+mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
+{
+ char buf[66], *b = buf + sizeof buf;
+ mrb_int num = mrb_fixnum(x);
+ uint64_t val = (uint64_t)num;
+ char d;
+
+ if (base != 2) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
+ }
+ if (val == 0) {
+ return mrb_str_new_lit(mrb, "0");
+ }
+ *--b = '\0';
+ do {
+ *--b = mrb_digitmap[(int)(val % base)];
+ } while (val /= base);
+
+ if (num < 0) {
+ b = remove_sign_bits(b, base);
+ switch (base) {
+ case 16: d = 'f'; break;
+ case 8: d = '7'; break;
+ case 2: d = '1'; break;
+ default: d = 0; break;
+ }
+
+ if (d && *b != d) {
+ *--b = d;
+ }
+ }
+
+ return mrb_str_new_cstr(mrb, b);
+}
+
+#define FNONE 0
+#define FSHARP 1
+#define FMINUS 2
+#define FPLUS 4
+#define FZERO 8
+#define FSPACE 16
+#define FWIDTH 32
+#define FPREC 64
+#define FPREC0 128
+
+#define CHECK(l) do {\
+/* int cr = ENC_CODERANGE(result);*/\
+ while ((l) >= bsiz - blen) {\
+ bsiz*=2;\
+ if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
+ }\
+ mrb_str_resize(mrb, result, bsiz);\
+/* ENC_CODERANGE_SET(result, cr);*/\
+ buf = RSTRING_PTR(result);\
+} while (0)
+
+#define PUSH(s, l) do { \
+ CHECK(l);\
+ memcpy(&buf[blen], s, l);\
+ blen += (mrb_int)(l);\
+} while (0)
+
+#define FILL(c, l) do { \
+ CHECK(l);\
+ memset(&buf[blen], c, l);\
+ blen += (l);\
+} while (0)
+
+static void
+check_next_arg(mrb_state *mrb, int posarg, int nextarg)
+{
+ switch (posarg) {
+ case -1:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg));
+ break;
+ case -2:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+check_pos_arg(mrb_state *mrb, int posarg, int n)
+{
+ if (posarg > 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
+ mrb_fixnum_value(n), mrb_fixnum_value(posarg));
+ }
+ if (posarg == -2) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
+ }
+ if (n < 1) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
+ }
+}
+
+static void
+check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
+{
+ if (posarg > 0) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)",
+ mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg));
+ }
+ if (posarg == -1) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
+ }
+}
+
+#define GETNEXTARG() (\
+ check_next_arg(mrb, posarg, nextarg),\
+ (posarg = nextarg++, GETNTHARG(posarg)))
+
+#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG())
+
+#define GETPOSARG(n) (\
+ check_pos_arg(mrb, posarg, n),\
+ (posarg = -1, GETNTHARG(n)))
+
+#define GETNTHARG(nth) \
+ ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth])
+
+#define GETNAMEARG(id, name, len) (\
+ check_name_arg(mrb, posarg, name, len),\
+ (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value())))
+
+#define GETNUM(n, val) \
+ for (; p < end && ISDIGIT(*p); p++) {\
+ int next_n = 10 * n + (*p - '0'); \
+ if (next_n / 10 != n) {\
+ mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
+ } \
+ n = next_n; \
+ } \
+ if (p >= end) { \
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \
+ }
+
+#define GETASTER(num) do { \
+ mrb_value tmp_v; \
+ t = p++; \
+ n = 0; \
+ GETNUM(n, val); \
+ if (*p == '$') { \
+ tmp_v = GETPOSARG(n); \
+ } \
+ else { \
+ tmp_v = GETNEXTARG(); \
+ p = t; \
+ } \
+ num = mrb_int(mrb, tmp_v); \
+} while (0)
+
+static mrb_value
+get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
+{
+ mrb_value tmp;
+
+ if (!mrb_undef_p(*hash)) return *hash;
+ if (argc != 2) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
+ }
+ tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash");
+ if (mrb_nil_p(tmp)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
+ }
+ return (*hash = tmp);
+}
+
+/*
+ * call-seq:
+ * format(format_string [, arguments...] ) -> string
+ * sprintf(format_string [, arguments...] ) -> string
+ *
+ * Returns the string resulting from applying <i>format_string</i> to
+ * any additional arguments. Within the format string, any characters
+ * other than format sequences are copied to the result.
+ *
+ * The syntax of a format sequence is follows.
+ *
+ * %[flags][width][.precision]type
+ *
+ * A format
+ * sequence consists of a percent sign, followed by optional flags,
+ * width, and precision indicators, then terminated with a field type
+ * character. The field type controls how the corresponding
+ * <code>sprintf</code> argument is to be interpreted, while the flags
+ * modify that interpretation.
+ *
+ * The field type characters are:
+ *
+ * Field | Integer Format
+ * ------+--------------------------------------------------------------
+ * b | Convert argument as a binary number.
+ * | Negative numbers will be displayed as a two's complement
+ * | prefixed with '..1'.
+ * B | Equivalent to 'b', but uses an uppercase 0B for prefix
+ * | in the alternative format by #.
+ * d | Convert argument as a decimal number.
+ * i | Identical to 'd'.
+ * o | Convert argument as an octal number.
+ * | Negative numbers will be displayed as a two's complement
+ * | prefixed with '..7'.
+ * u | Identical to 'd'.
+ * x | Convert argument as a hexadecimal number.
+ * | Negative numbers will be displayed as a two's complement
+ * | prefixed with '..f' (representing an infinite string of
+ * | leading 'ff's).
+ * X | Equivalent to 'x', but uses uppercase letters.
+ *
+ * Field | Float Format
+ * ------+--------------------------------------------------------------
+ * e | Convert floating point argument into exponential notation
+ * | with one digit before the decimal point as [-]d.dddddde[+-]dd.
+ * | The precision specifies the number of digits after the decimal
+ * | point (defaulting to six).
+ * E | Equivalent to 'e', but uses an uppercase E to indicate
+ * | the exponent.
+ * f | Convert floating point argument as [-]ddd.dddddd,
+ * | where the precision specifies the number of digits after
+ * | the decimal point.
+ * g | Convert a floating point number using exponential form
+ * | if the exponent is less than -4 or greater than or
+ * | equal to the precision, or in dd.dddd form otherwise.
+ * | The precision specifies the number of significant digits.
+ * G | Equivalent to 'g', but use an uppercase 'E' in exponent form.
+ * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
+ * | which is consisted from optional sign, "0x", fraction part
+ * | as hexadecimal, "p", and exponential part as decimal.
+ * A | Equivalent to 'a', but use uppercase 'X' and 'P'.
+ *
+ * Field | Other Format
+ * ------+--------------------------------------------------------------
+ * c | Argument is the numeric code for a single character or
+ * | a single character string itself.
+ * p | The valuing of argument.inspect.
+ * s | Argument is a string to be substituted. If the format
+ * | sequence contains a precision, at most that many characters
+ * | will be copied.
+ * % | A percent sign itself will be displayed. No argument taken.
+ *
+ * The flags modifies the behavior of the formats.
+ * The flag characters are:
+ *
+ * Flag | Applies to | Meaning
+ * ---------+---------------+-----------------------------------------
+ * space | bBdiouxX | Leave a space at the start of
+ * | aAeEfgG | non-negative numbers.
+ * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
+ * | | a minus sign with absolute value for
+ * | | negative values.
+ * ---------+---------------+-----------------------------------------
+ * (digit)$ | all | Specifies the absolute argument number
+ * | | for this field. Absolute and relative
+ * | | argument numbers cannot be mixed in a
+ * | | sprintf string.
+ * ---------+---------------+-----------------------------------------
+ * # | bBoxX | Use an alternative format.
+ * | aAeEfgG | For the conversions 'o', increase the precision
+ * | | until the first digit will be '0' if
+ * | | it is not formatted as complements.
+ * | | For the conversions 'x', 'X', 'b' and 'B'
+ * | | on non-zero, prefix the result with "0x",
+ * | | "0X", "0b" and "0B", respectively.
+ * | | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G',
+ * | | force a decimal point to be added,
+ * | | even if no digits follow.
+ * | | For 'g' and 'G', do not remove trailing zeros.
+ * ---------+---------------+-----------------------------------------
+ * + | bBdiouxX | Add a leading plus sign to non-negative
+ * | aAeEfgG | numbers.
+ * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
+ * | | a minus sign with absolute value for
+ * | | negative values.
+ * ---------+---------------+-----------------------------------------
+ * - | all | Left-justify the result of this conversion.
+ * ---------+---------------+-----------------------------------------
+ * 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
+ * | aAeEfgG | For 'o', 'x', 'X', 'b' and 'B', radix-1
+ * | (numeric fmt) | is used for negative numbers formatted as
+ * | | complements.
+ * ---------+---------------+-----------------------------------------
+ * * | all | Use the next argument as the field width.
+ * | | If negative, left-justify the result. If the
+ * | | asterisk is followed by a number and a dollar
+ * | | sign, use the indicated argument as the width.
+ *
+ * Examples of flags:
+ *
+ * # '+' and space flag specifies the sign of non-negative numbers.
+ * sprintf("%d", 123) #=> "123"
+ * sprintf("%+d", 123) #=> "+123"
+ * sprintf("% d", 123) #=> " 123"
+ *
+ * # '#' flag for 'o' increases number of digits to show '0'.
+ * # '+' and space flag changes format of negative numbers.
+ * sprintf("%o", 123) #=> "173"
+ * sprintf("%#o", 123) #=> "0173"
+ * sprintf("%+o", -123) #=> "-173"
+ * sprintf("%o", -123) #=> "..7605"
+ * sprintf("%#o", -123) #=> "..7605"
+ *
+ * # '#' flag for 'x' add a prefix '0x' for non-zero numbers.
+ * # '+' and space flag disables complements for negative numbers.
+ * sprintf("%x", 123) #=> "7b"
+ * sprintf("%#x", 123) #=> "0x7b"
+ * sprintf("%+x", -123) #=> "-7b"
+ * sprintf("%x", -123) #=> "..f85"
+ * sprintf("%#x", -123) #=> "0x..f85"
+ * sprintf("%#x", 0) #=> "0"
+ *
+ * # '#' for 'X' uses the prefix '0X'.
+ * sprintf("%X", 123) #=> "7B"
+ * sprintf("%#X", 123) #=> "0X7B"
+ *
+ * # '#' flag for 'b' add a prefix '0b' for non-zero numbers.
+ * # '+' and space flag disables complements for negative numbers.
+ * sprintf("%b", 123) #=> "1111011"
+ * sprintf("%#b", 123) #=> "0b1111011"
+ * sprintf("%+b", -123) #=> "-1111011"
+ * sprintf("%b", -123) #=> "..10000101"
+ * sprintf("%#b", -123) #=> "0b..10000101"
+ * sprintf("%#b", 0) #=> "0"
+ *
+ * # '#' for 'B' uses the prefix '0B'.
+ * sprintf("%B", 123) #=> "1111011"
+ * sprintf("%#B", 123) #=> "0B1111011"
+ *
+ * # '#' for 'e' forces to show the decimal point.
+ * sprintf("%.0e", 1) #=> "1e+00"
+ * sprintf("%#.0e", 1) #=> "1.e+00"
+ *
+ * # '#' for 'f' forces to show the decimal point.
+ * sprintf("%.0f", 1234) #=> "1234"
+ * sprintf("%#.0f", 1234) #=> "1234."
+ *
+ * # '#' for 'g' forces to show the decimal point.
+ * # It also disables stripping lowest zeros.
+ * sprintf("%g", 123.4) #=> "123.4"
+ * sprintf("%#g", 123.4) #=> "123.400"
+ * sprintf("%g", 123456) #=> "123456"
+ * sprintf("%#g", 123456) #=> "123456."
+ *
+ * The field width is an optional integer, followed optionally by a
+ * period and a precision. The width specifies the minimum number of
+ * characters that will be written to the result for this field.
+ *
+ * Examples of width:
+ *
+ * # padding is done by spaces, width=20
+ * # 0 or radix-1. <------------------>
+ * sprintf("%20d", 123) #=> " 123"
+ * sprintf("%+20d", 123) #=> " +123"
+ * sprintf("%020d", 123) #=> "00000000000000000123"
+ * sprintf("%+020d", 123) #=> "+0000000000000000123"
+ * sprintf("% 020d", 123) #=> " 0000000000000000123"
+ * sprintf("%-20d", 123) #=> "123 "
+ * sprintf("%-+20d", 123) #=> "+123 "
+ * sprintf("%- 20d", 123) #=> " 123 "
+ * sprintf("%020x", -123) #=> "..ffffffffffffffff85"
+ *
+ * For
+ * numeric fields, the precision controls the number of decimal places
+ * displayed. For string fields, the precision determines the maximum
+ * number of characters to be copied from the string. (Thus, the format
+ * sequence <code>%10.10s</code> will always contribute exactly ten
+ * characters to the result.)
+ *
+ * Examples of precisions:
+ *
+ * # precision for 'd', 'o', 'x' and 'b' is
+ * # minimum number of digits <------>
+ * sprintf("%20.8d", 123) #=> " 00000123"
+ * sprintf("%20.8o", 123) #=> " 00000173"
+ * sprintf("%20.8x", 123) #=> " 0000007b"
+ * sprintf("%20.8b", 123) #=> " 01111011"
+ * sprintf("%20.8d", -123) #=> " -00000123"
+ * sprintf("%20.8o", -123) #=> " ..777605"
+ * sprintf("%20.8x", -123) #=> " ..ffff85"
+ * sprintf("%20.8b", -11) #=> " ..110101"
+ *
+ * # "0x" and "0b" for '#x' and '#b' is not counted for
+ * # precision but "0" for '#o' is counted. <------>
+ * sprintf("%#20.8d", 123) #=> " 00000123"
+ * sprintf("%#20.8o", 123) #=> " 00000173"
+ * sprintf("%#20.8x", 123) #=> " 0x0000007b"
+ * sprintf("%#20.8b", 123) #=> " 0b01111011"
+ * sprintf("%#20.8d", -123) #=> " -00000123"
+ * sprintf("%#20.8o", -123) #=> " ..777605"
+ * sprintf("%#20.8x", -123) #=> " 0x..ffff85"
+ * sprintf("%#20.8b", -11) #=> " 0b..110101"
+ *
+ * # precision for 'e' is number of
+ * # digits after the decimal point <------>
+ * sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03"
+ *
+ * # precision for 'f' is number of
+ * # digits after the decimal point <------>
+ * sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"
+ *
+ * # precision for 'g' is number of
+ * # significant digits <------->
+ * sprintf("%20.8g", 1234.56789) #=> " 1234.5679"
+ *
+ * # <------->
+ * sprintf("%20.8g", 123456789) #=> " 1.2345679e+08"
+ *
+ * # precision for 's' is
+ * # maximum number of characters <------>
+ * sprintf("%20.8s", "string test") #=> " string t"
+ *
+ * Examples:
+ *
+ * sprintf("%d %04x", 123, 123) #=> "123 007b"
+ * sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"
+ * sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"
+ * sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"
+ * sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"
+ * sprintf("%u", -123) #=> "-123"
+ *
+ * For more complex formatting, Ruby supports a reference by name.
+ * %<name>s style uses format style, but %{name} style doesn't.
+ *
+ * Exapmles:
+ * sprintf("%<foo>d : %<bar>f", { :foo => 1, :bar => 2 })
+ * #=> 1 : 2.000000
+ * sprintf("%{foo}f", { :foo => 1 })
+ * # => "1f"
+ */
+
+mrb_value
+mrb_f_sprintf(mrb_state *mrb, mrb_value obj)
+{
+ mrb_int argc;
+ mrb_value *argv;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+
+ if (argc <= 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments");
+ return mrb_nil_value();
+ }
+ else {
+ return mrb_str_format(mrb, argc - 1, argv + 1, argv[0]);
+ }
+}
+
+mrb_value
+mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
+{
+ const char *p, *end;
+ char *buf;
+ mrb_int blen;
+ mrb_int bsiz;
+ mrb_value result;
+ mrb_int n;
+ mrb_int width;
+ mrb_int prec;
+ int nextarg = 1;
+ int posarg = 0;
+ mrb_value nextvalue;
+ mrb_value str;
+ mrb_value hash = mrb_undef_value();
+
+#define CHECK_FOR_WIDTH(f) \
+ if ((f) & FWIDTH) { \
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \
+ } \
+ if ((f) & FPREC0) { \
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \
+ }
+#define CHECK_FOR_FLAGS(f) \
+ if ((f) & FWIDTH) { \
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \
+ } \
+ if ((f) & FPREC0) { \
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \
+ }
+
+ ++argc;
+ --argv;
+ fmt = mrb_str_to_str(mrb, fmt);
+ p = RSTRING_PTR(fmt);
+ end = p + RSTRING_LEN(fmt);
+ blen = 0;
+ bsiz = 120;
+ result = mrb_str_new_capa(mrb, bsiz);
+ buf = RSTRING_PTR(result);
+ memset(buf, 0, bsiz);
+
+ for (; p < end; p++) {
+ const char *t;
+ mrb_sym id = 0;
+ int flags = FNONE;
+
+ for (t = p; t < end && *t != '%'; t++) ;
+ if (t + 1 == end) ++t;
+ PUSH(p, t - p);
+ if (t >= end)
+ goto sprint_exit; /* end of fmt string */
+
+ p = t + 1; /* skip '%' */
+
+ width = prec = -1;
+ nextvalue = mrb_undef_value();
+
+retry:
+ switch (*p) {
+ default:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1));
+ break;
+
+ case ' ':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FSPACE;
+ p++;
+ goto retry;
+
+ case '#':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FSHARP;
+ p++;
+ goto retry;
+
+ case '+':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FPLUS;
+ p++;
+ goto retry;
+
+ case '-':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FMINUS;
+ p++;
+ goto retry;
+
+ case '0':
+ CHECK_FOR_FLAGS(flags);
+ flags |= FZERO;
+ p++;
+ goto retry;
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = 0;
+ GETNUM(n, width);
+ if (*p == '$') {
+ if (!mrb_undef_p(nextvalue)) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n));
+ }
+ nextvalue = GETPOSARG(n);
+ p++;
+ goto retry;
+ }
+ CHECK_FOR_WIDTH(flags);
+ width = n;
+ flags |= FWIDTH;
+ goto retry;
+
+ case '<':
+ case '{': {
+ const char *start = p;
+ char term = (*p == '<') ? '>' : '}';
+ mrb_value symname;
+
+ for (; p < end && *p != term; )
+ p++;
+ if (id) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>",
+ mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id));
+ }
+ symname = mrb_str_new(mrb, start + 1, p - start - 1);
+ id = mrb_intern_str(mrb, symname);
+ nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (int)(p - start + 1));
+ if (mrb_undef_p(nextvalue)) {
+ mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1));
+ }
+ if (term == '}') goto format_s;
+ p++;
+ goto retry;
+ }
+
+ case '*':
+ CHECK_FOR_WIDTH(flags);
+ flags |= FWIDTH;
+ GETASTER(width);
+ if (width < 0) {
+ flags |= FMINUS;
+ width = -width;
+ }
+ p++;
+ goto retry;
+
+ case '.':
+ if (flags & FPREC0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "precision given twice");
+ }
+ flags |= FPREC|FPREC0;
+
+ prec = 0;
+ p++;
+ if (*p == '*') {
+ GETASTER(prec);
+ if (prec < 0) { /* ignore negative precision */
+ flags &= ~FPREC;
+ }
+ p++;
+ goto retry;
+ }
+
+ GETNUM(prec, precision);
+ goto retry;
+
+ case '\n':
+ case '\0':
+ p--;
+ /* fallthrough */
+ case '%':
+ if (flags != FNONE) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %");
+ }
+ PUSH("%", 1);
+ break;
+
+ case 'c': {
+ mrb_value val = GETARG();
+ mrb_value tmp;
+ char *c;
+
+ tmp = mrb_check_string_type(mrb, val);
+ if (!mrb_nil_p(tmp)) {
+ if (RSTRING_LEN(tmp) != 1) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character");
+ }
+ }
+ else if (mrb_fixnum_p(val)) {
+ mrb_int n = mrb_fixnum(val);
+
+ if (n < 0x80) {
+ char buf[1];
+
+ buf[0] = (char)n;
+ tmp = mrb_str_new(mrb, buf, 1);
+ }
+ else {
+ tmp = mrb_funcall(mrb, val, "chr", 0);
+ mrb_check_type(mrb, tmp, MRB_TT_STRING);
+ }
+ }
+ else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character");
+ }
+ c = RSTRING_PTR(tmp);
+ n = RSTRING_LEN(tmp);
+ if (!(flags & FWIDTH)) {
+ PUSH(c, n);
+ }
+ else if ((flags & FMINUS)) {
+ PUSH(c, n);
+ if (width>0) FILL(' ', width-1);
+ }
+ else {
+ if (width>0) FILL(' ', width-1);
+ PUSH(c, n);
+ }
+ }
+ break;
+
+ case 's':
+ case 'p':
+ format_s:
+ {
+ mrb_value arg = GETARG();
+ mrb_int len;
+ mrb_int slen;
+
+ if (*p == 'p') arg = mrb_inspect(mrb, arg);
+ str = mrb_obj_as_string(mrb, arg);
+ len = RSTRING_LEN(str);
+ if (RSTRING(result)->flags & MRB_STR_EMBED) {
+ mrb_int tmp_n = len;
+ RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK;
+ RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT;
+ }
+ else {
+ RSTRING(result)->as.heap.len = blen;
+ }
+ if (flags&(FPREC|FWIDTH)) {
+ slen = RSTRING_LEN(str);
+ if (slen < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence");
+ }
+ if ((flags&FPREC) && (prec < slen)) {
+ char *p = RSTRING_PTR(str) + prec;
+ slen = prec;
+ len = (mrb_int)(p - RSTRING_PTR(str));
+ }
+ /* need to adjust multi-byte string pos */
+ if ((flags&FWIDTH) && (width > slen)) {
+ width -= (int)slen;
+ if (!(flags&FMINUS)) {
+ FILL(' ', width);
+ }
+ PUSH(RSTRING_PTR(str), len);
+ if (flags&FMINUS) {
+ FILL(' ', width);
+ }
+ break;
+ }
+ }
+ PUSH(RSTRING_PTR(str), len);
+ }
+ break;
+
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'b':
+ case 'B':
+ case 'u': {
+ mrb_value val = GETARG();
+ char nbuf[68], *s;
+ const char *prefix = NULL;
+ int sign = 0, dots = 0;
+ char sc = 0;
+ mrb_int v = 0;
+ int base;
+ mrb_int len;
+
+ if (flags & FSHARP) {
+ switch (*p) {
+ case 'o': prefix = "0"; break;
+ case 'x': prefix = "0x"; break;
+ case 'X': prefix = "0X"; break;
+ case 'b': prefix = "0b"; break;
+ case 'B': prefix = "0B"; break;
+ default: break;
+ }
+ }
+
+ bin_retry:
+ switch (mrb_type(val)) {
+ case MRB_TT_FLOAT:
+ val = mrb_flo_to_fixnum(mrb, val);
+ if (mrb_fixnum_p(val)) goto bin_retry;
+ break;
+ case MRB_TT_STRING:
+ val = mrb_str_to_inum(mrb, val, 0, TRUE);
+ goto bin_retry;
+ case MRB_TT_FIXNUM:
+ v = mrb_fixnum(val);
+ break;
+ default:
+ val = mrb_Integer(mrb, val);
+ goto bin_retry;
+ }
+
+ switch (*p) {
+ case 'o':
+ base = 8; break;
+ case 'x':
+ case 'X':
+ base = 16; break;
+ case 'b':
+ case 'B':
+ base = 2; break;
+ case 'u':
+ case 'd':
+ case 'i':
+ sign = 1;
+ default:
+ base = 10; break;
+ }
+
+ if (sign) {
+ if (v >= 0) {
+ if (flags & FPLUS) {
+ sc = '+';
+ width--;
+ }
+ else if (flags & FSPACE) {
+ sc = ' ';
+ width--;
+ }
+ }
+ else {
+ sc = '-';
+ width--;
+ }
+ mrb_assert(base == 10);
+ snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v);
+ s = nbuf;
+ if (v < 0) s++; /* skip minus sign */
+ }
+ else {
+ s = nbuf;
+ if (v < 0) {
+ dots = 1;
+ }
+ switch (base) {
+ case 2:
+ if (v < 0) {
+ val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
+ }
+ else {
+ val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
+ }
+ strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1);
+ break;
+ case 8:
+ snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v);
+ break;
+ case 16:
+ snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v);
+ break;
+ }
+ if (v < 0) {
+ char d;
+
+ s = remove_sign_bits(s, base);
+ switch (base) {
+ case 16: d = 'f'; break;
+ case 8: d = '7'; break;
+ case 2: d = '1'; break;
+ default: d = 0; break;
+ }
+
+ if (d && *s != d) {
+ *--s = d;
+ }
+ }
+ }
+ {
+ size_t size;
+ size = strlen(s);
+ /* PARANOID: assert(size <= MRB_INT_MAX) */
+ len = (mrb_int)size;
+ }
+
+ if (*p == 'X') {
+ char *pp = s;
+ int c;
+ while ((c = (int)(unsigned char)*pp) != 0) {
+ *pp = toupper(c);
+ pp++;
+ }
+ }
+
+ if (prefix && !prefix[1]) { /* octal */
+ if (dots) {
+ prefix = NULL;
+ }
+ else if (len == 1 && *s == '0') {
+ len = 0;
+ if (flags & FPREC) prec--;
+ }
+ else if ((flags & FPREC) && (prec > len)) {
+ prefix = NULL;
+ }
+ }
+ else if (len == 1 && *s == '0') {
+ prefix = NULL;
+ }
+
+ if (prefix) {
+ size_t size;
+ size = strlen(prefix);
+ /* PARANOID: assert(size <= MRB_INT_MAX).
+ * this check is absolutely paranoid. */
+ width -= (mrb_int)size;
+ }
+
+ if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) {
+ prec = width;
+ width = 0;
+ }
+ else {
+ if (prec < len) {
+ if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0;
+ prec = len;
+ }
+ width -= prec;
+ }
+
+ if (!(flags&FMINUS) && width > 0) {
+ FILL(' ', width);
+ width = 0;
+ }
+
+ if (sc) PUSH(&sc, 1);
+
+ if (prefix) {
+ int plen = (int)strlen(prefix);
+ PUSH(prefix, plen);
+ }
+ if (dots) {
+ prec -= 2;
+ width -= 2;
+ PUSH("..", 2);
+ }
+
+ if (prec > len) {
+ CHECK(prec - len);
+ if ((flags & (FMINUS|FPREC)) != FMINUS) {
+ char c = '0';
+ FILL(c, prec - len);
+ } else if (v < 0) {
+ char c = sign_bits(base, p);
+ FILL(c, prec - len);
+ }
+ }
+ PUSH(s, len);
+ if (width > 0) {
+ FILL(' ', width);
+ }
+ }
+ break;
+
+ case 'f':
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'E':
+ case 'a':
+ case 'A': {
+ mrb_value val = GETARG();
+ double fval;
+ int i, need = 6;
+ char fbuf[32];
+
+ fval = mrb_float(mrb_Float(mrb, val));
+ if (!isfinite(fval)) {
+ const char *expr;
+ const int elen = 3;
+ char sign = '\0';
+
+ if (isnan(fval)) {
+ expr = "NaN";
+ }
+ else {
+ expr = "Inf";
+ }
+ need = elen;
+ if (!isnan(fval) && fval < 0.0)
+ sign = '-';
+ else if (flags & (FPLUS|FSPACE))
+ sign = (flags & FPLUS) ? '+' : ' ';
+ if (sign)
+ ++need;
+ if ((flags & FWIDTH) && need < width)
+ need = width;
+
+ if (need < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big");
+ }
+ FILL(' ', need);
+ if (flags & FMINUS) {
+ if (sign)
+ buf[blen - need--] = sign;
+ memcpy(&buf[blen - need], expr, elen);
+ }
+ else {
+ if (sign)
+ buf[blen - elen - 1] = sign;
+ memcpy(&buf[blen - elen], expr, elen);
+ }
+ break;
+ }
+
+ fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
+ need = 0;
+ if (*p != 'e' && *p != 'E') {
+ i = INT_MIN;
+ frexp(fval, &i);
+ if (i > 0)
+ need = BIT_DIGITS(i);
+ }
+ need += (flags&FPREC) ? prec : 6;
+ if ((flags&FWIDTH) && need < width)
+ need = width;
+ need += 20;
+ if (need <= 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR,
+ (width > prec ? "width too big" : "prec too big"));
+ }
+
+ CHECK(need);
+ n = snprintf(&buf[blen], need, fbuf, fval);
+ if (n < 0) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
+ }
+ blen += n;
+ }
+ break;
+ }
+ flags = FNONE;
+ }
+
+ sprint_exit:
+#if 0
+ /* XXX - We cannot validate the number of arguments if (digit)$ style used.
+ */
+ if (posarg >= 0 && nextarg < argc) {
+ const char *mesg = "too many arguments for format string";
+ if (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg);
+ if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg));
+ }
+#endif
+ mrb_str_resize(mrb, result, blen);
+
+ return result;
+}
+
+static void
+fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec)
+{
+ char *end = buf + size;
+ int n;
+
+ *buf++ = '%';
+ if (flags & FSHARP) *buf++ = '#';
+ if (flags & FPLUS) *buf++ = '+';
+ if (flags & FMINUS) *buf++ = '-';
+ if (flags & FZERO) *buf++ = '0';
+ if (flags & FSPACE) *buf++ = ' ';
+
+ if (flags & FWIDTH) {
+ n = snprintf(buf, end - buf, "%d", (int)width);
+ buf += n;
+ }
+
+ if (flags & FPREC) {
+ n = snprintf(buf, end - buf, ".%d", (int)prec);
+ buf += n;
+ }
+
+ *buf++ = c;
+ *buf = '\0';
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb
new file mode 100644
index 00000000..e3b66ef9
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-sprintf/test/sprintf.rb
@@ -0,0 +1,109 @@
+#assert('Kernel.sprintf') do
+#end
+
+assert('String#%') do
+ assert_equal "one=1", "one=%d" % 1
+ assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ]
+ assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
+ assert_equal 15, ("%b" % (1<<14)).size
+end
+
+assert('String#% with inf') do
+ inf = Float::INFINITY
+
+ assert_equal "Inf", "%f" % inf
+ assert_equal "Inf", "%2f" % inf
+ assert_equal "Inf", "%3f" % inf
+ assert_equal " Inf", "%4f" % inf
+ assert_equal " Inf", "%5f" % inf
+
+ assert_equal "+Inf", "%+f" % inf
+ assert_equal "+Inf", "%+2f" % inf
+ assert_equal "+Inf", "%+3f" % inf
+ assert_equal "+Inf", "%+4f" % inf
+ assert_equal " +Inf", "%+5f" % inf
+
+ assert_equal "Inf", "%-f" % inf
+ assert_equal "Inf", "%-2f" % inf
+ assert_equal "Inf", "%-3f" % inf
+ assert_equal "Inf ", "%-4f" % inf
+ assert_equal "Inf ", "%-5f" % inf
+
+ assert_equal " Inf", "% f" % inf
+ assert_equal " Inf", "% 2f" % inf
+ assert_equal " Inf", "% 3f" % inf
+ assert_equal " Inf", "% 4f" % inf
+ assert_equal " Inf", "% 5f" % inf
+end
+
+assert('String#% with nan') do
+ nan = Float::NAN
+
+ assert_equal "NaN", "%f" % nan
+ assert_equal "NaN", "%2f" % nan
+ assert_equal "NaN", "%3f" % nan
+ assert_equal " NaN", "%4f" % nan
+ assert_equal " NaN", "%5f" % nan
+
+ assert_equal "+NaN", "%+f" % nan
+ assert_equal "+NaN", "%+2f" % nan
+ assert_equal "+NaN", "%+3f" % nan
+ assert_equal "+NaN", "%+4f" % nan
+ assert_equal " +NaN", "%+5f" % nan
+
+ assert_equal "NaN", "%-f" % nan
+ assert_equal "NaN", "%-2f" % nan
+ assert_equal "NaN", "%-3f" % nan
+ assert_equal "NaN ", "%-4f" % nan
+ assert_equal "NaN ", "%-5f" % nan
+
+ assert_equal " NaN", "% f" % nan
+ assert_equal " NaN", "% 2f" % nan
+ assert_equal " NaN", "% 3f" % nan
+ assert_equal " NaN", "% 4f" % nan
+ assert_equal " NaN", "% 5f" % nan
+end
+
+assert("String#% with invalid chr") do
+ begin
+ class Fixnum
+ alias_method :chr_, :chr if method_defined?(:chr)
+
+ def chr
+ nil
+ end
+ end
+
+ assert_raise TypeError do
+ "%c" % 0x80
+ end
+ ensure
+ class Fixnum
+ if method_defined?(:chr_)
+ alias_method :chr, :chr_
+ remove_method :chr_
+ end
+ end
+ end
+end
+
+assert("String#% %b") do
+ assert_equal("..10115", "%0b5" % -5)
+end
+
+assert("String#% %d") do
+ assert_equal(" 10", "%4d" % 10)
+ assert_equal("1000", "%4d" % 1000)
+ assert_equal("10000", "%4d" % 10000)
+end
+
+assert("String#% invalid format") do
+ assert_raise ArgumentError do
+ "%?" % ""
+ end
+end
+
+assert("String#% invalid format shared substring") do
+ fmt = ("x"*30+"%!")[0...-1]
+ assert_equal fmt, sprintf(fmt, "")
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake
new file mode 100644
index 00000000..9812f2cc
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrbgem.rake
@@ -0,0 +1,6 @@
+MRuby::Gem::Specification.new('mruby-string-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'String class extension'
+ spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb
new file mode 100644
index 00000000..c3a7eb38
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -0,0 +1,355 @@
+class String
+
+ ##
+ # call-seq:
+ # String.try_convert(obj) -> string or nil
+ #
+ # Try to convert <i>obj</i> into a String, using to_str method.
+ # Returns converted string or nil if <i>obj</i> cannot be converted
+ # for any reason.
+ #
+ # String.try_convert("str") #=> "str"
+ # String.try_convert(/re/) #=> nil
+ #
+ def self.try_convert(obj)
+ if obj.respond_to?(:to_str)
+ obj.to_str
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
+ # string.clear -> string
+ #
+ # Makes string empty.
+ #
+ # a = "abcde"
+ # a.clear #=> ""
+ #
+ def clear
+ self.replace("")
+ end
+
+ ##
+ # call-seq:
+ # str.lstrip -> new_str
+ #
+ # Returns a copy of <i>str</i> with leading whitespace removed. See also
+ # <code>String#rstrip</code> and <code>String#strip</code>.
+ #
+ # " hello ".lstrip #=> "hello "
+ # "hello".lstrip #=> "hello"
+ #
+ def lstrip
+ a = 0
+ z = self.size - 1
+ a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
+ (z >= 0) ? self[a..z] : ""
+ end
+
+ ##
+ # call-seq:
+ # str.rstrip -> new_str
+ #
+ # Returns a copy of <i>str</i> with trailing whitespace removed. See also
+ # <code>String#lstrip</code> and <code>String#strip</code>.
+ #
+ # " hello ".rstrip #=> " hello"
+ # "hello".rstrip #=> "hello"
+ #
+ def rstrip
+ a = 0
+ z = self.size - 1
+ z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
+ (z >= 0) ? self[a..z] : ""
+ end
+
+ ##
+ # call-seq:
+ # str.strip -> new_str
+ #
+ # Returns a copy of <i>str</i> with leading and trailing whitespace removed.
+ #
+ # " hello ".strip #=> "hello"
+ # "\tgoodbye\r\n".strip #=> "goodbye"
+ #
+ def strip
+ a = 0
+ z = self.size - 1
+ a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
+ z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
+ (z >= 0) ? self[a..z] : ""
+ end
+
+ ##
+ # call-seq:
+ # str.lstrip! -> self or nil
+ #
+ # Removes leading whitespace from <i>str</i>, returning <code>nil</code> if no
+ # change was made. See also <code>String#rstrip!</code> and
+ # <code>String#strip!</code>.
+ #
+ # " hello ".lstrip #=> "hello "
+ # "hello".lstrip! #=> nil
+ #
+ def lstrip!
+ raise RuntimeError, "can't modify frozen String" if frozen?
+ s = self.lstrip
+ (s == self) ? nil : self.replace(s)
+ end
+
+ ##
+ # call-seq:
+ # str.rstrip! -> self or nil
+ #
+ # Removes trailing whitespace from <i>str</i>, returning <code>nil</code> if
+ # no change was made. See also <code>String#lstrip!</code> and
+ # <code>String#strip!</code>.
+ #
+ # " hello ".rstrip #=> " hello"
+ # "hello".rstrip! #=> nil
+ #
+ def rstrip!
+ raise RuntimeError, "can't modify frozen String" if frozen?
+ s = self.rstrip
+ (s == self) ? nil : self.replace(s)
+ end
+
+ ##
+ # call-seq:
+ # str.strip! -> str or nil
+ #
+ # Removes leading and trailing whitespace from <i>str</i>. Returns
+ # <code>nil</code> if <i>str</i> was not altered.
+ #
+ def strip!
+ raise RuntimeError, "can't modify frozen String" if frozen?
+ s = self.strip
+ (s == self) ? nil : self.replace(s)
+ end
+
+ ##
+ # call-seq:
+ # str.casecmp(other_str) -> -1, 0, +1 or nil
+ #
+ # Case-insensitive version of <code>String#<=></code>.
+ #
+ # "abcdef".casecmp("abcde") #=> 1
+ # "aBcDeF".casecmp("abcdef") #=> 0
+ # "abcdef".casecmp("abcdefg") #=> -1
+ # "abcdef".casecmp("ABCDEF") #=> 0
+ #
+ def casecmp(str)
+ self.downcase <=> str.to_str.downcase
+ rescue NoMethodError
+ raise TypeError, "no implicit conversion of #{str.class} into String"
+ end
+
+ def partition(sep)
+ raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String
+ n = index(sep)
+ unless n.nil?
+ m = n + sep.size
+ [ slice(0, n), sep, slice(m, size - m) ]
+ else
+ [ self, "", "" ]
+ end
+ end
+
+ def rpartition(sep)
+ raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String
+ n = rindex(sep)
+ unless n.nil?
+ m = n + sep.size
+ [ slice(0, n), sep, slice(m, size - m) ]
+ else
+ [ "", "", self ]
+ end
+ end
+
+ ##
+ # call-seq:
+ # str.slice!(fixnum) -> new_str or nil
+ # str.slice!(fixnum, fixnum) -> new_str or nil
+ # str.slice!(range) -> new_str or nil
+ # str.slice!(other_str) -> new_str or nil
+ #
+ # Deletes the specified portion from <i>str</i>, and returns the portion
+ # deleted.
+ #
+ # string = "this is a string"
+ # string.slice!(2) #=> "i"
+ # string.slice!(3..6) #=> " is "
+ # string.slice!("r") #=> "r"
+ # string #=> "thsa sting"
+ #
+ def slice!(arg1, arg2=nil)
+ raise RuntimeError, "can't modify frozen String" if frozen?
+ raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil?
+
+ if !arg1.nil? && !arg2.nil?
+ idx = arg1
+ idx += self.size if arg1 < 0
+ if idx >= 0 && idx <= self.size && arg2 > 0
+ str = self[idx, arg2]
+ else
+ return nil
+ end
+ else
+ validated = false
+ if arg1.kind_of?(Range)
+ beg = arg1.begin
+ ed = arg1.end
+ beg += self.size if beg < 0
+ ed += self.size if ed < 0
+ ed -= 1 if arg1.exclude_end?
+ validated = true
+ elsif arg1.kind_of?(String)
+ validated = true
+ else
+ idx = arg1
+ idx += self.size if arg1 < 0
+ validated = true if idx >=0 && arg1 < self.size
+ end
+ if validated
+ str = self[arg1]
+ else
+ return nil
+ end
+ end
+ unless str.nil? || str == ""
+ if !arg1.nil? && !arg2.nil?
+ idx = arg1 >= 0 ? arg1 : self.size+arg1
+ str2 = self[0...idx] + self[idx+arg2..-1].to_s
+ else
+ if arg1.kind_of?(Range)
+ idx = beg >= 0 ? beg : self.size+beg
+ idx2 = ed>= 0 ? ed : self.size+ed
+ str2 = self[0...idx] + self[idx2+1..-1].to_s
+ elsif arg1.kind_of?(String)
+ idx = self.index(arg1)
+ str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil?
+ else
+ idx = arg1 >= 0 ? arg1 : self.size+arg1
+ str2 = self[0...idx] + self[idx+1..-1].to_s
+ end
+ end
+ self.replace(str2) unless str2.nil?
+ end
+ str
+ end
+
+ ##
+ # call-seq:
+ # str.insert(index, other_str) -> str
+ #
+ # Inserts <i>other_str</i> before the character at the given
+ # <i>index</i>, modifying <i>str</i>. Negative indices count from the
+ # end of the string, and insert <em>after</em> the given character.
+ # The intent is insert <i>aString</i> so that it starts at the given
+ # <i>index</i>.
+ #
+ # "abcd".insert(0, 'X') #=> "Xabcd"
+ # "abcd".insert(3, 'X') #=> "abcXd"
+ # "abcd".insert(4, 'X') #=> "abcdX"
+ # "abcd".insert(-3, 'X') #=> "abXcd"
+ # "abcd".insert(-1, 'X') #=> "abcdX"
+ #
+ def insert(idx, str)
+ if idx == -1
+ return self << str
+ elsif idx < 0
+ idx += 1
+ end
+ self[idx, 0] = str
+ self
+ end
+
+ ##
+ # call-seq:
+ # str.ljust(integer, padstr=' ') -> new_str
+ #
+ # If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ # <code>String</code> of length <i>integer</i> with <i>str</i> left justified
+ # and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ #
+ # "hello".ljust(4) #=> "hello"
+ # "hello".ljust(20) #=> "hello "
+ # "hello".ljust(20, '1234') #=> "hello123412341234123"
+ def ljust(idx, padstr = ' ')
+ raise ArgumentError, 'zero width padding' if padstr == ''
+ return self if idx <= self.size
+ pad_repetitions = (idx / padstr.length).ceil
+ padding = (padstr * pad_repetitions)[0...(idx - self.length)]
+ self + padding
+ end
+
+ ##
+ # call-seq:
+ # str.rjust(integer, padstr=' ') -> new_str
+ #
+ # If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ # <code>String</code> of length <i>integer</i> with <i>str</i> right justified
+ # and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ #
+ # "hello".rjust(4) #=> "hello"
+ # "hello".rjust(20) #=> " hello"
+ # "hello".rjust(20, '1234') #=> "123412341234123hello"
+ def rjust(idx, padstr = ' ')
+ raise ArgumentError, 'zero width padding' if padstr == ''
+ return self if idx <= self.size
+ pad_repetitions = (idx / padstr.length).ceil
+ padding = (padstr * pad_repetitions)[0...(idx - self.length)]
+ padding + self
+ end
+
+ def chars(&block)
+ if block_given?
+ self.split('').each do |i|
+ block.call(i)
+ end
+ self
+ else
+ self.split('')
+ end
+ end
+
+ def each_char(&block)
+ return to_enum :each_char unless block
+
+ split('').each do |i|
+ block.call(i)
+ end
+ self
+ end
+
+ def codepoints(&block)
+ len = self.size
+
+ if block_given?
+ self.split('').each do|x|
+ block.call(x.ord)
+ end
+ self
+ else
+ self.split('').map{|x| x.ord}
+ end
+ end
+ alias each_codepoint codepoints
+
+ ##
+ # call-seq:
+ # str.prepend(other_str) -> str
+ #
+ # Prepend---Prepend the given string to <i>str</i>.
+ #
+ # a = "world"
+ # a.prepend("hello ") #=> "hello world"
+ # a #=> "hello world"
+ def prepend(arg)
+ self[0, 0] = arg
+ self
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c
new file mode 100644
index 00000000..6bc035d6
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/src/string.c
@@ -0,0 +1,685 @@
+#include <string.h>
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/range.h>
+
+static mrb_value
+mrb_str_getbyte(mrb_state *mrb, mrb_value str)
+{
+ mrb_int pos;
+ mrb_get_args(mrb, "i", &pos);
+
+ if (pos < 0)
+ pos += RSTRING_LEN(str);
+ if (pos < 0 || RSTRING_LEN(str) <= pos)
+ return mrb_nil_value();
+
+ return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
+}
+
+static mrb_value
+mrb_str_setbyte(mrb_state *mrb, mrb_value str)
+{
+ mrb_int pos, byte;
+ long len;
+
+ mrb_get_args(mrb, "ii", &pos, &byte);
+
+ len = RSTRING_LEN(str);
+ if (pos < -len || len <= pos)
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
+ if (pos < 0)
+ pos += len;
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ byte &= 0xff;
+ RSTRING_PTR(str)[pos] = byte;
+ return mrb_fixnum_value((unsigned char)byte);
+}
+
+static mrb_value
+mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+{
+ mrb_value a1;
+ mrb_int len;
+ int argc;
+
+ argc = mrb_get_args(mrb, "o|i", &a1, &len);
+ if (argc == 2) {
+ return mrb_str_substr(mrb, str, mrb_fixnum(a1), len);
+ }
+ switch (mrb_type(a1)) {
+ case MRB_TT_RANGE:
+ {
+ mrb_int beg;
+
+ len = RSTRING_LEN(str);
+ switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) {
+ case 0: /* not range */
+ break;
+ case 1: /* range */
+ return mrb_str_substr(mrb, str, beg, len);
+ case 2: /* out of range */
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
+ break;
+ }
+ return mrb_nil_value();
+ }
+ case MRB_TT_FLOAT:
+ a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
+ /* fall through */
+ case MRB_TT_FIXNUM:
+ return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1);
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument");
+ }
+ /* not reached */
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * str.swapcase! -> str or nil
+ *
+ * Equivalent to <code>String#swapcase</code>, but modifies the receiver in
+ * place, returning <i>str</i>, or <code>nil</code> if no changes were made.
+ * Note: case conversion is effective only in ASCII region.
+ */
+static mrb_value
+mrb_str_swapcase_bang(mrb_state *mrb, mrb_value str)
+{
+ char *p, *pend;
+ int modify = 0;
+ struct RString *s = mrb_str_ptr(str);
+
+ mrb_str_modify(mrb, s);
+ p = RSTRING_PTR(str);
+ pend = p + RSTRING_LEN(str);
+ while (p < pend) {
+ if (ISUPPER(*p)) {
+ *p = TOLOWER(*p);
+ modify = 1;
+ }
+ else if (ISLOWER(*p)) {
+ *p = TOUPPER(*p);
+ modify = 1;
+ }
+ p++;
+ }
+
+ if (modify) return str;
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * str.swapcase -> new_str
+ *
+ * Returns a copy of <i>str</i> with uppercase alphabetic characters converted
+ * to lowercase and lowercase characters converted to uppercase.
+ * Note: case conversion is effective only in ASCII region.
+ *
+ * "Hello".swapcase #=> "hELLO"
+ * "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11"
+ */
+static mrb_value
+mrb_str_swapcase(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+
+ str = mrb_str_dup(mrb, self);
+ mrb_str_swapcase_bang(mrb, str);
+ return str;
+}
+
+static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
+
+/*
+ * call-seq:
+ * str << integer -> str
+ * str.concat(integer) -> str
+ * str << obj -> str
+ * str.concat(obj) -> str
+ *
+ * Append---Concatenates the given object to <i>str</i>. If the object is a
+ * <code>Integer</code>, it is considered as a codepoint, and is converted
+ * to a character before concatenation.
+ *
+ * a = "hello "
+ * a << "world" #=> "hello world"
+ * a.concat(33) #=> "hello world!"
+ */
+static mrb_value
+mrb_str_concat_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+
+ mrb_get_args(mrb, "o", &str);
+ if (mrb_fixnum_p(str))
+ str = mrb_fixnum_chr(mrb, str);
+ else
+ str = mrb_string_type(mrb, str);
+ mrb_str_concat(mrb, self, str);
+ return self;
+}
+
+/*
+ * call-seq:
+ * str.start_with?([prefixes]+) -> true or false
+ *
+ * Returns true if +str+ starts with one of the +prefixes+ given.
+ *
+ * "hello".start_with?("hell") #=> true
+ *
+ * # returns true if one of the prefixes matches.
+ * "hello".start_with?("heaven", "hell") #=> true
+ * "hello".start_with?("heaven", "paradise") #=> false
+ * "h".start_with?("heaven", "hell") #=> false
+ */
+static mrb_value
+mrb_str_start_with(mrb_state *mrb, mrb_value self)
+{
+ mrb_value *argv, sub;
+ mrb_int argc, i;
+ mrb_get_args(mrb, "*", &argv, &argc);
+
+ for (i = 0; i < argc; i++) {
+ size_t len_l, len_r;
+ int ai = mrb_gc_arena_save(mrb);
+ sub = mrb_string_type(mrb, argv[i]);
+ mrb_gc_arena_restore(mrb, ai);
+ len_l = RSTRING_LEN(self);
+ len_r = RSTRING_LEN(sub);
+ if (len_l >= len_r) {
+ if (memcmp(RSTRING_PTR(self), RSTRING_PTR(sub), len_r) == 0) {
+ return mrb_true_value();
+ }
+ }
+ }
+ return mrb_false_value();
+}
+
+/*
+ * call-seq:
+ * str.end_with?([suffixes]+) -> true or false
+ *
+ * Returns true if +str+ ends with one of the +suffixes+ given.
+ */
+static mrb_value
+mrb_str_end_with(mrb_state *mrb, mrb_value self)
+{
+ mrb_value *argv, sub;
+ mrb_int argc, i;
+ mrb_get_args(mrb, "*", &argv, &argc);
+
+ for (i = 0; i < argc; i++) {
+ size_t len_l, len_r;
+ int ai = mrb_gc_arena_save(mrb);
+ sub = mrb_string_type(mrb, argv[i]);
+ mrb_gc_arena_restore(mrb, ai);
+ len_l = RSTRING_LEN(self);
+ len_r = RSTRING_LEN(sub);
+ if (len_l >= len_r) {
+ if (memcmp(RSTRING_PTR(self) + (len_l - len_r),
+ RSTRING_PTR(sub),
+ len_r) == 0) {
+ return mrb_true_value();
+ }
+ }
+ }
+ return mrb_false_value();
+}
+
+static mrb_value
+mrb_str_hex(mrb_state *mrb, mrb_value self)
+{
+ return mrb_str_to_inum(mrb, self, 16, FALSE);
+}
+
+static mrb_value
+mrb_str_oct(mrb_state *mrb, mrb_value self)
+{
+ return mrb_str_to_inum(mrb, self, 8, FALSE);
+}
+
+/*
+ * call-seq:
+ * string.chr -> string
+ *
+ * Returns a one-character string at the beginning of the string.
+ *
+ * a = "abcde"
+ * a.chr #=> "a"
+ */
+static mrb_value
+mrb_str_chr(mrb_state *mrb, mrb_value self)
+{
+ return mrb_str_substr(mrb, self, 0, 1);
+}
+
+static mrb_value
+mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
+{
+ mrb_int cp = mrb_fixnum(num);
+#ifdef MRB_UTF8_STRING
+ char utf8[4];
+ mrb_int len;
+
+ if (cp < 0 || 0x10FFFF < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ }
+ if (cp < 0x80) {
+ utf8[0] = (char)cp;
+ len = 1;
+ }
+ else if (cp < 0x800) {
+ utf8[0] = (char)(0xC0 | (cp >> 6));
+ utf8[1] = (char)(0x80 | (cp & 0x3F));
+ len = 2;
+ }
+ else if (cp < 0x10000) {
+ utf8[0] = (char)(0xE0 | (cp >> 12));
+ utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | ( cp & 0x3F));
+ len = 3;
+ }
+ else {
+ utf8[0] = (char)(0xF0 | (cp >> 18));
+ utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
+ utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ utf8[3] = (char)(0x80 | ( cp & 0x3F));
+ len = 4;
+ }
+ return mrb_str_new(mrb, utf8, len);
+#else
+ char c;
+
+ if (cp < 0 || 0xff < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ }
+ c = (char)cp;
+ return mrb_str_new(mrb, &c, 1);
+#endif
+}
+
+/*
+ * call-seq:
+ * string.lines -> array of string
+ *
+ * Returns strings per line;
+ *
+ * a = "abc\ndef"
+ * a.lines #=> ["abc\n", "def"]
+ */
+static mrb_value
+mrb_str_lines(mrb_state *mrb, mrb_value self)
+{
+ mrb_value result;
+ mrb_value blk;
+ int ai;
+ mrb_int len;
+ mrb_value arg;
+ char *b = RSTRING_PTR(self);
+ char *p = b, *t;
+ char *e = b + RSTRING_LEN(self);
+
+ mrb_get_args(mrb, "&", &blk);
+
+ result = mrb_ary_new(mrb);
+ ai = mrb_gc_arena_save(mrb);
+ if (!mrb_nil_p(blk)) {
+ while (p < e) {
+ t = p;
+ while (p < e && *p != '\n') p++;
+ if (*p == '\n') p++;
+ len = (mrb_int) (p - t);
+ arg = mrb_str_new(mrb, t, len);
+ mrb_yield_argv(mrb, blk, 1, &arg);
+ mrb_gc_arena_restore(mrb, ai);
+ if (b != RSTRING_PTR(self)) {
+ ptrdiff_t diff = p - b;
+ b = RSTRING_PTR(self);
+ p = b + diff;
+ }
+ e = b + RSTRING_LEN(self);
+ }
+ return self;
+ }
+ while (p < e) {
+ t = p;
+ while (p < e && *p != '\n') p++;
+ if (*p == '\n') p++;
+ len = (mrb_int) (p - t);
+ mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * string.succ -> string
+ *
+ * Returns next sequence of the string;
+ *
+ * a = "abc"
+ * a.succ #=> "abd"
+ */
+static mrb_value
+mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
+{
+ mrb_value result;
+ unsigned char *p, *e, *b, *t;
+ const char *prepend;
+ struct RString *s = mrb_str_ptr(self);
+ mrb_int l;
+
+ if (RSTRING_LEN(self) == 0)
+ return self;
+
+ mrb_str_modify(mrb, s);
+ l = RSTRING_LEN(self);
+ b = p = (unsigned char*) RSTRING_PTR(self);
+ t = e = p + l;
+ *(e--) = 0;
+
+ // find trailing ascii/number
+ while (e >= b) {
+ if (ISALNUM(*e))
+ break;
+ e--;
+ }
+ if (e < b) {
+ e = p + l - 1;
+ result = mrb_str_new_lit(mrb, "");
+ }
+ else {
+ // find leading letter of the ascii/number
+ b = e;
+ while (b > p) {
+ if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z'))
+ break;
+ b--;
+ }
+ if (!ISALNUM(*b))
+ b++;
+ result = mrb_str_new(mrb, (char*) p, b - p);
+ }
+
+ while (e >= b) {
+ if (!ISALNUM(*e)) {
+ if (*e == 0xff) {
+ mrb_str_cat_lit(mrb, result, "\x01");
+ (*e) = 0;
+ }
+ else
+ (*e)++;
+ break;
+ }
+ prepend = NULL;
+ if (*e == '9') {
+ if (e == b) prepend = "1";
+ *e = '0';
+ }
+ else if (*e == 'z') {
+ if (e == b) prepend = "a";
+ *e = 'a';
+ }
+ else if (*e == 'Z') {
+ if (e == b) prepend = "A";
+ *e = 'A';
+ }
+ else {
+ (*e)++;
+ break;
+ }
+ if (prepend) mrb_str_cat_cstr(mrb, result, prepend);
+ e--;
+ }
+ result = mrb_str_cat(mrb, result, (char*) b, t - b);
+ l = RSTRING_LEN(result);
+ mrb_str_resize(mrb, self, l);
+ memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l);
+ return self;
+}
+
+static mrb_value
+mrb_str_succ(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+
+ str = mrb_str_dup(mrb, self);
+ mrb_str_succ_bang(mrb, str);
+ return str;
+}
+
+#ifdef MRB_UTF8_STRING
+static const char utf8len_codepage_zero[256] =
+{
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,
+};
+
+static mrb_int
+utf8code(unsigned char* p)
+{
+ mrb_int len;
+
+ if (p[0] < 0x80)
+ return p[0];
+
+ len = utf8len_codepage_zero[p[0]];
+ if (len > 1 && (p[1] & 0xc0) == 0x80) {
+ if (len == 2)
+ return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
+ if ((p[2] & 0xc0) == 0x80) {
+ if (len == 3)
+ return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
+ + (p[2] & 0x3f);
+ if ((p[3] & 0xc0) == 0x80) {
+ if (len == 4)
+ return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
+ + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
+ if ((p[4] & 0xc0) == 0x80) {
+ if (len == 5)
+ return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
+ + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
+ + (p[4] & 0x3f);
+ if ((p[5] & 0xc0) == 0x80 && len == 6)
+ return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
+ + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
+ + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
+ }
+ }
+ }
+ }
+ return p[0];
+}
+
+static mrb_value
+mrb_str_ord(mrb_state* mrb, mrb_value str)
+{
+ if (RSTRING_LEN(str) == 0)
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
+ return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str)));
+}
+#else
+static mrb_value
+mrb_str_ord(mrb_state* mrb, mrb_value str)
+{
+ if (RSTRING_LEN(str) == 0)
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
+ return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[0]);
+}
+#endif
+
+static mrb_bool
+all_digits_p(const char *s, mrb_int len)
+{
+ while (len-- > 0) {
+ if (!ISDIGIT(*s)) return FALSE;
+ s++;
+ }
+ return TRUE;
+}
+
+/*
+ * call-seq:
+ * str.upto(other_str, exclusive=false) {|s| block } -> str
+ * str.upto(other_str, exclusive=false) -> an_enumerator
+ *
+ * Iterates through successive values, starting at <i>str</i> and
+ * ending at <i>other_str</i> inclusive, passing each value in turn to
+ * the block. The <code>String#succ</code> method is used to generate
+ * each value. If optional second argument exclusive is omitted or is false,
+ * the last value will be included; otherwise it will be excluded.
+ *
+ * If no block is given, an enumerator is returned instead.
+ *
+ * "a8".upto("b6") {|s| print s, ' ' }
+ * for s in "a8".."b6"
+ * print s, ' '
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * a8 a9 b0 b1 b2 b3 b4 b5 b6
+ * a8 a9 b0 b1 b2 b3 b4 b5 b6
+ *
+ * If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
+ * both are recognized as decimal numbers. In addition, the width of
+ * string (e.g. leading zeros) is handled appropriately.
+ *
+ * "9".upto("11").to_a #=> ["9", "10", "11"]
+ * "25".upto("5").to_a #=> []
+ * "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"]
+ */
+static mrb_value
+mrb_str_upto(mrb_state *mrb, mrb_value beg)
+{
+ mrb_value end;
+ mrb_value exclusive = mrb_false_value();
+ mrb_value block = mrb_nil_value();
+ mrb_value current, after_end;
+ mrb_int n;
+ mrb_bool excl;
+
+ mrb_get_args(mrb, "o|o&", &end, &exclusive, &block);
+
+ if (mrb_nil_p(block)) {
+ return mrb_funcall(mrb, beg, "to_enum", 3, mrb_symbol_value(mrb_intern_lit(mrb, "upto")), end, exclusive);
+ }
+ end = mrb_string_type(mrb, end);
+ excl = mrb_test(exclusive);
+
+ /* single character */
+ if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 &&
+ ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) {
+ char c = RSTRING_PTR(beg)[0];
+ char e = RSTRING_PTR(end)[0];
+ int ai = mrb_gc_arena_save(mrb);
+
+ if (c > e || (excl && c == e)) return beg;
+ for (;;) {
+ mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1));
+ mrb_gc_arena_restore(mrb, ai);
+ if (!excl && c == e) break;
+ c++;
+ if (excl && c == e) break;
+ }
+ return beg;
+ }
+ /* both edges are all digits */
+ if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) &&
+ all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) &&
+ all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) {
+ mrb_int min_width = RSTRING_LEN(beg);
+ mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE));
+ mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE));
+ int ai = mrb_gc_arena_save(mrb);
+
+ while (bi <= ei) {
+ mrb_value ns, str;
+
+ if (excl && bi == ei) break;
+ ns = mrb_format(mrb, "%S", mrb_fixnum_value(bi));
+ if (min_width > RSTRING_LEN(ns)) {
+ str = mrb_str_new(mrb, NULL, min_width);
+ memset(RSTRING_PTR(str), '0', min_width-RSTRING_LEN(ns));
+ memcpy(RSTRING_PTR(str)+min_width-RSTRING_LEN(ns),
+ RSTRING_PTR(ns), RSTRING_LEN(ns));
+ }
+ else {
+ str = ns;
+ }
+ mrb_yield(mrb, block, str);
+ mrb_gc_arena_restore(mrb, ai);
+ bi++;
+ }
+
+ return beg;
+ }
+ /* normal case */
+ n = mrb_int(mrb, mrb_funcall(mrb, beg, "<=>", 1, end));
+ if (n > 0 || (excl && n == 0)) return beg;
+
+ after_end = mrb_funcall(mrb, end, "succ", 0);
+ current = mrb_str_dup(mrb, beg);
+ while (!mrb_str_equal(mrb, current, after_end)) {
+ int ai = mrb_gc_arena_save(mrb);
+ mrb_value next = mrb_nil_value();
+ if (excl || !mrb_str_equal(mrb, current, end))
+ next = mrb_funcall(mrb, current, "succ", 0);
+ mrb_yield(mrb, block, current);
+ if (mrb_nil_p(next)) break;
+ current = mrb_str_to_str(mrb, next);
+ if (excl && mrb_str_equal(mrb, current, end)) break;
+ if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0)
+ break;
+ mrb_gc_arena_restore(mrb, ai);
+ }
+
+ return beg;
+}
+
+void
+mrb_mruby_string_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass * s = mrb->string_class;
+
+ mrb_define_method(mrb, s, "dump", mrb_str_dump, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "swapcase!", mrb_str_swapcase_bang, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "concat", mrb_str_concat_m, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "<<", mrb_str_concat_m, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "start_with?", mrb_str_start_with, MRB_ARGS_REST());
+ mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST());
+ mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE());
+ mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
+ mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
+ mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY());
+
+ mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_string_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb
new file mode 100644
index 00000000..2a568c7d
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-string-ext/test/string.rb
@@ -0,0 +1,667 @@
+##
+# String(Ext) Test
+
+UTF8STRING = ("\343\201\202".size == 1)
+
+assert('String.try_convert') do
+ assert_nil String.try_convert(nil)
+ assert_nil String.try_convert(:foo)
+ assert_equal "", String.try_convert("")
+ assert_equal "1,2,3", String.try_convert("1,2,3")
+end
+
+assert('String#getbyte') do
+ str1 = "hello"
+ bytes1 = [104, 101, 108, 108, 111]
+ assert_equal bytes1[0], str1.getbyte(0)
+ assert_equal bytes1[-1], str1.getbyte(-1)
+ assert_equal bytes1[6], str1.getbyte(6)
+
+ str2 = "\xFF"
+ bytes2 = [0xFF]
+ assert_equal bytes2[0], str2.getbyte(0)
+end
+
+assert('String#setbyte') do
+ str1 = "hello"
+ h = "H".getbyte(0)
+ str1.setbyte(0, h)
+ assert_equal(h, str1.getbyte(0))
+ assert_equal("Hello", str1)
+end
+
+assert("String#setbyte raises IndexError if arg conversion resizes String") do
+ $s = "01234\n"
+ class Tmp
+ def to_i
+ $s.chomp! ''
+ 95
+ end
+ end
+ tmp = Tmp.new
+ assert_raise(IndexError) { $s.setbyte(5, tmp) }
+end
+
+assert('String#byteslice') do
+ str1 = "hello"
+ assert_equal("e", str1.byteslice(1))
+ assert_equal("o", str1.byteslice(-1))
+ assert_equal("ell", str1.byteslice(1..3))
+ assert_equal("el", str1.byteslice(1...3))
+end
+
+assert('String#dump') do
+ ("\1" * 100).dump # should not raise an exception - regress #1210
+ "\0".inspect == "\"\\000\"" and
+ "foo".dump == "\"foo\""
+end
+
+assert('String#strip') do
+ s = " abc "
+ "".strip == "" and " \t\r\n\f\v".strip == "" and
+ "\0a\0".strip == "\0a" and
+ "abc".strip == "abc" and
+ " abc".strip == "abc" and
+ "abc ".strip == "abc" and
+ " abc ".strip == "abc" and
+ s == " abc "
+end
+
+assert('String#lstrip') do
+ s = " abc "
+ s.lstrip
+ "".lstrip == "" and " \t\r\n\f\v".lstrip == "" and
+ "\0a\0".lstrip == "\0a\0" and
+ "abc".lstrip == "abc" and
+ " abc".lstrip == "abc" and
+ "abc ".lstrip == "abc " and
+ " abc ".lstrip == "abc " and
+ s == " abc "
+end
+
+assert('String#rstrip') do
+ s = " abc "
+ s.rstrip
+ "".rstrip == "" and " \t\r\n\f\v".rstrip == "" and
+ "\0a\0".rstrip == "\0a" and
+ "abc".rstrip == "abc" and
+ " abc".rstrip == " abc" and
+ "abc ".rstrip == "abc" and
+ " abc ".rstrip == " abc" and
+ s == " abc "
+end
+
+assert('String#strip!') do
+ s = " abc "
+ t = "abc"
+ s.strip! == "abc" and s == "abc" and t.strip! == nil
+end
+
+assert('String#lstrip!') do
+ s = " abc "
+ t = "abc "
+ s.lstrip! == "abc " and s == "abc " and t.lstrip! == nil
+end
+
+assert('String#rstrip!') do
+ s = " abc "
+ t = " abc"
+ s.rstrip! == " abc" and s == " abc" and t.rstrip! == nil
+end
+
+assert('String#swapcase') do
+ assert_equal "hELLO", "Hello".swapcase
+ assert_equal "CyBeR_pUnK11", "cYbEr_PuNk11".swapcase
+end
+
+assert('String#swapcase!') do
+ s = "Hello"
+ t = s.clone
+ t.swapcase!
+ assert_equal s.swapcase, t
+end
+
+assert('String#concat') do
+ assert_equal "Hello World!", "Hello " << "World" << 33
+ assert_equal "Hello World!", "Hello ".concat("World").concat(33)
+
+ o = Object.new
+ def o.to_str
+ "to_str"
+ end
+ assert_equal "hi to_str", "hi " << o
+
+ assert_raise(TypeError) { "".concat(Object.new) }
+end
+
+assert('String#casecmp') do
+ assert_equal 1, "abcdef".casecmp("abcde")
+ assert_equal 0, "aBcDeF".casecmp("abcdef")
+ assert_equal(-1, "abcdef".casecmp("abcdefg"))
+ assert_equal 0, "abcdef".casecmp("ABCDEF")
+ o = Object.new
+ def o.to_str
+ "ABCDEF"
+ end
+ assert_equal 0, "abcdef".casecmp(o)
+end
+
+assert('String#start_with?') do
+ assert_true "hello".start_with?("heaven", "hell")
+ assert_true !"hello".start_with?("heaven", "paradise")
+ assert_true !"h".start_with?("heaven", "hell")
+ assert_raise TypeError do "hello".start_with?(true) end
+end
+
+assert('String#end_with?') do
+ assert_true "string".end_with?("ing", "mng")
+ assert_true !"string".end_with?("str", "tri")
+ assert_true !"ng".end_with?("ing", "mng")
+ assert_raise TypeError do "hello".end_with?(true) end
+end
+
+assert('String#partition') do
+ assert_equal ["a", "x", "axa"], "axaxa".partition("x")
+ assert_equal ["aaaaa", "", ""], "aaaaa".partition("x")
+ assert_equal ["", "", "aaaaa"], "aaaaa".partition("")
+ assert_equal ["", "a", "aaaa"], "aaaaa".partition("a")
+ assert_equal ["aaaa", "b", ""], "aaaab".partition("b")
+ assert_equal ["", "b", "aaaa"], "baaaa".partition("b")
+ assert_equal ["", "", ""], "".partition("a")
+end
+
+assert('String#rpartition') do
+ assert_equal ["axa", "x", "a"], "axaxa".rpartition("x")
+ assert_equal ["", "", "aaaaa"], "aaaaa".rpartition("x")
+ assert_equal ["aaaaa", "", ""], "aaaaa".rpartition("")
+ assert_equal ["aaaa", "a", ""], "aaaaa".rpartition("a")
+ assert_equal ["aaaa", "b", ""], "aaaab".rpartition("b")
+ assert_equal ["", "b", "aaaa"], "baaaa".rpartition("b")
+ assert_equal ["", "", ""], "".rpartition("a")
+end
+
+assert('String#hex') do
+ assert_equal 16, "10".hex
+ assert_equal 255, "ff".hex
+ assert_equal 16, "0x10".hex
+ assert_equal (-16), "-0x10".hex
+ assert_equal 0, "xyz".hex
+ assert_equal 16, "10z".hex
+ assert_equal 16, "1_0".hex
+ assert_equal 0, "".hex
+end
+
+assert('String#oct') do
+ assert_equal 8, "10".oct
+ assert_equal 7, "7".oct
+ assert_equal 0, "8".oct
+ assert_equal 0, "9".oct
+ assert_equal 0, "xyz".oct
+ assert_equal 8, "10z".oct
+ assert_equal 8, "1_0".oct
+ assert_equal 8, "010".oct
+ assert_equal (-8), "-10".oct
+end
+
+assert('String#chr') do
+ assert_equal "a", "abcde".chr
+ # test Fixnum#chr as well
+ assert_equal "a", 97.chr
+end
+
+assert('String#lines') do
+ assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines
+ assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines
+ assert_equal ["\n", "\n", "\n"], "\n\n\n".lines
+ assert_equal [], "".lines
+end
+
+assert('String#clear') do
+ # embed string
+ s = "foo"
+ assert_equal("", s.clear)
+ assert_equal("", s)
+
+ # not embed string and not shared string
+ s = "foo" * 100
+ a = s
+ assert_equal("", s.clear)
+ assert_equal("", s)
+ assert_equal("", a)
+
+ # shared string
+ s = "foo" * 100
+ a = s[10, 90] # create shared string
+ assert_equal("", s.clear) # clear
+ assert_equal("", s) # s is cleared
+ assert_not_equal("", a) # a should not be affected
+end
+
+assert('String#slice!') do
+ a = "AooBar"
+ b = a.dup
+ assert_equal "A", a.slice!(0)
+ assert_equal "AooBar", b
+
+ a = "FooBar"
+ assert_equal "r", a.slice!(-1)
+ assert_equal "FooBa", a
+
+ a = "FooBar"
+ assert_nil a.slice!(6)
+ assert_nil a.slice!(-7)
+ assert_equal "FooBar", a
+
+ a = "FooBar"
+ assert_equal "Foo", a.slice!(0, 3)
+ assert_equal "Bar", a
+
+ a = "FooBar"
+ assert_equal "Bar", a.slice!(-3, 3)
+ assert_equal "Foo", a
+
+ a = "FooBar"
+ assert_equal "", a.slice!(6, 2)
+ assert_equal "FooBar", a
+
+ a = "FooBar"
+ assert_nil a.slice!(-7,10)
+ assert_equal "FooBar", a
+
+ a = "FooBar"
+ assert_equal "Foo", a.slice!(0..2)
+ assert_equal "Bar", a
+
+ a = "FooBar"
+ assert_equal "Bar", a.slice!(-3..-1)
+ assert_equal "Foo", a
+
+ a = "FooBar"
+ assert_equal "", a.slice!(6..2)
+ assert_equal "FooBar", a
+
+ a = "FooBar"
+ assert_nil a.slice!(-10..-7)
+ assert_equal "FooBar", a
+
+ a = "FooBar"
+ assert_equal "Foo", a.slice!("Foo")
+ assert_equal "Bar", a
+
+ a = "FooBar"
+ assert_nil a.slice!("xyzzy")
+ assert_equal "FooBar", a
+
+ assert_raise(ArgumentError) { "foo".slice! }
+end
+
+assert('String#succ') do
+ assert_equal "", "".succ
+ assert_equal "1", "0".succ
+ assert_equal "10", "9".succ
+ assert_equal "01", "00".succ
+ assert_equal "a1", "a0".succ
+ assert_equal "A1", "A0".succ
+ assert_equal "10", "09".succ
+ assert_equal "b0", "a9".succ
+ assert_equal "B0", "A9".succ
+
+ assert_equal "b", "a".succ
+ assert_equal "aa", "z".succ
+ assert_equal "ab", "aa".succ
+ assert_equal "Ab", "Aa".succ
+ assert_equal "0b", "0a".succ
+ assert_equal "ba", "az".succ
+ assert_equal "Ba", "Az".succ
+ assert_equal "1a", "0z".succ
+
+ assert_equal "B", "A".succ
+ assert_equal "AA", "Z".succ
+ assert_equal "AB", "AA".succ
+ assert_equal "aB", "aA".succ
+ assert_equal "0B", "0A".succ
+ assert_equal "BA", "AZ".succ
+ assert_equal "bA", "aZ".succ
+ assert_equal "1A", "0Z".succ
+
+ assert_equal ".", "-".succ
+ assert_equal "\x01\x00", "\xff".succ
+ assert_equal "-b", "-a".succ
+ assert_equal "-aa", "-z".succ
+ assert_equal "-a-b-", "-a-a-".succ
+ assert_equal "-b-", "-a-".succ
+ assert_equal "-aa-", "-z-".succ
+ assert_equal "あb", "あa".succ
+ assert_equal "あba", "あaz".succ
+
+ a = ""; a.succ!
+ assert_equal "", a
+ a = "0"; a.succ!
+ assert_equal "1", a
+ a = "9"; a.succ!
+ assert_equal "10", a
+ a = "00"; a.succ!
+ assert_equal "01", a
+ a = "a0"; a.succ!
+ assert_equal "a1", a
+ a = "A0"; a.succ!
+ assert_equal "A1", a
+ a = "09"; a.succ!
+ assert_equal "10", a
+ a = "a9"; a.succ!
+ assert_equal "b0", a
+ a = "A9"; a.succ!
+ assert_equal "B0", a
+
+ a = "a"; a.succ!
+ assert_equal "b", a
+ a = "z"; a.succ!
+ assert_equal "aa", a
+ a = "aa"; a.succ!
+ assert_equal "ab", a
+ a = "Aa"; a.succ!
+ assert_equal "Ab", a
+ a = "0a"; a.succ!
+ assert_equal "0b", a
+ a = "az"; a.succ!
+ assert_equal "ba", a
+ a = "Az"; a.succ!
+ assert_equal "Ba", a
+ a = "0z"; a.succ!
+ assert_equal "1a", a
+
+ a = "A"; a.succ!
+ assert_equal "B", a
+ a = "Z"; a.succ!
+ assert_equal "AA", a
+ a = "AA"; a.succ!
+ assert_equal "AB", a
+ a = "aA"; a.succ!
+ assert_equal "aB", a
+ a = "0A"; a.succ!
+ assert_equal "0B", a
+ a = "AZ"; a.succ!
+ assert_equal "BA", a
+ a = "aZ"; a.succ!
+ assert_equal "bA", a
+ a = "0Z"; a.succ!
+ assert_equal "1A", a
+
+ a = "-"; a.succ!
+ assert_equal ".", a
+ a = "\xff"; a.succ!
+ assert_equal "\x01\x00", a
+ a = "-a"; a.succ!
+ assert_equal "-b", a
+ a = "-z"; a.succ!
+ assert_equal "-aa", a
+ a = "-a-a-"; a.succ!
+ assert_equal "-a-b-", a
+ a = "-a-"; a.succ!
+ assert_equal "-b-", a
+ a = "-z-"; a.succ!
+ assert_equal "-aa-", a
+ a = "あb"; a.succ!
+ assert_equal "あc", a
+ a = "あaz"; a.succ!
+ assert_equal "あba", a
+end
+
+assert('String#next') do
+ assert_equal "01", "00".next
+
+ a = "00"; a.next!
+ assert_equal "01", a
+end
+
+assert('String#insert') do
+ assert_equal "Xabcd", "abcd".insert(0, 'X')
+ assert_equal "abcXd", "abcd".insert(3, 'X')
+ assert_equal "abcdX", "abcd".insert(4, 'X')
+ assert_equal "abXcd", "abcd".insert(-3, 'X')
+ assert_equal "abcdX", "abcd".insert(-1, 'X')
+ assert_raise(IndexError) { "abcd".insert(5, 'X') }
+ assert_raise(IndexError) { "abcd".insert(-6, 'X') }
+
+ a = "abcd"
+ a.insert(0, 'X')
+ assert_equal "Xabcd", a
+end
+
+assert('String#prepend') do
+ a = "world"
+ assert_equal "hello world", a.prepend("hello ")
+ assert_equal "hello world", a
+end
+
+assert('String#ljust') do
+ assert_equal "hello", "hello".ljust(4)
+ assert_equal "hello ", "hello".ljust(20)
+ assert_equal 20, "hello".ljust(20).length
+ assert_equal "hello123412341234123", "hello".ljust(20, '1234')
+ assert_equal "hello", "hello".ljust(-3)
+end
+
+assert('String#rjust') do
+ assert_equal "hello", "hello".rjust(4)
+ assert_equal " hello", "hello".rjust(20)
+ assert_equal 20, "hello".rjust(20).length
+ assert_equal "123412341234123hello", "hello".rjust(20, '1234')
+ assert_equal "hello", "hello".rjust(-3)
+end
+
+if UTF8STRING
+ assert('String#ljust with UTF8') do
+ assert_equal "helloん ", "helloん".ljust(20)
+ assert_equal "helloó ", "helloó".ljust(34)
+ assert_equal 34, "helloó".ljust(34).length
+ assert_equal "helloんんんんんんんんんんんんんん", "hello".ljust(19, 'ん')
+ assert_equal "helloんんんんんんんんんんんんんんん", "hello".ljust(20, 'ん')
+ end
+
+ assert('String#rjust with UTF8') do
+ assert_equal " helloん", "helloん".rjust(20)
+ assert_equal " helloó", "helloó".rjust(34)
+ # assert_equal 34, "helloó".rjust(34).length
+ assert_equal "んんんんんんんんんんんんんんhello", "hello".rjust(19, 'ん')
+ assert_equal "んんんんんんんんんんんんんんんhello", "hello".rjust(20, 'ん')
+ end
+
+ assert('UTF8 byte counting') do
+ ret = ' '
+ ret[-6..-1] = "helloó"
+ assert_equal 34, ret.length
+ end
+end
+
+assert('String#ljust should not change string') do
+ a = "hello"
+ a.ljust(20)
+ assert_equal "hello", a
+end
+
+assert('String#rjust should not change string') do
+ a = "hello"
+ a.rjust(20)
+ assert_equal "hello", a
+end
+
+assert('String#ljust should raise on zero width padding') do
+ assert_raise(ArgumentError) { "foo".ljust(10, '') }
+end
+
+assert('String#rjust should raise on zero width padding') do
+ assert_raise(ArgumentError) { "foo".rjust(10, '') }
+end
+
+assert('String#upto') do
+ assert_equal %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8".upto("b6").to_a
+ assert_equal ["9", "10", "11"], "9".upto("11").to_a
+ assert_equal [], "25".upto("5").to_a
+ assert_equal ["07", "08", "09", "10", "11"], "07".upto("11").to_a
+
+if UTF8STRING
+ assert_equal ["あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お"], "あ".upto("お").to_a
+end
+
+ assert_equal ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9".upto("A").to_a
+
+ a = "aa"
+ start = "aa"
+ count = 0
+ assert_equal("aa", a.upto("zz") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(676, count)
+
+ a = "a"
+ start = "a"
+ count = 0
+ assert_equal("a", a.upto("a") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "a"
+ start = "a"
+ count = 0
+ assert_equal("a", a.upto("b", true) {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "0"
+ start = "0"
+ count = 0
+ assert_equal("0", a.upto("0") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "0"
+ start = "0"
+ count = 0
+ assert_equal("0", a.upto("-1") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(0, count)
+
+ a = "-1"
+ start = "-1"
+ count = 0
+ assert_equal("-1", a.upto("-2") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(2, count)
+
+ assert_raise(TypeError) { "a".upto(:c) {} }
+end
+
+assert('String#ord') do
+ got = "hello!".split('').map {|x| x.ord}
+ expect = [104, 101, 108, 108, 111, 33]
+ unless UTF8STRING
+ got << "\xff".ord
+ expect << 0xff
+ end
+ assert_equal expect, got
+end
+
+assert('String#ord(UTF-8)') do
+ got = "こんにちは世界!".split('').map {|x| x.ord}
+ expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21]
+ assert_equal expect, got
+end if UTF8STRING
+
+assert('String#chr') do
+ assert_equal "h", "hello!".chr
+end
+assert('String#chr(UTF-8)') do
+ assert_equal "こ", "こんにちは世界!".chr
+end if UTF8STRING
+
+assert('String#chars') do
+ expect = ["h", "e", "l", "l", "o", "!"]
+ assert_equal expect, "hello!".chars
+ s = ""
+ "hello!".chars do |x|
+ s += x
+ end
+ assert_equal "hello!", s
+end
+
+assert('String#chars(UTF-8)') do
+ expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']
+ assert_equal expect, "こんにちは世界!".chars
+ s = ""
+ "こんにちは世界!".chars do |x|
+ s += x
+ end
+ assert_equal "こんにちは世界!", s
+end if UTF8STRING
+
+assert('String#each_char') do
+ s = ""
+ "hello!".each_char do |x|
+ s += x
+ end
+ assert_equal "hello!", s
+end
+
+assert('String#each_char(UTF-8)') do
+ s = ""
+ "こんにちは世界!".each_char do |x|
+ s += x
+ end
+ assert_equal "こんにちは世界!", s
+end if UTF8STRING
+
+assert('String#codepoints') do
+ expect = [104, 101, 108, 108, 111, 33]
+ assert_equal expect, "hello!".codepoints
+ cp = []
+ "hello!".codepoints do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end
+
+assert('String#codepoints(UTF-8)') do
+ expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
+ assert_equal expect, "こんにちは世界!".codepoints
+ cp = []
+ "こんにちは世界!".codepoints do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end if UTF8STRING
+
+assert('String#each_codepoint') do
+ expect = [104, 101, 108, 108, 111, 33]
+ cp = []
+ "hello!".each_codepoint do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end
+
+assert('String#each_codepoint(UTF-8)') do
+ expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
+ cp = []
+ "こんにちは世界!".each_codepoint do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end if UTF8STRING
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake
new file mode 100644
index 00000000..2826ad2a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-struct') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard Struct class'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb
new file mode 100644
index 00000000..7cf3dd3a
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/mrblib/struct.rb
@@ -0,0 +1,103 @@
+##
+# Struct
+#
+# ISO 15.2.18
+
+if Object.const_defined?(:Struct)
+ class Struct
+
+ ##
+ # Calls the given block for each element of +self+
+ # and pass the respective element.
+ #
+ # ISO 15.2.18.4.4
+ def each(&block)
+ self.class.members.each{|field|
+ block.call(self[field])
+ }
+ self
+ end
+
+ ##
+ # Calls the given block for each element of +self+
+ # and pass the name and value of the respectiev
+ # element.
+ #
+ # ISO 15.2.18.4.5
+ def each_pair(&block)
+ self.class.members.each{|field|
+ block.call(field.to_sym, self[field])
+ }
+ self
+ end
+
+ ##
+ # Calls the given block for each element of +self+
+ # and returns an array with all elements of which
+ # block is not false.
+ #
+ # ISO 15.2.18.4.7
+ def select(&block)
+ ary = []
+ self.class.members.each{|field|
+ val = self[field]
+ ary.push(val) if block.call(val)
+ }
+ ary
+ end
+
+ def _inspect
+ name = self.class.to_s
+ if name[0] == "#"
+ str = "#<struct "
+ else
+ str = "#<struct #{name} "
+ end
+ buf = []
+ self.each_pair do |k,v|
+ buf.push [k.to_s + "=" + v._inspect]
+ end
+ str + buf.join(", ") + ">"
+ end
+
+ ##
+ # call-seq:
+ # struct.to_s -> string
+ # struct.inspect -> string
+ #
+ # Describe the contents of this struct in a string.
+ #
+ # 15.2.18.4.10(x)
+ #
+ def inspect
+ begin
+ self._inspect
+ rescue SystemStackError
+ "#<struct #{self.class.to_s}:...>"
+ end
+ end
+
+ ##
+ # 15.2.18.4.11(x)
+ #
+ alias to_s inspect
+ end
+
+ ##
+ # call-seq:
+ # hsh.dig(key,...) -> object
+ #
+ # Extracts the nested value specified by the sequence of <i>key</i>
+ # objects by calling +dig+ at each step, returning +nil+ if any
+ # intermediate step is +nil+.
+ #
+ def dig(idx,*args)
+ n = self[idx]
+ if args.size > 0
+ n&.dig(*args)
+ else
+ n
+ end
+ end
+end
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c
new file mode 100644
index 00000000..67762a94
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/src/struct.c
@@ -0,0 +1,714 @@
+/*
+** struct.c - Struct class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <string.h>
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/hash.h>
+#include <mruby/range.h>
+
+#define RSTRUCT_LEN(st) RARRAY_LEN(st)
+#define RSTRUCT_PTR(st) RARRAY_PTR(st)
+
+static struct RClass *
+struct_class(mrb_state *mrb)
+{
+ return mrb_class_get(mrb, "Struct");
+}
+
+static inline mrb_value
+struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
+{
+ struct RClass* kclass;
+ struct RClass* sclass = struct_class(mrb);
+ mrb_value ans;
+
+ for (;;) {
+ ans = mrb_iv_get(mrb, c, id);
+ if (!mrb_nil_p(ans)) return ans;
+ kclass = RCLASS_SUPER(c);
+ if (kclass == 0 || kclass == sclass)
+ return mrb_nil_value();
+ c = mrb_obj_value(kclass);
+ }
+}
+
+static mrb_value
+struct_s_members(mrb_state *mrb, struct RClass *klass)
+{
+ mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
+
+ if (mrb_nil_p(members)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
+ }
+ if (!mrb_array_p(members)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
+ }
+ return members;
+}
+
+static mrb_value
+struct_members(mrb_state *mrb, mrb_value s)
+{
+ mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s));
+ if (!mrb_array_p(s)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
+ }
+ if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
+ if (RSTRUCT_LEN(s) == 0) { /* probably uninitialized */
+ mrb_ary_resize(mrb, s, RARRAY_LEN(members));
+ }
+ else {
+ mrb_raisef(mrb, E_TYPE_ERROR,
+ "struct size differs (%S required %S given)",
+ mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ }
+ }
+ return members;
+}
+
+static mrb_value
+mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value members, ary;
+
+ members = struct_s_members(mrb, mrb_class_ptr(klass));
+ ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
+ mrb_ary_replace(mrb, ary, members);
+ return ary;
+}
+
+static void
+mrb_struct_modify(mrb_state *mrb, mrb_value strct)
+{
+ if (MRB_FROZEN_P(mrb_basic_ptr(strct))) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen struct");
+ }
+
+ mrb_write_barrier(mrb, mrb_basic_ptr(strct));
+}
+
+/* 15.2.18.4.6 */
+/*
+ * call-seq:
+ * struct.members -> array
+ *
+ * Returns an array of strings representing the names of the instance
+ * variables.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.members #=> [:name, :address, :zip]
+ */
+
+static mrb_value
+mrb_struct_members(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
+}
+
+static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id);
+
+static mrb_value
+mrb_struct_ref(mrb_state *mrb, mrb_value obj)
+{
+ return struct_aref_sym(mrb, obj, mrb->c->ci->mid);
+}
+
+static mrb_sym
+mrb_id_attrset(mrb_state *mrb, mrb_sym id)
+{
+ const char *name;
+ char *buf;
+ mrb_int len;
+ mrb_sym mid;
+
+ name = mrb_sym2name_len(mrb, id, &len);
+ buf = (char *)mrb_malloc(mrb, (size_t)len+2);
+ memcpy(buf, name, (size_t)len);
+ buf[len] = '=';
+ buf[len+1] = '\0';
+
+ mid = mrb_intern(mrb, buf, len+1);
+ mrb_free(mrb, buf);
+ return mid;
+}
+
+static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val);
+
+static mrb_value
+mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value val;
+
+ const char *name;
+ mrb_int slen;
+ mrb_sym mid;
+
+ mrb_get_args(mrb, "o", &val);
+
+ /* get base id */
+ name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
+ mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
+
+ return mrb_struct_aset_sym(mrb, obj, mid, val);
+}
+
+static mrb_bool
+is_local_id(mrb_state *mrb, const char *name)
+{
+ if (!name) return FALSE;
+ return !ISUPPER(name[0]);
+}
+
+static mrb_bool
+is_const_id(mrb_state *mrb, const char *name)
+{
+ if (!name) return FALSE;
+ return ISUPPER(name[0]);
+}
+
+static void
+make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
+{
+ const mrb_value *ptr_members = RARRAY_PTR(members);
+ mrb_int i;
+ mrb_int len = RARRAY_LEN(members);
+ int ai = mrb_gc_arena_save(mrb);
+
+ for (i=0; i<len; i++) {
+ mrb_sym id = mrb_symbol(ptr_members[i]);
+ const char *name = mrb_sym2name_len(mrb, id, NULL);
+
+ if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
+ mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
+ mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ }
+}
+
+static mrb_value
+make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
+{
+ mrb_value nstr;
+ mrb_sym id;
+ struct RClass *c;
+
+ if (mrb_nil_p(name)) {
+ c = mrb_class_new(mrb, klass);
+ }
+ else {
+ /* old style: should we warn? */
+ name = mrb_str_to_str(mrb, name);
+ id = mrb_obj_to_sym(mrb, name);
+ if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
+ mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
+ }
+ if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
+ mrb_warn(mrb, "redefining constant Struct::%S", name);
+ mrb_const_remove(mrb, mrb_obj_value(klass), id);
+ }
+ c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
+ }
+ MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
+ nstr = mrb_obj_value(c);
+ mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
+
+ mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
+ /* RSTRUCT(nstr)->basic.c->super = c->c; */
+ make_struct_define_accessors(mrb, members, c);
+ return nstr;
+}
+
+/* 15.2.18.3.1 */
+/*
+ * call-seq:
+ * Struct.new( [aString] [, aSym]+> ) -> StructClass
+ * StructClass.new(arg, ...) -> obj
+ * StructClass[arg, ...] -> obj
+ *
+ * Creates a new class, named by <i>aString</i>, containing accessor
+ * methods for the given symbols. If the name <i>aString</i> is
+ * omitted, an anonymous structure class will be created. Otherwise,
+ * the name of this struct will appear as a constant in class
+ * <code>Struct</code>, so it must be unique for all
+ * <code>Struct</code>s in the system and should start with a capital
+ * letter. Assigning a structure class to a constant effectively gives
+ * the class the name of the constant.
+ *
+ * <code>Struct::new</code> returns a new <code>Class</code> object,
+ * which can then be used to create specific instances of the new
+ * structure. The number of actual parameters must be
+ * less than or equal to the number of attributes defined for this
+ * class; unset parameters default to <code>nil</code>. Passing too many
+ * parameters will raise an <code>ArgumentError</code>.
+ *
+ * The remaining methods listed in this section (class and instance)
+ * are defined for this generated class.
+ *
+ * # Create a structure with a name in Struct
+ * Struct.new("Customer", :name, :address) #=> Struct::Customer
+ * Struct::Customer.new("Dave", "123 Main") #=> #<struct Struct::Customer name="Dave", address="123 Main">
+ *
+ * # Create a structure named by its constant
+ * Customer = Struct.new(:name, :address) #=> Customer
+ * Customer.new("Dave", "123 Main") #=> #<struct Customer name="Dave", address="123 Main">
+ */
+static mrb_value
+mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value name, rest;
+ mrb_value *pargv;
+ mrb_int argcnt;
+ mrb_int i;
+ mrb_value b, st;
+ mrb_sym id;
+ mrb_value *argv;
+ mrb_int argc;
+
+ name = mrb_nil_value();
+ mrb_get_args(mrb, "*&", &argv, &argc, &b);
+ if (argc == 0) { /* special case to avoid crash */
+ rest = mrb_ary_new(mrb);
+ }
+ else {
+ if (argc > 0) name = argv[0];
+ pargv = &argv[1];
+ argcnt = argc-1;
+ if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
+ /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
+ name = mrb_nil_value();
+ pargv = &argv[0];
+ argcnt++;
+ }
+ rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
+ for (i=0; i<RARRAY_LEN(rest); i++) {
+ id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
+ mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
+ }
+ }
+ st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
+ if (!mrb_nil_p(b)) {
+ mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
+ }
+
+ return st;
+}
+
+static mrb_int
+num_members(mrb_state *mrb, struct RClass *klass)
+{
+ mrb_value members;
+
+ members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
+ if (!mrb_array_p(members)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "broken members");
+ }
+ return RARRAY_LEN(members);
+}
+
+/* 15.2.18.4.8 */
+/*
+ */
+static mrb_value
+mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
+{
+ struct RClass *klass = mrb_obj_class(mrb, self);
+ mrb_int i, n;
+
+ n = num_members(mrb, klass);
+ if (n < argc) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs");
+ }
+
+ for (i = 0; i < argc; i++) {
+ mrb_ary_set(mrb, self, i, argv[i]);
+ }
+ for (i = argc; i < n; i++) {
+ mrb_ary_set(mrb, self, i, mrb_nil_value());
+ }
+ return self;
+}
+
+static mrb_value
+mrb_struct_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value *argv;
+ mrb_int argc;
+
+ mrb_get_args(mrb, "*!", &argv, &argc);
+ return mrb_struct_initialize_withArg(mrb, argc, argv, self);
+}
+
+/* 15.2.18.4.9 */
+/* :nodoc: */
+static mrb_value
+mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
+{
+ mrb_value s;
+
+ mrb_get_args(mrb, "o", &s);
+
+ if (mrb_obj_equal(mrb, copy, s)) return copy;
+ if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
+ mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
+ }
+ if (!mrb_array_p(s)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
+ }
+ mrb_ary_replace(mrb, copy, s);
+ return copy;
+}
+
+static mrb_value
+struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
+{
+ mrb_value members, *ptr;
+ const mrb_value *ptr_members;
+ mrb_int i, len;
+
+ members = struct_members(mrb, obj);
+ ptr_members = RARRAY_PTR(members);
+ len = RARRAY_LEN(members);
+ ptr = RSTRUCT_PTR(obj);
+ for (i=0; i<len; i++) {
+ mrb_value slot = ptr_members[i];
+ if (mrb_symbol_p(slot) && mrb_symbol(slot) == id) {
+ return ptr[i];
+ }
+ }
+ mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
+ return mrb_nil_value(); /* not reached */
+}
+
+static mrb_value
+struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
+{
+ if (i < 0) i = RSTRUCT_LEN(s) + i;
+ if (i < 0)
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %S too small for struct(size:%S)",
+ mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ if (RSTRUCT_LEN(s) <= i)
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %S too large for struct(size:%S)",
+ mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ return RSTRUCT_PTR(s)[i];
+}
+
+/* 15.2.18.4.2 */
+/*
+ * call-seq:
+ * struct[symbol] -> anObject
+ * struct[fixnum] -> anObject
+ *
+ * Attribute Reference---Returns the value of the instance variable
+ * named by <i>symbol</i>, or indexed (0..length-1) by
+ * <i>fixnum</i>. Will raise <code>NameError</code> if the named
+ * variable does not exist, or <code>IndexError</code> if the index is
+ * out of range.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ *
+ * joe["name"] #=> "Joe Smith"
+ * joe[:name] #=> "Joe Smith"
+ * joe[0] #=> "Joe Smith"
+ */
+static mrb_value
+mrb_struct_aref(mrb_state *mrb, mrb_value s)
+{
+ mrb_value idx;
+
+ mrb_get_args(mrb, "o", &idx);
+ if (mrb_string_p(idx)) {
+ mrb_value sym = mrb_check_intern_str(mrb, idx);
+
+ if (mrb_nil_p(sym)) {
+ mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
+ }
+ idx = sym;
+ }
+ if (mrb_symbol_p(idx)) {
+ return struct_aref_sym(mrb, s, mrb_symbol(idx));
+ }
+ return struct_aref_int(mrb, s, mrb_int(mrb, idx));
+}
+
+static mrb_value
+mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
+{
+ mrb_value members, *ptr;
+ const mrb_value *ptr_members;
+ mrb_int i, len;
+
+ members = struct_members(mrb, s);
+ len = RARRAY_LEN(members);
+ ptr = RSTRUCT_PTR(s);
+ ptr_members = RARRAY_PTR(members);
+ for (i=0; i<len; i++) {
+ if (mrb_symbol(ptr_members[i]) == id) {
+ mrb_struct_modify(mrb, s);
+ ptr[i] = val;
+ return val;
+ }
+ }
+ mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
+ return val; /* not reach */
+}
+
+/* 15.2.18.4.3 */
+/*
+ * call-seq:
+ * struct[symbol] = obj -> obj
+ * struct[fixnum] = obj -> obj
+ *
+ * Attribute Assignment---Assigns to the instance variable named by
+ * <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
+ * returns it. Will raise a <code>NameError</code> if the named
+ * variable does not exist, or an <code>IndexError</code> if the index
+ * is out of range.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ *
+ * joe["name"] = "Luke"
+ * joe[:zip] = "90210"
+ *
+ * joe.name #=> "Luke"
+ * joe.zip #=> "90210"
+ */
+
+static mrb_value
+mrb_struct_aset(mrb_state *mrb, mrb_value s)
+{
+ mrb_int i;
+ mrb_value idx;
+ mrb_value val;
+
+ mrb_get_args(mrb, "oo", &idx, &val);
+
+ if (mrb_string_p(idx)) {
+ mrb_value sym = mrb_check_intern_str(mrb, idx);
+
+ if (mrb_nil_p(sym)) {
+ mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
+ }
+ idx = sym;
+ }
+ if (mrb_symbol_p(idx)) {
+ return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
+ }
+
+ i = mrb_int(mrb, idx);
+ if (i < 0) i = RSTRUCT_LEN(s) + i;
+ if (i < 0) {
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %S too small for struct(size:%S)",
+ mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ }
+ if (RSTRUCT_LEN(s) <= i) {
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %S too large for struct(size:%S)",
+ mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ }
+ mrb_struct_modify(mrb, s);
+ return RSTRUCT_PTR(s)[i] = val;
+}
+
+/* 15.2.18.4.1 */
+/*
+ * call-seq:
+ * struct == other_struct -> true or false
+ *
+ * Equality---Returns <code>true</code> if <i>other_struct</i> is
+ * equal to this one: they must be of the same class as generated by
+ * <code>Struct::new</code>, and the values of all instance variables
+ * must be equal (according to <code>Object#==</code>).
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
+ * joe == joejr #=> true
+ * joe == jane #=> false
+ */
+
+static mrb_value
+mrb_struct_equal(mrb_state *mrb, mrb_value s)
+{
+ mrb_value s2;
+ mrb_value *ptr, *ptr2;
+ mrb_int i, len;
+
+ mrb_get_args(mrb, "o", &s2);
+ if (mrb_obj_equal(mrb, s, s2)) {
+ return mrb_true_value();
+ }
+ if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
+ return mrb_false_value();
+ }
+ if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
+ mrb_bug(mrb, "inconsistent struct"); /* should never happen */
+ }
+ ptr = RSTRUCT_PTR(s);
+ ptr2 = RSTRUCT_PTR(s2);
+ len = RSTRUCT_LEN(s);
+ for (i=0; i<len; i++) {
+ if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
+ return mrb_false_value();
+ }
+ }
+
+ return mrb_true_value();
+}
+
+/* 15.2.18.4.12(x) */
+/*
+ * code-seq:
+ * struct.eql?(other) -> true or false
+ *
+ * Two structures are equal if they are the same object, or if all their
+ * fields are equal (using <code>eql?</code>).
+ */
+static mrb_value
+mrb_struct_eql(mrb_state *mrb, mrb_value s)
+{
+ mrb_value s2;
+ mrb_value *ptr, *ptr2;
+ mrb_int i, len;
+
+ mrb_get_args(mrb, "o", &s2);
+ if (mrb_obj_equal(mrb, s, s2)) {
+ return mrb_true_value();
+ }
+ if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
+ return mrb_false_value();
+ }
+ if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
+ mrb_bug(mrb, "inconsistent struct"); /* should never happen */
+ }
+ ptr = RSTRUCT_PTR(s);
+ ptr2 = RSTRUCT_PTR(s2);
+ len = RSTRUCT_LEN(s);
+ for (i=0; i<len; i++) {
+ if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
+ return mrb_false_value();
+ }
+ }
+
+ return mrb_true_value();
+}
+
+/*
+ * call-seq:
+ * struct.length -> Fixnum
+ * struct.size -> Fixnum
+ *
+ * Returns number of struct members.
+ */
+static mrb_value
+mrb_struct_len(mrb_state *mrb, mrb_value self)
+{
+ return mrb_fixnum_value(RSTRUCT_LEN(self));
+}
+
+/*
+ * call-seq:
+ * struct.to_a -> array
+ * struct.values -> array
+ *
+ * Create an array from struct values.
+ */
+static mrb_value
+mrb_struct_to_a(mrb_state *mrb, mrb_value self)
+{
+ return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self));
+}
+
+/*
+ * call-seq:
+ * struct.to_h -> hash
+ *
+ * Create a hash from member names and struct values.
+ */
+static mrb_value
+mrb_struct_to_h(mrb_state *mrb, mrb_value self)
+{
+ mrb_value members, ret;
+ mrb_int i;
+
+ members = struct_members(mrb, self);
+ ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
+
+ for (i = 0; i < RARRAY_LEN(members); ++i) {
+ mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]);
+ }
+
+ return ret;
+}
+
+static mrb_value
+mrb_struct_values_at(mrb_state *mrb, mrb_value self)
+{
+ mrb_int argc;
+ mrb_value *argv;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+
+ return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
+}
+
+/*
+ * A <code>Struct</code> is a convenient way to bundle a number of
+ * attributes together, using accessor methods, without having to write
+ * an explicit class.
+ *
+ * The <code>Struct</code> class is a generator of specific classes,
+ * each one of which is defined to hold a set of variables and their
+ * accessors. In these examples, we'll call the generated class
+ * "<i>Customer</i>Class," and we'll show an example instance of that
+ * class as "<i>Customer</i>Inst."
+ *
+ * In the descriptions that follow, the parameter <i>symbol</i> refers
+ * to a symbol, which is either a quoted string or a
+ * <code>Symbol</code> (such as <code>:name</code>).
+ */
+void
+mrb_mruby_struct_gem_init(mrb_state* mrb)
+{
+ struct RClass *st;
+ st = mrb_define_class(mrb, "Struct", mrb->object_class);
+ MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY);
+
+ mrb_define_class_method(mrb, st, "new", mrb_struct_s_def, MRB_ARGS_ANY()); /* 15.2.18.3.1 */
+
+ mrb_define_method(mrb, st, "==", mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */
+ mrb_define_method(mrb, st, "[]", mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */
+ mrb_define_method(mrb, st, "[]=", mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */
+ mrb_define_method(mrb, st, "members", mrb_struct_members, MRB_ARGS_NONE()); /* 15.2.18.4.6 */
+ mrb_define_method(mrb, st, "initialize", mrb_struct_initialize, MRB_ARGS_ANY()); /* 15.2.18.4.8 */
+ mrb_define_method(mrb, st, "initialize_copy", mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */
+ mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */
+
+ mrb_define_method(mrb, st, "size", mrb_struct_len, MRB_ARGS_NONE());
+ mrb_define_method(mrb, st, "length", mrb_struct_len, MRB_ARGS_NONE());
+ mrb_define_method(mrb, st, "to_a", mrb_struct_to_a, MRB_ARGS_NONE());
+ mrb_define_method(mrb, st, "values", mrb_struct_to_a, MRB_ARGS_NONE());
+ mrb_define_method(mrb, st, "to_h", mrb_struct_to_h, MRB_ARGS_NONE());
+ mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_ANY());
+}
+
+void
+mrb_mruby_struct_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb
new file mode 100644
index 00000000..421fe4b5
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-struct/test/struct.rb
@@ -0,0 +1,212 @@
+##
+# Struct ISO Test
+
+assert('Struct', '15.2.18') do
+ assert_equal Class, Struct.class
+end
+
+assert('Struct.new', '15.2.18.3.1') do
+ c = Struct.new(:m1, :m2)
+ assert_equal Struct, c.superclass
+ assert_equal [:m1, :m2], c.members
+end
+
+# Check crash bug with Struc.new and no params.
+assert('Struct.new', '15.2.18.3.1') do
+ c = Struct.new()
+ assert_equal Struct, c.superclass
+ assert_equal [], c.members
+end
+
+assert('Struct#==', '15.2.18.4.1') do
+ c = Struct.new(:m1, :m2)
+ cc1 = c.new(1,2)
+ cc2 = c.new(1,2)
+ assert_true cc1 == cc2
+
+ Struct.new(:m1, :m2) { def foo; end }
+ assert_raise(NoMethodError) { Struct.new(:m1).new.foo }
+end
+
+assert('Struct#[]', '15.2.18.4.2') do
+ c = Struct.new(:m1, :m2)
+ cc = c.new(1,2)
+ assert_equal 1, cc[:m1]
+ assert_equal 2, cc["m2"]
+ assert_equal 1, cc[0]
+ assert_equal 2, cc[-1]
+ assert_raise(TypeError) { cc[[]] }
+ assert_raise(IndexError) { cc[2] }
+ assert_raise(NameError) { cc['tama'] }
+end
+
+assert('Struct#[]=', '15.2.18.4.3') do
+ c = Struct.new(:m1, :m2)
+ cc = c.new(1,2)
+ cc[:m1] = 3
+ assert_equal 3, cc[:m1]
+ cc["m2"] = 3
+ assert_equal 3, cc["m2"]
+ cc[0] = 4
+ assert_equal 4, cc[0]
+ cc[-1] = 5
+ assert_equal 5, cc[-1]
+ assert_raise(TypeError) { cc[[]] = 3 }
+ assert_raise(IndexError) { cc[2] = 7 }
+ assert_raise(NameError) { cc['pochi'] = 8 }
+end
+
+assert('Struct#each', '15.2.18.4.4') do
+ c = Struct.new(:m1, :m2)
+ cc = c.new(1,2)
+ a = []
+ cc.each{|x|
+ a << x
+ }
+ assert_equal [1, 2], a
+end
+
+assert('Struct#each_pair', '15.2.18.4.5') do
+ c = Struct.new(:m1, :m2)
+ cc = c.new(1,2)
+ a = []
+ cc.each_pair{|k,v|
+ a << [k,v]
+ }
+ assert_equal [[:m1, 1], [:m2, 2]], a
+end
+
+assert('Struct#members', '15.2.18.4.6') do
+ c = Struct.new(:m1, :m2)
+ assert_equal [:m1, :m2], c.new(1,2).members
+end
+
+assert('Struct#select', '15.2.18.4.7') do
+ c = Struct.new(:m1, :m2)
+ assert_equal([2]) { c.new(1,2).select{|v| v % 2 == 0} }
+end
+
+assert('large struct') do
+ c = Struct.new(:m1, :m2, :m3, :m4, :m5, :m6, :m7, :m8, :m9, :m10, :m11, :m12, :m13)
+ cc = c.new(1,2,3,4,5,6,7,8,9,10,11,12,13)
+ assert_equal 1, cc.m1
+ assert_equal 2, cc.m2
+ assert_equal 3, cc.m3
+ assert_equal 4, cc.m4
+ assert_equal 5, cc.m5
+ assert_equal 6, cc.m6
+ assert_equal 7, cc.m7
+ assert_equal 8, cc.m8
+ assert_equal 9, cc.m9
+ assert_equal 10, cc.m10
+ assert_equal 13, cc.m13
+
+ cc.m13 = 'test'
+ assert_equal 'test', cc.m13
+
+ assert_raise(NoMethodError) { cc.m14 }
+end
+
+assert('wrong struct arg count') do
+ c = Struct.new(:m1)
+ assert_raise ArgumentError do
+ cc = c.new(1,2,3)
+ end
+end
+
+assert('struct dup') do
+ c = Struct.new(:m1, :m2, :m3, :m4, :m5)
+ cc = c.new(1,2,3,4,5)
+ assert_nothing_raised {
+ assert_equal(cc, cc.dup)
+ }
+end
+
+assert('struct inspect') do
+ c = Struct.new(:m1, :m2, :m3, :m4, :m5)
+ cc = c.new(1,2,3,4,5)
+ assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect
+end
+
+assert('Struct#length, Struct#size') do
+ s = Struct.new(:f1, :f2).new(0, 1)
+ assert_equal 2, s.size
+ assert_equal 2, s.length
+end
+
+assert('Struct#to_a, Struct#values') do
+ s = Struct.new(:mem1, :mem2).new('a', 'b')
+ assert_equal ['a', 'b'], s.to_a
+ assert_equal ['a', 'b'], s.values
+end
+
+assert('Struct#to_h') do
+ s = Struct.new(:white, :red, :green).new('ruuko', 'yuzuki', 'hitoe')
+ assert_equal(:white => 'ruuko', :red => 'yuzuki', :green => 'hitoe') { s.to_h }
+end
+
+assert('Struct#values_at') do
+ a = Struct.new(:blue, :purple).new('aki', 'io')
+ assert_equal ['aki'], a.values_at(0)
+ assert_equal ['io', 'aki'], a.values_at(1, 0)
+ assert_raise(IndexError) { a.values_at 2 }
+end
+
+assert("Struct#dig") do
+ a = Struct.new(:blue, :purple).new('aki', Struct.new(:red).new(1))
+ assert_equal 'aki', a.dig(:blue)
+ assert_equal 1, a.dig(:purple, :red)
+ assert_equal 1, a.dig(1, 0)
+end
+
+assert("Struct.new removes existing constant") do
+ skip "redefining Struct with same name cause warnings"
+ begin
+ assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
+ ensure
+ Struct.remove_const :Test
+ end
+end
+
+assert("Struct#initialize_copy requires struct to be the same type") do
+ begin
+ Struct.new("Test", :a)
+ a = Struct::Test.new("a")
+ Struct.remove_const :Test
+ Struct.new("Test", :a, :b)
+ assert_raise(TypeError) do
+ a.initialize_copy(Struct::Test.new("a", "b"))
+ end
+ ensure
+ Struct.remove_const :Test
+ end
+end
+
+assert("Struct.new does not allow array") do
+ assert_raise(TypeError) do
+ Struct.new("Test", [:a])
+ end
+end
+
+assert("Struct.new generates subclass of Struct") do
+ begin
+ original_struct = Struct
+ Struct = String
+ assert_equal original_struct, original_struct.new.superclass
+ ensure
+ Struct = original_struct
+ end
+end
+
+assert 'Struct#freeze' do
+ c = Struct.new :m
+
+ o = c.new
+ o.m = :test
+ assert_equal :test, o.m
+
+ o.freeze
+ assert_raise(RuntimeError) { o.m = :modify }
+ assert_raise(RuntimeError) { o[:m] = :modify }
+ assert_equal :test, o.m
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake
new file mode 100644
index 00000000..4f3fa43b
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Symbol class extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
new file mode 100644
index 00000000..1e3d24b8
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
@@ -0,0 +1,65 @@
+class Symbol
+ include Comparable
+
+ alias intern to_sym
+
+ def to_proc
+ ->(obj,*args,&block) do
+ obj.__send__(self, *args, &block)
+ end
+ end
+
+ ##
+ # call-seq:
+ # sym.capitalize -> symbol
+ #
+ # Same as <code>sym.to_s.capitalize.intern</code>.
+
+ def capitalize
+ (self.to_s.capitalize! || self).to_sym
+ end
+
+ ##
+ # call-seq:
+ # sym.downcase -> symbol
+ #
+ # Same as <code>sym.to_s.downcase.intern</code>.
+
+ def downcase
+ (self.to_s.downcase! || self).to_sym
+ end
+
+ ##
+ # call-seq:
+ # sym.upcase -> symbol
+ #
+ # Same as <code>sym.to_s.upcase.intern</code>.
+
+ def upcase
+ (self.to_s.upcase! || self).to_sym
+ end
+
+ ##
+ # call-seq:
+ # sym.casecmp(other) -> -1, 0, +1 or nil
+ #
+ # Case-insensitive version of <code>Symbol#<=></code>.
+
+ def casecmp(other)
+ return nil unless other.kind_of?(Symbol)
+ lhs = self.to_s; lhs.upcase!
+ rhs = other.to_s; rhs.upcase!
+ lhs <=> rhs
+ end
+
+ #
+ # call-seq:
+ # sym.empty? -> true or false
+ #
+ # Returns that _sym_ is :"" or not.
+
+ def empty?
+ self.length == 0
+ end
+
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c
new file mode 100644
index 00000000..a992dbfc
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/src/symbol.c
@@ -0,0 +1,64 @@
+#include <mruby.h>
+#include <mruby/khash.h>
+#include <mruby/array.h>
+
+typedef struct symbol_name {
+ size_t len;
+ const char *name;
+} symbol_name;
+
+/*
+ * call-seq:
+ * Symbol.all_symbols => array
+ *
+ * Returns an array of all the symbols currently in Ruby's symbol
+ * table.
+ *
+ * Symbol.all_symbols.size #=> 903
+ * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink,
+ * :chown, :EOFError, :$;, :String,
+ * :LOCK_SH, :"setuid?", :$<,
+ * :default_proc, :compact, :extend,
+ * :Tms, :getwd, :$=, :ThreadGroup,
+ * :wait2, :$>]
+ */
+static mrb_value
+mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym i, lim;
+ mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx);
+
+ for (i=1, lim=mrb->symidx+1; i<lim; i++) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(i));
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * sym.length -> integer
+ *
+ * Same as <code>sym.to_s.length</code>.
+ */
+static mrb_value
+mrb_sym_length(mrb_state *mrb, mrb_value self)
+{
+ mrb_int len;
+ mrb_sym2name_len(mrb, mrb_symbol(self), &len);
+ return mrb_fixnum_value(len);
+}
+
+void
+mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
+{
+ struct RClass *s = mrb->symbol_class;
+ mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_symbol_ext_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb
new file mode 100644
index 00000000..6070d141
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -0,0 +1,48 @@
+##
+# Symbol(Ext) Test
+
+assert('Symbol#to_proc') do
+ assert_equal 5, :abs.to_proc[-5]
+end
+
+assert('Symbol.all_symbols') do
+ foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort
+ symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort
+ assert_equal foo, symbols
+end
+
+assert("Symbol#length") do
+ assert_equal 5, :hello.size
+ assert_equal 5, :mruby.length
+end
+
+assert("Symbol#capitalize") do
+ assert_equal :Hello, :hello.capitalize
+ assert_equal :Hello, :HELLO.capitalize
+ assert_equal :Hello, :Hello.capitalize
+end
+
+assert("Symbol#downcase") do
+ assert_equal :hello, :hEllO.downcase
+ assert_equal :hello, :hello.downcase
+end
+
+assert("Symbol#upcase") do
+ assert_equal :HELLO, :hEllO.upcase
+ assert_equal :HELLO, :HELLO.upcase
+end
+
+assert("Symbol#casecmp") do
+ assert_equal 0, :HELLO.casecmp(:hEllO)
+ assert_equal 1, :HELLO.casecmp(:hEllN)
+ assert_equal(-1, :HELLO.casecmp(:hEllP))
+ assert_nil :HELLO.casecmp("hEllO")
+end
+
+assert("Symbol#empty?") do
+ assert_true :''.empty?
+end
+
+assert('Symbol#intern') do
+ assert_equal :test, :test.intern
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md
new file mode 100644
index 00000000..fa4b91e3
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/README.md
@@ -0,0 +1,7 @@
+Running Tests
+=============
+
+To run the tests, execute the following from the project's root directory.
+
+ $ make test
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c
new file mode 100644
index 00000000..14e93ff3
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/driver.c
@@ -0,0 +1,172 @@
+/*
+** mrbtest - Test for Embeddable Ruby
+**
+** This program runs Ruby test programs in test/t directory
+** against the current mruby implementation.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/data.h>
+#include <mruby/compile.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/array.h>
+
+void
+mrb_init_mrbtest(mrb_state *);
+
+/* Print a short remark for the user */
+static void
+print_hint(void)
+{
+ printf("mrbtest - Embeddable Ruby Test\n\n");
+}
+
+static int
+check_error(mrb_state *mrb)
+{
+ /* Error check */
+ /* $ko_test and $kill_test should be 0 */
+ mrb_value ko_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$ko_test"));
+ mrb_value kill_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$kill_test"));
+
+ return mrb_fixnum_p(ko_test) && mrb_fixnum(ko_test) == 0 && mrb_fixnum_p(kill_test) && mrb_fixnum(kill_test) == 0;
+}
+
+static int
+eval_test(mrb_state *mrb)
+{
+ /* evaluate the test */
+ mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
+ /* did an exception occur? */
+ if (mrb->exc) {
+ mrb_print_error(mrb);
+ mrb->exc = 0;
+ return EXIT_FAILURE;
+ }
+ else if (!check_error(mrb)) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+static void
+t_printstr(mrb_state *mrb, mrb_value obj)
+{
+ char *s;
+ int len;
+
+ if (mrb_string_p(obj)) {
+ s = RSTRING_PTR(obj);
+ len = RSTRING_LEN(obj);
+ fwrite(s, len, 1, stdout);
+ }
+}
+
+mrb_value
+mrb_t_printstr(mrb_state *mrb, mrb_value self)
+{
+ mrb_value argv;
+
+ mrb_get_args(mrb, "o", &argv);
+ t_printstr(mrb, argv);
+
+ return argv;
+}
+
+void
+mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
+{
+ struct RClass *krn, *mrbtest;
+
+ krn = mrb->kernel_module;
+ mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1));
+
+ mrbtest = mrb_define_module(mrb, "Mrbtest");
+
+ mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
+
+#ifdef MRB_USE_FLOAT
+ mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6));
+#else
+ mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12));
+#endif
+
+ if (verbose) {
+ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
+ }
+}
+
+void
+mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
+{
+ mrb_value res_src;
+
+ if (mrb_src->exc) {
+ mrb_print_error(mrb_src);
+ exit(EXIT_FAILURE);
+ }
+
+#define TEST_COUNT_PASS(name) \
+ do { \
+ res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name)); \
+ if (mrb_fixnum_p(res_src)) { \
+ mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \
+ mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_fixnum_value(mrb_fixnum(res_dst) + mrb_fixnum(res_src))); \
+ } \
+ } while (FALSE) \
+
+ TEST_COUNT_PASS(ok_test);
+ TEST_COUNT_PASS(ko_test);
+ TEST_COUNT_PASS(kill_test);
+
+#undef TEST_COUNT_PASS
+
+ res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts"));
+
+ if (mrb_array_p(res_src)) {
+ mrb_int i;
+ mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts"));
+ for (i = 0; i < RARRAY_LEN(res_src); ++i) {
+ mrb_value val_src = RARRAY_PTR(res_src)[i];
+ mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src)));
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ mrb_state *mrb;
+ int ret;
+ mrb_bool verbose = FALSE;
+
+ print_hint();
+
+ /* new interpreter instance */
+ mrb = mrb_open();
+ if (mrb == NULL) {
+ fprintf(stderr, "Invalid mrb_state, exiting test driver");
+ return EXIT_FAILURE;
+ }
+
+ if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') {
+ printf("verbose mode: enable\n\n");
+ verbose = TRUE;
+ }
+
+ mrb_init_test_driver(mrb, verbose);
+ mrb_init_mrbtest(mrb);
+ ret = eval_test(mrb);
+ mrb_close(mrb);
+
+ return ret;
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c
new file mode 100644
index 00000000..17ac1bde
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/init_mrbtest.c
@@ -0,0 +1,38 @@
+#include <stdlib.h>
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/variable.h>
+
+extern const uint8_t mrbtest_assert_irep[];
+
+void mrbgemtest_init(mrb_state* mrb);
+void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose);
+void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src);
+
+void
+mrb_init_mrbtest(mrb_state *mrb)
+{
+ mrb_state *core_test;
+
+ mrb_load_irep(mrb, mrbtest_assert_irep);
+
+ core_test = mrb_open_core(mrb_default_allocf, NULL);
+ if (core_test == NULL) {
+ fprintf(stderr, "Invalid mrb_state, exiting %s", __FUNCTION__);
+ exit(EXIT_FAILURE);
+ }
+ mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));
+ mrb_load_irep(core_test, mrbtest_assert_irep);
+ mrb_t_pass_result(mrb, core_test);
+
+#ifndef DISABLE_GEMS
+ mrbgemtest_init(mrb);
+#endif
+
+ if (mrb->exc) {
+ mrb_print_error(mrb);
+ exit(EXIT_FAILURE);
+ }
+ mrb_close(core_test);
+}
+
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake
new file mode 100644
index 00000000..ae4c2f13
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-test/mrbgem.rake
@@ -0,0 +1,187 @@
+MRuby::Gem::Specification.new('mruby-test') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby test'
+
+ build.bins << 'mrbtest'
+ spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
+
+ spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
+
+ clib = "#{build_dir}/mrbtest.c"
+ mlib = clib.ext(exts.object)
+ exec = exefile("#{build.build_dir}/bin/mrbtest")
+
+ libmruby = libfile("#{build.build_dir}/lib/libmruby")
+ libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core")
+
+ mrbtest_lib = libfile("#{build_dir}/mrbtest")
+ mrbtest_objs = []
+
+ driver_obj = objfile("#{build_dir}/driver")
+ driver = "#{spec.dir}/driver.c"
+
+ assert_c = "#{build_dir}/assert.c"
+ assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
+ assert_lib = assert_c.ext(exts.object)
+ mrbtest_objs << assert_lib
+
+ file assert_lib => assert_c
+ file assert_c => assert_rb do |t|
+ open(t.name, 'w') do |f|
+ mrbc.run f, assert_rb, 'mrbtest_assert_irep'
+ end
+ end
+
+ gem_table = build.gems.generate_gem_table build
+
+ build.gems.each do |g|
+ test_rbobj = g.test_rbireps.ext(exts.object)
+ g.test_objs << test_rbobj
+ dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions)
+
+ file test_rbobj => g.test_rbireps
+ file g.test_rbireps => [g.test_rbfiles].flatten do |t|
+ FileUtils.mkdir_p File.dirname(t.name)
+ open(t.name, 'w') do |f|
+ g.print_gem_test_header(f)
+ test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
+ File.expand_path(g.test_preload, dir)
+ }.find {|file| File.exist?(file) }
+
+ f.puts %Q[/*]
+ f.puts %Q[ * This file contains a test code for #{g.name} gem.]
+ f.puts %Q[ *]
+ f.puts %Q[ * IMPORTANT:]
+ f.puts %Q[ * This file was generated!]
+ f.puts %Q[ * All manual changes will get lost.]
+ f.puts %Q[ */]
+ if test_preload.nil?
+ f.puts %Q[extern const uint8_t mrbtest_assert_irep[];]
+ else
+ g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload"
+ end
+ g.test_rbfiles.flatten.each_with_index do |rbfile, i|
+ g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}"
+ end
+ f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty?
+ dep_list.each do |d|
+ f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);]
+ f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);]
+ end
+ f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);]
+ f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);]
+ f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {]
+ unless g.test_rbfiles.empty?
+ f.puts %Q[ mrb_state *mrb2;]
+ unless g.test_args.empty?
+ f.puts %Q[ mrb_value test_args_hash;]
+ end
+ f.puts %Q[ int ai;]
+ g.test_rbfiles.count.times do |i|
+ f.puts %Q[ ai = mrb_gc_arena_save(mrb);]
+ f.puts %Q[ mrb2 = mrb_open_core(mrb_default_allocf, NULL);]
+ f.puts %Q[ if (mrb2 == NULL) {]
+ f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __FUNCTION__);]
+ f.puts %Q[ exit(EXIT_FAILURE);]
+ f.puts %Q[ }]
+ dep_list.each do |d|
+ f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);]
+ f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);]
+ end
+ f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));]
+ if test_preload.nil?
+ f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);]
+ else
+ f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);]
+ end
+ f.puts %Q[ if (mrb2->exc) {]
+ f.puts %Q[ mrb_print_error(mrb2);]
+ f.puts %Q[ exit(EXIT_FAILURE);]
+ f.puts %Q[ }]
+ f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
+
+ unless g.test_args.empty?
+ f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb, #{g.test_args.length}); ]
+ g.test_args.each do |arg_name, arg_value|
+ escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"')
+ escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"')
+ f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ]
+ end
+ f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ]
+ end
+
+ f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init?
+
+ f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});]
+ f.puts %Q[ ]
+
+ f.puts %Q[ mrb_t_pass_result(mrb, mrb2);]
+ f.puts %Q[ mrb_close(mrb2);]
+ f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
+ end
+ end
+ f.puts %Q[}]
+ end
+ end
+ end
+
+ build.gems.each do |v|
+ mrbtest_objs.concat v.test_objs
+ end
+
+ file mrbtest_lib => mrbtest_objs do |t|
+ build.archiver.run t.name, t.prerequisites
+ end
+
+ unless build.build_mrbtest_lib_only?
+ file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t|
+ gem_flags = build.gems.map { |g| g.linker.flags }
+ gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries }
+ gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries }
+ gem_libraries = build.gems.map { |g| g.linker.libraries }
+ gem_library_paths = build.gems.map { |g| g.linker.library_paths }
+ build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries
+ end
+ end
+
+ init = "#{spec.dir}/init_mrbtest.c"
+
+ # store the last gem selection and make the re-build
+ # of the test gem depending on a change to the gem
+ # selection
+ active_gems = "#{build_dir}/active_gems.lst"
+ FileUtils.mkdir_p File.dirname(active_gems)
+ open(active_gems, 'w+') do |f|
+ build.gems.each do |g|
+ f.puts g.name
+ end
+ end
+ file clib => active_gems
+
+ file mlib => clib
+ file clib => init do |t|
+ _pp "GEN", "*.rb", "#{clib.relative_path}"
+ FileUtils.mkdir_p File.dirname(clib)
+ open(clib, 'w') do |f|
+ f.puts %Q[/*]
+ f.puts %Q[ * This file contains a list of all]
+ f.puts %Q[ * test functions.]
+ f.puts %Q[ *]
+ f.puts %Q[ * IMPORTANT:]
+ f.puts %Q[ * This file was generated!]
+ f.puts %Q[ * All manual changes will get lost.]
+ f.puts %Q[ */]
+ f.puts %Q[]
+ f.puts IO.read(init)
+ build.gems.each do |g|
+ f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
+ end
+ f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {]
+ build.gems.each do |g|
+ f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);]
+ end
+ f.puts %Q[}]
+ end
+ end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake
new file mode 100644
index 00000000..45b2ead7
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-time') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'standard Time class'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb
new file mode 100644
index 00000000..df0d8ca8
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/mrblib/time.rb
@@ -0,0 +1,9 @@
+class Time
+ def sunday?; wday == 0 end
+ def monday?; wday == 1 end
+ def tuesday?; wday == 2 end
+ def wednesday?; wday == 3 end
+ def thursday?; wday == 4 end
+ def friday?; wday == 5 end
+ def saturday?; wday == 6 end
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c
new file mode 100644
index 00000000..5e862483
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/src/time.c
@@ -0,0 +1,869 @@
+/*
+** time.c - Time class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <math.h>
+#include <time.h>
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#ifndef DISABLE_STDIO
+#include <stdio.h>
+#else
+#include <string.h>
+#endif
+
+#define NDIV(x,y) (-(-((x)+1)/(y))-1)
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+double round(double x) {
+ return floor(x + 0.5);
+}
+#endif
+
+#if !defined(__MINGW64__) && defined(_WIN32)
+# define llround(x) round(x)
+#endif
+
+#if defined(__MINGW64__) || defined(__MINGW32__)
+# include <sys/time.h>
+#endif
+
+/** Time class configuration */
+
+/* gettimeofday(2) */
+/* C99 does not have gettimeofday that is required to retrieve microseconds */
+/* uncomment following macro on platforms without gettimeofday(2) */
+/* #define NO_GETTIMEOFDAY */
+
+/* gmtime(3) */
+/* C99 does not have reentrant gmtime_r() so it might cause troubles under */
+/* multi-threading environment. undef following macro on platforms that */
+/* does not have gmtime_r() and localtime_r(). */
+/* #define NO_GMTIME_R */
+
+#ifdef _WIN32
+#if _MSC_VER
+/* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */
+#define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL)
+#define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL)
+#else
+#define NO_GMTIME_R
+#endif
+#endif
+
+/* asctime(3) */
+/* mruby usually use its own implementation of struct tm to string conversion */
+/* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */
+/* By default mruby tries to use asctime_r() which is reentrant. */
+/* Undef following macro on platforms that does not have asctime_r(). */
+/* #define NO_ASCTIME_R */
+
+/* timegm(3) */
+/* mktime() creates tm structure for localtime; timegm() is for UTC time */
+/* define following macro to use probably faster timegm() on the platform */
+/* #define USE_SYSTEM_TIMEGM */
+
+/** end of Time class configuration */
+
+#ifndef NO_GETTIMEOFDAY
+# ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN /* don't include winsock.h */
+# include <windows.h>
+# define gettimeofday my_gettimeofday
+
+# ifdef _MSC_VER
+# define UI64(x) x##ui64
+# else
+# define UI64(x) x##ull
+# endif
+
+typedef long suseconds_t;
+
+# if (!defined __MINGW64__) && (!defined __MINGW32__)
+struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+};
+# endif
+
+static int
+gettimeofday(struct timeval *tv, void *tz)
+{
+ if (tz) {
+ mrb_assert(0); /* timezone is not supported */
+ }
+ if (tv) {
+ union {
+ FILETIME ft;
+ unsigned __int64 u64;
+ } t;
+ GetSystemTimeAsFileTime(&t.ft); /* 100 ns intervals since Windows epoch */
+ t.u64 -= UI64(116444736000000000); /* Unix epoch bias */
+ t.u64 /= 10; /* to microseconds */
+ tv->tv_sec = (time_t)(t.u64 / (1000 * 1000));
+ tv->tv_usec = t.u64 % (1000 * 1000);
+ }
+ return 0;
+}
+# else
+# include <sys/time.h>
+# endif
+#endif
+#ifdef NO_GMTIME_R
+#define gmtime_r(t,r) gmtime(t)
+#define localtime_r(t,r) localtime(t)
+#endif
+
+#ifndef USE_SYSTEM_TIMEGM
+#define timegm my_timgm
+
+static unsigned int
+is_leapyear(unsigned int y)
+{
+ return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
+}
+
+static time_t
+timegm(struct tm *tm)
+{
+ static const unsigned int ndays[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+ };
+ time_t r = 0;
+ int i;
+ unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)];
+
+ for (i = 70; i < tm->tm_year; ++i)
+ r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
+ for (i = 0; i < tm->tm_mon; ++i)
+ r += nday[i] * 24 * 60 * 60;
+ r += (tm->tm_mday - 1) * 24 * 60 * 60;
+ r += tm->tm_hour * 60 * 60;
+ r += tm->tm_min * 60;
+ r += tm->tm_sec;
+ return r;
+}
+#endif
+
+/* Since we are limited to using ISO C99, this implementation is based
+* on time_t. That means the resolution of time is only precise to the
+* second level. Also, there are only 2 timezones, namely UTC and LOCAL.
+*/
+
+enum mrb_timezone {
+ MRB_TIMEZONE_NONE = 0,
+ MRB_TIMEZONE_UTC = 1,
+ MRB_TIMEZONE_LOCAL = 2,
+ MRB_TIMEZONE_LAST = 3
+};
+
+typedef struct mrb_timezone_name {
+ const char name[8];
+ size_t len;
+} mrb_timezone_name;
+
+static const mrb_timezone_name timezone_names[] = {
+ { "none", sizeof("none") - 1 },
+ { "UTC", sizeof("UTC") - 1 },
+ { "LOCAL", sizeof("LOCAL") - 1 },
+};
+
+#ifndef DISABLE_STDIO
+static const char mon_names[12][4] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+};
+
+static const char wday_names[7][4] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+};
+#endif
+
+struct mrb_time {
+ time_t sec;
+ time_t usec;
+ enum mrb_timezone timezone;
+ struct tm datetime;
+};
+
+static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
+
+/** Updates the datetime of a mrb_time based on it's timezone and
+seconds setting. Returns self on success, NULL of failure. */
+static struct mrb_time*
+time_update_datetime(mrb_state *mrb, struct mrb_time *self)
+{
+ struct tm *aid;
+
+ if (self->timezone == MRB_TIMEZONE_UTC) {
+ aid = gmtime_r(&self->sec, &self->datetime);
+ }
+ else {
+ aid = localtime_r(&self->sec, &self->datetime);
+ }
+ if (!aid) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
+ /* not reached */
+ return NULL;
+ }
+#ifdef NO_GMTIME_R
+ self->datetime = *aid; /* copy data */
+#endif
+
+ return self;
+}
+
+static mrb_value
+mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm)
+{
+ return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm));
+}
+
+void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
+
+/* Allocates a mrb_time object and initializes it. */
+static struct mrb_time*
+time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
+{
+ struct mrb_time *tm;
+ time_t tsec = 0;
+
+ mrb_check_num_exact(mrb, (mrb_float)sec);
+ mrb_check_num_exact(mrb, (mrb_float)usec);
+
+ if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
+ goto out_of_range;
+ }
+ if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) {
+ goto out_of_range;
+ }
+ tsec = (time_t)sec;
+ if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) {
+ out_of_range:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
+ }
+ tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
+ tm->sec = tsec;
+ tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
+ if (tm->usec < 0) {
+ long sec2 = (long)NDIV(usec,1000000); /* negative div */
+ tm->usec -= sec2 * 1000000;
+ tm->sec += sec2;
+ }
+ else if (tm->usec >= 1000000) {
+ long sec2 = (long)(usec / 1000000);
+ tm->usec -= sec2 * 1000000;
+ tm->sec += sec2;
+ }
+ tm->timezone = timezone;
+ time_update_datetime(mrb, tm);
+
+ return tm;
+}
+
+static mrb_value
+mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone)
+{
+ return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone));
+}
+
+static struct mrb_time*
+current_mrb_time(mrb_state *mrb)
+{
+ struct mrb_time *tm;
+
+ tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
+#if defined(TIME_UTC)
+ {
+ struct timespec ts;
+ if (timespec_get(&ts, TIME_UTC) == 0) {
+ mrb_free(mrb, tm);
+ mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons");
+ }
+ tm->sec = ts.tv_sec;
+ tm->usec = ts.tv_nsec / 1000;
+ }
+#elif defined(NO_GETTIMEOFDAY)
+ {
+ static time_t last_sec = 0, last_usec = 0;
+
+ tm->sec = time(NULL);
+ if (tm->sec != last_sec) {
+ last_sec = tm->sec;
+ last_usec = 0;
+ }
+ else {
+ /* add 1 usec to differentiate two times */
+ last_usec += 1;
+ }
+ tm->usec = last_usec;
+ }
+#else
+ {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ tm->sec = tv.tv_sec;
+ tm->usec = tv.tv_usec;
+ }
+#endif
+ tm->timezone = MRB_TIMEZONE_LOCAL;
+ time_update_datetime(mrb, tm);
+
+ return tm;
+}
+
+/* Allocates a new Time object with given millis value. */
+static mrb_value
+mrb_time_now(mrb_state *mrb, mrb_value self)
+{
+ return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
+}
+
+/* 15.2.19.6.1 */
+/* Creates an instance of time at the given time in seconds, etc. */
+static mrb_value
+mrb_time_at(mrb_state *mrb, mrb_value self)
+{
+ mrb_float f, f2 = 0;
+
+ mrb_get_args(mrb, "f|f", &f, &f2);
+ return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL);
+}
+
+static struct mrb_time*
+time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
+ mrb_int ahour, mrb_int amin, mrb_int asec, mrb_int ausec,
+ enum mrb_timezone timezone)
+{
+ time_t nowsecs;
+ struct tm nowtime = { 0 };
+
+ nowtime.tm_year = (int)ayear - 1900;
+ nowtime.tm_mon = (int)amonth - 1;
+ nowtime.tm_mday = (int)aday;
+ nowtime.tm_hour = (int)ahour;
+ nowtime.tm_min = (int)amin;
+ nowtime.tm_sec = (int)asec;
+ nowtime.tm_isdst = -1;
+
+ if (nowtime.tm_mon < 0 || nowtime.tm_mon > 11
+ || nowtime.tm_mday < 1 || nowtime.tm_mday > 31
+ || nowtime.tm_hour < 0 || nowtime.tm_hour > 24
+ || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0))
+ || nowtime.tm_min < 0 || nowtime.tm_min > 59
+ || nowtime.tm_sec < 0 || nowtime.tm_sec > 60)
+ mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range");
+
+ if (timezone == MRB_TIMEZONE_UTC) {
+ nowsecs = timegm(&nowtime);
+ }
+ else {
+ nowsecs = mktime(&nowtime);
+ }
+ if (nowsecs == (time_t)-1) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
+ }
+
+ return time_alloc(mrb, (double)nowsecs, ausec, timezone);
+}
+
+/* 15.2.19.6.2 */
+/* Creates an instance of time at the given time in UTC. */
+static mrb_value
+mrb_time_gm(mrb_state *mrb, mrb_value self)
+{
+ mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0;
+
+ mrb_get_args(mrb, "i|iiiiii",
+ &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
+ return mrb_time_wrap(mrb, mrb_class_ptr(self),
+ time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_UTC));
+}
+
+
+/* 15.2.19.6.3 */
+/* Creates an instance of time at the given time in local time zone. */
+static mrb_value
+mrb_time_local(mrb_state *mrb, mrb_value self)
+{
+ mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0;
+
+ mrb_get_args(mrb, "i|iiiiii",
+ &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
+ return mrb_time_wrap(mrb, mrb_class_ptr(self),
+ time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL));
+}
+
+static struct mrb_time*
+time_get_ptr(mrb_state *mrb, mrb_value time)
+{
+ struct mrb_time *tm;
+
+ tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time);
+ if (!tm) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
+ }
+ return tm;
+}
+
+static mrb_value
+mrb_time_eq(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other;
+ struct mrb_time *tm1, *tm2;
+ mrb_bool eq_p;
+
+ mrb_get_args(mrb, "o", &other);
+ tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
+ eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec;
+
+ return mrb_bool_value(eq_p);
+}
+
+static mrb_value
+mrb_time_cmp(mrb_state *mrb, mrb_value self)
+{
+ mrb_value other;
+ struct mrb_time *tm1, *tm2;
+
+ mrb_get_args(mrb, "o", &other);
+ tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
+ if (!tm1 || !tm2) return mrb_nil_value();
+ if (tm1->sec > tm2->sec) {
+ return mrb_fixnum_value(1);
+ }
+ else if (tm1->sec < tm2->sec) {
+ return mrb_fixnum_value(-1);
+ }
+ /* tm1->sec == tm2->sec */
+ if (tm1->usec > tm2->usec) {
+ return mrb_fixnum_value(1);
+ }
+ else if (tm1->usec < tm2->usec) {
+ return mrb_fixnum_value(-1);
+ }
+ return mrb_fixnum_value(0);
+}
+
+static mrb_value
+mrb_time_plus(mrb_state *mrb, mrb_value self)
+{
+ mrb_float f;
+ struct mrb_time *tm;
+
+ mrb_get_args(mrb, "f", &f);
+ tm = time_get_ptr(mrb, self);
+ return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone);
+}
+
+static mrb_value
+mrb_time_minus(mrb_state *mrb, mrb_value self)
+{
+ mrb_float f;
+ mrb_value other;
+ struct mrb_time *tm, *tm2;
+
+ mrb_get_args(mrb, "o", &other);
+ tm = time_get_ptr(mrb, self);
+ tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
+ if (tm2) {
+ f = (mrb_float)(tm->sec - tm2->sec)
+ + (mrb_float)(tm->usec - tm2->usec) / 1.0e6;
+ return mrb_float_value(mrb, f);
+ }
+ else {
+ mrb_get_args(mrb, "f", &f);
+ return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec-f, (double)tm->usec, tm->timezone);
+ }
+}
+
+/* 15.2.19.7.30 */
+/* Returns week day number of time. */
+static mrb_value
+mrb_time_wday(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_wday);
+}
+
+/* 15.2.19.7.31 */
+/* Returns year day number of time. */
+static mrb_value
+mrb_time_yday(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_yday + 1);
+}
+
+/* 15.2.19.7.32 */
+/* Returns year of time. */
+static mrb_value
+mrb_time_year(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_year + 1900);
+}
+
+/* 15.2.19.7.33 */
+/* Returns name of time's timezone. */
+static mrb_value
+mrb_time_zone(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value();
+ if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value();
+ return mrb_str_new_static(mrb,
+ timezone_names[tm->timezone].name,
+ timezone_names[tm->timezone].len);
+}
+
+/* 15.2.19.7.4 */
+/* Returns a string that describes the time. */
+static mrb_value
+mrb_time_asctime(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm = time_get_ptr(mrb, self);
+ struct tm *d = &tm->datetime;
+ int len;
+
+#if defined(DISABLE_STDIO)
+ char *s;
+# ifdef NO_ASCTIME_R
+ s = asctime(d);
+# else
+ char buf[32];
+ s = asctime_r(d, buf);
+# endif
+ len = strlen(s)-1; /* truncate the last newline */
+#else
+ char buf[256];
+
+ len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
+ wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
+ d->tm_hour, d->tm_min, d->tm_sec,
+ tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "",
+ d->tm_year + 1900);
+#endif
+ return mrb_str_new(mrb, buf, len);
+}
+
+/* 15.2.19.7.6 */
+/* Returns the day in the month of the time. */
+static mrb_value
+mrb_time_day(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_mday);
+}
+
+
+/* 15.2.19.7.7 */
+/* Returns true if daylight saving was applied for this time. */
+static mrb_value
+mrb_time_dst_p(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_bool_value(tm->datetime.tm_isdst);
+}
+
+/* 15.2.19.7.8 */
+/* 15.2.19.7.10 */
+/* Returns the Time object of the UTC(GMT) timezone. */
+static mrb_value
+mrb_time_getutc(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm, *tm2;
+
+ tm = time_get_ptr(mrb, self);
+ tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
+ *tm2 = *tm;
+ tm2->timezone = MRB_TIMEZONE_UTC;
+ time_update_datetime(mrb, tm2);
+ return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
+}
+
+/* 15.2.19.7.9 */
+/* Returns the Time object of the LOCAL timezone. */
+static mrb_value
+mrb_time_getlocal(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm, *tm2;
+
+ tm = time_get_ptr(mrb, self);
+ tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
+ *tm2 = *tm;
+ tm2->timezone = MRB_TIMEZONE_LOCAL;
+ time_update_datetime(mrb, tm2);
+ return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
+}
+
+/* 15.2.19.7.15 */
+/* Returns hour of time. */
+static mrb_value
+mrb_time_hour(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_hour);
+}
+
+/* 15.2.19.7.16 */
+/* Initializes a time by setting the amount of milliseconds since the epoch.*/
+static mrb_value
+mrb_time_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0,
+ amin = 0, asec = 0, ausec = 0;
+ int n;
+ struct mrb_time *tm;
+
+ n = mrb_get_args(mrb, "|iiiiiii",
+ &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
+ tm = (struct mrb_time*)DATA_PTR(self);
+ if (tm) {
+ mrb_free(mrb, tm);
+ }
+ mrb_data_init(self, NULL, &mrb_time_type);
+
+ if (n == 0) {
+ tm = current_mrb_time(mrb);
+ }
+ else {
+ tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL);
+ }
+ mrb_data_init(self, tm, &mrb_time_type);
+ return self;
+}
+
+/* 15.2.19.7.17(x) */
+/* Initializes a copy of this time object. */
+static mrb_value
+mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
+{
+ mrb_value src;
+ struct mrb_time *t1, *t2;
+
+ mrb_get_args(mrb, "o", &src);
+ if (mrb_obj_equal(mrb, copy, src)) return copy;
+ if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
+ mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
+ }
+ t1 = (struct mrb_time *)DATA_PTR(copy);
+ t2 = (struct mrb_time *)DATA_PTR(src);
+ if (!t2) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
+ }
+ if (!t1) {
+ t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
+ mrb_data_init(copy, t1, &mrb_time_type);
+ }
+ *t1 = *t2;
+ return copy;
+}
+
+/* 15.2.19.7.18 */
+/* Sets the timezone attribute of the Time object to LOCAL. */
+static mrb_value
+mrb_time_localtime(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ tm->timezone = MRB_TIMEZONE_LOCAL;
+ time_update_datetime(mrb, tm);
+ return self;
+}
+
+/* 15.2.19.7.19 */
+/* Returns day of month of time. */
+static mrb_value
+mrb_time_mday(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_mday);
+}
+
+/* 15.2.19.7.20 */
+/* Returns minutes of time. */
+static mrb_value
+mrb_time_min(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_min);
+}
+
+/* 15.2.19.7.21 and 15.2.19.7.22 */
+/* Returns month of time. */
+static mrb_value
+mrb_time_mon(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_mon + 1);
+}
+
+/* 15.2.19.7.23 */
+/* Returns seconds in minute of time. */
+static mrb_value
+mrb_time_sec(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_fixnum_value(tm->datetime.tm_sec);
+}
+
+
+/* 15.2.19.7.24 */
+/* Returns a Float with the time since the epoch in seconds. */
+static mrb_value
+mrb_time_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
+}
+
+/* 15.2.19.7.25 */
+/* Returns a Fixnum with the time since the epoch in seconds. */
+static mrb_value
+mrb_time_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
+ return mrb_float_value(mrb, (mrb_float)tm->sec);
+ }
+ return mrb_fixnum_value((mrb_int)tm->sec);
+}
+
+/* 15.2.19.7.26 */
+/* Returns a Float with the time since the epoch in microseconds. */
+static mrb_value
+mrb_time_usec(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
+ return mrb_float_value(mrb, (mrb_float)tm->usec);
+ }
+ return mrb_fixnum_value((mrb_int)tm->usec);
+}
+
+/* 15.2.19.7.27 */
+/* Sets the timezone attribute of the Time object to UTC. */
+static mrb_value
+mrb_time_utc(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ tm->timezone = MRB_TIMEZONE_UTC;
+ time_update_datetime(mrb, tm);
+ return self;
+}
+
+/* 15.2.19.7.28 */
+/* Returns true if this time is in the UTC timezone false if not. */
+static mrb_value
+mrb_time_utc_p(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_time *tm;
+
+ tm = time_get_ptr(mrb, self);
+ return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
+}
+
+
+void
+mrb_mruby_time_gem_init(mrb_state* mrb)
+{
+ struct RClass *tc;
+ /* ISO 15.2.19.2 */
+ tc = mrb_define_class(mrb, "Time", mrb->object_class);
+ MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA);
+ mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable"));
+ mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */
+ mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */
+ mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */
+ mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */
+ mrb_define_class_method(mrb, tc, "now", mrb_time_now, MRB_ARGS_NONE()); /* 15.2.19.6.5 */
+ mrb_define_class_method(mrb, tc, "utc", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.6 */
+
+ mrb_define_method(mrb, tc, "==" , mrb_time_eq , MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */
+ mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */
+ mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */
+ mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */
+ mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */
+ mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */
+ mrb_define_method(mrb, tc, "dst?" , mrb_time_dst_p , MRB_ARGS_NONE()); /* 15.2.19.7.7 */
+ mrb_define_method(mrb, tc, "getgm" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.8 */
+ mrb_define_method(mrb, tc, "getlocal",mrb_time_getlocal,MRB_ARGS_NONE()); /* 15.2.19.7.9 */
+ mrb_define_method(mrb, tc, "getutc" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.10 */
+ mrb_define_method(mrb, tc, "gmt?" , mrb_time_utc_p , MRB_ARGS_NONE()); /* 15.2.19.7.11 */
+ mrb_define_method(mrb, tc, "gmtime" , mrb_time_utc , MRB_ARGS_NONE()); /* 15.2.19.7.13 */
+ mrb_define_method(mrb, tc, "hour" , mrb_time_hour, MRB_ARGS_NONE()); /* 15.2.19.7.15 */
+ mrb_define_method(mrb, tc, "localtime", mrb_time_localtime, MRB_ARGS_NONE()); /* 15.2.19.7.18 */
+ mrb_define_method(mrb, tc, "mday" , mrb_time_mday, MRB_ARGS_NONE()); /* 15.2.19.7.19 */
+ mrb_define_method(mrb, tc, "min" , mrb_time_min, MRB_ARGS_NONE()); /* 15.2.19.7.20 */
+
+ mrb_define_method(mrb, tc, "mon" , mrb_time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.21 */
+ mrb_define_method(mrb, tc, "month", mrb_time_mon, MRB_ARGS_NONE()); /* 15.2.19.7.22 */
+
+ mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE()); /* 15.2.19.7.23 */
+ mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE()); /* 15.2.19.7.25 */
+ mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */
+ mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE()); /* 15.2.19.7.26 */
+ mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE()); /* 15.2.19.7.27 */
+ mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE()); /* 15.2.19.7.28 */
+ mrb_define_method(mrb, tc, "wday", mrb_time_wday, MRB_ARGS_NONE()); /* 15.2.19.7.30 */
+ mrb_define_method(mrb, tc, "yday", mrb_time_yday, MRB_ARGS_NONE()); /* 15.2.19.7.31 */
+ mrb_define_method(mrb, tc, "year", mrb_time_year, MRB_ARGS_NONE()); /* 15.2.19.7.32 */
+ mrb_define_method(mrb, tc, "zone", mrb_time_zone, MRB_ARGS_NONE()); /* 15.2.19.7.33 */
+
+ mrb_define_method(mrb, tc, "initialize", mrb_time_initialize, MRB_ARGS_REQ(1)); /* 15.2.19.7.16 */
+ mrb_define_method(mrb, tc, "initialize_copy", mrb_time_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.19.7.17 */
+
+ /*
+ methods not available:
+ gmt_offset(15.2.19.7.12)
+ gmtoff(15.2.19.7.14)
+ utc_offset(15.2.19.7.29)
+ */
+}
+
+void
+mrb_mruby_time_gem_final(mrb_state* mrb)
+{
+}
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb
new file mode 100644
index 00000000..52b93117
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-time/test/time.rb
@@ -0,0 +1,228 @@
+##
+# Time ISO Test
+
+assert('Time.new', '15.2.3.3.3') do
+ Time.new.class == Time
+end
+
+assert('Time', '15.2.19') do
+ Time.class == Class
+end
+
+assert('Time.at', '15.2.19.6.1') do
+ assert_kind_of(Time, Time.at(1300000000.0))
+
+ assert_raise(FloatDomainError) { Time.at(Float::NAN) }
+ assert_raise(FloatDomainError) { Time.at(Float::INFINITY) }
+ assert_raise(FloatDomainError) { Time.at(-Float::INFINITY) }
+ assert_raise(FloatDomainError) { Time.at(0, Float::NAN) }
+ assert_raise(FloatDomainError) { Time.at(0, Float::INFINITY) }
+ assert_raise(FloatDomainError) { Time.at(0, -Float::INFINITY) }
+end
+
+assert('Time.gm', '15.2.19.6.2') do
+ Time.gm(2012, 12, 23)
+end
+
+assert('Time.local', '15.2.19.6.3') do
+ Time.local(2012, 12, 23)
+end
+
+assert('Time.mktime', '15.2.19.6.4') do
+ Time.mktime(2012, 12, 23)
+end
+
+assert('Time.now', '15.2.19.6.5') do
+ Time.now.class == Time
+end
+
+assert('Time.utc', '15.2.19.6.6') do
+ Time.utc(2012, 12, 23)
+end
+
+assert('Time#+', '15.2.19.7.1') do
+ t1 = Time.at(1300000000.0)
+ t2 = t1.+(60)
+
+ assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011")
+
+ assert_raise(FloatDomainError) { Time.at(0) + Float::NAN }
+ assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY }
+ assert_raise(FloatDomainError) { Time.at(0) + -Float::INFINITY }
+end
+
+assert('Time#-', '15.2.19.7.2') do
+ t1 = Time.at(1300000000.0)
+ t2 = t1.-(60)
+
+ assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011")
+
+ assert_raise(FloatDomainError) { Time.at(0) - Float::NAN }
+ assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY }
+ assert_raise(FloatDomainError) { Time.at(0) - -Float::INFINITY }
+end
+
+assert('Time#<=>', '15.2.19.7.3') do
+ t1 = Time.at(1300000000.0)
+ t2 = Time.at(1400000000.0)
+ t3 = Time.at(1500000000.0)
+
+ t2.<=>(t1) == 1 and
+ t2.<=>(t2) == 0 and
+ t2.<=>(t3) == -1 and
+ t2.<=>(nil) == nil
+end
+
+assert('Time#asctime', '15.2.19.7.4') do
+ Time.at(1300000000.0).utc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+end
+
+assert('Time#ctime', '15.2.19.7.5') do
+ Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011"
+end
+
+assert('Time#day', '15.2.19.7.6') do
+ Time.gm(2012, 12, 23).day == 23
+end
+
+assert('Time#dst?', '15.2.19.7.7') do
+ not Time.gm(2012, 12, 23).utc.dst?
+end
+
+assert('Time#getgm', '15.2.19.7.8') do
+ Time.at(1300000000.0).getgm.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+end
+
+assert('Time#getlocal', '15.2.19.7.9') do
+ t1 = Time.at(1300000000.0)
+ t2 = Time.at(1300000000.0)
+ t3 = t1.getlocal
+
+ t1 == t3 and t3 == t2.getlocal
+end
+
+assert('Time#getutc', '15.2.19.7.10') do
+ Time.at(1300000000.0).getutc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+end
+
+assert('Time#gmt?', '15.2.19.7.11') do
+ Time.at(1300000000.0).utc.gmt?
+end
+
+# ATM not implemented
+# assert('Time#gmt_offset', '15.2.19.7.12') do
+
+assert('Time#gmtime', '15.2.19.7.13') do
+ Time.at(1300000000.0).gmtime
+end
+
+# ATM not implemented
+# assert('Time#gmtoff', '15.2.19.7.14') do
+
+assert('Time#hour', '15.2.19.7.15') do
+ Time.gm(2012, 12, 23, 7, 6).hour == 7
+end
+
+# ATM doesn't really work
+# assert('Time#initialize', '15.2.19.7.16') do
+
+assert('Time#initialize_copy', '15.2.19.7.17') do
+ time_tmp_2 = Time.at(7.0e6)
+ time_tmp_2.clone == time_tmp_2
+end
+
+assert('Time#localtime', '15.2.19.7.18') do
+ t1 = Time.at(1300000000.0)
+ t2 = Time.at(1300000000.0)
+
+ t1.localtime
+ t1 == t2.getlocal
+end
+
+assert('Time#mday', '15.2.19.7.19') do
+ Time.gm(2012, 12, 23).mday == 23
+end
+
+assert('Time#min', '15.2.19.7.20') do
+ Time.gm(2012, 12, 23, 7, 6).min == 6
+end
+
+assert('Time#mon', '15.2.19.7.21') do
+ Time.gm(2012, 12, 23).mon == 12
+end
+
+assert('Time#month', '15.2.19.7.22') do
+ Time.gm(2012, 12, 23).month == 12
+end
+
+assert('Times#sec', '15.2.19.7.23') do
+ Time.gm(2012, 12, 23, 7, 6, 40).sec == 40
+end
+
+assert('Time#to_f', '15.2.19.7.24') do
+ Time.at(1300000000.0).to_f == 1300000000.0
+end
+
+assert('Time#to_i', '15.2.19.7.25') do
+ Time.at(1300000000.0).to_i == 1300000000
+end
+
+assert('Time#usec', '15.2.19.7.26') do
+ Time.at(1300000000.0).usec == 0
+end
+
+assert('Time#utc', '15.2.19.7.27') do
+ Time.at(1300000000.0).utc
+end
+
+assert('Time#utc?', '15.2.19.7.28') do
+ Time.at(1300000000.0).utc.utc?
+end
+
+# ATM not implemented
+# assert('Time#utc_offset', '15.2.19.7.29') do
+
+assert('Time#wday', '15.2.19.7.30') do
+ Time.gm(2012, 12, 23).wday == 0
+end
+
+assert('Time#yday', '15.2.19.7.31') do
+ Time.gm(2012, 12, 23).yday == 358
+end
+
+assert('Time#year', '15.2.19.7.32') do
+ Time.gm(2012, 12, 23).year == 2012
+end
+
+assert('Time#zone', '15.2.19.7.33') do
+ Time.at(1300000000.0).utc.zone == 'UTC'
+end
+
+# Not ISO specified
+
+assert('Time#to_s') do
+ Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011"
+end
+
+assert('Time#inspect') do
+ Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011"
+end
+
+assert('day of week methods') do
+ t = Time.gm(2012, 12, 24)
+ assert_false t.sunday?
+ assert_true t.monday?
+ assert_false t.tuesday?
+ assert_false t.wednesday?
+ assert_false t.thursday?
+ assert_false t.friday?
+ assert_false t.saturday?
+end
+
+assert('2000 times 500us make a second') do
+ t = Time.utc 2015
+ 2000.times do
+ t += 0.0005
+ end
+ t.usec == 0
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake
new file mode 100644
index 00000000..ce77e0bc
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'toplevel object (main) methods extension'
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb
new file mode 100644
index 00000000..77456239
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/mrblib/toplevel.rb
@@ -0,0 +1,11 @@
+
+def self.include (*modules)
+ self.class.include(*modules)
+end
+
+def self.private(*methods)
+end
+def self.protected(*methods)
+end
+def self.public(*methods)
+end
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb
new file mode 100644
index 00000000..aebdd8b4
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-toplevel-ext/test/toplevel.rb
@@ -0,0 +1,24 @@
+##
+# Toplevel Self(Ext) Test
+
+assert('Toplevel#include') do
+ module ToplevelTestModule1
+ def method_foo
+ :foo
+ end
+
+ CONST_BAR = :bar
+ end
+
+ module ToplevelTestModule2
+ CONST_BAR = :bar2
+ end
+
+ self.include ToplevelTestModule2, ToplevelTestModule1
+
+ assert_true self.class.included_modules.include?( ToplevelTestModule1 )
+ assert_true self.class.included_modules.include?( ToplevelTestModule2 )
+ assert_equal :foo, method_foo
+ assert_equal :bar2, CONST_BAR
+end
+