summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--CONTRIBUTING.md13
-rw-r--r--Doxyfile2408
-rw-r--r--README.md23
-rw-r--r--Rakefile15
-rw-r--r--build_config.rb6
-rw-r--r--doc/guides/compile.md84
-rw-r--r--doc/guides/mrbgems.md5
-rw-r--r--doc/limitations.md70
-rw-r--r--doc/mruby_logo_red_icon.pngbin0 -> 8561 bytes
-rw-r--r--doc/opcode.md188
-rw-r--r--include/mruby.h270
-rw-r--r--include/mruby/array.h7
-rw-r--r--include/mruby/boxing_nan.h11
-rw-r--r--include/mruby/boxing_no.h21
-rw-r--r--include/mruby/boxing_word.h138
-rw-r--r--include/mruby/class.h8
-rw-r--r--include/mruby/common.h6
-rw-r--r--include/mruby/compile.h10
-rw-r--r--include/mruby/data.h4
-rw-r--r--include/mruby/debug.h4
-rw-r--r--include/mruby/dump.h4
-rw-r--r--include/mruby/error.h40
-rw-r--r--include/mruby/gc.h4
-rw-r--r--include/mruby/hash.h4
-rw-r--r--include/mruby/irep.h8
-rw-r--r--include/mruby/istruct.h4
-rw-r--r--include/mruby/khash.h4
-rw-r--r--include/mruby/numeric.h38
-rw-r--r--include/mruby/object.h16
-rw-r--r--include/mruby/opcode.h4
-rw-r--r--include/mruby/proc.h4
-rw-r--r--include/mruby/range.h6
-rw-r--r--include/mruby/re.h4
-rw-r--r--include/mruby/string.h226
-rw-r--r--include/mruby/throw.h4
-rw-r--r--include/mruby/value.h59
-rw-r--r--include/mruby/variable.h7
-rw-r--r--include/mruby/version.h4
-rw-r--r--lib/mruby-core-ext.rb41
-rw-r--r--lib/mruby/build.rb38
-rw-r--r--lib/mruby/build/command.rb65
-rw-r--r--lib/mruby/build/load_gems.rb30
-rw-r--r--lib/mruby/gem.rb5
-rw-r--r--lib/mruby/lockfile.rb73
-rwxr-xr-xminirake38
-rw-r--r--mrbgems/default.gembox3
-rw-r--r--mrbgems/mruby-array-ext/mrbgem.rake1
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb18
-rw-r--r--mrbgems/mruby-array-ext/src/array.c2
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb51
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c4
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h4
-rw-r--r--mrbgems/mruby-bin-mirb/tools/mirb/mirb.c27
-rw-r--r--mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c3
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb8
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c70
-rw-r--r--mrbgems/mruby-compiler/core/keywords3
-rw-r--r--mrbgems/mruby-compiler/core/lex.def110
-rw-r--r--mrbgems/mruby-compiler/core/parse.y18
-rw-r--r--mrbgems/mruby-complex/mrblib/complex.rb27
-rw-r--r--mrbgems/mruby-complex/src/complex.c156
-rw-r--r--mrbgems/mruby-complex/test/complex.rb25
-rw-r--r--mrbgems/mruby-enum-chain/mrblib/chain.rb36
-rw-r--r--mrbgems/mruby-enum-chain/test/enum_chain.rb50
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb4
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb3
-rw-r--r--mrbgems/mruby-enumerator/mrbgem.rake1
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb15
-rw-r--r--mrbgems/mruby-eval/src/eval.c87
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c19
-rw-r--r--mrbgems/mruby-io/README.md4
-rw-r--r--mrbgems/mruby-io/mrbgem.rake2
-rw-r--r--mrbgems/mruby-io/mrblib/io.rb22
-rw-r--r--mrbgems/mruby-io/src/file.c22
-rw-r--r--mrbgems/mruby-io/src/file_test.c2
-rw-r--r--mrbgems/mruby-io/src/io.c35
-rw-r--r--mrbgems/mruby-io/test/file.rb29
-rw-r--r--mrbgems/mruby-io/test/io.rb40
-rw-r--r--mrbgems/mruby-io/test/mruby_io_test.c6
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c13
-rw-r--r--mrbgems/mruby-math/src/math.c7
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c11
-rw-r--r--mrbgems/mruby-metaprog/test/metaprog.rb4
-rw-r--r--mrbgems/mruby-method/src/method.c20
-rw-r--r--mrbgems/mruby-numeric-ext/src/numeric_ext.c54
-rw-r--r--mrbgems/mruby-numeric-ext/test/numeric.rb8
-rw-r--r--mrbgems/mruby-object-ext/src/object.c15
-rw-r--r--mrbgems/mruby-object-ext/test/nil.rb4
-rw-r--r--mrbgems/mruby-pack/src/pack.c30
-rw-r--r--mrbgems/mruby-pack/test/pack.rb17
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.c224
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.h80
-rw-r--r--mrbgems/mruby-random/src/random.c281
-rw-r--r--mrbgems/mruby-random/src/random.h12
-rw-r--r--mrbgems/mruby-range-ext/mrblib/range.rb38
-rw-r--r--mrbgems/mruby-range-ext/src/range.c23
-rw-r--r--mrbgems/mruby-range-ext/test/range.rb112
-rw-r--r--mrbgems/mruby-rational/mrblib/rational.rb41
-rw-r--r--mrbgems/mruby-rational/src/rational.c121
-rw-r--r--mrbgems/mruby-rational/test/rational.rb20
-rw-r--r--mrbgems/mruby-sleep/.gitignore4
-rw-r--r--mrbgems/mruby-sleep/.travis.yml29
-rw-r--r--mrbgems/mruby-sleep/.travis_build_config.rb6
-rw-r--r--mrbgems/mruby-sleep/Rakefile29
-rw-r--r--mrbgems/mruby-socket/README.md2
-rw-r--r--mrbgems/mruby-socket/src/socket.c12
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c36
-rw-r--r--mrbgems/mruby-string-ext/mrbgem.rake1
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb2
-rw-r--r--mrbgems/mruby-string-ext/src/string.c219
-rw-r--r--mrbgems/mruby-string-ext/test/numeric.rb29
-rw-r--r--mrbgems/mruby-string-ext/test/range.rb26
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb69
-rw-r--r--mrbgems/mruby-struct/mrblib/struct.rb12
-rw-r--r--mrbgems/mruby-struct/src/struct.c43
-rw-r--r--mrbgems/mruby-struct/test/struct.rb7
-rw-r--r--mrbgems/mruby-symbol-ext/test/symbol.rb2
-rw-r--r--mrbgems/mruby-test/driver.c4
-rw-r--r--mrbgems/mruby-test/mrbgem.rake7
-rw-r--r--mrbgems/mruby-test/vformat.c200
-rw-r--r--mrbgems/mruby-time/include/mruby/time.h3
-rw-r--r--mrbgems/mruby-time/src/time.c223
-rw-r--r--mrbgems/mruby-time/test/time.rb3
-rw-r--r--mrblib/array.rb30
-rw-r--r--mrblib/enum.rb22
-rw-r--r--mrblib/hash.rb12
-rw-r--r--mrblib/kernel.rb2
-rw-r--r--mrblib/numeric.rb8
-rw-r--r--mrblib/string.rb54
-rw-r--r--src/array.c121
-rw-r--r--src/backtrace.c10
-rw-r--r--src/class.c303
-rw-r--r--src/codedump.c2
-rw-r--r--src/error.c186
-rw-r--r--src/etc.c36
-rw-r--r--src/gc.c8
-rw-r--r--src/hash.c22
-rw-r--r--src/kernel.c9
-rw-r--r--src/load.c5
-rw-r--r--src/numeric.c38
-rw-r--r--src/object.c37
-rw-r--r--src/proc.c8
-rw-r--r--src/range.c4
-rw-r--r--src/state.c58
-rw-r--r--src/string.c1069
-rw-r--r--src/symbol.c72
-rw-r--r--src/variable.c17
-rw-r--r--src/vm.c34
-rw-r--r--tasks/doc.rake48
-rw-r--r--test/assert.rb158
-rw-r--r--test/t/array.rb3
-rw-r--r--test/t/enumerable.rb4
-rw-r--r--test/t/float.rb14
-rw-r--r--test/t/hash.rb2
-rw-r--r--test/t/kernel.rb14
-rw-r--r--test/t/module.rb74
-rw-r--r--test/t/numeric.rb88
-rw-r--r--test/t/range.rb8
-rw-r--r--test/t/string.rb216
-rw-r--r--test/t/vformat.rb92
164 files changed, 7062 insertions, 2867 deletions
diff --git a/.gitignore b/.gitignore
index 400c77386..48f085183 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+*.lock
*.bak
*.bc
*.d
@@ -30,5 +31,4 @@ tags
/benchmark/*.png
/doc/api
-
-/src/y.tab.c
+/doc/capi
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6bababb89..3a7428a88 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,9 +33,16 @@ mruby should be highly portable to other systems and compilers. For this it is
recommended to keep your code as close as possible to the C99 standard
(http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf).
-Although we target C99, Visual C++ is also an important target for mruby. For
-this reason a declaration of a local variable has to be at the beginning of a
-scope block.
+Although we target C99, we've heard some compilers in the embedded environment
+still requires declarations of local variables to be at the beginning of a
+scope. Until we confirm the situation has changed, we use the old-style
+variable declaration.
+
+Visual C++ is also an important target for mruby (supported version is 2013 or
+later). For this reason features that are not supported by Visual C++ may not
+be used (e.g. `%z` of `strftime()`).
+
+NOTE: Old GCC requires `-std=gnu99` option to enable C99 support.
#### Reduce library dependencies to a minimum
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 000000000..8724fe1ac
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,2408 @@
+# Doxyfile 1.8.13
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "mruby"
+PROJECT_NUMBER = 2.0.1
+
+PROJECT_BRIEF = "mruby is the lightweight implementation of the Ruby language"
+
+PROJECT_LOGO = doc/mruby_logo_red_icon.png
+
+OUTPUT_DIRECTORY = doc/capi
+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+INPUT = README.md \
+ src \
+ include \
+ include/mruby \
+ mrblib \
+ doc \
+ doc/guides
+
+# Red for Ruby
+HTML_COLORSTYLE_HUE = 359
+
+# The following expansions
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = NO
+
+# This tells doxygen to search the places that make sense
+SEARCH_INCLUDES = YES
+INCLUDE_PATH = include include/mruby
+INCLUDE_FILE_PATTERNS = *.h
+
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS = -I./include
+
+# This thing creates documentation elements for everything, even when its not documented. Its a little ugly to do it right now because huge swathes of code aren't documented.
+EXTRACT_ALL = NO
+
+# Document MRB_INLINE functions
+EXTRACT_STATIC = YES
+
+JAVADOC_AUTOBRIEF = YES
+QT_AUTOBRIEF = NO
+
+QUIET = YES
+WARN_IF_UNDOCUMENTED = NO
+
+#===========================================================================
+# BELOW THIS LINE IS CRUFT GENERATED BY doxygen -g
+# If you edit anything below this, bring it up here so its easier to read.
+#===========================================================================
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/README.md b/README.md
index 2acc8de1a..ec3492530 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ We don't have a mailing list, but you can use [GitHub issues](https://github.com
## How to compile and install (mruby and gems)
-See the [doc/guides/compile.md](doc/guides/compile.md) file.
+See the [compile.md](https://github.com/mruby/mruby/blob/master/doc/guides/compile.md) file.
## Running Tests
@@ -50,16 +50,29 @@ Or
$ ruby ./minirake test
+## Building documentation
+
+There are two sets of documentation in mruby: the mruby API (generated by yard) and C API (Doxygen)
+
+To build both of them, simply go
+
+ rake doc
+
+You can also view them in your browser
+
+ rake view_api
+ rake view_capi
+
## How to customize mruby (mrbgems)
mruby contains a package manager called *mrbgems*. To create extensions
in C and/or Ruby you should create a *GEM*. For a documentation of how to
-use mrbgems consult the file [doc/guides/mrbgems.md](doc/guides/mrbgems.md). For example code of
-how to use mrbgems look into the folder *examples/mrbgems/*.
+use mrbgems consult the file [mrbgems.md](https://github.com/mruby/mruby/blob/master/doc/guides/mrbgems.md).
+For example code of how to use mrbgems look into the folder *examples/mrbgems/*.
## License
-mruby is released under the [MIT License](LICENSE).
+mruby is released under the [MIT License](https://github.com/mruby/mruby/blob/master/LICENSE).
## Note for License
@@ -88,5 +101,5 @@ file in your pull request.
[ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579
[build-status-img]: https://travis-ci.org/mruby/mruby.svg?branch=master
-[contribution-guidelines]: CONTRIBUTING.md
+[contribution-guidelines]: https://github.com/mruby/mruby/blob/master/CONTRIBUTING.md
[travis-ci]: https://travis-ci.org/mruby/mruby
diff --git a/Rakefile b/Rakefile
index 533153290..28e2d75c8 100644
--- a/Rakefile
+++ b/Rakefile
@@ -10,7 +10,6 @@ $LOAD_PATH << File.join(MRUBY_ROOT, "lib")
# load build systems
require "mruby-core-ext"
require "mruby/build"
-require "mruby/gem"
# load configuration file
MRUBY_CONFIG = (ENV['MRUBY_CONFIG'] && ENV['MRUBY_CONFIG'] != '') ? ENV['MRUBY_CONFIG'] : "#{MRUBY_ROOT}/build_config.rb"
@@ -31,6 +30,7 @@ load "#{MRUBY_ROOT}/tasks/libmruby.rake"
load "#{MRUBY_ROOT}/tasks/benchmark.rake"
load "#{MRUBY_ROOT}/tasks/gitlab.rake"
+load "#{MRUBY_ROOT}/tasks/doc.rake"
def install_D(src, dst)
opts = { :verbose => $verbose }
@@ -118,6 +118,7 @@ task :all => depfiles do
MRuby.each_target do
print_build_summary
end
+ MRuby::Lockfile.write
end
desc "run all mruby tests"
@@ -150,19 +151,9 @@ task :clean do
end
desc "clean everything!"
-task :deep_clean => ["clean"] do
+task :deep_clean => ["clean", "clean_doc"] do
MRuby.each_target do |t|
FileUtils.rm_rf t.gem_clone_dir, { :verbose => $verbose }
end
puts "Cleaned up mrbgems build folder"
end
-
-desc 'generate document'
-task :doc do
- begin
- sh "mrbdoc"
- rescue
- puts "ERROR: To generate documents, you should install yard-mruby gem."
- puts " $ gem install yard-mruby"
- end
-end
diff --git a/build_config.rb b/build_config.rb
index 751317c7a..c0c079c89 100644
--- a/build_config.rb
+++ b/build_config.rb
@@ -18,9 +18,9 @@ MRuby::Build.new do |conf|
# end
# conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
# conf.gem :core => 'mruby-eval'
- # conf.gem :mgem => 'mruby-io'
- # conf.gem :github => 'iij/mruby-io'
- # conf.gem :git => '[email protected]:iij/mruby-io.git', :branch => 'master', :options => '-v'
+ # conf.gem :mgem => 'mruby-onig-regexp'
+ # conf.gem :github => 'mattn/mruby-onig-regexp'
+ # conf.gem :git => '[email protected]:mattn/mruby-onig-regexp.git', :branch => 'master', :options => '-v'
# include the default GEMs
conf.gembox 'default'
diff --git a/doc/guides/compile.md b/doc/guides/compile.md
index 2aaf6f1fe..6a093310a 100644
--- a/doc/guides/compile.md
+++ b/doc/guides/compile.md
@@ -6,11 +6,11 @@ binaries.
## Prerequisites
To compile mruby out of the source code you need the following tools:
-* C Compiler (i.e. ```gcc```)
-* Linker (i.e. ```gcc```)
-* Archive utility (i.e. ```ar```)
-* Parser generator (i.e. ```bison```)
-* Ruby 1.8 or 1.9 (i.e. ```ruby``` or ```jruby```)
+* C Compiler (e.g. `gcc`)
+* Linker (e.g. `gcc`)
+* Archive utility (e.g. `ar`)
+* Parser generator (e.g. `bison`)
+* Ruby 2.0 or later (e.g. `ruby` or `jruby`)
Optional:
* GIT (to update mruby source and integrate mrbgems easier)
@@ -32,10 +32,10 @@ All tools necessary to compile mruby can be set or modified here. In case
you want to maintain an additional *build_config.rb* you can define a
customized path using the *$MRUBY_CONFIG* environment variable.
-To compile just call ```./minirake``` inside of the mruby source root. To
-generate and execute the test tools call ```./minirake test```. To clean
-all build files call ```./minirake clean```. To see full command line on
-build, call ```./minirake -v```.
+To compile just call `./minirake` inside of the mruby source root. To
+generate and execute the test tools call `./minirake test`. To clean
+all build files call `./minirake clean`. To see full command line on
+build, call `./minirake -v`.
## Build Configuration
@@ -79,7 +79,7 @@ toolchain :android
```
Requires the custom standalone Android NDK and the toolchain path
-in ```ANDROID_STANDALONE_TOOLCHAIN```.
+in `ANDROID_STANDALONE_TOOLCHAIN`.
### Binaries
@@ -97,7 +97,7 @@ conf.gem "#{root}/mrbgems/mruby-bin-mirb"
### File Separator
Some environments require a different file separator character. It is possible to
-set the character via ```conf.file_separator```.
+set the character via `conf.file_separator`.
```ruby
conf.file_separator = '/'
```
@@ -119,7 +119,7 @@ end
C Compiler has header searcher to detect installed library.
-If you need a include path of header file use ```search_header_path```:
+If you need a include path of header file use `search_header_path`:
```ruby
# Searches ```iconv.h```.
# If found it will return include path of the header file.
@@ -127,7 +127,7 @@ If you need a include path of header file use ```search_header_path```:
fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h'
```
-If you need a full file name of header file use ```search_header```:
+If you need a full file name of header file use `search_header`:
```ruby
# Searches ```iconv.h```.
# If found it will return full path of the header file.
@@ -136,11 +136,11 @@ iconv_h = conf.cc.search_header 'iconv.h'
print "iconv.h found: #{iconv_h}\n"
```
-Header searcher uses compiler's ```include_paths``` by default.
+Header searcher uses compiler's `include_paths` by default.
When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain)
-it will use compiler specific include paths too. (For example ```/usr/local/include```, ```/usr/include```)
+it will use compiler specific include paths too. (For example `/usr/local/include`, `/usr/include`)
-If you need a special header search paths define a singleton method ```header_search_paths``` to C compiler:
+If you need a special header search paths define a singleton method `header_search_paths` to C compiler:
```ruby
def conf.cc.header_search_paths
['/opt/local/include'] + include_paths
@@ -222,7 +222,7 @@ See doc/mrbgems/README.md for more option about mrbgems.
Configuration Mrbtest build process.
-If you want mrbtest.a only, You should set ```conf.build_mrbtest_lib_only```
+If you want mrbtest.a only, You should set `conf.build_mrbtest_lib_only`
```ruby
conf.build_mrbtest_lib_only
```
@@ -230,9 +230,9 @@ conf.build_mrbtest_lib_only
### Bintest
Tests for mrbgem tools using CRuby.
-To have bintests place \*.rb scripts to ```bintest/``` directory of mrbgems.
-See ```mruby-bin-*/bintest/*.rb``` if you need examples.
-If you want a temporary files use `tempfile` module of CRuby instead of ```/tmp/```.
+To have bintests place \*.rb scripts to `bintest/` directory of mrbgems.
+See `mruby-bin-*/bintest/*.rb` if you need examples.
+If you want a temporary files use `tempfile` module of CRuby instead of `/tmp/`.
You can enable it with following:
```ruby
@@ -247,8 +247,8 @@ correctly. To support mrbgems written in C++, mruby can be
configured to use C++ exception.
There are two levels of C++ exception handling. The one is
-```enable_cxx_exception``` that enables C++ exception, but
-uses C ABI. The other is ```enable_cxx_abi``` where all
+`enable_cxx_exception` that enables C++ exception, but
+uses C ABI. The other is `enable_cxx_abi` where all
files are compiled by C++ compiler.
When you mix C++ code, C++ exception would be enabled automatically.
@@ -266,7 +266,7 @@ C++ exception, add following:
conf.disable_cxx_exception
```
and you will get an error when you try to use C++ gem.
-Note that it must be called before ```enable_cxx_exception``` or ```gem``` method.
+Note that it must be called before `enable_cxx_exception` or `gem` method.
### Debugging mode
@@ -276,17 +276,17 @@ conf.enable_debug
```
When debugging mode is enabled
-* Macro ```MRB_DEBUG``` would be defined.
- * Which means ```mrb_assert()``` macro is enabled.
-* Debug information of irep would be generated by ```mrbc```.
- * Because ```-g``` flag would be added to ```mrbc``` runner.
+* Macro `MRB_DEBUG` would be defined.
+ * Which means `mrb_assert()` macro is enabled.
+* Debug information of irep would be generated by `mrbc`.
+ * Because `-g` flag would be added to `mrbc` runner.
* You can have better backtrace of mruby scripts with this.
## Cross-Compilation
mruby can also be cross-compiled from one platform to another. To
achieve this the *build_config.rb* needs to contain an instance of
-```MRuby::CrossBuild```. This instance defines the compilation
+`MRuby::CrossBuild`. This instance defines the compilation
tools and flags for the target platform. An example could look
like this:
```ruby
@@ -298,12 +298,12 @@ MRuby::CrossBuild.new('32bit') do |conf|
end
```
-All configuration options of ```MRuby::Build``` can also be used
-in ```MRuby::CrossBuild```.
+All configuration options of `MRuby::Build` can also be used
+in `MRuby::CrossBuild`.
### Mrbtest in Cross-Compilation
-In cross compilation, you can run ```mrbtest``` on emulator if
+In cross compilation, you can run `mrbtest` on emulator if
you have it by changing configuration of test runner.
```ruby
conf.test_runner do |t|
@@ -350,15 +350,15 @@ in *build/host/src*)
result will be stored in *build/host/src/y.tab.c*)
* compile *build/host/src/y.tab.c* to *build/host/src/y.tab.o*
* create *build/host/lib/libmruby_core.a* out of all object files (C only)
-* create ```build/host/bin/mrbc``` by compiling *tools/mrbc/mrbc.c* and
+* create `build/host/bin/mrbc` by compiling *tools/mrbc/mrbc.c* and
linking with *build/host/lib/libmruby_core.a*
* create *build/host/mrblib/mrblib.c* by compiling all \*.rb files
-under *mrblib* with ```build/host/bin/mrbc```
+under *mrblib* with `build/host/bin/mrbc`
* compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o*
* create *build/host/lib/libmruby.a* out of all object files (C and Ruby)
-* create ```build/host/bin/mruby``` by compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
+* create `build/host/bin/mruby` by compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
linking with *build/host/lib/libmruby.a*
-* create ```build/host/bin/mirb``` by compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
+* create `build/host/bin/mirb` by compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
linking with *build/host/lib/libmruby.a*
```
@@ -427,15 +427,15 @@ in *build/i386/src*)
result will be stored in *build/i386/src/y.tab.c*)
* cross-compile *build/i386/src/y.tab.c* to *build/i386/src/y.tab.o*
* create *build/i386/mrblib/mrblib.c* by compiling all \*.rb files
-under *mrblib* with the native ```build/host/bin/mrbc```
+under *mrblib* with the native `build/host/bin/mrbc`
* cross-compile *build/host/mrblib/mrblib.c* to *build/host/mrblib/mrblib.o*
* create *build/i386/lib/libmruby.a* out of all object files (C and Ruby)
-* create ```build/i386/bin/mruby``` by cross-compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
+* create `build/i386/bin/mruby` by cross-compiling *mrbgems/mruby-bin-mruby/tools/mruby/mruby.c* and
linking with *build/i386/lib/libmruby.a*
-* create ```build/i386/bin/mirb``` by cross-compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
+* create `build/i386/bin/mirb` by cross-compiling *mrbgems/mruby-bin-mirb/tools/mirb/mirb.c* and
linking with *build/i386/lib/libmruby.a*
* create *build/i386/lib/libmruby_core.a* out of all object files (C only)
-* create ```build/i386/bin/mrbc``` by cross-compiling *tools/mrbc/mrbc.c* and
+* create `build/i386/bin/mrbc` by cross-compiling *tools/mrbc/mrbc.c* and
linking with *build/i386/lib/libmruby_core.a*
```
@@ -463,7 +463,7 @@ linking with *build/i386/lib/libmruby_core.a*
### Minimal Library
To build a minimal mruby library you need to use the Cross Compiling
-feature due to the reason that there are functions (i.e. stdio) which
+feature due to the reason that there are functions (e.g. stdio) which
can't be disabled for the main build.
```ruby
@@ -477,12 +477,12 @@ end
This configuration defines a cross compile build called 'Minimal' which
is using the GCC and compiles for the host machine. It also disables
-all usages of stdio and doesn't compile any binaries (i.e. mrbc).
+all usages of stdio and doesn't compile any binaries (e.g. mrbc).
## Test Environment
mruby's build process includes a test environment. In case you start the testing
-of mruby, a native binary called ```mrbtest``` will be generated and executed.
+of mruby, a native binary called `mrbtest` will be generated and executed.
This binary contains all test cases which are defined under *test/t*. In case
of a cross-compilation an additional cross-compiled *mrbtest* binary is
generated. You can copy this binary and run on your target system.
diff --git a/doc/guides/mrbgems.md b/doc/guides/mrbgems.md
index 0fcc936ed..184f62954 100644
--- a/doc/guides/mrbgems.md
+++ b/doc/guides/mrbgems.md
@@ -37,6 +37,11 @@ conf.gem :mgem => 'mruby-yaml'
conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted
```
+For specifying commit hash to checkout use `:checksum_hash` option:
+```ruby
+conf.gem mgem: 'mruby-redis', checksum_hash: '3446d19fc4a3f9697b5ddbf2a904f301c42f2f4e'
+```
+
If there is missing dependencies, mrbgem dependencies solver will reference
mrbgem from core or mgem-list.
diff --git a/doc/limitations.md b/doc/limitations.md
index 9b4ed9c6f..e6f40a942 100644
--- a/doc/limitations.md
+++ b/doc/limitations.md
@@ -14,17 +14,17 @@ This document does not contain a complete list of limitations.
Please help to improve it by submitting your findings.
-## ```1/2``` gives ```0.5```
+## `1/2` gives `0.5`
-Since mruby does not have ```Bignum```, bigger integers are represented
-by ```Float``` numbers. To enhance interoperability between ```Fixnum```
-and ```Float```, mruby provides ```Float#upto``` and other iterating
-methods for the ```Float``` class. As a side effect, ```1/2``` gives ```0.5```
-not ```0```.
+Since mruby does not have `Bignum`, bigger integers are represented
+by `Float` numbers. To enhance interoperability between `Fixnum`
+and `Float`, mruby provides `Float#upto` and other iterating
+methods for the `Float` class. As a side effect, `1/2` gives `0.5`
+not `0`.
-## ```Array``` passed to ```puts```
+## `Array` passed to `puts`
-Passing an Array to ```puts``` results in different output.
+Passing an Array to `puts` results in different output.
```ruby
puts [1,2,3]
@@ -44,9 +44,9 @@ puts [1,2,3]
[1, 2, 3]
```
-## ```Kernel.raise``` in rescue clause
+## `Kernel.raise` in rescue clause
-```Kernel.raise``` without arguments does not raise the current exception within
+`Kernel.raise` without arguments does not raise the current exception within
a rescue clause.
```ruby
@@ -59,7 +59,7 @@ end
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-```ZeroDivisionError``` is raised.
+`ZeroDivisionError` is raised.
#### mruby [2.0.1 (2019-4-4)]
@@ -67,13 +67,13 @@ No exception is raised.
## Fiber execution can't cross C function boundary
-mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This
+mruby's `Fiber` is implemented in a similar way to Lua's co-routine. This
results in the consequence that you can't switch context within C functions.
-Only exception is ```mrb_fiber_yield``` at return.
+Only exception is `mrb_fiber_yield` at return.
-## ```Array``` does not support instance variables
+## `Array` does not support instance variables
-To reduce memory consumption ```Array``` does not support instance variables.
+To reduce memory consumption `Array` does not support instance variables.
```ruby
class Liste < Array
@@ -87,11 +87,11 @@ p Liste.new "foobar"
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-``` [] ```
+` [] `
#### mruby [2.0.1 (2019-4-4)]
-```ArgumentError``` is raised.
+`ArgumentError` is raised.
## Method visibility
@@ -126,10 +126,10 @@ true
true
```
-## defined?
+## `defined?`
-The ```defined?``` keyword is considered too complex to be fully
-implemented. It is recommended to use ```const_defined?``` and
+The `defined?` keyword is considered too complex to be fully
+implemented. It is recommended to use `const_defined?` and
other reflection methods instead.
```ruby
@@ -144,9 +144,9 @@ nil
#### mruby [2.0.1 (2019-4-4)]
-```NameError``` is raised.
+`NameError` is raised.
-## ```alias``` on global variables
+## `alias` on global variables
Aliasing a global variable works in CRuby but is not part
of the ISO standard.
@@ -157,7 +157,7 @@ alias $a $__a__
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-``` nil ```
+` nil `
#### mruby [2.0.1 (2019-4-4)]
@@ -178,15 +178,15 @@ end
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-```ArgumentError``` is raised.
-The re-defined ```+``` operator does not accept any arguments.
+`ArgumentError` is raised.
+The re-defined `+` operator does not accept any arguments.
#### mruby [2.0.1 (2019-4-4)]
-``` 'ab' ```
+` 'ab' `
Behavior of the operator wasn't changed.
-## Kernel#binding is not supported
+## `Kernel#binding` is not supported
`Kernel#binding` method is not supported.
@@ -238,7 +238,7 @@ Destructured arguments (`b` and `c` in above example) cannot be accessed
from the default expression of optional arguments and keyword arguments,
since actual assignment is done after the evaluation of those default
expressions. Thus:
-
+
```ruby
def f(a,(b,c),d=b)
p [a,b,c,d]
@@ -247,3 +247,17 @@ f(1,[2,3])
```
CRuby gives `[1,2,3,nil]`. mruby raises `NoMethodError` for `b`.
+
+## `nil?` redefinition in conditional expressions
+
+Redefinition of `nil?` is ignored in conditional expressions.
+
+```ruby
+a = "a"
+def a.nil?
+ true
+end
+puts(a.nil? ? "truthy" : "falsy")
+```
+
+Ruby outputs `falsy`. mruby outputs `truthy`.
diff --git a/doc/mruby_logo_red_icon.png b/doc/mruby_logo_red_icon.png
new file mode 100644
index 000000000..9006c84eb
--- /dev/null
+++ b/doc/mruby_logo_red_icon.png
Binary files differ
diff --git a/doc/opcode.md b/doc/opcode.md
index eab82a26f..574a3c83f 100644
--- a/doc/opcode.md
+++ b/doc/opcode.md
@@ -31,97 +31,97 @@ with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
## table.1 Instruction Table
-|Instruction Name |Operand type |Semantics
-|-----------------|-------------|-----------------
-|OP_NOP | - |
-|OP_MOVE" |BB |R(a) = R(b)
-|OP_LOADL" |BB |R(a) = Pool(b)
-|OP_LOADI" |BsB |R(a) = mrb_int(b)
-|OP_LOADI_0' |B |R(a) = 0
-|OP_LOADI_1' |B |R(a) = 1
-|OP_LOADI_2' |B |R(a) = 2
-|OP_LOADI_3' |B |R(a) = 3
-|OP_LOADSYM" |BB |R(a) = Syms(b)
-|OP_LOADNIL' |B |R(a) = nil
-|OP_LOADSELF' |B |R(a) = self
-|OP_LOADT' |B |R(a) = true
-|OP_LOADF' |B |R(a) = false
-|OP_GETGV" |BB |R(a) = getglobal(Syms(b))
-|OP_SETGV" |BB |setglobal(Syms(b), R(a))
-|OP_GETSV" |BB |R(a) = Special[b]
-|OP_SETSV" |BB |Special[b] = R(a)
-|OP_GETIV" |BB |R(a) = ivget(Syms(b))
-|OP_SETIV" |BB |ivset(Syms(b),R(a))
-|OP_GETCV" |BB |R(a) = cvget(Syms(b))
-|OP_SETCV" |BB |cvset(Syms(b),R(a))
-|OP_GETCONST" |BB |R(a) = constget(Syms(b))
-|OP_SETCONST" |BB |constset(Syms(b),R(a))
-|OP_GETMCNST" |BB |R(a) = R(a)::Syms(b)
-|OP_SETMCNST" |BB |R(a+1)::Syms(b) = R(a)
-|OP_GETUPVAR' |BBB |R(a) = uvget(b,c)
-|OP_SETUPVAR' |BBB |uvset(b,c,R(a))
-|OP_JMP |S |pc+=a
-|OP_JMPIF' |SB |if R(b) pc+=a
-|OP_JMPNOT' |SB |if !R(b) pc+=a
-|OP_ONERR |sS |rescue_push(pc+a)
-|OP_EXCEPT' |B |R(a) = exc
-|OP_RESCUE" |BB |R(b) = R(a).isa?(R(b))
-|OP_POPERR |B |a.times{rescue_pop()}
-|OP_RAISE' |B |raise(R(a))
-|OP_EPUSH' |B |ensure_push(SEQ[a])
-|OP_EPOP |B |A.times{ensure_pop().call}
-|OP_SENDV" |BB |R(a) = call(R(a),Syms(b),*R(a+1))
-|OP_SENDVB" |BB |R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2))
-|OP_SEND" |BBB |R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c))
-|OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1))
-|OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv)
-|OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1))
-|OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4)
-|OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1)
-|OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo
-|OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo
-|OP_RETURN' |B |return R(a) (normal)
-|OP_RETURN_BLK' |B |return R(a) (in-block return)
-|OP_BREAK' |B |break R(a)
-|OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4)
-|OP_ADD" |BB |R(a) = R(a)+R(a+1)
-|OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c)
-|OP_SUB" |BB |R(a) = R(a)-R(a+1)
-|OP_SUBI" |BB |R(a) = R(a)-C
-|OP_MUL" |BB |R(a) = R(a)*R(a+1)
-|OP_DIV" |BB |R(a) = R(a)/R(a+1)
-|OP_EQ" |BB |R(a) = R(a)==R(a+1)
-|OP_LT" |BB |R(a) = R(a)<R(a+1)
-|OP_LE" |BB |R(a) = R(a)<=R(a+1)
-|OP_GT" |BB |R(a) = R(a)>R(a+1)
-|OP_GE" |BB |R(a) = R(a)>=R(a+1)
-|OP_ARRAY' |BB |R(a) = ary_new(R(a),R(a+1)..R(a+b))
-|OP_ARRAY2" |BB |R(a) = ary_new(R(b),R(b+1)..R(b+c))
-|OP_ARYCAT' |B |ary_cat(R(a),R(a+1))
-|OP_ARYPUSH' |B |ary_push(R(a),R(a+1))
-|OP_AREF' |BB |R(a) = R(a)[b]
-|OP_ASET' |BB |R(a)[b] = R(a+1)
-|OP_APOST' |BB |*R(a),R(A+1)..R(A+C) = R(a)[B..]
-|OP_STRING" |BB |R(a) = str_dup(Lit(b))
-|OP_STRCAT' |B |str_cat(R(a),R(a+1))
-|OP_HASH' |BB |R(a) = hash_new(R(a),R(a+1)..R(a+b))
-|OP_HASHADD' |BB |R(a) = hash_push(R(a),R(a+1)..R(a+b))
-|OP_LAMBDA" |BB |R(a) = lambda(SEQ[b],OP_L_LAMBDA)
-|OP_BLOCK" |BB |R(a) = lambda(SEQ[b],OP_L_BLOCK)
-|OP_METHOD" |BB |R(a) = lambda(SEQ[b],OP_L_METHOD)
-|OP_RANGE_INC' |B |R(a) = range_new(R(a),R(a+1),FALSE)
-|OP_RANGE_EXC' |B |R(a) = range_new(R(a),R(a+1),TRUE)
-|OP_OCLASS' |B |R(a) = ::Object
-|OP_CLASS" |BB |R(a) = newclass(R(a),Syms(b),R(a+1))
-|OP_MODULE" |BB |R(a) = newmodule(R(a),Syms(b))
-|OP_EXEC" |BB |R(a) = blockexec(R(a),SEQ[b])
-|OP_DEF" |BB |R(a).newmethod(Syms(b),R(a+1))
-|OP_ALIAS' |B |alias_method(R(a),R(a+1),R(a+2))
-|OP_UNDEF" |BB |undef_method(R(a),Syms(b))
-|OP_SCLASS' |B |R(a) = R(a).singleton_class
-|OP_TCLASS' |B |R(a) = target_class
-|OP_ERR' |B |raise(RuntimeError, Lit(Bx))
-|OP_EXT1 |- |make 1st operand 16bit
-|OP_EXT2 |- |make 2nd operand 16bit
-|OP_EXT3 |- |make 1st and 2nd operands 16bit
-|OP_STOP |- |stop VM
+| Instruction Name | Operand type | Semantics |
+|:-----------------|--------------|---------------------|
+| OP_NOP | - | |
+| OP_MOVE" | BB | R(a) = R(b)
+| OP_LOADL" | BB | R(a) = Pool(b)
+| OP_LOADI" | BsB | R(a) = mrb_int(b)
+| OP_LOADI_0' | B | R(a) = 0
+| OP_LOADI_1' | B | R(a) = 1
+| OP_LOADI_2' | B | R(a) = 2
+| OP_LOADI_3' | B | R(a) = 3
+| OP_LOADSYM" | BB | R(a) = Syms(b)
+| OP_LOADNIL' | B | R(a) = nil
+| OP_LOADSELF' | B | R(a) = self
+| OP_LOADT' | B | R(a) = true
+| OP_LOADF' | B | R(a) = false
+| OP_GETGV" | BB | R(a) = getglobal(Syms(b))
+| OP_SETGV" | BB | setglobal(Syms(b), R(a))
+| OP_GETSV" | BB | R(a) = Special[b]
+| OP_SETSV" | BB | Special[b] = R(a)
+| OP_GETIV" | BB | R(a) = ivget(Syms(b))
+| OP_SETIV" | BB | ivset(Syms(b),R(a))
+| OP_GETCV" | BB | R(a) = cvget(Syms(b))
+| OP_SETCV" | BB | cvset(Syms(b),R(a))
+| OP_GETCONST" | BB | R(a) = constget(Syms(b))
+| OP_SETCONST" | BB | constset(Syms(b),R(a))
+| OP_GETMCNST" | BB | R(a) = R(a)::Syms(b)
+| OP_SETMCNST" | BB | R(a+1)::Syms(b) = R(a)
+| OP_GETUPVAR' | BBB | R(a) = uvget(b,c)
+| OP_SETUPVAR' | BBB | uvset(b,c,R(a))
+| OP_JMP | S | pc+=a
+| OP_JMPIF' | SB | if R(b) pc+=a
+| OP_JMPNOT' | SB | if !R(b) pc+=a
+| OP_ONERR | sS | rescue_push(pc+a)
+| OP_EXCEPT' | B | R(a) = exc
+| OP_RESCUE" | BB | R(b) = R(a).isa?(R(b))
+| OP_POPERR | B | a.times{rescue_pop()}
+| OP_RAISE' | B | raise(R(a))
+| OP_EPUSH' | B | ensure_push(SEQ[a])
+| OP_EPOP | B | A.times{ensure_pop().call}
+| OP_SENDV" | BB | R(a) = call(R(a),Syms(b),*R(a+1))
+| OP_SENDVB" | BB | R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2))
+| OP_SEND" | BBB | R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c))
+| OP_SENDB" | BBB | R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1))
+| OP_CALL' | B | R(a) = self.call(frame.argc, frame.argv)
+| OP_SUPER' | BB | R(a) = super(R(a+1),... ,R(a+b+1))
+| OP_ARGARY' | BS | R(a) = argument array (16=5:1:5:1:4)
+| OP_ENTER | W | arg setup according to flags (23=5:5:1:5:5:1:1)
+| OP_KARG" | BB | R(a) = kdict[Syms(Bx)] # todo
+| OP_KARG2" | BB | R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo
+| OP_RETURN' | B | return R(a) (normal)
+| OP_RETURN_BLK' | B | return R(a) (in-block return)
+| OP_BREAK' | B | break R(a)
+| OP_BLKPUSH' | BS | R(a) = block (16=5:1:5:1:4)
+| OP_ADD" | BB | R(a) = R(a)+R(a+1)
+| OP_ADDI" | BBB | R(a) = R(a)+mrb_int(c)
+| OP_SUB" | BB | R(a) = R(a)-R(a+1)
+| OP_SUBI" | BB | R(a) = R(a)-C
+| OP_MUL" | BB | R(a) = R(a)*R(a+1)
+| OP_DIV" | BB | R(a) = R(a)/R(a+1)
+| OP_EQ" | BB | R(a) = R(a)==R(a+1)
+| OP_LT" | BB | R(a) = R(a)<R(a+1)
+| OP_LE" | BB | R(a) = R(a)<=R(a+1)
+| OP_GT" | BB | R(a) = R(a)>R(a+1)
+| OP_GE" | BB | R(a) = R(a)>=R(a+1)
+| OP_ARRAY' | BB | R(a) = ary_new(R(a),R(a+1)..R(a+b))
+| OP_ARRAY2" | BB | R(a) = ary_new(R(b),R(b+1)..R(b+c))
+| OP_ARYCAT' | B | ary_cat(R(a),R(a+1))
+| OP_ARYPUSH' | B | ary_push(R(a),R(a+1))
+| OP_AREF' | BB | R(a) = R(a)[b]
+| OP_ASET' | BB | R(a)[b] = R(a+1)
+| OP_APOST' | BB | *R(a),R(A+1)..R(A+C) = R(a)[B..]
+| OP_STRING" | BB | R(a) = str_dup(Lit(b))
+| OP_STRCAT' | B | str_cat(R(a),R(a+1))
+| OP_HASH' | BB | R(a) = hash_new(R(a),R(a+1)..R(a+b))
+| OP_HASHADD' | BB | R(a) = hash_push(R(a),R(a+1)..R(a+b))
+| OP_LAMBDA" | BB | R(a) = lambda(SEQ[b],OP_L_LAMBDA)
+| OP_BLOCK" | BB | R(a) = lambda(SEQ[b],OP_L_BLOCK)
+| OP_METHOD" | BB | R(a) = lambda(SEQ[b],OP_L_METHOD)
+| OP_RANGE_INC' | B | R(a) = range_new(R(a),R(a+1),FALSE)
+| OP_RANGE_EXC' | B | R(a) = range_new(R(a),R(a+1),TRUE)
+| OP_OCLASS' | B | R(a) = ::Object
+| OP_CLASS" | BB | R(a) = newclass(R(a),Syms(b),R(a+1))
+| OP_MODULE" | BB | R(a) = newmodule(R(a),Syms(b))
+| OP_EXEC" | BB | R(a) = blockexec(R(a),SEQ[b])
+| OP_DEF" | BB | R(a).newmethod(Syms(b),R(a+1))
+| OP_ALIAS' | B | alias_method(R(a),R(a+1),R(a+2))
+| OP_UNDEF" | BB | undef_method(R(a),Syms(b))
+| OP_SCLASS' | B | R(a) = R(a).singleton_class
+| OP_TCLASS' | B | R(a) = target_class
+| OP_ERR' | B | raise(RuntimeError, Lit(Bx))
+| OP_EXT1 | - | make 1st operand 16bit
+| OP_EXT2 | - | make 2nd operand 16bit
+| OP_EXT3 | - | make 1st and 2nd operands 16bit
+| OP_STOP | - | stop VM
diff --git a/include/mruby.h b/include/mruby.h
index 3ef399716..52c233211 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -25,6 +25,10 @@
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
*/
+/**
+ * @file mruby.h
+ */
+
#ifndef MRUBY_H
#define MRUBY_H
@@ -97,11 +101,14 @@ MRB_BEGIN_DECL
typedef uint8_t mrb_code;
/**
- * Required arguments signature type.
+ * \class mrb_aspec
+ *
+ * Specifies the number of arguments a function takes
+ *
+ * Example: `MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1)` for a method that expects 2..3 arguments
*/
typedef uint32_t mrb_aspec;
-
struct mrb_irep;
struct mrb_state;
@@ -127,8 +134,8 @@ typedef struct {
uint16_t ridx;
uint16_t epos;
struct REnv *env;
- mrb_code *pc; /* return address */
- mrb_code *err; /* error position */
+ const mrb_code *pc; /* return address */
+ const mrb_code *err; /* error position */
int argc;
int acc;
struct RClass *target_class;
@@ -170,7 +177,16 @@ struct mrb_context {
# define MRB_METHOD_CACHE_SIZE (1<<7)
#endif
-typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value);
+/**
+ * Function pointer type for a function callable by mruby.
+ *
+ * The arguments to the function are stored on the mrb_state. To get them see mrb_get_args
+ *
+ * @param mrb The mruby state
+ * @param self The self object
+ * @return [mrb_value] The function's return value
+ */
+typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value self);
#ifdef MRB_METHOD_TABLE_INLINE
typedef uintptr_t mrb_method_t;
@@ -243,8 +259,8 @@ typedef struct mrb_state {
#endif
#ifdef MRB_ENABLE_DEBUG_HOOK
- void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
- void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
+ void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
+ void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
#endif
#ifdef MRB_BYTECODE_DECODE_OPTION
@@ -285,9 +301,9 @@ typedef struct mrb_state {
* //free(TheAnimals);
* }
*
- * @param [mrb_state *] mrb The current mruby state.
- * @param [const char *] name The name of the defined class.
- * @param [struct RClass *] super The new class parent.
+ * @param mrb The current mruby state.
+ * @param name The name of the defined class.
+ * @param super The new class parent.
* @return [struct RClass *] Reference to the newly defined class.
* @see mrb_define_class_under
*/
@@ -296,12 +312,12 @@ MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct
/**
* Defines a new module.
*
- * @param [mrb_state *] mrb_state* The current mruby state.
- * @param [const char *] char* The name of the module.
+ * @param mrb The current mruby state.
+ * @param name The name of the module.
* @return [struct RClass *] Reference to the newly defined module.
*/
-MRB_API struct RClass *mrb_define_module(mrb_state *, const char*);
-MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
+MRB_API struct RClass *mrb_define_module(mrb_state *mrb, const char *name);
+MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value val);
/**
* Include a module in another class or module.
@@ -310,11 +326,11 @@ MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
* module B
* include A
* end
- * @param [mrb_state *] mrb_state* The current mruby state.
- * @param [struct RClass *] RClass* A reference to module or a class.
- * @param [struct RClass *] RClass* A reference to the module to be included.
+ * @param mrb The current mruby state.
+ * @param cla A reference to module or a class.
+ * @param included A reference to the module to be included.
*/
-MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
+MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *cla, struct RClass *included);
/**
* Prepends a module in another class or module.
@@ -323,11 +339,11 @@ MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
* module B
* prepend A
* end
- * @param [mrb_state *] mrb_state* The current mruby state.
- * @param [struct RClass *] RClass* A reference to module or a class.
- * @param [struct RClass *] RClass* A reference to the module to be prepended.
+ * @param mrb The current mruby state.
+ * @param cla A reference to module or a class.
+ * @param prepended A reference to the module to be prepended.
*/
-MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
+MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *cla, struct RClass *prepended);
/**
* Defines a global function in ruby.
@@ -336,7 +352,6 @@ MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
*
* Example:
*
- * !!!c
* mrb_value example_method(mrb_state* mrb, mrb_value self)
* {
* puts("Executing example command!");
@@ -348,11 +363,11 @@ MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
* mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE());
* }
*
- * @param [mrb_state *] mrb The MRuby state reference.
- * @param [struct RClass *] cla The class pointer where the method will be defined.
- * @param [const char *] name The name of the method being defined.
- * @param [mrb_func_t] func The function pointer to the method definition.
- * @param [mrb_aspec] aspec The method parameters declaration.
+ * @param mrb The MRuby state reference.
+ * @param cla The class pointer where the method will be defined.
+ * @param name The name of the method being defined.
+ * @param func The function pointer to the method definition.
+ * @param aspec The method parameters declaration.
*/
MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec);
@@ -375,14 +390,20 @@ MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *n
* foo = mrb_define_class(mrb, "Foo", mrb->object_class);
* mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
* }
- * @param [mrb_state *] mrb_state* The MRuby state reference.
- * @param [struct RClass *] RClass* The class where the class method will be defined.
- * @param [const char *] char* The name of the class method being defined.
- * @param [mrb_func_t] mrb_func_t The function pointer to the class method definition.
- * @param [mrb_aspec] mrb_aspec The method parameters declaration.
+ * @param mrb The MRuby state reference.
+ * @param cla The class where the class method will be defined.
+ * @param name The name of the class method being defined.
+ * @param fun The function pointer to the class method definition.
+ * @param aspec The method parameters declaration.
+ */
+MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t fun, mrb_aspec aspec);
+
+/**
+ * Defines a singleton method
+ *
+ * @see mrb_define_class_method
*/
-MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
-MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec);
+MRB_API void mrb_define_singleton_method(mrb_state *mrb, struct RObject *cla, const char *name, mrb_func_t fun, mrb_aspec aspec);
/**
* Defines a module function.
@@ -403,13 +424,13 @@ MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char
* foo = mrb_define_module(mrb, "Foo");
* mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
* }
- * @param [mrb_state *] mrb_state* The MRuby state reference.
- * @param [struct RClass *] RClass* The module where the module function will be defined.
- * @param [const char *] char* The name of the module function being defined.
- * @param [mrb_func_t] mrb_func_t The function pointer to the module function definition.
- * @param [mrb_aspec] mrb_aspec The method parameters declaration.
+ * @param mrb The MRuby state reference.
+ * @param cla The module where the module function will be defined.
+ * @param name The name of the module function being defined.
+ * @param fun The function pointer to the module function definition.
+ * @param aspec The method parameters declaration.
*/
-MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
+MRB_API void mrb_define_module_function(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t fun, mrb_aspec aspec);
/**
* Defines a constant.
@@ -432,12 +453,12 @@ MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*,
* mrb_value
* mrb_example_gem_final(mrb_state* mrb){
* }
- * @param [mrb_state *] mrb_state* The MRuby state reference.
- * @param [struct RClass *] RClass* A class or module the constant is defined in.
- * @param [const char *] name The name of the constant being defined.
- * @param [mrb_value] mrb_value The value for the constant.
+ * @param mrb The MRuby state reference.
+ * @param cla A class or module the constant is defined in.
+ * @param name The name of the constant being defined.
+ * @param val The value for the constant.
*/
-MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value);
+MRB_API void mrb_define_const(mrb_state* mrb, struct RClass* cla, const char *name, mrb_value val);
/**
* Undefines a method.
@@ -483,11 +504,11 @@ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_
*
* mrb_example_gem_final(mrb_state* mrb){
* }
- * @param [mrb_state*] mrb_state* The mruby state reference.
- * @param [struct RClass*] RClass* A class the method will be undefined from.
- * @param [const char*] const char* The name of the method to be undefined.
+ * @param mrb The mruby state reference.
+ * @param cla The class the method will be undefined from.
+ * @param name The name of the method to be undefined.
*/
-MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
+MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *cla, const char *name);
MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym);
/**
@@ -523,11 +544,11 @@ MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym);
* void
* mrb_example_gem_final(mrb_state* mrb){
* }
- * @param [mrb_state*] mrb_state* The mruby state reference.
- * @param [RClass*] RClass* A class the class method will be undefined from.
- * @param [const char*] const char* The name of the class method to be undefined.
+ * @param mrb The mruby state reference.
+ * @param cls A class the class method will be undefined from.
+ * @param name The name of the class method to be undefined.
*/
-MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
+MRB_API void mrb_undef_class_method(mrb_state *mrb, struct RClass *cls, const char *name);
/**
* Initialize a new object instance of c class.
@@ -551,10 +572,10 @@ MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
* obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new
* mrb_p(mrb, obj); // => Kernel#p
* }
- * @param [mrb_state*] mrb The current mruby state.
- * @param [RClass*] c Reference to the class of the new object.
- * @param [mrb_int] argc Number of arguments in argv
- * @param [const mrb_value *] argv Array of mrb_value to initialize the object
+ * @param mrb The current mruby state.
+ * @param c Reference to the class of the new object.
+ * @param argc Number of arguments in argv
+ * @param argv Array of mrb_value to initialize the object
* @return [mrb_value] The newly initialized object
*/
MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv);
@@ -580,8 +601,8 @@ MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const
* mrb_p(mrb, obj); // => Kernel#p
* }
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] super The super class or parent.
+ * @param mrb The current mruby state.
+ * @param super The super class or parent.
* @return [struct RClass *] Reference to the new class.
*/
MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
@@ -597,7 +618,7 @@ MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
* example_module = mrb_module_new(mrb);
* }
*
- * @param [mrb_state*] mrb The current mruby state.
+ * @param mrb The current mruby state.
* @return [struct RClass *] Reference to the new module.
*/
MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
@@ -624,24 +645,24 @@ MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
* }
* }
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name A string representing the name of the class.
+ * @param mrb The current mruby state.
+ * @param name A string representing the name of the class.
* @return [mrb_bool] A boolean value.
*/
MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name);
/**
* Gets a class.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name The name of the class.
+ * @param mrb The current mruby state.
+ * @param name The name of the class.
* @return [struct RClass *] A reference to the class.
*/
MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
/**
* Gets a exception class.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name The name of the class.
+ * @param mrb The current mruby state.
+ * @param name The name of the class.
* @return [struct RClass *] A reference to the class.
*/
MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name);
@@ -670,35 +691,35 @@ MRB_API struct RClass * mrb_exc_get(mrb_state *mrb, const char *name);
* }
* }
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] outer The name of the outer class.
- * @param [const char *] name A string representing the name of the inner class.
+ * @param mrb The current mruby state.
+ * @param outer The name of the outer class.
+ * @param name A string representing the name of the inner class.
* @return [mrb_bool] A boolean value.
*/
MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name);
/**
* Gets a child class.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] outer The name of the parent class.
- * @param [const char *] name The name of the class.
+ * @param mrb The current mruby state.
+ * @param outer The name of the parent class.
+ * @param name The name of the class.
* @return [struct RClass *] A reference to the class.
*/
MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
/**
* Gets a module.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [const char *] name The name of the module.
+ * @param mrb The current mruby state.
+ * @param name The name of the module.
* @return [struct RClass *] A reference to the module.
*/
MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name);
/**
* Gets a module defined under another module.
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] outer The name of the outer module.
- * @param [const char *] name The name of the module.
+ * @param mrb The current mruby state.
+ * @param outer The name of the outer module.
+ * @param name The name of the module.
* @return [struct RClass *] A reference to the module.
*/
MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
@@ -712,8 +733,8 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
*
* Equivalent to:
* Object#dup
- * @param [mrb_state*] mrb The current mruby state.
- * @param [mrb_value] obj Object to be duplicate.
+ * @param mrb The current mruby state.
+ * @param obj Object to be duplicate.
* @return [mrb_value] The newly duplicated object.
*/
MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj);
@@ -753,9 +774,9 @@ MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj);
* }
* }
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] c A reference to a class.
- * @param [mrb_sym] mid A symbol referencing a method id.
+ * @param mrb The current mruby state.
+ * @param c A reference to a class.
+ * @param mid A symbol referencing a method id.
* @return [mrb_bool] A boolean value.
*/
MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid);
@@ -763,10 +784,10 @@ MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mi
/**
* Defines a new class under a given module
*
- * @param [mrb_state*] mrb The current mruby state.
- * @param [struct RClass *] outer Reference to the module under which the new class will be defined
- * @param [const char *] name The name of the defined class
- * @param [struct RClass *] super The new class parent
+ * @param mrb The current mruby state.
+ * @param outer Reference to the module under which the new class will be defined
+ * @param name The name of the defined class
+ * @param super The new class parent
* @return [struct RClass *] Reference to the newly defined class
* @see mrb_define_class
*/
@@ -836,16 +857,18 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
* | `S` | {String} | {mrb_value} | when `!` follows, the value may be `nil` |
* | `A` | {Array} | {mrb_value} | when `!` follows, the value may be `nil` |
* | `H` | {Hash} | {mrb_value} | when `!` follows, the value may be `nil` |
- * | `s` | {String} | char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil` |
+ * | `s` | {String} | char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil` |
* | `z` | {String} | char * | `NULL` terminated string; `z!` gives `NULL` for `nil` |
* | `a` | {Array} | {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` |
- * | `f` | {Float} | {mrb_float} | |
- * | `i` | {Integer} | {mrb_int} | |
+ * | `f` | {Fixnum}/{Float} | {mrb_float} | |
+ * | `i` | {Fixnum}/{Float} | {mrb_int} | |
* | `b` | boolean | {mrb_bool} | |
- * | `n` | {Symbol} | {mrb_sym} | |
+ * | `n` | {String}/{Symbol} | {mrb_sym} | |
+ * | `d` | data | void *, {mrb_data_type} const | 2nd argument will be used to check data type so it won't be modified; when `!` follows, the value may be `nil` |
+ * | `I` | inline struct | void * | |
* | `&` | block | {mrb_value} | &! raises exception if no block given. |
- * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; *! avoid copy of the stack. |
- * | &vert; | optional | | After this spec following specs would be optional. |
+ * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; `*!` avoid copy of the stack. |
+ * | <code>\|</code> | optional | | After this spec following specs would be optional. |
* | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. |
*
* @see mrb_get_args
@@ -856,7 +879,7 @@ typedef const char *mrb_args_format;
* Retrieve arguments from mrb_state.
*
* @param mrb The current MRuby state.
- * @param format [mrb_args_format] is a list of format specifiers
+ * @param format is a list of format specifiers
* @param ... The passing variadic arguments must be a pointer of retrieving type.
* @return the number of arguments retrieved.
* @see mrb_args_format
@@ -889,6 +912,8 @@ MRB_API mrb_value* mrb_get_argv(mrb_state *mrb);
/**
* Call existing ruby functions.
*
+ * Example:
+ *
* #include <stdio.h>
* #include <mruby.h>
* #include "mruby/compile.h"
@@ -905,15 +930,16 @@ MRB_API mrb_value* mrb_get_argv(mrb_state *mrb);
* mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i));
* fclose(fp);
* mrb_close(mrb);
- * }
- * @param [mrb_state*] mrb_state* The current mruby state.
- * @param [mrb_value] mrb_value A reference to an mruby value.
- * @param [const char*] const char* The name of the method.
- * @param [mrb_int] mrb_int The number of arguments the method has.
- * @param [...] ... Variadic values(not type safe!).
- * @return [mrb_value] mrb_value mruby function value.
+ * }
+ *
+ * @param mrb The current mruby state.
+ * @param val A reference to an mruby value.
+ * @param name The name of the method.
+ * @param argc The number of arguments the method has.
+ * @param ... Variadic values(not type safe!).
+ * @return [mrb_value] mruby function value.
*/
-MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
+MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value val, const char *name, mrb_int argc, ...);
/**
* Call existing ruby functions. This is basically the type safe version of mrb_funcall.
*
@@ -935,15 +961,15 @@ MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
* fclose(fp);
* mrb_close(mrb);
* }
- * @param [mrb_state*] mrb_state* The current mruby state.
- * @param [mrb_value] mrb_value A reference to an mruby value.
- * @param [mrb_sym] mrb_sym The symbol representing the method.
- * @param [mrb_int] mrb_int The number of arguments the method has.
- * @param [const mrb_value*] mrb_value* Pointer to the object.
+ * @param mrb The current mruby state.
+ * @param val A reference to an mruby value.
+ * @param name_sym The symbol representing the method.
+ * @param argc The number of arguments the method has.
+ * @param obj Pointer to the object.
* @return [mrb_value] mrb_value mruby function value.
* @see mrb_funcall
*/
-MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*);
+MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value val, mrb_sym name_sym, mrb_int argc, const mrb_value* obj);
/**
* Call existing ruby functions with a block.
*/
@@ -951,16 +977,19 @@ MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int
/**
* Create a symbol
*
+ * Example:
+ *
* # Ruby style:
* :pizza # => :pizza
*
* // C style:
* mrb_sym m_sym = mrb_intern_lit(mrb, "pizza"); // => :pizza
- * @param [mrb_state*] mrb_state* The current mruby state.
- * @param [const char*] const char* The name of the method.
+ *
+ * @param mrb The current mruby state.
+ * @param str The string to be symbolized
* @return [mrb_sym] mrb_sym A symbol.
*/
-MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*);
+MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char* str);
MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t);
MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t);
#define mrb_intern_lit(mrb, lit) mrb_intern_static(mrb, lit, mrb_strlen_lit(lit))
@@ -1055,7 +1084,7 @@ MRB_API mrb_value mrb_top_self(mrb_state *);
MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value);
MRB_API mrb_value mrb_top_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
MRB_API mrb_value mrb_vm_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
-MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, mrb_code*);
+MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, const mrb_code*);
/* compatibility macros */
#define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k))
#define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0)
@@ -1184,6 +1213,7 @@ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj);
MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val);
#define mrb_int(mrb, val) mrb_fixnum(mrb_to_int(mrb, val))
+/* string type checking (contrary to the name, it doesn't convert) */
MRB_API mrb_value mrb_to_str(mrb_state *mrb, mrb_value val);
MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t);
@@ -1210,31 +1240,31 @@ MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RC
MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func);
-/*
+/**
* Resume a Fiber
*
- * @mrbgem mruby-fiber
+ * Implemented in mruby-fiber
*/
MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv);
-/*
+/**
* Yield a Fiber
*
- * @mrbgem mruby-fiber
+ * Implemented in mruby-fiber
*/
MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv);
-/*
+/**
* Check if a Fiber is alive
*
- * @mrbgem mruby-fiber
+ * Implemented in mruby-fiber
*/
MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib);
-/*
+/**
* FiberError reference
*
- * @mrbgem mruby-fiber
+ * Implemented in mruby-fiber
*/
#define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
MRB_API void mrb_stack_extend(mrb_state*, mrb_int);
diff --git a/include/mruby/array.h b/include/mruby/array.h
index 0c0a002f5..9664214d6 100644
--- a/include/mruby/array.h
+++ b/include/mruby/array.h
@@ -1,5 +1,5 @@
-/*
-** mruby/array.h - Array class
+/**
+** @file mruby/array.h - Array class
**
** See Copyright Notice in mruby.h
*/
@@ -33,6 +33,7 @@ struct RArray {
} aux;
mrb_value *ptr;
} heap;
+ void *ary[3];
} as;
};
@@ -45,7 +46,7 @@ struct RArray {
#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED_MASK))
#define ARY_EMBED_LEN(a) ((mrb_int)(((a)->flags & MRB_ARY_EMBED_MASK) - 1))
#define ARY_SET_EMBED_LEN(a,len) ((a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((uint32_t)(len) + 1))
-#define ARY_EMBED_PTR(a) ((mrb_value*)&(a)->as)
+#define ARY_EMBED_PTR(a) ((mrb_value*)(&(a)->as.ary))
#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len)
#define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr)
diff --git a/include/mruby/boxing_nan.h b/include/mruby/boxing_nan.h
index ff6f6082d..af598e34e 100644
--- a/include/mruby/boxing_nan.h
+++ b/include/mruby/boxing_nan.h
@@ -1,5 +1,5 @@
-/*
-** mruby/boxing_nan.h - nan boxing mrb_value definition
+/**
+** @file mruby/boxing_nan.h - nan boxing mrb_value definition
**
** See Copyright Notice in mruby.h
*/
@@ -20,13 +20,6 @@
#endif
#define MRB_FIXNUM_SHIFT 0
-#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
-
-#ifdef MRB_ENDIAN_BIG
-#define MRB_ENDIAN_LOHI(a,b) a b
-#else
-#define MRB_ENDIAN_LOHI(a,b) b a
-#endif
/* value representation by nan-boxing:
* float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF
diff --git a/include/mruby/boxing_no.h b/include/mruby/boxing_no.h
index 86ce64555..19372b587 100644
--- a/include/mruby/boxing_no.h
+++ b/include/mruby/boxing_no.h
@@ -1,5 +1,5 @@
-/*
-** mruby/boxing_no.h - unboxed mrb_value definition
+/**
+** @file mruby/boxing_no.h - unboxed mrb_value definition
**
** See Copyright Notice in mruby.h
*/
@@ -8,17 +8,18 @@
#define MRUBY_BOXING_NO_H
#define MRB_FIXNUM_SHIFT 0
-#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
-typedef struct mrb_value {
- union {
+union mrb_value_union {
#ifndef MRB_WITHOUT_FLOAT
- mrb_float f;
+ mrb_float f;
#endif
- void *p;
- mrb_int i;
- mrb_sym sym;
- } value;
+ void *p;
+ mrb_int i;
+ mrb_sym sym;
+};
+
+typedef struct mrb_value {
+ union mrb_value_union value;
enum mrb_vtype tt;
} mrb_value;
diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h
index 2fb84052a..1a0c59ddb 100644
--- a/include/mruby/boxing_word.h
+++ b/include/mruby/boxing_word.h
@@ -1,5 +1,5 @@
-/*
-** mruby/boxing_word.h - word boxing mrb_value definition
+/**
+** @file mruby/boxing_word.h - word boxing mrb_value definition
**
** See Copyright Notice in mruby.h
*/
@@ -27,43 +27,59 @@ struct RCptr {
void *p;
};
-#define MRB_FIXNUM_SHIFT 1
-#ifdef MRB_WITHOUT_FLOAT
-#define MRB_TT_HAS_BASIC MRB_TT_CPTR
-#else
-#define MRB_TT_HAS_BASIC MRB_TT_FLOAT
-#endif
-
enum mrb_special_consts {
- MRB_Qnil = 0,
- MRB_Qfalse = 2,
- MRB_Qtrue = 4,
- MRB_Qundef = 6,
+ MRB_Qnil = 0,
+ MRB_Qfalse = 4,
+ MRB_Qtrue = 12,
+ MRB_Qundef = 20,
};
-#define MRB_FIXNUM_FLAG 0x01
-#define MRB_SYMBOL_FLAG 0x0e
-#define MRB_SPECIAL_SHIFT 8
+#define MRB_FIXNUM_SHIFT 1
+#define MRB_SYMBOL_SHIFT 2
+#define MRB_FIXNUM_FLAG (1 << (MRB_FIXNUM_SHIFT - 1))
+#define MRB_SYMBOL_FLAG (1 << (MRB_SYMBOL_SHIFT - 1))
+#define MRB_FIXNUM_MASK ((1 << MRB_FIXNUM_SHIFT) - 1)
+#define MRB_SYMBOL_MASK ((1 << MRB_SYMBOL_SHIFT) - 1)
+#define MRB_IMMEDIATE_MASK 0x07
-#if defined(MRB_64BIT)
+#ifdef MRB_64BIT
#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT)
#define MRB_SYMBOL_MAX UINT32_MAX
#else
-#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT - MRB_SPECIAL_SHIFT)
-#define MRB_SYMBOL_MAX (UINT32_MAX >> MRB_SPECIAL_SHIFT)
+#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT - MRB_SYMBOL_SHIFT)
+#define MRB_SYMBOL_MAX (UINT32_MAX >> MRB_SYMBOL_SHIFT)
#endif
+#define BOXWORD_SHIFT_VALUE(o,n,t) \
+ ((((t)(o).w)) >> MRB_##n##_SHIFT)
+#define BOXWORD_SET_SHIFT_VALUE(o,n,v) \
+ ((o).w = (((unsigned long)(v)) << MRB_##n##_SHIFT) | MRB_##n##_FLAG)
+#define BOXWORD_SHIFT_VALUE_P(o,n) \
+ (((o).w & MRB_##n##_MASK) == MRB_##n##_FLAG)
+
+/*
+ * mrb_value representation:
+ *
+ * nil : ...0000 0000 (all bits are zero)
+ * false : ...0000 0100
+ * true : ...0000 1100
+ * undef : ...0001 0100
+ * fixnum: ...IIII III1
+ * symbol: ...SSSS SS10 (high-order 32-bit are symbol value in 64-bit mode)
+ * object: ...PPPP P000
+ */
typedef union mrb_value {
union {
void *p;
+#ifdef MRB_64BIT
+ /* use struct to avoid bit shift. */
struct {
- unsigned int i_flag : MRB_FIXNUM_SHIFT;
- mrb_int i : (MRB_INT_BIT - MRB_FIXNUM_SHIFT);
- };
- struct {
- unsigned int sym_flag : MRB_SPECIAL_SHIFT;
- mrb_sym sym : MRB_SYMBOL_BITSIZE;
+ MRB_ENDIAN_LOHI(
+ mrb_sym sym;
+ ,uint32_t sym_flag;
+ )
};
+#endif
struct RBasic *bp;
#ifndef MRB_WITHOUT_FLOAT
struct RFloat *fp;
@@ -88,8 +104,42 @@ MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float);
#ifndef MRB_WITHOUT_FLOAT
#define mrb_float(o) (o).value.fp->f
#endif
-#define mrb_fixnum(o) ((mrb_int)(o).value.i)
+#define mrb_fixnum(o) BOXWORD_SHIFT_VALUE(o, FIXNUM, mrb_int)
+#ifdef MRB_64BIT
#define mrb_symbol(o) (o).value.sym
+#else
+#define mrb_symbol(o) BOXWORD_SHIFT_VALUE(o, SYMBOL, mrb_sym)
+#endif
+#define mrb_bool(o) (((o).w & ~(unsigned long)MRB_Qfalse) != 0)
+
+#define mrb_immediate_p(o) ((o).w & MRB_IMMEDIATE_MASK || (o).w == MRB_Qnil)
+#define mrb_fixnum_p(o) BOXWORD_SHIFT_VALUE_P(o, FIXNUM)
+#ifdef MRB_64BIT
+#define mrb_symbol_p(o) ((o).value.sym_flag == MRB_SYMBOL_FLAG)
+#else
+#define mrb_symbol_p(o) BOXWORD_SHIFT_VALUE_P(o, SYMBOL)
+#endif
+#define mrb_undef_p(o) ((o).w == MRB_Qundef)
+#define mrb_nil_p(o) ((o).w == MRB_Qnil)
+#define mrb_false_p(o) ((o).w == MRB_Qfalse)
+#define mrb_true_p(o) ((o).w == MRB_Qtrue)
+
+#ifndef MRB_WITHOUT_FLOAT
+#define SET_FLOAT_VALUE(mrb,r,v) ((r) = mrb_word_boxing_float_value(mrb, v))
+#endif
+#define SET_CPTR_VALUE(mrb,r,v) ((r) = mrb_word_boxing_cptr_value(mrb, v))
+#define SET_UNDEF_VALUE(r) ((r).w = MRB_Qundef)
+#define SET_NIL_VALUE(r) ((r).w = MRB_Qnil)
+#define SET_FALSE_VALUE(r) ((r).w = MRB_Qfalse)
+#define SET_TRUE_VALUE(r) ((r).w = MRB_Qtrue)
+#define SET_BOOL_VALUE(r,b) ((b) ? SET_TRUE_VALUE(r) : SET_FALSE_VALUE(r))
+#define SET_INT_VALUE(r,n) BOXWORD_SET_SHIFT_VALUE(r, FIXNUM, n)
+#ifdef MRB_64BIT
+#define SET_SYM_VALUE(r,v) ((r).value.sym = v, (r).value.sym_flag = MRB_SYMBOL_FLAG)
+#else
+#define SET_SYM_VALUE(r,n) BOXWORD_SET_SHIFT_VALUE(r, SYMBOL, n)
+#endif
+#define SET_OBJ_VALUE(r,v) ((r).value.p = v)
MRB_INLINE enum mrb_vtype
mrb_type(mrb_value o)
@@ -103,45 +153,13 @@ mrb_type(mrb_value o)
case MRB_Qundef:
return MRB_TT_UNDEF;
}
- if (o.value.i_flag == MRB_FIXNUM_FLAG) {
+ if (mrb_fixnum_p(o)) {
return MRB_TT_FIXNUM;
}
- if (o.value.sym_flag == MRB_SYMBOL_FLAG) {
+ if (mrb_symbol_p(o)) {
return MRB_TT_SYMBOL;
}
return o.value.bp->tt;
}
-#define mrb_bool(o) ((o).w != MRB_Qnil && (o).w != MRB_Qfalse)
-#define mrb_fixnum_p(o) ((o).value.i_flag == MRB_FIXNUM_FLAG)
-#define mrb_undef_p(o) ((o).w == MRB_Qundef)
-#define mrb_nil_p(o) ((o).w == MRB_Qnil)
-#define mrb_false_p(o) ((o).w == MRB_Qfalse)
-#define mrb_true_p(o) ((o).w == MRB_Qtrue)
-
-#ifndef MRB_WITHOUT_FLOAT
-#define SET_FLOAT_VALUE(mrb,r,v) ((r) = mrb_word_boxing_float_value(mrb, v))
-#endif
-#define SET_CPTR_VALUE(mrb,r,v) ((r) = mrb_word_boxing_cptr_value(mrb, v))
-#define SET_UNDEF_VALUE(r) ((r).w = MRB_Qundef)
-#define SET_NIL_VALUE(r) ((r).w = MRB_Qnil)
-#define SET_FALSE_VALUE(r) ((r).w = MRB_Qfalse)
-#define SET_TRUE_VALUE(r) ((r).w = MRB_Qtrue)
-#define SET_BOOL_VALUE(r,b) ((b) ? SET_TRUE_VALUE(r) : SET_FALSE_VALUE(r))
-#define SET_INT_VALUE(r,n) do { \
- (r).w = 0; \
- (r).value.i_flag = MRB_FIXNUM_FLAG; \
- (r).value.i = (n); \
-} while (0)
-#define SET_SYM_VALUE(r,v) do { \
- (r).w = 0; \
- (r).value.sym_flag = MRB_SYMBOL_FLAG; \
- (r).value.sym = (v); \
-} while (0)
-#define SET_OBJ_VALUE(r,v) do { \
- (r).w = 0; \
- (r).value.p = (v); \
- if ((r).value.bp) (r).value.bp->tt = ((struct RObject*)(v))->tt; \
-} while (0)
-
#endif /* MRUBY_BOXING_WORD_H */
diff --git a/include/mruby/class.h b/include/mruby/class.h
index c79a487b5..7c925f3b3 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -1,5 +1,5 @@
-/*
-** mruby/class.h - Class class
+/**
+** @file mruby/class.h - Class class
**
** See Copyright Notice in mruby.h
*/
@@ -75,8 +75,8 @@ mrb_class(mrb_state *mrb, mrb_value v)
MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*);
MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym);
-MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
-MRB_API struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
+struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
+struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, mrb_method_t);
MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec);
MRB_API void mrb_alias_method(mrb_state*, struct RClass *c, mrb_sym a, mrb_sym b);
diff --git a/include/mruby/common.h b/include/mruby/common.h
index dc9e3acc5..5be9a40c6 100644
--- a/include/mruby/common.h
+++ b/include/mruby/common.h
@@ -1,5 +1,5 @@
-/*
-**"common.h - mruby common platform definition"
+/**
+** @file common.h - mruby common platform definition"
**
** See Copyright Notice in mruby.h
*/
@@ -62,6 +62,7 @@ MRB_BEGIN_DECL
#define MRB_INLINE static inline
/** Declare a public MRuby API function. */
+#ifndef MRB_API
#if defined(MRB_BUILD_AS_DLL)
#if defined(MRB_CORE) || defined(MRB_LIB)
# define MRB_API __declspec(dllexport)
@@ -71,6 +72,7 @@ MRB_BEGIN_DECL
#else
# define MRB_API extern
#endif
+#endif
MRB_END_DECL
diff --git a/include/mruby/compile.h b/include/mruby/compile.h
index 8f8f2ebd7..ac9a9892a 100644
--- a/include/mruby/compile.h
+++ b/include/mruby/compile.h
@@ -1,5 +1,5 @@
-/*
-** mruby/compile.h - mruby parser
+/**
+** @file mruby/compile.h - mruby parser
**
** See Copyright Notice in mruby.h
*/
@@ -24,7 +24,7 @@ typedef struct mrbc_context {
mrb_sym *syms;
int slen;
char *filename;
- short lineno;
+ uint16_t lineno;
int (*partial_hook)(struct mrb_parser_state*);
void *partial_data;
struct RClass *target_class;
@@ -67,7 +67,7 @@ enum mrb_lex_state_enum {
/* saved error message */
struct mrb_parser_message {
- int lineno;
+ uint16_t lineno;
int column;
char* message;
};
@@ -119,7 +119,7 @@ struct mrb_parser_state {
#endif
mrbc_context *cxt;
mrb_sym filename_sym;
- int lineno;
+ uint16_t lineno;
int column;
enum mrb_lex_state_enum lstate;
diff --git a/include/mruby/data.h b/include/mruby/data.h
index 54466dcd6..35ec2c25b 100644
--- a/include/mruby/data.h
+++ b/include/mruby/data.h
@@ -1,5 +1,5 @@
-/*
-** mruby/data.h - Data class
+/**
+** @file mruby/data.h - Data class
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/debug.h b/include/mruby/debug.h
index e08c47cfc..f28dd645a 100644
--- a/include/mruby/debug.h
+++ b/include/mruby/debug.h
@@ -1,5 +1,5 @@
-/*
-** mruby/debug.h - mruby debug info
+/**
+** @file mruby/debug.h - mruby debug info
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/dump.h b/include/mruby/dump.h
index 65ed8af9d..46c3b63ce 100644
--- a/include/mruby/dump.h
+++ b/include/mruby/dump.h
@@ -1,5 +1,5 @@
-/*
-** mruby/dump.h - mruby binary dumper (mrbc binary format)
+/**
+** @file mruby/dump.h - mruby binary dumper (mrbc binary format)
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/error.h b/include/mruby/error.h
index 237c701ad..20090d197 100644
--- a/include/mruby/error.h
+++ b/include/mruby/error.h
@@ -1,5 +1,5 @@
-/*
-** mruby/error.h - Exception class
+/**
+** @file mruby/error.h - Exception class
**
** See Copyright Notice in mruby.h
*/
@@ -32,23 +32,51 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
/* declaration for `fail` method */
MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
+#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT) || defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING)
struct RBreak {
MRB_OBJECT_HEADER;
struct RProc *proc;
mrb_value val;
};
+#define mrb_break_value_get(brk) ((brk)->val)
+#define mrb_break_value_set(brk, v) ((brk)->val = v)
+#else
+struct RBreak {
+ MRB_OBJECT_HEADER;
+ struct RProc *proc;
+ union mrb_value_union value;
+};
+#define RBREAK_VALUE_TT_MASK ((1 << 8) - 1)
+static inline mrb_value
+mrb_break_value_get(struct RBreak *brk)
+{
+ mrb_value val;
+ val.value = brk->value;
+ val.tt = brk->flags & RBREAK_VALUE_TT_MASK;
+ return val;
+}
+static inline void
+mrb_break_value_set(struct RBreak *brk, mrb_value val)
+{
+ brk->value = val.value;
+ brk->flags &= ~RBREAK_VALUE_TT_MASK;
+ brk->flags |= val.tt;
+}
+#endif /* MRB_64BIT || MRB_USE_FLOAT || MRB_NAN_BOXING || MRB_WORD_BOXING */
+#define mrb_break_proc_get(brk) ((brk)->proc)
+#define mrb_break_proc_set(brk, p) ((brk)->proc = p)
/**
* Protect
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state);
/**
* Ensure
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
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);
@@ -56,7 +84,7 @@ MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
/**
* Rescue
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
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);
@@ -64,7 +92,7 @@ MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
/**
* Rescue exception
*
- * @mrbgem mruby-error
+ * Implemented in the mruby-error mrbgem
*/
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,
diff --git a/include/mruby/gc.h b/include/mruby/gc.h
index 2a3ff4182..4d9fb60eb 100644
--- a/include/mruby/gc.h
+++ b/include/mruby/gc.h
@@ -1,5 +1,5 @@
-/*
-** mruby/gc.h - garbage collector for mruby
+/**
+** @file mruby/gc.h - garbage collector for mruby
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/hash.h b/include/mruby/hash.h
index 7e2ed5596..734a8c3b5 100644
--- a/include/mruby/hash.h
+++ b/include/mruby/hash.h
@@ -1,5 +1,5 @@
-/*
-** mruby/hash.h - Hash class
+/**
+** @file mruby/hash.h - Hash class
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/irep.h b/include/mruby/irep.h
index d42fd0fb8..4393129c7 100644
--- a/include/mruby/irep.h
+++ b/include/mruby/irep.h
@@ -1,5 +1,5 @@
-/*
-** mruby/irep.h - mrb_irep structure
+/**
+** @file mruby/irep.h - mrb_irep structure
**
** See Copyright Notice in mruby.h
*/
@@ -32,7 +32,7 @@ typedef struct mrb_irep {
uint16_t nregs; /* Number of register variables */
uint8_t flags;
- mrb_code *iseq;
+ const mrb_code *iseq;
mrb_value *pool;
mrb_sym *syms;
struct mrb_irep **reps;
@@ -80,7 +80,7 @@ struct mrb_insn_data {
uint8_t c;
};
-struct mrb_insn_data mrb_decode_insn(mrb_code *pc);
+struct mrb_insn_data mrb_decode_insn(const mrb_code *pc);
MRB_END_DECL
diff --git a/include/mruby/istruct.h b/include/mruby/istruct.h
index 23c9bfa36..45b1fadae 100644
--- a/include/mruby/istruct.h
+++ b/include/mruby/istruct.h
@@ -1,5 +1,5 @@
-/*
-** mruby/istruct.h - Inline structures
+/**
+** @file mruby/istruct.h - Inline structures
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/khash.h b/include/mruby/khash.h
index 9c40c6b80..c00357061 100644
--- a/include/mruby/khash.h
+++ b/include/mruby/khash.h
@@ -1,5 +1,5 @@
-/*
-** mruby/khash.c - Hash for mruby
+/**
+** @file mruby/khash.h - Hash for mruby
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h
index 34707e441..a176d96cd 100644
--- a/include/mruby/numeric.h
+++ b/include/mruby/numeric.h
@@ -1,5 +1,5 @@
-/*
-** mruby/numeric.h - Numeric, Integer, Float, Fixnum class
+/**
+** @file mruby/numeric.h - Numeric, Integer, Float, Fixnum class
**
** See Copyright Notice in mruby.h
*/
@@ -23,8 +23,12 @@ MRB_BEGIN_DECL
#define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int)
#define FIXABLE(f) TYPED_FIXABLE(f,mrb_int)
#ifndef MRB_WITHOUT_FLOAT
+#ifdef MRB_INT64
+#define FIXABLE_FLOAT(f) ((f)>=-9223372036854775808.0 && (f)<9223372036854775808.0)
+#else
#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,mrb_float)
#endif
+#endif
#ifndef MRB_WITHOUT_FLOAT
MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val);
@@ -156,6 +160,36 @@ mrb_int_mul_overflow(mrb_int multiplier, mrb_int multiplicand, mrb_int *product)
#endif
+#ifndef MRB_WITHOUT_FLOAT
+# include <stdint.h>
+# include <float.h>
+
+# define MRB_FLT_RADIX FLT_RADIX
+
+# ifdef MRB_USE_FLOAT
+# define MRB_FLT_MANT_DIG FLT_MANT_DIG
+# define MRB_FLT_EPSILON FLT_EPSILON
+# define MRB_FLT_DIG FLT_DIG
+# define MRB_FLT_MIN_EXP FLT_MIN_EXP
+# define MRB_FLT_MIN FLT_MIN
+# define MRB_FLT_MIN_10_EXP FLT_MIN_10_EXP
+# define MRB_FLT_MAX_EXP FLT_MAX_EXP
+# define MRB_FLT_MAX FLT_MAX
+# define MRB_FLT_MAX_10_EXP FLT_MAX_10_EXP
+
+# else /* not MRB_USE_FLOAT */
+# define MRB_FLT_MANT_DIG DBL_MANT_DIG
+# define MRB_FLT_EPSILON DBL_EPSILON
+# define MRB_FLT_DIG DBL_DIG
+# define MRB_FLT_MIN_EXP DBL_MIN_EXP
+# define MRB_FLT_MIN DBL_MIN
+# define MRB_FLT_MIN_10_EXP DBL_MIN_10_EXP
+# define MRB_FLT_MAX_EXP DBL_MAX_EXP
+# define MRB_FLT_MAX DBL_MAX
+# define MRB_FLT_MAX_10_EXP DBL_MAX_10_EXP
+# endif /* MRB_USE_FLOAT */
+#endif /* MRB_WITHOUT_FLOAT */
+
MRB_END_DECL
#endif /* MRUBY_NUMERIC_H */
diff --git a/include/mruby/object.h b/include/mruby/object.h
index 373e3bec7..53511a1bb 100644
--- a/include/mruby/object.h
+++ b/include/mruby/object.h
@@ -1,5 +1,5 @@
-/*
-** mruby/object.h - mruby object definition
+/**
+** @file mruby/object.h - mruby object definition
**
** See Copyright Notice in mruby.h
*/
@@ -8,15 +8,14 @@
#define MRUBY_OBJECT_H
#define MRB_OBJECT_HEADER \
- enum mrb_vtype tt:8;\
- uint32_t color:3;\
- uint32_t flags:21;\
- struct RClass *c;\
- struct RBasic *gcnext
+ struct RClass *c; \
+ struct RBasic *gcnext; \
+ enum mrb_vtype tt:8; \
+ uint32_t color:3; \
+ uint32_t flags:21
#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & (flag))
-
struct RBasic {
MRB_OBJECT_HEADER;
};
@@ -33,7 +32,6 @@ struct RObject {
};
#define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v)))
-#define mrb_immediate_p(x) (mrb_type(x) < MRB_TT_HAS_BASIC)
#define mrb_special_const_p(x) mrb_immediate_p(x)
struct RFiber {
diff --git a/include/mruby/opcode.h b/include/mruby/opcode.h
index d513ca472..95e6736a4 100644
--- a/include/mruby/opcode.h
+++ b/include/mruby/opcode.h
@@ -1,5 +1,5 @@
-/*
-** mruby/opcode.h - RiteVM operation codes
+/**
+** @file mruby/opcode.h - RiteVM operation codes
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/proc.h b/include/mruby/proc.h
index a8b16db1d..d6459f8bf 100644
--- a/include/mruby/proc.h
+++ b/include/mruby/proc.h
@@ -1,5 +1,5 @@
-/*
-** mruby/proc.h - Proc class
+/**
+** @file mruby/proc.h - Proc class
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/range.h b/include/mruby/range.h
index ee6eb8d1d..fea700c24 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -1,5 +1,5 @@
-/*
-** mruby/range.h - Range class
+/**
+** @file mruby/range.h - Range class
**
** See Copyright Notice in mruby.h
*/
@@ -14,7 +14,7 @@
*/
MRB_BEGIN_DECL
-#if defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING)
+#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT) || defined(MRB_WORD_BOXING)
# define MRB_RANGE_EMBED
#endif
diff --git a/include/mruby/re.h b/include/mruby/re.h
index 1d09d06c9..2d48019cf 100644
--- a/include/mruby/re.h
+++ b/include/mruby/re.h
@@ -1,5 +1,5 @@
-/*
-** mruby/re.h - Regexp class
+/**
+** @file mruby/re.h - Regexp class
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/string.h b/include/mruby/string.h
index 22445f654..fd078b9f3 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -1,5 +1,5 @@
-/*
-** mruby/string.h - String class
+/**
+** @file mruby/string.h - String class
**
** See Copyright Notice in mruby.h
*/
@@ -16,7 +16,8 @@ MRB_BEGIN_DECL
extern const char mrb_digitmap[];
-#define RSTRING_EMBED_LEN_MAX ((mrb_int)(sizeof(void*) * 3 - 1))
+#define RSTRING_EMBED_LEN_MAX \
+ ((mrb_int)(sizeof(void*) * 3 + sizeof(void*) - 32 / CHAR_BIT - 1))
struct RString {
MRB_OBJECT_HEADER;
@@ -30,9 +31,15 @@ struct RString {
} aux;
char *ptr;
} heap;
- char ary[RSTRING_EMBED_LEN_MAX + 1];
} as;
};
+struct RStringEmbed {
+ MRB_OBJECT_HEADER;
+ char ary[];
+};
+
+#define RSTR_SET_TYPE_FLAG(s, type) (RSTR_UNSET_TYPE_FLAG(s), (s)->flags |= MRB_STR_##type)
+#define RSTR_UNSET_TYPE_FLAG(s) ((s)->flags &= ~(MRB_STR_TYPE_MASK|MRB_STR_EMBED_LEN_MASK))
#define RSTR_EMBED_P(s) ((s)->flags & MRB_STR_EMBED)
#define RSTR_SET_EMBED_FLAG(s) ((s)->flags |= MRB_STR_EMBED)
@@ -50,9 +57,12 @@ struct RString {
(s)->as.heap.len = (mrb_int)(n);\
}\
} while (0)
+#define RSTR_EMBED_PTR(s) (((struct RStringEmbed*)(s))->ary)
#define RSTR_EMBED_LEN(s)\
(mrb_int)(((s)->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT)
-#define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? (s)->as.ary : (s)->as.heap.ptr)
+#define RSTR_EMBEDDABLE_P(len) ((len) <= RSTRING_EMBED_LEN_MAX)
+
+#define RSTR_PTR(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_PTR(s) : (s)->as.heap.ptr)
#define RSTR_LEN(s) ((RSTR_EMBED_P(s)) ? RSTR_EMBED_LEN(s) : (s)->as.heap.len)
#define RSTR_CAPA(s) (RSTR_EMBED_P(s) ? RSTRING_EMBED_LEN_MAX : (s)->as.heap.aux.capa)
@@ -68,10 +78,24 @@ struct RString {
#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
+#ifdef MRB_UTF8_STRING
+# define RSTR_ASCII_P(s) ((s)->flags & MRB_STR_ASCII)
+# define RSTR_SET_ASCII_FLAG(s) ((s)->flags |= MRB_STR_ASCII)
+# define RSTR_UNSET_ASCII_FLAG(s) ((s)->flags &= ~MRB_STR_ASCII)
+# define RSTR_WRITE_ASCII_FLAG(s, v) (RSTR_UNSET_ASCII_FLAG(s), (s)->flags |= v)
+# define RSTR_COPY_ASCII_FLAG(dst, src) RSTR_WRITE_ASCII_FLAG(dst, RSTR_ASCII_P(src))
+#else
+# define RSTR_ASCII_P(s) (void)0
+# define RSTR_SET_ASCII_FLAG(s) (void)0
+# define RSTR_UNSET_ASCII_FLAG(s) (void)0
+# define RSTR_WRITE_ASCII_FLAG(s, v) (void)0
+# define RSTR_COPY_ASCII_FLAG(dst, src) (void)0
+#endif
+
#define RSTR_POOL_P(s) ((s)->flags & MRB_STR_POOL)
#define RSTR_SET_POOL_FLAG(s) ((s)->flags |= MRB_STR_POOL)
-/*
+/**
* Returns a pointer from a Ruby string
*/
#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s)))
@@ -82,32 +106,38 @@ struct RString {
#define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s))
#define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s))
MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
+#define RSTRING_CSTR(mrb,s) mrb_string_cstr(mrb, s)
#define MRB_STR_SHARED 1
#define MRB_STR_FSHARED 2
#define MRB_STR_NOFREE 4
-#define MRB_STR_POOL 8
-#define MRB_STR_NO_UTF 16
-#define MRB_STR_EMBED 32
-#define MRB_STR_EMBED_LEN_MASK 0x7c0
+#define MRB_STR_EMBED 8 /* type flags up to here */
+#define MRB_STR_POOL 16 /* status flags from here */
+#define MRB_STR_ASCII 32
#define MRB_STR_EMBED_LEN_SHIFT 6
+#define MRB_STR_EMBED_LEN_BITSIZE 5
+#define MRB_STR_EMBED_LEN_MASK (((1 << MRB_STR_EMBED_LEN_BITSIZE) - 1) << MRB_STR_EMBED_LEN_SHIFT)
+#define MRB_STR_TYPE_MASK (MRB_STR_POOL - 1)
+
void mrb_gc_free_str(mrb_state*, struct RString*);
-MRB_API void mrb_str_modify(mrb_state*, struct RString*);
-/*
+MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s);
+/* mrb_str_modify() with keeping ASCII flag if set */
+MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s);
+
+/**
* Finds the index of a substring in a string
*/
MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_int);
#define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off);
-/*
+/**
* Appends self to other. Returns self as a concatenated string.
*
*
- * Example:
+ * Example:
*
- * !!!c
* int
* main(int argc,
* char **argv)
@@ -129,32 +159,30 @@ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_i
* // Concatenates str2 to str1.
* mrb_str_concat(mrb, str1, str2);
*
- * // Prints new Concatenated Ruby string.
- * mrb_p(mrb, str1);
- *
- * mrb_close(mrb);
- * return 0;
- * }
+ * // Prints new Concatenated Ruby string.
+ * mrb_p(mrb, str1);
*
+ * mrb_close(mrb);
+ * return 0;
+ * }
*
- * Result:
+ * Result:
*
* => "abcdef"
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] self String to concatenate.
- * @param [mrb_value] other String to append to self.
+ * @param mrb The current mruby state.
+ * @param self String to concatenate.
+ * @param other String to append to self.
* @return [mrb_value] Returns a new String appending other to self.
*/
-MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
+MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other);
-/*
+/**
* Adds two strings together.
*
*
- * Example:
+ * Example:
*
- * !!!c
* int
* main(int argc,
* char **argv)
@@ -181,52 +209,51 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
* // Concatenates both Ruby strings.
* c = mrb_str_plus(mrb, a, b);
*
- * // Prints new Concatenated Ruby string.
- * mrb_p(mrb, c);
+ * // Prints new Concatenated Ruby string.
+ * mrb_p(mrb, c);
*
- * mrb_close(mrb);
- * return 0;
- * }
+ * mrb_close(mrb);
+ * return 0;
+ * }
*
*
- * Result:
+ * Result:
*
* => "abc" # First string
* => "def" # Second string
* => "abcdef" # First & Second concatenated.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] a First string to concatenate.
- * @param [mrb_value] b Second string to concatenate.
+ * @param mrb The current mruby state.
+ * @param a First string to concatenate.
+ * @param b Second string to concatenate.
* @return [mrb_value] Returns a new String containing a concatenated to b.
*/
-MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value);
+MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b);
-/*
+/**
* Converts pointer into a Ruby string.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [void*] p The pointer to convert to Ruby string.
+ * @param mrb The current mruby state.
+ * @param p The pointer to convert to Ruby string.
* @return [mrb_value] Returns a new Ruby String.
*/
-MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*);
+MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p);
-/*
+/**
* Returns an object as a Ruby string.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] obj An object to return as a Ruby string.
+ * @param mrb The current mruby state.
+ * @param obj An object to return as a Ruby string.
* @return [mrb_value] An object as a Ruby string.
*/
MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
-/*
+/**
* Resizes the string's length. Returns the amount of characters
* in the specified by len.
*
* Example:
*
- * !!!c
* int
* main(int argc,
* char **argv)
@@ -251,21 +278,20 @@ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
*
* Result:
*
- * => "Hello"
+ * => "Hello"
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str The Ruby string to resize.
- * @param [mrb_value] len The length.
+ * @param mrb The current mruby state.
+ * @param str The Ruby string to resize.
+ * @param len The length.
* @return [mrb_value] An object as a Ruby string.
*/
MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
-/*
+/**
* Returns a sub string.
*
- * Example:
+ * Example:
*
- * !!!c
* int
* main(int argc,
* char const **argv)
@@ -291,24 +317,24 @@ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
* return 0;
* }
*
- * Result:
+ * Result:
*
* => "He"
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str Ruby string.
- * @param [mrb_int] beg The beginning point of the sub-string.
- * @param [mrb_int] len The end point of the sub-string.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
+ * @param beg The beginning point of the sub-string.
+ * @param len The end point of the sub-string.
* @return [mrb_value] An object as a Ruby sub-string.
*/
MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
-/*
+/**
* Returns a Ruby string type.
*
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str Ruby string.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
* @return [mrb_value] A Ruby string.
*/
MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str);
@@ -320,33 +346,30 @@ MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str);
MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, size_t capa);
MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
-MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
+/* NULL terminated C string from mrb_value */
+MRB_API const char *mrb_string_cstr(mrb_state *mrb, mrb_value str);
+/* NULL terminated C string from mrb_value; `str` will be updated */
+MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *str);
+/* obslete: use RSTRING_PTR() */
MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value str);
-/*
- * Returns the length of the Ruby string.
- *
- *
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str Ruby string.
- * @return [mrb_int] The length of the passed in Ruby string.
- */
+/* obslete: use RSTRING_LEN() */
MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value str);
-/*
+/**
* Duplicates a string object.
*
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str Ruby string.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
* @return [mrb_value] Duplicated Ruby string.
*/
MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
-/*
+/**
* Returns a symbol from a passed in Ruby string.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] self Ruby string.
+ * @param mrb The current mruby state.
+ * @param self Ruby string.
* @return [mrb_value] A symbol.
*/
MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self);
@@ -356,40 +379,40 @@ MRB_API mrb_value mrb_cstr_to_inum(mrb_state *mrb, const char *s, mrb_int base,
MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck);
MRB_API double mrb_cstr_to_dbl(mrb_state *mrb, const char *s, mrb_bool badcheck);
-/*
+/**
* Returns a converted string type.
* For type checking, non converting `mrb_to_str` is recommended.
*/
MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
-/*
+/**
* Returns true if the strings match and false if the strings don't match.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str1 Ruby string to compare.
- * @param [mrb_value] str2 Ruby string to compare.
+ * @param mrb The current mruby state.
+ * @param str1 Ruby string to compare.
+ * @param str2 Ruby string to compare.
* @return [mrb_value] boolean value.
*/
MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2);
-/*
- * Returns a concated string comprised of a Ruby string and a C string.
+/**
+ * Returns a concatenated string comprised of a Ruby string and a C string.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str Ruby string.
- * @param [const char *] ptr A C string.
- * @param [size_t] len length of C string.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
+ * @param ptr A C string.
+ * @param len length of C string.
* @return [mrb_value] A Ruby string.
* @see mrb_str_cat_cstr
*/
MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len);
-/*
- * Returns a concated string comprised of a Ruby string and a C string.
+/**
+ * Returns a concatenated string comprised of a Ruby string and a C string.
*
- * @param [mrb_state] mrb The current mruby state.
- * @param [mrb_value] str Ruby string.
- * @param [const char *] ptr A C string.
+ * @param mrb The current mruby state.
+ * @param str Ruby string.
+ * @param ptr A C string.
* @return [mrb_value] A Ruby string.
* @see mrb_str_cat
*/
@@ -397,17 +420,17 @@ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *pt
MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2);
#define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit))
-/*
+/**
* Adds str2 to the end of str1.
*/
MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2);
-/*
+/**
* Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1.
*/
MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
-/*
+/**
* Returns a newly allocated C string from a Ruby string.
* This is an utility function to pass a Ruby string to C library functions.
*
@@ -418,8 +441,8 @@ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
* - Caller can modify returned string without affecting Ruby string
* (e.g. it can be used for mkstemp(3)).
*
- * @param [mrb_state *] mrb The current mruby state.
- * @param [mrb_value] str Ruby string. Must be an instance of String.
+ * @param mrb The current mruby state.
+ * @param str Ruby string. Must be an instance of String.
* @return [char *] A newly allocated C string.
*/
MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str);
@@ -428,7 +451,7 @@ mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str);
uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str);
mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str);
-/*
+/**
* Returns a printable version of str, surrounded by quote marks, with special characters escaped.
*/
mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
@@ -438,6 +461,9 @@ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
#define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len)
#define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2)
+mrb_bool mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp);
+mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
+
#ifdef MRB_UTF8_STRING
mrb_int mrb_utf8_len(const char *str, mrb_int byte_len);
#endif
diff --git a/include/mruby/throw.h b/include/mruby/throw.h
index 4a1fd8d60..1f1298d7d 100644
--- a/include/mruby/throw.h
+++ b/include/mruby/throw.h
@@ -1,5 +1,5 @@
-/*
-** mruby/throw.h - mruby exception throwing handler
+/**
+** @file mruby/throw.h - mruby exception throwing handler
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/value.h b/include/mruby/value.h
index be3dd397f..52ce93d58 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -1,5 +1,5 @@
-/*
-** mruby/value.h - mruby value definitions
+/**
+** @file mruby/value.h - mruby value definitions
**
** See Copyright Notice in mruby.h
*/
@@ -9,12 +9,28 @@
#include "common.h"
-/**
+/*
* MRuby Value definition functions and macros.
*/
MRB_BEGIN_DECL
+/**
+ * mruby Symbol.
+ * @class mrb_sym
+ *
+ * You can create an mrb_sym by simply using mrb_str_intern() or mrb_intern_cstr()
+ */
typedef uint32_t mrb_sym;
+
+/**
+ * mruby Boolean.
+ * @class mrb_bool
+ *
+ *
+ * Used internally to represent boolean. Can be TRUE or FALSE.
+ * Not to be confused with Ruby's boolean classes, which can be
+ * obtained using mrb_false_value() and mrb_true_value()
+ */
typedef uint8_t mrb_bool;
struct mrb_state;
@@ -62,6 +78,11 @@ struct mrb_state;
# define MRB_PRIx PRIx32
#endif
+#ifdef MRB_ENDIAN_BIG
+# define MRB_ENDIAN_LOHI(a,b) a b
+#else
+# define MRB_ENDIAN_LOHI(a,b) b a
+#endif
#ifndef MRB_WITHOUT_FLOAT
MRB_API double mrb_float_read(const char*, char**);
@@ -92,13 +113,13 @@ static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
enum mrb_vtype {
MRB_TT_FALSE = 0, /* 0 */
- MRB_TT_FREE, /* 1 */
- MRB_TT_TRUE, /* 2 */
+ MRB_TT_TRUE, /* 1 */
+ MRB_TT_FLOAT, /* 2 */
MRB_TT_FIXNUM, /* 3 */
MRB_TT_SYMBOL, /* 4 */
MRB_TT_UNDEF, /* 5 */
- MRB_TT_FLOAT, /* 6 */
- MRB_TT_CPTR, /* 7 */
+ MRB_TT_CPTR, /* 6 */
+ MRB_TT_FREE, /* 7 */
MRB_TT_OBJECT, /* 8 */
MRB_TT_CLASS, /* 9 */
MRB_TT_MODULE, /* 10 */
@@ -145,9 +166,17 @@ typedef void mrb_value;
#include "boxing_no.h"
#endif
+#define MRB_TT_HAS_BASIC MRB_TT_FREE
+
+#ifndef mrb_immediate_p
+#define mrb_immediate_p(o) (mrb_type(o) < MRB_TT_HAS_BASIC)
+#endif
#ifndef mrb_fixnum_p
#define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM)
#endif
+#ifndef mrb_symbol_p
+#define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
+#endif
#ifndef mrb_undef_p
#define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF)
#endif
@@ -170,7 +199,6 @@ typedef void mrb_value;
#ifndef MRB_WITHOUT_FLOAT
#define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
#endif
-#define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
#define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY)
#define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING)
#define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH)
@@ -178,8 +206,10 @@ typedef void mrb_value;
#define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION)
#define mrb_test(o) mrb_bool(o)
-/*
+/**
* Returns a float in Ruby.
+ *
+ * Takes a float and boxes it into an mrb_value
*/
#ifndef MRB_WITHOUT_FLOAT
MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
@@ -200,8 +230,10 @@ mrb_cptr_value(struct mrb_state *mrb, void *p)
return v;
}
-/*
+/**
* Returns a fixnum in Ruby.
+ *
+ * Takes an integer and boxes it into an mrb_value
*/
MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i)
{
@@ -228,8 +260,7 @@ mrb_obj_value(void *p)
return v;
}
-
-/*
+/**
* Get a nil mrb_value object.
*
* @return
@@ -242,7 +273,7 @@ MRB_INLINE mrb_value mrb_nil_value(void)
return v;
}
-/*
+/**
* Returns false in Ruby.
*/
MRB_INLINE mrb_value mrb_false_value(void)
@@ -252,7 +283,7 @@ MRB_INLINE mrb_value mrb_false_value(void)
return v;
}
-/*
+/**
* Returns true in Ruby.
*/
MRB_INLINE mrb_value mrb_true_value(void)
diff --git a/include/mruby/variable.h b/include/mruby/variable.h
index ff01e5cc8..6e918cf57 100644
--- a/include/mruby/variable.h
+++ b/include/mruby/variable.h
@@ -1,5 +1,5 @@
-/*
-** mruby/variable.h - mruby variables
+/**
+** @file mruby/variable.h - mruby variables
**
** See Copyright Notice in mruby.h
*/
@@ -97,18 +97,15 @@ MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
*
* Example:
*
- * !!!ruby
* # Ruby style
* $value = nil
*
- * !!!c
* // C style
* mrb_sym sym = mrb_intern_lit(mrb, "$value");
* mrb_gv_remove(mrb, sym);
*
* @param mrb The mruby state reference
* @param sym The name of the global variable
- * @param val The value of the global variable
*/
MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym);
diff --git a/include/mruby/version.h b/include/mruby/version.h
index 206078df0..3140ea481 100644
--- a/include/mruby/version.h
+++ b/include/mruby/version.h
@@ -1,5 +1,5 @@
-/*
-** mruby/version.h - mruby version definition
+/**
+** @file mruby/version.h - mruby version definition
**
** See Copyright Notice in mruby.h
*/
diff --git a/lib/mruby-core-ext.rb b/lib/mruby-core-ext.rb
index 08e6f6148..7b78bfa91 100644
--- a/lib/mruby-core-ext.rb
+++ b/lib/mruby-core-ext.rb
@@ -1,3 +1,5 @@
+autoload :Pathname, 'pathname'
+
class Object
class << self
def attr_block(*syms)
@@ -16,45 +18,6 @@ class String
def relative_path
relative_path_from(Dir.pwd)
end
-
- # Compatible with 1.9 on 1.8
- unless (sprintf("%{a}", :a => 1) rescue false)
- def %(params)
- if params.is_a?(Hash)
- str = self.clone
- params.each do |k, v|
- str.gsub!("%{#{k}}") { v }
- end
- str
- else
- if params.is_a?(Array)
- sprintf(self, *params)
- else
- sprintf(self, params)
- end
- end
- end
- end
-end
-
-class Symbol
- # Compatible with 1.9 on 1.8
- unless method_defined?(:to_proc)
- def to_proc
- proc { |obj, *args| obj.send(self, *args) }
- end
- end
-end
-
-module Enumerable
- # Compatible with 1.9 on 1.8
- unless method_defined?(:each_with_object)
- def each_with_object(memo)
- return to_enum :each_with_object, memo unless block_given?
- each { |obj| yield obj, memo }
- memo
- end
- end
end
$pp_show = true
diff --git a/lib/mruby/build.rb b/lib/mruby/build.rb
index 887a5518e..dfce42bea 100644
--- a/lib/mruby/build.rb
+++ b/lib/mruby/build.rb
@@ -1,7 +1,11 @@
+require "mruby-core-ext"
require "mruby/build/load_gems"
require "mruby/build/command"
module MRuby
+ autoload :Gem, "mruby/gem"
+ autoload :Lockfile, "mruby/lockfile"
+
class << self
def targets
@targets ||= {}
@@ -22,7 +26,6 @@ module MRuby
def initialize(name, &block)
@name, @initializer = name.to_s, block
- MRuby::Toolchain.toolchains ||= {}
MRuby::Toolchain.toolchains[@name] = self
end
@@ -30,13 +33,8 @@ module MRuby
conf.instance_exec(conf, params, &@initializer)
end
- def self.load
- Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file|
- Kernel.load file
- end
- end
+ self.toolchains = {}
end
- Toolchain.load
class Build
class << self
@@ -70,7 +68,7 @@ module MRuby
@file_separator = '/'
@build_dir = "#{build_dir}/#{@name}"
- @gem_clone_dir = "#{build_dir}/mrbgems"
+ @gem_clone_dir = "#{build_dir}/repos/#{@name}"
@cc = Command::Compiler.new(self, %w(.c))
@cxx = Command::Compiler.new(self, %w(.cc .cxx .cpp))
@objc = Command::Compiler.new(self, %w(.m))
@@ -90,6 +88,7 @@ module MRuby
@cxx_abi_enabled = false
@enable_bintest = false
@enable_test = false
+ @enable_lock = true
@toolchains = []
MRuby.targets[@name] = self
@@ -118,6 +117,14 @@ module MRuby
@enable_debug = true
end
+ def disable_lock
+ @enable_lock = false
+ end
+
+ def lock_enabled?
+ Lockfile.enabled? && @enable_lock
+ end
+
def disable_cxx_exception
if @cxx_exception_enabled or @cxx_abi_enabled
raise "cxx_exception already enabled"
@@ -196,10 +203,15 @@ EOS
end
def toolchain(name, params={})
- tc = Toolchain.toolchains[name.to_s]
- fail "Unknown #{name} toolchain" unless tc
+ name = name.to_s
+ tc = Toolchain.toolchains[name] || begin
+ path = "#{MRUBY_ROOT}/tasks/toolchains/#{name}.rake"
+ fail "Unknown #{name} toolchain" unless File.exist?(path)
+ load path
+ Toolchain.toolchains[name]
+ end
tc.setup(self, params)
- @toolchains.unshift name.to_s
+ @toolchains.unshift name
end
def primary_toolchain
@@ -226,6 +238,10 @@ EOS
gem :core => 'mruby-bin-mrbc'
end
+ def locks
+ Lockfile.build(@name)
+ end
+
def mrbcfile
return @mrbcfile if @mrbcfile
diff --git a/lib/mruby/build/command.rb b/lib/mruby/build/command.rb
index 0f18e0e62..a98f2ca7e 100644
--- a/lib/mruby/build/command.rb
+++ b/lib/mruby/build/command.rb
@@ -127,13 +127,40 @@ module MRuby
end
private
+
+ #
+ # === Example of +.d+ file
+ #
+ # ==== Without <tt>-MP</tt> compiler flag
+ #
+ # /build/host/src/array.o: \
+ # /src/array.c \
+ # /include/mruby/common.h \
+ # /include/mruby/value.h \
+ # /src/value_array.h
+ #
+ # ==== With <tt>-MP</tt> compiler flag
+ #
+ # /build/host/src/array.o: \
+ # /src/array.c \
+ # /include/mruby/common.h \
+ # /include/mruby/value.h \
+ # /src/value_array.h
+ #
+ # /include/mruby/common.h:
+ #
+ # /include/mruby/value.h:
+ #
+ # /src/value_array.h:
+ #
def get_dependencies(file)
file = file.ext('d') unless File.extname(file) == '.d'
+ deps = []
if File.exist?(file)
- File.read(file).gsub("\\\n ", "").scan(/^\S+:\s+(.+)$/).flatten.map {|s| s.split(' ') }.flatten
- else
- []
- end + [ MRUBY_CONFIG ]
+ File.foreach(file){|line| deps << $1 if /^ +(.*?)(?: *\\)?$/ =~ line}
+ deps.uniq!
+ end
+ deps << MRUBY_CONFIG
end
end
@@ -243,15 +270,16 @@ module MRuby
class Command::Git < Command
attr_accessor :flags
- attr_accessor :clone_options, :pull_options, :checkout_options
+ attr_accessor :clone_options, :pull_options, :checkout_options, :reset_options
def initialize(build)
super
@command = 'git'
@flags = %w[]
@clone_options = "clone %{flags} %{url} %{dir}"
- @pull_options = "pull"
- @checkout_options = "checkout %{checksum_hash}"
+ @pull_options = "--git-dir '%{repo_dir}/.git' --work-tree '%{repo_dir}' pull"
+ @checkout_options = "--git-dir '%{repo_dir}/.git' --work-tree '%{repo_dir}' checkout %{checksum_hash}"
+ @reset_options = "--git-dir '%{repo_dir}/.git' --work-tree '%{repo_dir}' reset %{checksum_hash}"
end
def run_clone(dir, url, _flags = [])
@@ -260,19 +288,26 @@ module MRuby
end
def run_pull(dir, url)
- root = Dir.pwd
- Dir.chdir dir
_pp "GIT PULL", url, dir.relative_path
- _run pull_options
- Dir.chdir root
+ _run pull_options, { :repo_dir => dir }
end
def run_checkout(dir, checksum_hash)
- root = Dir.pwd
- Dir.chdir dir
_pp "GIT CHECKOUT", checksum_hash
- _run checkout_options, { :checksum_hash => checksum_hash }
- Dir.chdir root
+ _run checkout_options, { :checksum_hash => checksum_hash, :repo_dir => dir }
+ end
+
+ def run_reset_hard(dir, checksum_hash)
+ _pp "GIT RESET", checksum_hash
+ _run reset_options, { :checksum_hash => checksum_hash, :repo_dir => dir }
+ end
+
+ def commit_hash(dir)
+ `#{@command} --git-dir '#{dir}/.git' --work-tree '#{dir}' rev-parse --verify HEAD`.strip
+ end
+
+ def current_branch(dir)
+ `#{@command} --git-dir '#{dir}/.git' --work-tree '#{dir}' rev-parse --abbrev-ref HEAD`.strip
end
end
diff --git a/lib/mruby/build/load_gems.rb b/lib/mruby/build/load_gems.rb
index 723be6ffc..7f2c7202b 100644
--- a/lib/mruby/build/load_gems.rb
+++ b/lib/mruby/build/load_gems.rb
@@ -83,11 +83,18 @@ module MRuby
# by default the 'master' branch is used
branch = params[:branch] ? params[:branch] : 'master'
+ lock = locks[url] if lock_enabled?
+
if File.exist?(gemdir)
if $pull_gems
git.run_pull gemdir, url
- else
- gemdir
+ # Jump to the top of the branch
+ git.run_checkout(gemdir, branch)
+ git.run_reset_hard gemdir, "origin/#{branch}"
+ elsif params[:checksum_hash]
+ git.run_reset_hard(gemdir, params[:checksum_hash])
+ elsif lock
+ git.run_reset_hard(gemdir, lock['commit'])
end
else
options = [params[:options]] || []
@@ -96,14 +103,21 @@ module MRuby
options << "--depth 1" unless params[:checksum_hash]
FileUtils.mkdir_p "#{gem_clone_dir}"
git.run_clone gemdir, url, options
- end
- if params[:checksum_hash]
# Jump to the specified commit
- git.run_checkout gemdir, params[:checksum_hash]
- else
- # Jump to the top of the branch
- git.run_checkout gemdir, branch if $pull_gems
+ if params[:checksum_hash]
+ git.run_reset_hard gemdir, params[:checksum_hash]
+ elsif lock
+ git.run_reset_hard gemdir, lock['commit']
+ end
+ end
+
+ if lock_enabled?
+ locks[url] = {
+ 'url' => url,
+ 'branch' => git.current_branch(gemdir),
+ 'commit' => git.commit_hash(gemdir),
+ }
end
gemdir << "/#{params[:path]}" if params[:path]
diff --git a/lib/mruby/gem.rb b/lib/mruby/gem.rb
index 95c1d4bc3..6cb067b91 100644
--- a/lib/mruby/gem.rb
+++ b/lib/mruby/gem.rb
@@ -1,7 +1,6 @@
-require 'pathname'
require 'forwardable'
-require 'tsort'
-require 'shellwords'
+autoload :TSort, 'tsort'
+autoload :Shellwords, 'shellwords'
module MRuby
module Gem
diff --git a/lib/mruby/lockfile.rb b/lib/mruby/lockfile.rb
new file mode 100644
index 000000000..ce1442a89
--- /dev/null
+++ b/lib/mruby/lockfile.rb
@@ -0,0 +1,73 @@
+autoload :YAML, 'yaml'
+
+module MRuby
+ autoload :Source, 'mruby/source'
+
+ class Lockfile
+ class << self
+ def enable
+ @enabled = true
+ end
+
+ def disable
+ @enabled = false
+ end
+
+ def enabled?
+ @enabled
+ end
+
+ def build(target_name)
+ instance.build(target_name)
+ end
+
+ def write
+ instance.write if enabled?
+ end
+
+ def instance
+ @instance ||= new("#{MRUBY_CONFIG}.lock")
+ end
+ end
+
+ def initialize(filename)
+ @filename = filename
+ end
+
+ def build(target_name)
+ read[target_name] ||= {}
+ end
+
+ def write
+ locks = {"mruby_version" => mruby}
+ locks["builds"] = @builds if @builds
+ File.write(@filename, YAML.dump(locks))
+ end
+
+ private
+
+ def read
+ @builds ||= if File.exist?(@filename)
+ YAML.load_file(@filename)["builds"] || {}
+ else
+ {}
+ end
+ end
+
+ def mruby
+ mruby = {
+ 'version' => MRuby::Source::MRUBY_VERSION,
+ 'release_no' => MRuby::Source::MRUBY_RELEASE_NO,
+ }
+
+ git_dir = "#{MRUBY_ROOT}/.git"
+ if File.directory?(git_dir)
+ mruby['git_commit'] = `git --git-dir '#{git_dir}' --work-tree '#{MRUBY_ROOT}' rev-parse --verify HEAD`.strip
+ end
+
+ mruby
+ end
+
+ enable
+ end
+end
diff --git a/minirake b/minirake
index cd2d85854..8df395e97 100755
--- a/minirake
+++ b/minirake
@@ -206,25 +206,41 @@ module MiniRake
# task with the prerequisites and actions from the rule. Set the
# source attribute of the task appropriately for the rule. Return
# the enhanced task or nil of no rule was found.
- def enhance_with_matching_rule(task_name)
+ def enhance_with_matching_rule(task_name, level=0)
+ fail "Rule Recursion Too Deep: #{task_name}" if level >= 16
RULES.each do |pattern, extensions, block|
- if pattern.match(task_name)
- ext = extensions.first
- deps = extensions[1..-1]
+ next unless pattern && pattern.match(task_name)
+ sources = extensions.flat_map do |ext|
case ext
+ when /%/
+ task_name.pathmap(ext)
+ when %r{/}
+ ext
+ when /^\./
+ source = task_name.sub(pattern, ext)
+ source == ext ? task_name.ext(ext) : source
when String
- source = task_name.sub(/\.[^.]*$/, ext)
- when Proc
- source = ext.call(task_name)
+ ext
+ when Proc, Method
+ ext.arity == 1 ? ext.call(task_name) : ext.call
else
fail "Don't know how to handle rule dependent: #{ext.inspect}"
end
- if File.exist?(source)
- task = FileTask.define_task({task_name => [source]+deps}, &block)
- task.source = source
- return task
+ end
+ prereqs = sources.map do |source|
+ if File.exist?(source) || TASKS[source]
+ source
+ elsif parent = enhance_with_matching_rule(source, level + 1)
+ parent.name
+ else
+ break nil
end
end
+ if prereqs
+ task = FileTask.define_task(task_name => prereqs, &block)
+ task.source = prereqs.first
+ return task
+ end
end
nil
end
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 23e65fcee..9859c7d52 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -86,6 +86,9 @@ MRuby::GemBox.new do |conf|
# Use class/module extension
conf.gem :core => "mruby-class-ext"
+ # Use Method/UnboundMethod class
+ conf.gem :core => "mruby-method"
+
# Use mruby-compiler to build other mrbgems
conf.gem :core => "mruby-compiler"
end
diff --git a/mrbgems/mruby-array-ext/mrbgem.rake b/mrbgems/mruby-array-ext/mrbgem.rake
index 58d4428d4..882caf1ab 100644
--- a/mrbgems/mruby-array-ext/mrbgem.rake
+++ b/mrbgems/mruby-array-ext/mrbgem.rake
@@ -2,5 +2,4 @@ MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Array class extension'
- spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
end
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index 59b6087d2..fc5d87f2c 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -815,12 +815,11 @@ class Array
# a.permutation(0).to_a #=> [[]] # one permutation of length 0
# a.permutation(4).to_a #=> [] # no permutations of length 4
def permutation(n=self.size, &block)
- size = self.size
return to_enum(:permutation, n) unless block
- return if n > size
+ size = self.size
if n == 0
- yield []
- else
+ yield []
+ elsif 0 < n && n <= size
i = 0
while i<size
result = [self[i]]
@@ -835,6 +834,7 @@ class Array
i += 1
end
end
+ self
end
##
@@ -861,9 +861,8 @@ class Array
# a.combination(5).to_a #=> [] # no combinations of length 5
def combination(n, &block)
- size = self.size
return to_enum(:combination, n) unless block
- return if n > size
+ size = self.size
if n == 0
yield []
elsif n == 1
@@ -872,7 +871,7 @@ class Array
yield [self[i]]
i += 1
end
- else
+ elsif n <= size
i = 0
while i<size
result = [self[i]]
@@ -882,6 +881,7 @@ class Array
i += 1
end
end
+ self
end
##
@@ -903,8 +903,8 @@ class Array
column_count = nil
self.each do |row|
raise TypeError unless row.is_a?(Array)
- column_count ||= row.count
- raise IndexError, 'element size differs' unless column_count == row.count
+ column_count ||= row.size
+ raise IndexError, 'element size differs' unless column_count == row.size
end
Array.new(column_count) do |column_index|
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index 20c771a97..cb4798d49 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -194,7 +194,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
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, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY());
+ mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1));
}
void
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index 82f09297a..a4e328b71 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -1,6 +1,23 @@
##
# Array(Ext) Test
+def assert_permutation_combination(exp, receiver, meth, *args)
+ act = []
+ ret = receiver.__send__(meth, *args) { |v| act << v }
+ assert "assert_#{meth}" do
+ assert_equal(exp, act.sort)
+ assert_same(receiver, ret)
+ end
+end
+
+def assert_permutation(exp, receiver, *args)
+ assert_permutation_combination(exp, receiver, :permutation, *args)
+end
+
+def assert_combination(exp, receiver, *args)
+ assert_permutation_combination(exp, receiver, :combination, *args)
+end
+
assert("Array#assoc") do
s1 = [ "colors", "red", "blue", "green" ]
s2 = [ "letters", "a", "b", "c" ]
@@ -369,30 +386,24 @@ end
assert("Array#permutation") do
a = [1, 2, 3]
- assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
- a.permutation.to_a)
- assert_equal([[1],[2],[3]],
- a.permutation(1).to_a)
- assert_equal([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]],
- a.permutation(2).to_a)
- assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
- a.permutation(3).to_a)
- assert_equal([[]], a.permutation(0).to_a)
- assert_equal([], a.permutation(4).to_a)
+ assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a)
+ assert_permutation([[1],[2],[3]], a, 1)
+ assert_permutation([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], a, 2)
+ assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a, 3)
+ assert_permutation([[]], a, 0)
+ assert_permutation([], a, 4)
+ assert_permutation([], a, -1)
end
assert("Array#combination") do
a = [1, 2, 3, 4]
- assert_equal([[1],[2],[3],[4]],
- a.combination(1).to_a)
- assert_equal([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]],
- a.combination(2).to_a)
- assert_equal([[1,2,3],[1,2,4],[1,3,4],[2,3,4]],
- a.combination(3).to_a)
- assert_equal([[1,2,3,4]],
- a.combination(4).to_a)
- assert_equal([[]], a.combination(0).to_a)
- assert_equal([], a.combination(5).to_a)
+ assert_combination([[1],[2],[3],[4]], a, 1)
+ assert_combination([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a, 2)
+ assert_combination([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a, 3)
+ assert_combination([[1,2,3,4]], a, 4)
+ assert_combination([[]], a, 0)
+ assert_combination([], a, 5)
+ assert_combination([], a, -1)
end
assert('Array#transpose') do
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
index 513db4ded..530d824eb 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -428,7 +428,7 @@ mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
}
static mrb_bool
-check_start_pc_for_line(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, uint16_t line)
+check_start_pc_for_line(mrb_state *mrb, mrb_irep *irep, const mrb_code *pc, uint16_t line)
{
if (pc > irep->iseq) {
if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) {
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
index f888d1430..cdd7b6fa6 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
@@ -33,7 +33,7 @@ mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size
mrb_value
mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval)
{
- void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
+ void (*tmp)(struct mrb_state *, struct mrb_irep *, const mrb_code *, mrb_value *);
mrb_value ruby_code;
mrb_value s;
mrb_value v;
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
index 003406172..d2fa4c856 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
@@ -505,7 +505,7 @@ get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb)
}
static int32_t
-check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
+check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, const mrb_code *pc, mrb_value *regs)
{
struct RClass* c;
mrb_sym sym;
@@ -546,7 +546,7 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
}
static void
-mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
+mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, const mrb_code *pc, mrb_value *regs)
{
const char *file;
int32_t line;
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
index 7b14a899f..7c21de317 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
@@ -105,7 +105,7 @@ typedef struct mrb_debug_breakpoint {
typedef struct mrb_debug_context {
struct mrb_irep *root_irep;
struct mrb_irep *irep;
- mrb_code *pc;
+ const mrb_code *pc;
mrb_value *regs;
const char *prvfile;
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
index f17f9c57d..de2f90144 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
@@ -6,6 +6,10 @@
#ifndef MRDBCONF_H
#define MRDBCONF_H
+#ifndef MRB_ENABLE_DEBUG_HOOK
+# error Need 'MRB_ENABLE_DEBUG_HOOK' configuration in your 'build_config.rb'
+#endif
+
/* configuration options: */
/* maximum size for command buffer */
#define MAX_COMMAND_LINE 1024
diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
index 17b2ca16c..45ead75f1 100644
--- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
+++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
@@ -406,6 +406,26 @@ ctrl_c_handler(int signo)
}
#endif
+#ifndef DISABLE_MIRB_UNDERSCORE
+void decl_lv_underscore(mrb_state *mrb, mrbc_context *cxt)
+{
+ struct RProc *proc;
+ struct mrb_parser_state *parser;
+
+ parser = mrb_parse_string(mrb, "_=nil", cxt);
+ if (parser == NULL) {
+ fputs("create parser state error\n", stderr);
+ mrb_close(mrb);
+ exit(EXIT_FAILURE);
+ }
+
+ proc = mrb_generate_code(mrb, parser);
+ mrb_vm_run(mrb, proc, mrb_top_self(mrb), 0);
+
+ mrb_parser_free(parser);
+}
+#endif
+
int
main(int argc, char **argv)
{
@@ -471,6 +491,10 @@ main(int argc, char **argv)
cxt = mrbc_context_new(mrb);
+#ifndef DISABLE_MIRB_UNDERSCORE
+ decl_lv_underscore(mrb, cxt);
+#endif
+
/* Load libraries */
for (i = 0; i < args.libc; i++) {
FILE *lfp = fopen(args.libv[i], "r");
@@ -643,6 +667,9 @@ main(int argc, char **argv)
result = mrb_any_to_s(mrb, result);
}
p(mrb, result, 1);
+#ifndef DISABLE_MIRB_UNDERSCORE
+ *(mrb->c->stack + 1) = result;
+#endif
}
}
ruby_code[0] = '\0';
diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
index 2fd9da77d..4c8c680cb 100644
--- a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
+++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
@@ -71,7 +71,6 @@ get_outfilename(mrb_state *mrb, char *infile, const char *ext)
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;
@@ -86,7 +85,7 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
case 'o':
if (args->outfile) {
fprintf(stderr, "%s: an output file is already specified. (%s)\n",
- args->prog, outfile);
+ args->prog, args->outfile);
return -1;
}
if (argv[i][2] == '\0' && argv[i+1]) {
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index 09350ff49..e032ff79a 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -3,9 +3,11 @@ require 'open3'
def assert_mruby(exp_out, exp_err, exp_success, args)
out, err, stat = Open3.capture3(cmd("mruby"), *args)
- assert_operator(exp_out, :===, out, "standard output")
- assert_operator(exp_err, :===, err, "standard error")
- assert_equal(exp_success, stat.success?, "exit success?")
+ assert "assert_mruby" do
+ assert_operator(exp_out, :===, out, "standard output")
+ assert_operator(exp_err, :===, err, "standard error")
+ assert_equal(exp_success, stat.success?, "exit success?")
+ end
end
assert('regression for #1564') do
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index ed8fc3150..c0986893c 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -291,7 +291,7 @@ on_eval(codegen_scope *s)
}
struct mrb_insn_data
-mrb_decode_insn(mrb_code *pc)
+mrb_decode_insn(const mrb_code *pc)
{
struct mrb_insn_data data = { 0 };
mrb_code insn = READ_B();
@@ -956,7 +956,12 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
}
else {
pop_n(n);
- genop_2(s, OP_ARRAY, cursp(), n);
+ if (n == 0 && is_splat) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ genop_2(s, OP_ARRAY, cursp(), n);
+ }
push();
codegen(s, t->car, VAL);
pop(); pop();
@@ -1553,7 +1558,7 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_IF:
{
- int pos1, pos2;
+ int pos1, pos2, nil_p = FALSE;
node *elsepart = tree->cdr->cdr->car;
if (!tree->car) {
@@ -1570,32 +1575,59 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_NIL:
codegen(s, elsepart, val);
goto exit;
+ case NODE_CALL:
+ {
+ node *n = tree->car->cdr;
+ mrb_sym mid = nsym(n->cdr->car);
+ mrb_sym mnil = mrb_intern_lit(s->mrb, "nil?");
+ if (mid == mnil && n->cdr->cdr->car == NULL) {
+ nil_p = TRUE;
+ codegen(s, n->car, VAL);
+ }
+ }
+ break;
+ }
+ if (!nil_p) {
+ codegen(s, tree->car, VAL);
}
- codegen(s, tree->car, VAL);
pop();
- pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
-
- codegen(s, tree->cdr->car, val);
- if (elsepart) {
+ if (val || tree->cdr->car) {
+ if (nil_p) {
+ pos2 = genjmp2(s, OP_JMPNIL, cursp(), 0, val);
+ pos1 = genjmp(s, OP_JMP, 0);
+ dispatch(s, pos2);
+ }
+ else {
+ pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
+ }
+ codegen(s, tree->cdr->car, val);
if (val) pop();
- pos2 = genjmp(s, OP_JMP, 0);
- dispatch(s, pos1);
- codegen(s, elsepart, val);
- dispatch(s, pos2);
- }
- else {
- if (val) {
- pop();
+ if (elsepart || val) {
pos2 = genjmp(s, OP_JMP, 0);
dispatch(s, pos1);
- genop_1(s, OP_LOADNIL, cursp());
+ codegen(s, elsepart, val);
dispatch(s, pos2);
- push();
}
else {
dispatch(s, pos1);
}
}
+ else { /* empty then-part */
+ if (elsepart) {
+ if (nil_p) {
+ pos1 = genjmp2(s, OP_JMPNIL, cursp(), 0, val);
+ }
+ else {
+ pos1 = genjmp2(s, OP_JMPIF, cursp(), 0, val);
+ }
+ codegen(s, elsepart, val);
+ dispatch(s, pos1);
+ }
+ else if (val && !nil_p) {
+ genop_1(s, OP_LOADNIL, cursp());
+ push();
+ }
+ }
}
break;
@@ -2373,7 +2405,7 @@ codegen(codegen_scope *s, node *tree, int val)
mrb_value str;
int sym;
- str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
+ str = mrb_format(mrb, "$%d", nint(tree));
sym = new_sym(s, mrb_intern_str(mrb, str));
genop_2(s, OP_GETGV, cursp(), sym);
push();
diff --git a/mrbgems/mruby-compiler/core/keywords b/mrbgems/mruby-compiler/core/keywords
index 9cb86608c..a60ecd10a 100644
--- a/mrbgems/mruby-compiler/core/keywords
+++ b/mrbgems/mruby-compiler/core/keywords
@@ -1,8 +1,5 @@
%{
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;
diff --git a/mrbgems/mruby-compiler/core/lex.def b/mrbgems/mruby-compiler/core/lex.def
index 2ff266481..872bf40c1 100644
--- a/mrbgems/mruby-compiler/core/lex.def
+++ b/mrbgems/mruby-compiler/core/lex.def
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.1 */
/* 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) \
@@ -25,16 +25,13 @@
&& ('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 <[email protected]>."
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <[email protected]>."
#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"
+#line 5 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
struct kwtable;
#define TOTAL_KEYWORDS 40
@@ -52,7 +49,7 @@ inline
#endif
#endif
static unsigned int
-hash (const char *str, unsigned int len)
+hash (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
@@ -83,7 +80,7 @@ hash (const char *str, unsigned int len)
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51
};
- int hval = len;
+ register unsigned int hval = len;
switch (hval)
{
@@ -98,109 +95,103 @@ hash (const char *str, unsigned int len)
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 (const char *str, unsigned int len)
+mrb_reserved_word (register const char *str, register size_t len)
{
static const struct kwtable wordlist[] =
{
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 15 "/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"
+#line 20 "/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"
+#line 30 "/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"
+#line 23 "/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"
+#line 22 "/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"
+#line 39 "/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"
+#line 31 "/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"
+#line 24 "/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"
+#line 37 "/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"
+#line 21 "/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"
+#line 34 "/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"
+#line 40 "/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"
+#line 43 "/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"
+#line 42 "/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"
+#line 36 "/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"
+#line 18 "/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"
+#line 13 "/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"
+#line 19 "/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"
+#line 46 "/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"
+#line 25 "/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"
+#line 41 "/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"
+#line 32 "/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"
+#line 27 "/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"
+#line 44 "/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"
+#line 35 "/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"
+#line 26 "/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"
+#line 16 "/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"
+#line 33 "/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"
+#line 29 "/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"
+#line 38 "/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"
+#line 28 "/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"
+#line 14 "/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 9 "/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"
+#line 8 "/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"
+#line 7 "/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"
+#line 11 "/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"
+#line 12 "/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"
+#line 10 "/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"
+#line 17 "/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"
+#line 45 "/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)
{
- int key = hash (str, len);
+ register unsigned int key = hash (str, len);
- if (key <= MAX_HASH_VALUE && key >= 0)
+ if (key <= MAX_HASH_VALUE)
{
- const char *s = wordlist[key].name;
+ register const char *s = wordlist[key].name;
if (*str == *s && !strcmp (str + 1, s + 1))
return &wordlist[key];
@@ -208,4 +199,5 @@ mrb_reserved_word (const char *str, unsigned int len)
}
return 0;
}
-#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+#line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
+
diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 96a9453b6..d26a29a27 100644
--- a/mrbgems/mruby-compiler/core/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -233,7 +233,7 @@ parser_strdup(parser_state *p, const char *s)
#define strdup(s) parser_strdup(p, s)
static void
-dump_int(short i, char *s)
+dump_int(uint16_t i, char *s)
{
char *p = s;
char *t = s;
@@ -3831,7 +3831,7 @@ backref_error(parser_state *p, node *n)
yyerror_c(p, "can't set variable $", (char)intn(n->cdr));
}
else {
- mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
+ mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %d", c);
}
}
@@ -4731,6 +4731,7 @@ parser_yylex(parser_state *p)
case '\n':
maybe_heredoc:
heredoc_treat_nextline(p);
+ p->column = 0;
switch (p->lstate) {
case EXPR_BEG:
case EXPR_FNAME:
@@ -4738,7 +4739,6 @@ parser_yylex(parser_state *p)
case EXPR_CLASS:
case EXPR_VALUE:
p->lineno++;
- p->column = 0;
if (p->parsing_heredoc != NULL) {
if (p->lex_strterm) {
return parse_string(p);
@@ -4759,15 +4759,12 @@ parser_yylex(parser_state *p)
break;
case '#': /* comment as a whitespace */
pushback(p, '#');
- goto retry;
- case '\n': /* consecutive newlines */
p->lineno++;
- p->column = 0;
- pushback(p, '\n');
goto retry;
case '.':
if (!peek(p, '.')) {
pushback(p, '.');
+ p->lineno++;
goto retry;
}
pushback(p, c);
@@ -4775,6 +4772,7 @@ parser_yylex(parser_state *p)
case '&':
if (peek(p, '.')) {
pushback(p, '&');
+ p->lineno++;
goto retry;
}
pushback(p, c);
@@ -5022,10 +5020,10 @@ parser_yylex(parser_state *p)
}
if (c2) {
char buf[256];
- char cc = (char)c2;
+ char cc[] = { (char)c2, '\0' };
strcpy(buf, "invalid character syntax; use ?\\");
- strncat(buf, &cc, 1);
+ strncat(buf, cc, 2);
yyerror(p, buf);
}
}
@@ -5419,7 +5417,7 @@ parser_yylex(parser_state *p)
tokfix(p);
if (is_float) {
#ifdef MRB_WITHOUT_FLOAT
- yywarning(p, "floating point numbers are not supported");
+ yywarning_s(p, "floating point numbers are not supported", tok(p));
pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
diff --git a/mrbgems/mruby-complex/mrblib/complex.rb b/mrbgems/mruby-complex/mrblib/complex.rb
index 0299e7675..f32b84c8b 100644
--- a/mrbgems/mruby-complex/mrblib/complex.rb
+++ b/mrbgems/mruby-complex/mrblib/complex.rb
@@ -45,8 +45,7 @@ class Complex < Numeric
def /(rhs)
if rhs.is_a? Complex
- div = rhs.real * rhs.real + rhs.imaginary * rhs.imaginary
- Complex((real * rhs.real + imaginary * rhs.imaginary) / div, (rhs.real * imaginary - real * rhs.imaginary) / div)
+ __div__(rhs)
elsif rhs.is_a? Numeric
Complex(real / rhs, imaginary / rhs)
end
@@ -62,7 +61,7 @@ class Complex < Numeric
end
def abs
- Math.sqrt(abs2)
+ Math.hypot imaginary, real
end
alias_method :magnitude, :abs
@@ -104,18 +103,18 @@ class Complex < Numeric
end
alias_method :imag, :imaginary
-end
-[Fixnum, Float].each do |cls|
- [:+, :-, :*, :/, :==].each do |op|
- cls.instance_exec do
- original_operator_name = "__original_operator_#{op}_complex"
- alias_method original_operator_name, op
- define_method op do |rhs|
- if rhs.is_a? Complex
- Complex(self).send(op, rhs)
- else
- send(original_operator_name, rhs)
+ [Fixnum, Float].each do |cls|
+ [:+, :-, :*, :/, :==].each do |op|
+ cls.instance_exec do
+ original_operator_name = "__original_operator_#{op}_complex"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Complex
+ Complex(self).send(op, rhs)
+ else
+ send(original_operator_name, rhs)
+ end
end
end
end
diff --git a/mrbgems/mruby-complex/src/complex.c b/mrbgems/mruby-complex/src/complex.c
index 5371332cd..10fa42a2c 100644
--- a/mrbgems/mruby-complex/src/complex.c
+++ b/mrbgems/mruby-complex/src/complex.c
@@ -1,12 +1,23 @@
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/numeric.h>
+#include <math.h>
+
+#ifdef MRB_WITHOUT_FLOAT
+# error Complex conflicts 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
struct mrb_complex {
mrb_float real;
mrb_float imaginary;
};
+#ifdef MRB_USE_FLOAT
+#define F(x) x##f
+#else
+#define F(x) x
+#endif
+
#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT)
#define COMPLEX_USE_ISTRUCT
@@ -15,18 +26,15 @@ struct mrb_complex {
#define complex_ptr(mrb, v) (struct mrb_complex*)mrb_istruct_ptr(v)
-static mrb_value
-complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
{
- struct RClass *c = mrb_class_get(mrb, "Complex");
- struct RIStruct *s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
- mrb_value comp = mrb_obj_value(s);
- struct mrb_complex *p = complex_ptr(mrb, comp);
- p->real = real;
- p->imaginary = imaginary;
- MRB_SET_FROZEN_FLAG(s);
+ struct RIStruct *s;
+
+ s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ *p = (struct mrb_complex*)s->inline_data;
- return comp;
+ return (struct RBasic*)s;
}
#else
@@ -35,17 +43,14 @@ complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
static const struct mrb_data_type mrb_complex_type = {"Complex", mrb_free};
-static mrb_value
-complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
{
- struct RClass *c = mrb_class_get(mrb, "Complex");
- struct mrb_complex *p;
+ struct RData *d;
- p = (struct mrb_complex*)mrb_malloc(mrb, sizeof(struct mrb_complex));
- p->real = real;
- p->imaginary = imaginary;
+ Data_Make_Struct(mrb, c, struct mrb_complex, &mrb_complex_type, *p, d);
- return mrb_obj_value(Data_Wrap_Struct(mrb, c, &mrb_complex_type, p));
+ return (struct RBasic*)d;
}
static struct mrb_complex*
@@ -62,6 +67,19 @@ complex_ptr(mrb_state *mrb, mrb_value v)
#endif
static mrb_value
+complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+{
+ struct RClass *c = mrb_class_get(mrb, "Complex");
+ struct mrb_complex *p;
+ struct RBasic *comp = complex_alloc(mrb, c, &p);
+ p->real = real;
+ p->imaginary = imaginary;
+ MRB_SET_FROZEN_FLAG(comp);
+
+ return mrb_obj_value(comp);
+}
+
+static mrb_value
complex_real(mrb_state *mrb, mrb_value self)
{
struct mrb_complex *p = complex_ptr(mrb, self);
@@ -84,19 +102,17 @@ complex_s_rect(mrb_state *mrb, mrb_value self)
return complex_new(mrb, real, imaginary);
}
-#ifndef MRB_WITHOUT_FLOAT
static mrb_value
complex_to_f(mrb_state *mrb, mrb_value self)
{
struct mrb_complex *p = complex_ptr(mrb, self);
if (p->imaginary != 0) {
- mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self);
}
return mrb_float_value(mrb, p->real);
}
-#endif
static mrb_value
complex_to_i(mrb_state *mrb, mrb_value self)
@@ -104,7 +120,7 @@ complex_to_i(mrb_state *mrb, mrb_value self)
struct mrb_complex *p = complex_ptr(mrb, self);
if (p->imaginary != 0) {
- mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %v into Float", self);
}
return mrb_int_value(mrb, p->real);
}
@@ -115,6 +131,93 @@ complex_to_c(mrb_state *mrb, mrb_value self)
return self;
}
+/* Arithmetic on (significand, exponent) pairs avoids premature overflow in
+ complex division */
+struct float_pair {
+ mrb_float s;
+ int x;
+};
+
+static void
+add_pair(struct float_pair *s, struct float_pair const *a,
+ struct float_pair const *b)
+{
+ if (b->s == 0.0F) {
+ *s = *a;
+ } else if (a->s == 0.0F) {
+ *s = *b;
+ } else if (a->x >= b->x) {
+ s->s = a->s + F(ldexp)(b->s, b->x - a->x);
+ s->x = a->x;
+ } else {
+ s->s = F(ldexp)(a->s, a->x - b->x) + b->s;
+ s->x = b->x;
+ }
+}
+
+static void
+mul_pair(struct float_pair *p, struct float_pair const *a,
+ struct float_pair const *b)
+{
+ p->s = a->s * b->s;
+ p->x = a->x + b->x;
+}
+
+static void
+div_pair(struct float_pair *q, struct float_pair const *a,
+ struct float_pair const *b)
+{
+ q->s = a->s / b->s;
+ q->x = a->x - b->x;
+}
+
+static mrb_value
+complex_div(mrb_state *mrb, mrb_value self)
+{
+ mrb_value rhs;
+ struct mrb_complex *a, *b;
+ struct float_pair ar, ai, br, bi;
+ struct float_pair br2, bi2;
+ struct float_pair div;
+ struct float_pair ar_br, ai_bi;
+ struct float_pair ai_br, ar_bi;
+ struct float_pair zr, zi;
+
+ mrb_get_args(mrb, "o", &rhs);
+ a = complex_ptr(mrb, self);
+ b = complex_ptr(mrb, rhs);
+
+ /* Split floating point components into significand and exponent */
+ ar.s = F(frexp)(a->real, &ar.x);
+ ai.s = F(frexp)(a->imaginary, &ai.x);
+ br.s = F(frexp)(b->real, &br.x);
+ bi.s = F(frexp)(b->imaginary, &bi.x);
+
+ /* Perform arithmetic on (significand, exponent) pairs to produce
+ the result: */
+
+ /* the divisor */
+ mul_pair(&br2, &br, &br);
+ mul_pair(&bi2, &bi, &bi);
+ add_pair(&div, &br2, &bi2);
+
+ /* real component */
+ mul_pair(&ar_br, &ar, &br);
+ mul_pair(&ai_bi, &ai, &bi);
+ add_pair(&zr, &ar_br, &ai_bi);
+ div_pair(&zr, &zr, &div);
+
+ /* imaginary component */
+ mul_pair(&ai_br, &ai, &br);
+ mul_pair(&ar_bi, &ar, &bi);
+ ar_bi.s = -ar_bi.s;
+ add_pair(&zi, &ai_br, &ar_bi);
+ div_pair(&zi, &zi, &div);
+
+ /* assemble the result */
+ return complex_new(mrb, F(ldexp)(zr.s, zr.x), F(ldexp)(zi.s, zi.x));
+}
+
void mrb_mruby_complex_gem_init(mrb_state *mrb)
{
struct RClass *comp;
@@ -123,18 +226,21 @@ void mrb_mruby_complex_gem_init(mrb_state *mrb)
mrb_assert(sizeof(struct mrb_complex) < ISTRUCT_DATA_SIZE);
#endif
comp = mrb_define_class(mrb, "Complex", mrb_class_get(mrb, "Numeric"));
- //MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
+#ifdef COMPLEX_USE_ISTRUCT
+ MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
+#else
+ MRB_SET_INSTANCE_TT(comp, MRB_TT_DATA);
+#endif
mrb_undef_class_method(mrb, comp, "new");
mrb_define_class_method(mrb, comp, "rectangular", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_class_method(mrb, comp, "rect", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_method(mrb, mrb->kernel_module, "Complex", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_method(mrb, comp, "real", complex_real, MRB_ARGS_NONE());
mrb_define_method(mrb, comp, "imaginary", complex_imaginary, MRB_ARGS_NONE());
-#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
-#endif
mrb_define_method(mrb, comp, "to_i", complex_to_i, MRB_ARGS_NONE());
mrb_define_method(mrb, comp, "to_c", complex_to_c, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "__div__", complex_div, MRB_ARGS_REQ(1));
}
void
diff --git a/mrbgems/mruby-complex/test/complex.rb b/mrbgems/mruby-complex/test/complex.rb
index 6996eb214..d996e8277 100644
--- a/mrbgems/mruby-complex/test/complex.rb
+++ b/mrbgems/mruby-complex/test/complex.rb
@@ -1,6 +1,8 @@
def assert_complex(real, exp)
- assert_float real.real, exp.real
- assert_float real.imaginary, exp.imaginary
+ assert "assert_complex" do
+ assert_float real.real, exp.real
+ assert_float real.imaginary, exp.imaginary
+ end
end
assert 'Complex' do
@@ -48,7 +50,7 @@ assert 'Complex#-' do
end
assert 'Complex#-@' do
- assert_complex -Complex(1, 2), (-1 - 2i)
+ assert_complex(-Complex(1, 2), (-1 - 2i))
end
assert 'Complex#/' do
@@ -57,6 +59,15 @@ assert 'Complex#/' do
assert_complex Complex(-2, 9) / Complex(-9, 2), ((36 / 85) - (77i / 85))
assert_complex Complex(9, 8) / 4 , ((9 / 4) + 2i)
assert_complex Complex(20, 9) / 9.8 , (2.0408163265306123 + 0.9183673469387754i)
+ if 1e39.infinite? then
+ # MRB_USE_FLOAT in effect
+ ten = 1e21
+ one = 1e20
+ else
+ ten = 1e201
+ one = 1e200
+ end
+ assert_complex Complex(ten, ten) / Complex(one, one), Complex(10.0, 0.0)
end
assert 'Complex#==' do
@@ -68,6 +79,14 @@ end
assert 'Complex#abs' do
assert_float Complex(-1).abs, 1
assert_float Complex(3.0, -4.0).abs, 5.0
+ if 1e39.infinite? then
+ # MRB_USE_FLOAT in effect
+ exp = 125
+ else
+ exp = 1021
+ end
+ assert_true Complex(3.0*2.0**exp, 4.0*2.0**exp).abs.finite?
+ assert_float Complex(3.0*2.0**exp, 4.0*2.0**exp).abs, 5.0*2.0**exp
end
assert 'Complex#abs2' do
diff --git a/mrbgems/mruby-enum-chain/mrblib/chain.rb b/mrbgems/mruby-enum-chain/mrblib/chain.rb
index 98515ea14..43d0926c8 100644
--- a/mrbgems/mruby-enum-chain/mrblib/chain.rb
+++ b/mrbgems/mruby-enum-chain/mrblib/chain.rb
@@ -6,28 +6,30 @@ module Enumerable
def chain(*args)
Enumerator::Chain.new(self, *args)
end
+end
+class Enumerator
def +(other)
- Enumerator::Chain.new(self, other)
+ Chain.new(self, other)
end
-end
-class Enumerator
class Chain
include Enumerable
def initialize(*args)
- @enums = args
- end
-
- def initialize_copy(orig)
- @enums = orig.__copy_enums
+ @enums = args.freeze
+ @pos = -1
end
def each(&block)
- return to_enum unless block_given?
+ return to_enum unless block
- @enums.each { |e| e.each(&block) }
+ i = 0
+ while i < @enums.size
+ @pos = i
+ @enums[i].each(&block)
+ i += 1
+ end
self
end
@@ -40,21 +42,21 @@ class Enumerator
end
def rewind
- @enums.reverse_each do |e|
+ while 0 <= @pos && @pos < @enums.size
+ e = @enums[@pos]
e.rewind if e.respond_to?(:rewind)
+ @pos -= 1
end
self
end
- def inspect
- "#<#{self.class}: #{@enums.inspect}>"
+ def +(other)
+ self.class.new(self, other)
end
- def __copy_enums
- @enums.each_with_object([]) do |e, a|
- a << e.clone
- end
+ def inspect
+ "#<#{self.class}: #{@enums.inspect}>"
end
end
end
diff --git a/mrbgems/mruby-enum-chain/test/enum_chain.rb b/mrbgems/mruby-enum-chain/test/enum_chain.rb
index 4dd59bd37..45bbc9a77 100644
--- a/mrbgems/mruby-enum-chain/test/enum_chain.rb
+++ b/mrbgems/mruby-enum-chain/test/enum_chain.rb
@@ -12,9 +12,9 @@ assert("Enumerable#chain") do
assert_raise(NoMethodError) { c.chain }
end
-assert("Enumerable#+") do
+assert("Enumerator#+") do
a = [].each
- b = {}.reverse_each
+ b = {}.each
c = Object.new # not has #each method
assert_kind_of Enumerator::Chain, a + b
@@ -24,7 +24,7 @@ assert("Enumerable#+") do
assert_raise(NoMethodError) { c + a }
end
-assert("Enumerator.new") do
+assert("Enumerator::Chain.new") do
a = []
b = {}
c = Object.new # not has #each method
@@ -46,13 +46,13 @@ assert("Enumerator::Chain#each") do
assert_kind_of Enumerator, aa.each
assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a
- aa = a.chain(a.reverse_each)
+ aa = a.chain(6..9)
assert_kind_of Enumerator, aa.each
- assert_equal [1, 2, 3, 3, 2, 1], aa.each.to_a
+ assert_equal [1, 2, 3, 6, 7, 8, 9], aa.each.to_a
- aa = a.chain(a.reverse_each, a)
+ aa = a.chain((-3..-2).each_with_index, a)
assert_kind_of Enumerator, aa.each
- assert_equal [1, 2, 3, 3, 2, 1, 1, 2, 3], aa.each.to_a
+ assert_equal [1, 2, 3, [-3, 0], [-2, 1], 1, 2, 3], aa.each.to_a
aa = a.chain(Object.new)
assert_kind_of Enumerator, aa.each
@@ -65,12 +65,44 @@ assert("Enumerator::Chain#size") do
aa = a.chain(a)
assert_equal 6, aa.size
- aa = a.chain(a.reverse_each)
+ aa = a.chain(3..4)
assert_nil aa.size
- aa = a.chain(a.reverse_each, a)
+ aa = a.chain(3..4, a)
assert_nil aa.size
aa = a.chain(Object.new)
assert_nil aa.size
end
+
+assert("Enumerator::Chain#rewind") do
+ rewound = nil
+ e1 = [1, 2]
+ e2 = (4..6)
+ (class << e1; self end).define_method(:rewind) { rewound << self }
+ (class << e2; self end).define_method(:rewind) { rewound << self }
+ c = e1.chain(e2)
+
+ rewound = []
+ c.rewind
+ assert_equal [], rewound
+
+ rewound = []
+ c.each{break c}.rewind
+ assert_equal [e1], rewound
+
+ rewound = []
+ c.each{}.rewind
+ assert_equal [e2, e1], rewound
+end
+
+assert("Enumerator::Chain#+") do
+ a = [].chain
+ b = {}.chain
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a + b
+ assert_kind_of Enumerator::Chain, a + c
+ assert_kind_of Enumerator::Chain, b + a
+ assert_kind_of Enumerator::Chain, b + c
+end
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index 99b9cddba..b427bd67e 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -811,10 +811,6 @@ module Enumerable
h
end
- def nil.to_h
- {}
- end
-
def uniq(&block)
hash = {}
if block
diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb
index 64b1bbda9..6929d8ddc 100644
--- a/mrbgems/mruby-enum-ext/test/enum.rb
+++ b/mrbgems/mruby-enum-ext/test/enum.rb
@@ -186,8 +186,5 @@ assert("Enumerable#to_h") do
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
-
assert_equal({1=>4,3=>8}, c.new.to_h{|k,v|[k,v*2]})
end
diff --git a/mrbgems/mruby-enumerator/mrbgem.rake b/mrbgems/mruby-enumerator/mrbgem.rake
index 8757a15ea..abcc54e7a 100644
--- a/mrbgems/mruby-enumerator/mrbgem.rake
+++ b/mrbgems/mruby-enumerator/mrbgem.rake
@@ -2,6 +2,5 @@ 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/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index d609cadb5..dce0c2cf2 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -6,6 +6,17 @@ class << @obj
end
end
+def assert_take(exp, enumerator)
+ result = []
+ n = exp.size
+ enumerator.each do |v|
+ break if n == 0
+ result << v
+ n -= 1
+ end
+ assert_equal exp, result
+end
+
assert 'Enumerator.class' do
assert_equal Class, Enumerator.class
end
@@ -19,7 +30,7 @@ assert 'Enumerator.new' do
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 { |y| i = 0; loop { y << (i += 1) } }.take(3)
+ assert_take [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }
assert_raise(ArgumentError) { Enumerator.new }
# examples
@@ -30,7 +41,7 @@ assert 'Enumerator.new' do
a, b = b, a + b
end
end
- assert_equal [1,1,2,3,5,8,13,21,34,55], fib.take(10)
+ assert_take [1,1,2,3,5,8,13,21,34,55], fib
end
assert 'Enumerator#initialize_copy' do
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index a3b211ba2..e2388f026 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -102,25 +102,28 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
uint8_t c;
mrb_code insn;
int argc = irep_argc(irep);
+ mrb_code *iseq = (mrb_code *)irep->iseq;
+
+ mrb_assert((irep->flags & MRB_ISEQ_NO_FREE) == 0);
for (i = 0; i < irep->ilen; ) {
- insn = irep->iseq[i];
+ insn = iseq[i];
switch(insn){
case OP_EPUSH:
- b = PEEK_S(irep->iseq+i+1);
+ b = PEEK_S(iseq+i+1);
patch_irep(mrb, irep->reps[b], bnest + 1, top);
break;
case OP_LAMBDA:
case OP_BLOCK:
- a = PEEK_B(irep->iseq+i+1);
- b = PEEK_B(irep->iseq+i+2);
+ a = PEEK_B(iseq+i+1);
+ b = PEEK_B(iseq+i+2);
patch_irep(mrb, irep->reps[b], bnest + 1, top);
break;
case OP_SEND:
- b = PEEK_B(irep->iseq+i+2);
- c = PEEK_B(irep->iseq+i+3);
+ b = PEEK_B(iseq+i+2);
+ c = PEEK_B(iseq+i+3);
if (c != 0) {
break;
}
@@ -128,24 +131,24 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
uint16_t arg = search_variable(mrb, irep->syms[b], bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = OP_GETUPVAR;
- irep->iseq[i+2] = arg >> 8;
- irep->iseq[i+3] = arg & 0xff;
+ iseq[i] = OP_GETUPVAR;
+ iseq[i+2] = arg >> 8;
+ iseq[i+3] = arg & 0xff;
}
}
break;
case OP_MOVE:
- a = PEEK_B(irep->iseq+i+1);
- b = PEEK_B(irep->iseq+i+2);
+ a = PEEK_B(iseq+i+1);
+ b = PEEK_B(iseq+i+2);
/* src part */
if (potential_upvar_p(irep->lv, b, argc, irep->nlocals)) {
uint16_t arg = search_variable(mrb, irep->lv[b - 1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = insn = OP_GETUPVAR;
- irep->iseq[i+2] = arg >> 8;
- irep->iseq[i+3] = arg & 0xff;
+ iseq[i] = insn = OP_GETUPVAR;
+ iseq[i+2] = arg >> 8;
+ iseq[i+3] = arg & 0xff;
}
}
/* dst part */
@@ -153,18 +156,18 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
uint16_t arg = search_variable(mrb, irep->lv[a - 1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = insn = OP_SETUPVAR;
- irep->iseq[i+1] = (mrb_code)b;
- irep->iseq[i+2] = arg >> 8;
- irep->iseq[i+3] = arg & 0xff;
+ iseq[i] = insn = OP_SETUPVAR;
+ iseq[i+1] = (mrb_code)b;
+ iseq[i+2] = arg >> 8;
+ iseq[i+3] = arg & 0xff;
}
}
break;
case OP_GETUPVAR:
- a = PEEK_B(irep->iseq+i+1);
- b = PEEK_B(irep->iseq+i+2);
- c = PEEK_B(irep->iseq+i+3);
+ a = PEEK_B(iseq+i+1);
+ b = PEEK_B(iseq+i+2);
+ c = PEEK_B(iseq+i+3);
{
int lev = c+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
@@ -172,18 +175,18 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = OP_GETUPVAR;
- irep->iseq[i+2] = arg >> 8;
- irep->iseq[i+3] = arg & 0xff;
+ iseq[i] = OP_GETUPVAR;
+ iseq[i+2] = arg >> 8;
+ iseq[i+3] = arg & 0xff;
}
}
}
break;
case OP_SETUPVAR:
- a = PEEK_B(irep->iseq+i+1);
- b = PEEK_B(irep->iseq+i+2);
- c = PEEK_B(irep->iseq+i+3);
+ a = PEEK_B(iseq+i+1);
+ b = PEEK_B(iseq+i+2);
+ c = PEEK_B(iseq+i+3);
{
int lev = c+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
@@ -191,25 +194,25 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = OP_SETUPVAR;
- irep->iseq[i+1] = a;
- irep->iseq[i+2] = arg >> 8;
- irep->iseq[i+3] = arg & 0xff;
+ iseq[i] = OP_SETUPVAR;
+ iseq[i+1] = a;
+ iseq[i+2] = arg >> 8;
+ iseq[i+3] = arg & 0xff;
}
}
}
break;
case OP_EXT1:
- insn = PEEK_B(irep->iseq+i+1);
+ insn = PEEK_B(iseq+i+1);
i += mrb_insn_size1[insn]+1;
continue;
case OP_EXT2:
- insn = PEEK_B(irep->iseq+i+1);
+ insn = PEEK_B(iseq+i+1);
i += mrb_insn_size2[insn]+1;
continue;
case OP_EXT3:
- insn = PEEK_B(irep->iseq+i+1);
+ insn = PEEK_B(iseq+i+1);
i += mrb_insn_size3[insn]+1;
continue;
}
@@ -235,7 +238,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
}
cxt = mrbc_context_new(mrb);
- cxt->lineno = (short)line;
+ cxt->lineno = (uint16_t)line;
mrbc_filename(mrb, cxt, file ? file : "(eval)");
cxt->capture_errors = TRUE;
@@ -254,15 +257,15 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
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));
+ str = mrb_format(mrb, "file %s line %d: %s",
+ file,
+ p->error_buffer[0].lineno,
+ 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));
+ str = mrb_format(mrb, "line %d: %s",
+ p->error_buffer[0].lineno,
+ p->error_buffer[0].message);
}
mrb_parser_free(p);
mrbc_context_free(mrb, cxt);
diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c
index 17ce77c5d..b702a3811 100644
--- a/mrbgems/mruby-fiber/src/fiber.c
+++ b/mrbgems/mruby-fiber/src/fiber.c
@@ -191,14 +191,21 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
fiber_check_cfunc(mrb, c);
status = c->status;
- if (resume && status == MRB_FIBER_TRANSFERRED) {
- mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
- }
- if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {
+ switch (status) {
+ case MRB_FIBER_TRANSFERRED:
+ if (resume) {
+ mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
+ }
+ break;
+ case MRB_FIBER_RUNNING:
+ case MRB_FIBER_RESUMED:
mrb_raise(mrb, E_FIBER_ERROR, "double resume");
- }
- if (status == MRB_FIBER_TERMINATED) {
+ break;
+ case MRB_FIBER_TERMINATED:
mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
+ break;
+ default:
+ break;
}
old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
diff --git a/mrbgems/mruby-io/README.md b/mrbgems/mruby-io/README.md
index 256fb8195..ccf56f970 100644
--- a/mrbgems/mruby-io/README.md
+++ b/mrbgems/mruby-io/README.md
@@ -1,7 +1,5 @@
mruby-io
========
-[![Build Status](https://travis-ci.org/iij/mruby-io.svg?branch=master)](https://travis-ci.org/iij/mruby-io)
-
`IO` and `File` classes for mruby
@@ -9,7 +7,7 @@ mruby-io
Add the line below to your `build_config.rb`:
```
- conf.gem :github => 'iij/mruby-io'
+ conf.gem core: 'mruby-io'
```
## Implemented methods
diff --git a/mrbgems/mruby-io/mrbgem.rake b/mrbgems/mruby-io/mrbgem.rake
index d79964590..e4f0b7bb6 100644
--- a/mrbgems/mruby-io/mrbgem.rake
+++ b/mrbgems/mruby-io/mrbgem.rake
@@ -6,7 +6,7 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
spec.cc.include_paths << "#{build.root}/src"
case RUBY_PLATFORM
- when /mingw|mswin/
+ when /mingw|mswin|msys/
spec.linker.libraries += ['Ws2_32']
#spec.cc.include_paths += ["C:/Windows/system/include"]
spec.linker.library_paths += ["C:/Windows/system"]
diff --git a/mrbgems/mruby-io/mrblib/io.rb b/mrbgems/mruby-io/mrblib/io.rb
index 6211bf15f..f3c4de6fd 100644
--- a/mrbgems/mruby-io/mrblib/io.rb
+++ b/mrbgems/mruby-io/mrblib/io.rb
@@ -207,9 +207,9 @@ class IO
end
if length
- consume = (length <= @buf.size) ? length : @buf.size
- array.push @buf[0, consume]
- @buf = @buf[consume, @buf.size - consume]
+ consume = (length <= @buf.bytesize) ? length : @buf.bytesize
+ array.push @buf.byteslice(0, consume)
+ @buf = @buf.byteslice(consume, @buf.bytesize - consume)
length -= consume
break if length == 0
else
@@ -254,14 +254,14 @@ class IO
break
end
- if limit && limit <= @buf.size
- array.push @buf[0, limit]
- @buf = @buf[limit, @buf.size - limit]
+ if limit && limit <= @buf.bytesize
+ array.push @buf.byteslice(0, limit)
+ @buf = @buf.byteslice(limit, @buf.bytesize - limit)
break
elsif idx = @buf.index(rs)
- len = idx + rs.size
- array.push @buf[0, len]
- @buf = @buf[len, @buf.size - len]
+ len = idx + rs.bytesize
+ array.push @buf.byteslice(0, len)
+ @buf = @buf.byteslice(len, @buf.bytesize - len)
break
else
array.push @buf
@@ -284,8 +284,8 @@ class IO
def readchar
_read_buf
- c = @buf[0]
- @buf = @buf[1, @buf.size]
+ c = @buf.byteslice(0,1)
+ @buf = @buf.byteslice(1, @buf.bytesize)
c
end
diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c
index 243f634b4..2fbda90af 100644
--- a/mrbgems/mruby-io/src/file.c
+++ b/mrbgems/mruby-io/src/file.c
@@ -116,7 +116,7 @@ mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
for (i = 0; i < argc; i++) {
const char *utf8_path;
pathv = mrb_ensure_string_type(mrb, argv[i]);
- utf8_path = mrb_string_value_cstr(mrb, &pathv);
+ utf8_path = RSTRING_CSTR(mrb, pathv);
path = mrb_locale_from_utf8(utf8_path, -1);
if (UNLINK(path) < 0) {
mrb_locale_free(path);
@@ -134,8 +134,8 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
char *src, *dst;
mrb_get_args(mrb, "SS", &from, &to);
- src = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &from), -1);
- dst = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &to), -1);
+ src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1);
+ dst = mrb_locale_from_utf8(RSTRING_CSTR(mrb, to), -1);
if (rename(src, dst) < 0) {
#if defined(_WIN32) || defined(_WIN64)
if (CHMOD(dst, 0666) == 0 && UNLINK(dst) == 0 && rename(src, dst) == 0) {
@@ -146,7 +146,7 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
#endif
mrb_locale_free(src);
mrb_locale_free(dst);
- mrb_sys_fail(mrb, RSTRING_PTR(mrb_format(mrb, "(%S, %S)", from, to)));
+ mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to)));
}
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -248,7 +248,7 @@ mrb_file_realpath(mrb_state *mrb, mrb_value klass)
s = mrb_str_append(mrb, s, pathname);
pathname = s;
}
- cpath = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathname), -1);
+ cpath = mrb_locale_from_utf8(RSTRING_CSTR(mrb, pathname), -1);
result = mrb_str_buf_new(mrb, PATH_MAX);
if (realpath(cpath, RSTRING_PTR(result)) == NULL) {
mrb_locale_free(cpath);
@@ -300,14 +300,14 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
}
} else {
- const char *cuser = mrb_string_value_cstr(mrb, &username);
+ const char *cuser = RSTRING_CSTR(mrb, username);
struct passwd *pwd = getpwnam(cuser);
if (pwd == NULL) {
return mrb_nil_value();
}
home = pwd->pw_dir;
if (!mrb_file_is_absolute_path(home)) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%S", username);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%v", username);
}
}
home = mrb_locale_from_utf8(home, -1);
@@ -393,12 +393,12 @@ mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
int ai = mrb_gc_arena_save(mrb);
mrb_get_args(mrb, "SS", &from, &to);
- src = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &from), -1);
- dst = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &to), -1);
+ src = mrb_locale_from_utf8(RSTRING_CSTR(mrb, from), -1);
+ dst = mrb_locale_from_utf8(RSTRING_CSTR(mrb, to), -1);
if (symlink(src, dst) == -1) {
mrb_locale_free(src);
mrb_locale_free(dst);
- mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to)));
+ mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "(%v, %v)", from, to)));
}
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -416,7 +416,7 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) {
mrb_get_args(mrb, "i*", &mode, &filenames, &argc);
for (i = 0; i < argc; i++) {
- const char *utf8_path = mrb_string_value_cstr(mrb, &filenames[i]);
+ const char *utf8_path = RSTRING_CSTR(mrb, filenames[i]);
char *path = mrb_locale_from_utf8(utf8_path, -1);
if (CHMOD(path, mode) == -1) {
mrb_locale_free(path);
diff --git a/mrbgems/mruby-io/src/file_test.c b/mrbgems/mruby-io/src/file_test.c
index 85a221ff9..445bafde9 100644
--- a/mrbgems/mruby-io/src/file_test.c
+++ b/mrbgems/mruby-io/src/file_test.c
@@ -54,7 +54,7 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
return -1;
}
else {
- char *path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &obj), -1);
+ char *path = mrb_locale_from_utf8(RSTRING_CSTR(mrb, obj), -1);
int ret;
if (do_lstat) {
ret = LSTAT(path, st);
diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c
index 99441f16b..eb9c4097b 100644
--- a/mrbgems/mruby-io/src/io.c
+++ b/mrbgems/mruby-io/src/io.c
@@ -127,7 +127,7 @@ mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
break;
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
}
while (*m) {
@@ -141,7 +141,7 @@ mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
case ':':
/* XXX: PASSTHROUGH*/
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %s", mode);
}
}
@@ -191,8 +191,7 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
flags = fcntl(fd, F_GETFD);
if (flags == -1) {
- mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%S, F_GETFD) failed: %S",
- mrb_fixnum_value(fd), mrb_fixnum_value(errno));
+ mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%d, F_GETFD) failed: %d", fd, errno);
}
if (fd <= 2) {
flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */
@@ -202,8 +201,7 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
}
if (flags != flags2) {
if (fcntl(fd, F_SETFD, flags2) == -1) {
- mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%S, F_SETFD, %S) failed: %S",
- mrb_fixnum_value(fd), mrb_fixnum_value(flags2), mrb_fixnum_value(errno));
+ mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%d, F_SETFD, %d) failed: %d", fd, flags2, errno);
}
}
#endif
@@ -334,8 +332,8 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
- pname = mrb_string_value_cstr(mrb, &cmd);
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+ pname = RSTRING_CSTR(mrb, cmd);
+ flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
doexec = (strcmp("-", pname) != 0);
opt_in = option_to_fd(mrb, opt, "in");
@@ -381,7 +379,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
CloseHandle(ifd[1]);
CloseHandle(ofd[0]);
CloseHandle(ofd[1]);
- mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+ mrb_raisef(mrb, E_IO_ERROR, "command not found: %v", cmd);
}
CloseHandle(pi.hThread);
CloseHandle(ifd[0]);
@@ -430,8 +428,8 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
- pname = mrb_string_value_cstr(mrb, &cmd);
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+ pname = RSTRING_CSTR(mrb, cmd);
+ flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
doexec = (strcmp("-", pname) != 0);
opt_in = option_to_fd(mrb, opt, "in");
@@ -494,7 +492,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
close(fd);
}
mrb_proc_exec(pname);
- mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+ mrb_raisef(mrb, E_IO_ERROR, "command not found: %v", cmd);
_exit(127);
}
result = mrb_nil_value();
@@ -628,7 +626,7 @@ mrb_io_initialize(mrb_state *mrb, mrb_value io)
opt = mrb_hash_new(mrb);
}
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+ flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
@@ -760,7 +758,6 @@ mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass)
int
mrb_cloexec_open(mrb_state *mrb, const char *pathname, mrb_int flags, mrb_int mode)
{
- mrb_value emsg;
int fd, retry = FALSE;
char* fname = mrb_locale_from_utf8(pathname, -1);
@@ -783,9 +780,7 @@ reopen:
}
}
- emsg = mrb_format(mrb, "open %S", mrb_str_new_cstr(mrb, pathname));
- mrb_str_modify(mrb, mrb_str_ptr(emsg));
- mrb_sys_fail(mrb, RSTRING_PTR(emsg));
+ mrb_sys_fail(mrb, RSTRING_CSTR(mrb, mrb_format(mrb, "open %s", pathname)));
}
mrb_locale_free(fname);
@@ -812,8 +807,8 @@ mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass)
perm = 0666;
}
- pat = mrb_string_value_cstr(mrb, &path);
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+ pat = RSTRING_CSTR(mrb, path);
+ flags = mrb_io_modestr_to_flags(mrb, RSTRING_CSTR(mrb, mode));
modenum = mrb_io_flags_to_modenum(mrb, flags);
fd = mrb_cloexec_open(mrb, pat, modenum, perm);
return mrb_fixnum_value(fd);
@@ -1068,7 +1063,7 @@ mrb_io_s_select(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "*", &argv, &argc);
if (argc < 1 || argc > 4) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1..4)", mrb_fixnum_value(argc));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%i for 1..4)", argc);
}
timeout = mrb_nil_value();
diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb
index 1535ebb44..143096759 100644
--- a/mrbgems/mruby-io/test/file.rb
+++ b/mrbgems/mruby-io/test/file.rb
@@ -179,20 +179,25 @@ end
assert('File.symlink') do
target_name = "/usr/bin"
- symlink_name = "test-bin-dummy"
if !File.exist?(target_name)
skip("target directory of File.symlink is not found")
- else
- begin
- assert_equal 0, File.symlink(target_name, symlink_name)
- begin
- assert_equal true, File.symlink?(symlink_name)
- ensure
- File.delete symlink_name
- end
- rescue NotImplementedError => e
- skip e.message
- end
+ end
+
+ begin
+ tmpdir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+ rescue => e
+ skip e.message
+ end
+
+ symlink_name = "#{tmpdir}/test-bin-dummy"
+ begin
+ assert_equal 0, File.symlink(target_name, symlink_name)
+ assert_equal true, File.symlink?(symlink_name)
+ rescue NotImplementedError => e
+ skip e.message
+ ensure
+ File.delete symlink_name rescue nil
+ MRubyIOTestUtil.rmdir tmpdir rescue nil
end
end
diff --git a/mrbgems/mruby-io/test/io.rb b/mrbgems/mruby-io/test/io.rb
index 5004d0042..3aaa109a8 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -4,25 +4,27 @@
MRubyIOTestUtil.io_test_setup
$cr, $crlf, $cmd = MRubyIOTestUtil.win? ? [1, "\r\n", "cmd /c "] : [0, "\n", ""]
-assert_io_open = ->(meth) do
- fd = IO.sysopen($mrbtest_io_rfname)
- assert_equal Fixnum, fd.class
- io1 = IO.__send__(meth, fd)
- begin
- assert_equal IO, io1.class
- assert_equal $mrbtest_io_msg, io1.read
- ensure
- io1.close
- end
+def assert_io_open(meth)
+ assert "assert_io_open" do
+ fd = IO.sysopen($mrbtest_io_rfname)
+ assert_equal Fixnum, fd.class
+ io1 = IO.__send__(meth, fd)
+ begin
+ assert_equal IO, io1.class
+ assert_equal $mrbtest_io_msg, io1.read
+ ensure
+ io1.close
+ end
- io2 = IO.__send__(meth, IO.sysopen($mrbtest_io_rfname))do |io|
- if meth == :open
- assert_equal $mrbtest_io_msg, io.read
- else
- flunk "IO.#{meth} does not take block"
+ io2 = IO.__send__(meth, IO.sysopen($mrbtest_io_rfname))do |io|
+ if meth == :open
+ assert_equal $mrbtest_io_msg, io.read
+ else
+ flunk "IO.#{meth} does not take block"
+ end
end
+ io2.close unless meth == :open
end
- io2.close unless meth == :open
end
assert('IO.class', '15.2.20') do
@@ -38,7 +40,7 @@ assert('IO.ancestors', '15.2.20.3') do
end
assert('IO.open', '15.2.20.4.1') do
- assert_io_open.(:open)
+ assert_io_open(:open)
end
assert('IO#close', '15.2.20.5.1') do
@@ -224,11 +226,11 @@ assert('IO#dup for writable') do
end
assert('IO.for_fd') do
- assert_io_open.(:for_fd)
+ assert_io_open(:for_fd)
end
assert('IO.new') do
- assert_io_open.(:new)
+ assert_io_open(:new)
end
assert('IO gc check') do
diff --git a/mrbgems/mruby-io/test/mruby_io_test.c b/mrbgems/mruby-io/test/mruby_io_test.c
index 3312d6c7e..2c8a75fc9 100644
--- a/mrbgems/mruby-io/test/mruby_io_test.c
+++ b/mrbgems/mruby-io/test/mruby_io_test.c
@@ -136,9 +136,9 @@ mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
sun0.sun_family = AF_UNIX;
snprintf(sun0.sun_path, sizeof(sun0.sun_path), "%s", socketname);
if (bind(fd3, (struct sockaddr *)&sun0, sizeof(sun0)) == -1) {
- mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %S: %S",
- mrb_str_new_cstr(mrb, sun0.sun_path),
- mrb_fixnum_value(errno));
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %s: %d",
+ sun0.sun_path,
+ errno);
}
close(fd3);
#endif
diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c
index 8e7e03c56..376751e10 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -31,22 +31,21 @@ mrb_f_caller(mrb_state *mrb, mrb_value self)
}
}
else {
- v = mrb_to_int(mrb, v);
- lev = mrb_fixnum(v);
+ lev = mrb_int(mrb, v);
if (lev < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%v)", v);
}
n = bt_len - lev;
}
break;
case 2:
- lev = mrb_fixnum(mrb_to_int(mrb, v));
- n = mrb_fixnum(mrb_to_int(mrb, length));
+ lev = mrb_int(mrb, v);
+ n = mrb_int(mrb, length);
if (lev < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%v)", v);
}
if (n < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%v)", length);
}
break;
default:
diff --git a/mrbgems/mruby-math/src/math.c b/mrbgems/mruby-math/src/math.c
index caa16b789..88b33771b 100644
--- a/mrbgems/mruby-math/src/math.c
+++ b/mrbgems/mruby-math/src/math.c
@@ -4,6 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Math conflicts 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/array.h>
@@ -14,8 +18,7 @@ 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);
+ mrb_raisef(mrb, domainerror, "Numerical argument is out of domain - %s", func);
}
/* math functions not provided by Microsoft Visual C++ 2012 or older */
diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
index 62daa5227..97f53051f 100644
--- a/mrbgems/mruby-metaprog/src/metaprog.c
+++ b/mrbgems/mruby-metaprog/src/metaprog.c
@@ -414,7 +414,7 @@ check_cv_name_sym(mrb_state *mrb, mrb_sym id)
mrb_int len;
const char *name = mrb_sym2name_len(mrb, id, &len);
if (!cv_name_p(mrb, name, len)) {
- mrb_name_error(mrb, id, "'%S' is not allowed as a class variable name", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "'%n' is not allowed as a class variable name", id);
}
}
@@ -454,12 +454,10 @@ mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
if (!mrb_undef_p(val)) return val;
if (mrb_cv_defined(mrb, mod, id)) {
- mrb_name_error(mrb, id, "cannot remove %S for %S",
- mrb_sym2str(mrb, id), mod);
+ mrb_name_error(mrb, id, "cannot remove %n for %v", id, mod);
}
- mrb_name_error(mrb, id, "class variable %S not defined for %S",
- mrb_sym2str(mrb, id), mod);
+ mrb_name_error(mrb, id, "class variable %n not defined for %v", id, mod);
/* not reached */
return mrb_nil_value();
@@ -622,8 +620,7 @@ remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
}
}
- mrb_name_error(mrb, mid, "method '%S' not defined in %S",
- mrb_sym2str(mrb, mid), mod);
+ mrb_name_error(mrb, mid, "method '%n' not defined in %v", mid, mod);
}
/* 15.2.2.4.41 */
diff --git a/mrbgems/mruby-metaprog/test/metaprog.rb b/mrbgems/mruby-metaprog/test/metaprog.rb
index 1e1d4ff26..30b75bd60 100644
--- a/mrbgems/mruby-metaprog/test/metaprog.rb
+++ b/mrbgems/mruby-metaprog/test/metaprog.rb
@@ -44,6 +44,8 @@ assert('Kernel#instance_variable_set', '15.3.1.3.22') do
%w[@6 @% @@a @ a].each do |n|
assert_raise(NameError) { o.instance_variable_set(n, 1) }
end
+ assert_raise(FrozenError) { o.freeze.instance_variable_set(:@a, 2) }
+ assert_raise(FrozenError, ArgumentError) { nil.instance_variable_set(:@a, 2) }
end
assert('Kernel#instance_variables', '15.3.1.3.23') do
@@ -121,6 +123,8 @@ assert('Kernel#define_singleton_method') do
end
assert_equal :test_method, ret
assert_equal :singleton_method_ok, o.test_method
+ assert_raise(TypeError) { 2.define_singleton_method(:f){} }
+ assert_raise(FrozenError) { [].freeze.define_singleton_method(:f){} }
end
assert('Kernel#singleton_class') do
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c
index 9f1134227..b5050368d 100644
--- a/mrbgems/mruby-method/src/method.c
+++ b/mrbgems/mruby-method/src/method.c
@@ -29,8 +29,7 @@ unbound_method_bind(mrb_state *mrb, mrb_value self)
if (mrb_type(owner) == MRB_TT_SCLASS) {
mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object");
} else {
- const char *s = mrb_class_name(mrb, mrb_class_ptr(owner));
- mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %S", mrb_str_new_static(mrb, s, strlen(s)));
+ mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %v", owner);
}
}
me = method_object_alloc(mrb, mrb_class_get(mrb, "Method"));
@@ -270,16 +269,16 @@ method_to_s(mrb_state *mrb, mrb_value self)
mrb_str_cat_lit(mrb, str, ": ");
rklass = mrb_class_ptr(klass);
if (mrb_class_ptr(owner) == rklass) {
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, owner));
mrb_str_cat_lit(mrb, str, "#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, name));
}
else {
mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass));
mrb_str_cat_lit(mrb, str, "(");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, owner));
mrb_str_cat_lit(mrb, str, ")#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, name));
}
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -289,7 +288,6 @@ static void
mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, struct RClass **owner, struct RProc **proc, mrb_bool unbound)
{
mrb_value ret;
- const char *s;
*owner = c;
*proc = method_search_vm(mrb, owner, name);
@@ -313,13 +311,7 @@ mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym
return;
name_error:
- s = mrb_class_name(mrb, c);
- mrb_raisef(
- mrb, E_NAME_ERROR,
- "undefined method '%S' for class '%S'",
- mrb_sym2str(mrb, name),
- mrb_str_new_static(mrb, s, strlen(s))
- );
+ mrb_raisef(mrb, E_NAME_ERROR, "undefined method '%n' for class '%C'", name, c);
}
static mrb_value
diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
index cd8bbf187..f8aff54bc 100644
--- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c
+++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -1,38 +1,6 @@
#include <limits.h>
#include <mruby.h>
-
-static inline mrb_int
-to_int(mrb_state *mrb, mrb_value x)
-{
- x = mrb_to_int(mrb, x);
- return mrb_fixnum(x);
-}
-
-/*
- * Document-method: Integer#chr
- * call-seq:
- * int.chr -> string
- *
- * Returns a string containing the character represented by the +int+'s value
- * according to +encoding+.
- *
- * 65.chr #=> "A"
- * 230.chr #=> "\xE6"
- */
-static mrb_value
-mrb_int_chr(mrb_state *mrb, mrb_value x)
-{
- mrb_int chr;
- char c;
-
- chr = to_int(mrb, 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);
-}
+#include <mruby/numeric.h>
/*
* call-seq:
@@ -46,7 +14,7 @@ mrb_int_allbits(mrb_state *mrb, mrb_value self)
mrb_int n, m;
mrb_get_args(mrb, "i", &m);
- n = to_int(mrb, self);
+ n = mrb_int(mrb, self);
return mrb_bool_value((n & m) == m);
}
@@ -62,7 +30,7 @@ mrb_int_anybits(mrb_state *mrb, mrb_value self)
mrb_int n, m;
mrb_get_args(mrb, "i", &m);
- n = to_int(mrb, self);
+ n = mrb_int(mrb, self);
return mrb_bool_value((n & m) != 0);
}
@@ -78,7 +46,7 @@ mrb_int_nobits(mrb_state *mrb, mrb_value self)
mrb_int n, m;
mrb_get_args(mrb, "i", &m);
- n = to_int(mrb, self);
+ n = mrb_int(mrb, self);
return mrb_bool_value((n & m) == 0);
}
@@ -87,10 +55,22 @@ mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
{
struct RClass *i = mrb_module_get(mrb, "Integral");
- mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE());
mrb_define_method(mrb, i, "allbits?", mrb_int_allbits, MRB_ARGS_REQ(1));
mrb_define_method(mrb, i, "anybits?", mrb_int_anybits, MRB_ARGS_REQ(1));
mrb_define_method(mrb, i, "nobits?", mrb_int_nobits, MRB_ARGS_REQ(1));
+
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_define_const(mrb, mrb->float_class, "RADIX", mrb_fixnum_value(MRB_FLT_RADIX));
+ mrb_define_const(mrb, mrb->float_class, "MANT_DIG", mrb_fixnum_value(MRB_FLT_MANT_DIG));
+ mrb_define_const(mrb, mrb->float_class, "EPSILON", mrb_float_value(mrb, MRB_FLT_EPSILON));
+ mrb_define_const(mrb, mrb->float_class, "DIG", mrb_fixnum_value(MRB_FLT_DIG));
+ mrb_define_const(mrb, mrb->float_class, "MIN_EXP", mrb_fixnum_value(MRB_FLT_MIN_EXP));
+ mrb_define_const(mrb, mrb->float_class, "MIN", mrb_float_value(mrb, MRB_FLT_MIN));
+ mrb_define_const(mrb, mrb->float_class, "MIN_10_EXP", mrb_fixnum_value(MRB_FLT_MIN_10_EXP));
+ mrb_define_const(mrb, mrb->float_class, "MAX_EXP", mrb_fixnum_value(MRB_FLT_MAX_EXP));
+ mrb_define_const(mrb, mrb->float_class, "MAX", mrb_float_value(mrb, MRB_FLT_MAX));
+ mrb_define_const(mrb, mrb->float_class, "MAX_10_EXP", mrb_fixnum_value(MRB_FLT_MAX_10_EXP));
+#endif /* MRB_WITHOUT_FLOAT */
}
void
diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb
index c85cb61f2..efdf25a34 100644
--- a/mrbgems/mruby-numeric-ext/test/numeric.rb
+++ b/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -1,14 +1,6 @@
##
# 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
diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c
index 85db75b28..0aedc9a73 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -1,6 +1,7 @@
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
+#include <mruby/hash.h>
#include <mruby/proc.h>
/*
@@ -33,6 +34,19 @@ nil_to_f(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * nil.to_h -> {}
+ *
+ * Always returns an empty hash.
+ */
+
+static mrb_value
+nil_to_h(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_hash_new(mrb);
+}
+
+/*
+ * call-seq:
* nil.to_i -> 0
*
* Always returns zero.
@@ -117,6 +131,7 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE());
#endif
+ mrb_define_method(mrb, n, "to_h", nil_to_h, MRB_ARGS_NONE());
mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->kernel_module, "itself", mrb_f_itself, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-object-ext/test/nil.rb b/mrbgems/mruby-object-ext/test/nil.rb
index fbff20629..9975e785a 100644
--- a/mrbgems/mruby-object-ext/test/nil.rb
+++ b/mrbgems/mruby-object-ext/test/nil.rb
@@ -7,6 +7,10 @@ assert('NilClass#to_f') do
assert_equal 0.0, nil.to_f
end
+assert('NilClass#to_h') do
+ assert_equal Hash.new, nil.to_h
+end
+
assert('NilClass#to_i') do
assert_equal 0, nil.to_i
end
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c
index 46c0cc1fe..159ba09ac 100644
--- a/mrbgems/mruby-pack/src/pack.c
+++ b/mrbgems/mruby-pack/src/pack.c
@@ -529,8 +529,8 @@ utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
}
if (n > *lenp) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
- mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %d bytes, given %d bytes)",
+ n, *lenp);
}
*lenp = n--;
if (n != 0) {
@@ -976,7 +976,7 @@ alias:
case 4: t = 'L'; goto alias;
case 8: t = 'Q'; goto alias;
default:
- mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %d", (int)sizeof(int));
}
break;
case 'i':
@@ -985,7 +985,7 @@ alias:
case 4: t = 'l'; goto alias;
case 8: t = 'q'; goto alias;
default:
- mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
+ mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %d", (int)sizeof(int));
}
break;
case 'L':
@@ -1002,7 +1002,7 @@ alias:
case 'm':
dir = PACK_DIR_BASE64;
type = PACK_TYPE_STRING;
- flags |= PACK_FLAG_WIDTH;
+ flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
break;
case 'N': /* = "L>" */
dir = PACK_DIR_LONG;
@@ -1086,8 +1086,7 @@ alias:
count = -1;
} else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') {
if (strchr("sSiIlLqQ", (int)t) == NULL) {
- char ch_str = (char)ch;
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%S' allowed only after types sSiIlLqQ", mrb_str_new(mrb, &ch_str, 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%c' allowed only after types sSiIlLqQ", ch);
}
if (ch == '_' || ch == '!') {
flags |= PACK_FLAG_s;
@@ -1156,7 +1155,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
#endif
else if (type == PACK_TYPE_STRING) {
if (!mrb_string_p(o)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %T into String", o);
}
}
@@ -1196,7 +1195,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
default:
break;
}
- if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
+ if (dir == PACK_DIR_STR || dir == PACK_DIR_BASE64) { /* always consumes 1 entry */
aidx++;
break;
}
@@ -1249,6 +1248,9 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
case PACK_DIR_STR:
srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
break;
+ case PACK_DIR_BASE64:
+ srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
+ break;
}
continue;
}
@@ -1275,9 +1277,6 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
case PACK_DIR_QUAD:
srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
break;
- case PACK_DIR_BASE64:
- srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
- break;
#ifndef MRB_WITHOUT_FLOAT
case PACK_DIR_FLOAT:
srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
@@ -1299,7 +1298,12 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
if (single) break;
}
- if (single) return RARRAY_PTR(result)[0];
+ if (single) {
+ if (RARRAY_LEN(result) > 0) {
+ return RARRAY_PTR(result)[0];
+ }
+ return mrb_nil_value();
+ }
return result;
}
diff --git a/mrbgems/mruby-pack/test/pack.rb b/mrbgems/mruby-pack/test/pack.rb
index 110aee5db..6832adcc7 100644
--- a/mrbgems/mruby-pack/test/pack.rb
+++ b/mrbgems/mruby-pack/test/pack.rb
@@ -2,8 +2,10 @@ PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
def assert_pack tmpl, packed, unpacked
t = tmpl.inspect
- assert_equal packed, unpacked.pack(tmpl), "#{unpacked.inspect}.pack(#{t})"
- assert_equal unpacked, packed.unpack(tmpl), "#{packed.inspect}.unpack(#{t})"
+ assert "assert_pack" do
+ assert_equal packed, unpacked.pack(tmpl), "#{unpacked.inspect}.pack(#{t})"
+ assert_equal unpacked, packed.unpack(tmpl), "#{packed.inspect}.unpack(#{t})"
+ end
end
# pack & unpack 'm' (base64)
@@ -33,6 +35,17 @@ assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do
assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m")
end
+assert('["A", "B"].pack') do
+ assert_equal "QQ==\n", ["A", "B"].pack("m50")
+ assert_equal ["A"], "QQ==\n".unpack("m50")
+ assert_equal "QQ==Qg==", ["A", "B"].pack("m0 m0")
+ assert_equal ["A", "B"], "QQ==Qg==".unpack("m10 m10")
+end
+
+assert('["abc..xyzABC..XYZ"].pack("m0")') do
+ assert_pack "m0", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+end
+
# pack & unpack 'H'
assert('["3031"].pack("H*")') do
assert_pack "H*", "01", ["3031"]
diff --git a/mrbgems/mruby-random/src/mt19937ar.c b/mrbgems/mruby-random/src/mt19937ar.c
deleted file mode 100644
index 405bd5c20..000000000
--- a/mrbgems/mruby-random/src/mt19937ar.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-** 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/mrbgems/mruby-random/src/mt19937ar.h b/mrbgems/mruby-random/src/mt19937ar.h
deleted file mode 100644
index 7d382320d..000000000
--- a/mrbgems/mruby-random/src/mt19937ar.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-** 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/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index 68209840a..af9876ce7 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -9,62 +9,105 @@
#include <mruby/class.h>
#include <mruby/data.h>
#include <mruby/array.h>
-#include "mt19937ar.h"
+#include <mruby/istruct.h>
+#if INT32_MAX <= INTPTR_MAX
+# define XORSHIFT96
+# define NSEEDS 3
+#else
+# define NSEEDS 4
+#endif
+#define LASTSEED (NSEEDS-1)
#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);
+typedef struct rand_state {
+ uint32_t seed[NSEEDS];
+} rand_state;
static void
-mt_srand(mt_state *t, unsigned long seed)
+rand_init(rand_state *t)
{
- mrb_random_init_genrand(t, seed);
+ t->seed[0] = 123456789;
+ t->seed[1] = 362436069;
+ t->seed[2] = 521288629;
+#ifndef XORSHIFT96
+ t->seed[3] = 88675123;
+#endif
}
-static unsigned long
-mt_rand(mt_state *t)
+static uint32_t
+rand_seed(rand_state *t, uint32_t seed)
{
- return mrb_random_genrand_int32(t);
+ uint32_t old_seed = t->seed[LASTSEED];
+ rand_init(t);
+ t->seed[LASTSEED] = seed;
+ return old_seed;
}
-static double
-mt_rand_real(mt_state *t)
+#ifdef XORSHIFT96
+static uint32_t
+rand_uint32(rand_state *state)
{
- return mrb_random_genrand_real1(t);
+ uint32_t *seed = state->seed;
+ uint32_t x = seed[0];
+ uint32_t y = seed[1];
+ uint32_t z = seed[2];
+ uint32_t t;
+
+ t = (x ^ (x << 3)) ^ (y ^ (y >> 19)) ^ (z ^ (z << 6));
+ x = y; y = z; z = t;
+ seed[0] = x;
+ seed[1] = y;
+ seed[2] = z;
+
+ return z;
}
-
-static mrb_value
-mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed)
+#else /* XORSHIFT96 */
+static uint32_t
+rand_uint32(rand_state *state)
{
- 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));
+ uint32_t *seed = state->seed;
+ uint32_t x = seed[0];
+ uint32_t y = seed[1];
+ uint32_t z = seed[2];
+ uint32_t w = seed[3];
+ uint32_t t;
+
+ t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+ seed[0] = x;
+ seed[1] = y;
+ seed[2] = z;
+ seed[3] = w;
+
+ return w;
+}
+#endif /* XORSHIFT96 */
- return seed;
+#ifndef MRB_WITHOUT_FLOAT
+static double
+rand_real(rand_state *t)
+{
+ uint32_t x = rand_uint32(t);
+ return x*(1.0/4294967295.0);
}
+#endif
static mrb_value
-mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max)
+random_rand(mrb_state *mrb, rand_state *t, mrb_value max)
{
mrb_value value;
if (mrb_fixnum(max) == 0) {
- value = mrb_float_value(mrb, mt_rand_real(t));
+#ifndef MRB_WITHOUT_FLOAT
+ value = mrb_float_value(mrb, rand_real(t));
+#else
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Float not supported");
+#endif
}
else {
- value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max));
+ value = mrb_fixnum_value(rand_uint32(t) % mrb_fixnum(max));
}
return value;
@@ -90,106 +133,74 @@ get_opt(mrb_state* mrb)
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 void
+random_check(mrb_state *mrb, mrb_value random) {
+ struct RClass *c = mrb_class_get(mrb, "Random");
+ if (!mrb_obj_is_kind_of(mrb, random, c) || mrb_type(random) != MRB_TT_ISTRUCT) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Random instance required");
+ }
}
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);
+random_default(mrb_state *mrb) {
+ struct RClass *c = mrb_class_get(mrb, "Random");
+ mrb_value d = mrb_const_get(mrb, mrb_obj_value(c), mrb_intern_lit(mrb, "DEFAULT"));
+ if (!mrb_obj_is_kind_of(mrb, d, c)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Random::DEFAULT replaced");
+ }
+ return d;
}
-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);
-}
+#define random_ptr(v) (rand_state*)mrb_istruct_ptr(v)
+#define random_default_state(mrb) random_ptr(random_default(mrb))
static mrb_value
-mrb_random_init(mrb_state *mrb, mrb_value self)
+random_m_init(mrb_state *mrb, mrb_value self)
{
mrb_value seed;
- mt_state *t;
+ rand_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);
+ t = random_ptr(self);
if (mrb_nil_p(seed)) {
- t->has_seed = FALSE;
+ rand_init(t);
}
else {
- mrb_assert(mrb_fixnum_p(seed));
- t->has_seed = TRUE;
- t->seed = mrb_fixnum(seed);
+ rand_seed(t, (uint32_t)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)
+random_m_rand(mrb_state *mrb, mrb_value self)
{
mrb_value max;
- mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
+ rand_state *t = random_ptr(self);
max = get_opt(mrb);
- mrb_random_rand_seed(mrb, t);
- return mrb_random_mt_rand(mrb, t, max);
+ return random_rand(mrb, t, max);
}
static mrb_value
-mrb_random_srand(mrb_state *mrb, mrb_value self)
+random_m_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;
+ uint32_t seed;
+ uint32_t old_seed;
+ mrb_value sv;
+ rand_state *t = random_ptr(self);
+
+ sv = get_opt(mrb);
+ if (mrb_nil_p(sv)) {
+ seed = (uint32_t)time(NULL) + rand_uint32(t);
}
else {
- mrb_assert(mrb_fixnum_p(seed));
- t->has_seed = TRUE;
- t->seed = mrb_fixnum(seed);
+ seed = (uint32_t)mrb_fixnum(sv);
}
+ old_seed = rand_seed(t, seed);
- return old_seed;
+ return mrb_fixnum_value((mrb_int)old_seed);
}
/*
@@ -203,25 +214,28 @@ static mrb_value
mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
{
mrb_int i;
- mt_state *random = NULL;
+ mrb_value max;
+ mrb_value r = mrb_nil_value();
+ rand_state *random;
if (RARRAY_LEN(ary) > 1) {
- mrb_get_args(mrb, "|d", &random, &mt_state_type);
+ mrb_get_args(mrb, "|o", &r);
- if (random == NULL) {
- random = get_random_state(mrb);
+ if (mrb_nil_p(r)) {
+ random = random_default_state(mrb);
+ }
+ else {
+ random_check(mrb, r);
+ random = random_ptr(r);
}
- mrb_random_rand_seed(mrb, random);
-
mrb_ary_modify(mrb, mrb_ary_ptr(ary));
-
+ max = mrb_fixnum_value(RARRAY_LEN(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))));
+ j = mrb_fixnum(random_rand(mrb, random, max));
tmp = ptr[i];
ptr[i] = ptr[j];
@@ -268,15 +282,18 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
{
mrb_int n = 0;
mrb_bool given;
- mt_state *random = NULL;
+ mrb_value r = mrb_nil_value();
+ rand_state *random;
mrb_int len;
- mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
- if (random == NULL) {
- random = get_random_state(mrb);
+ mrb_get_args(mrb, "|i?o", &n, &given, &r);
+ if (mrb_nil_p(r)) {
+ random = random_default_state(mrb);
+ }
+ else {
+ random_check(mrb, r);
+ random = random_ptr(r);
}
- mrb_random_rand_seed(mrb, random);
- mt_rand(random);
len = RARRAY_LEN(ary);
if (!given) { /* pick one element */
switch (len) {
@@ -285,7 +302,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
case 1:
return RARRAY_PTR(ary)[0];
default:
- return RARRAY_PTR(ary)[mt_rand(random) % len];
+ return RARRAY_PTR(ary)[rand_uint32(random) % len];
}
}
else {
@@ -300,7 +317,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
for (;;) {
retry:
- r = mt_rand(random) % len;
+ r = (mrb_int)(rand_uint32(random) % len);
for (j=0; j<i; j++) {
if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) {
@@ -318,23 +335,39 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
}
}
+static mrb_value
+random_f_rand(mrb_state *mrb, mrb_value self)
+{
+ rand_state *t = random_default_state(mrb);
+ return random_rand(mrb, t, get_opt(mrb));
+}
+
+static mrb_value
+random_f_srand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value random = random_default(mrb);
+ return random_m_srand(mrb, random);
+}
+
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));
+ mrb_assert(sizeof(rand_state) <= ISTRUCT_DATA_SIZE);
+
+ mrb_define_method(mrb, mrb->kernel_module, "rand", random_f_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "srand", random_f_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_SET_INSTANCE_TT(random, MRB_TT_ISTRUCT);
+ mrb_define_class_method(mrb, random, "rand", random_f_rand, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, random, "srand", random_f_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, random, "initialize", random_m_init, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "rand", random_m_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "srand", random_m_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));
diff --git a/mrbgems/mruby-random/src/random.h b/mrbgems/mruby-random/src/random.h
deleted file mode 100644
index a4785ae5a..000000000
--- a/mrbgems/mruby-random/src/random.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-** 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/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb
index de7925ba7..a213beb57 100644
--- a/mrbgems/mruby-range-ext/mrblib/range.rb
+++ b/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -25,4 +25,42 @@ class Range
end
ary
end
+
+ def max(&block)
+ val = self.first
+ last = self.last
+ return super if block
+
+ # fast path for numerics
+ if val.kind_of?(Numeric) && last.kind_of?(Numeric)
+ raise TypeError if exclude_end? && !last.kind_of?(Fixnum)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ max = last
+ max -= 1 if exclude_end?
+ return max
+ end
+
+ # delegate to Enumerable
+ super
+ end
+
+ def min(&block)
+ val = self.first
+ last = self.last
+ return super if block
+
+ # fast path for numerics
+ if val.kind_of?(Numeric) && last.kind_of?(Numeric)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ min = val
+ return min
+ end
+
+ # delegate to Enumerable
+ super
+ end
end
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index 1f6690904..b8c76b796 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -106,6 +106,7 @@ range_last(mrb_state *mrb, mrb_value range)
* ('a'..'z').size #=> nil
*/
+#ifndef MRB_WITHOUT_FLOAT
static mrb_value
range_size(mrb_state *mrb, mrb_value range)
{
@@ -158,6 +159,28 @@ range_size(mrb_state *mrb, mrb_value range)
}
return mrb_nil_value();
}
+#else
+static mrb_value
+range_size(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg, end;
+ mrb_int excl;
+
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ excl = RANGE_EXCL(r) ? 0 : 1;
+
+ if (mrb_fixnum_p(beg) && mrb_fixnum_p(end)) {
+ mrb_int a = mrb_fixnum(beg);
+ mrb_int b = mrb_fixnum(end);
+ mrb_int c = b - a + excl;
+
+ return mrb_fixnum_value(c);
+ }
+ return mrb_nil_value();
+}
+#endif /* MRB_WITHOUT_FLOAT */
void
mrb_mruby_range_ext_gem_init(mrb_state* mrb)
diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb
index efcbdabe4..865e46d02 100644
--- a/mrbgems/mruby-range-ext/test/range.rb
+++ b/mrbgems/mruby-range-ext/test/range.rb
@@ -10,6 +10,8 @@ end
assert('Range#first') do
assert_equal 10, (10..20).first
assert_equal [10, 11, 12], (10..20).first(3)
+
+ skip unless Object.const_defined?(:Float)
assert_equal [0, 1, 2], (0..Float::INFINITY).first(3)
end
@@ -23,10 +25,118 @@ end
assert('Range#size') do
assert_equal 42, (1..42).size
assert_equal 41, (1...42).size
+ assert_nil ('a'..'z').size
+
+ skip unless Object.const_defined?(:Float)
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
+
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 10, (1..10).max
+ assert_equal 9, (1...10).max
+ assert_equal 536870911, (0...2**29).max
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (100..10).max
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ skip unless Object.const_defined?(:Float)
+
+ # returns the maximum value in the Float range when called with no arguments
+ assert_equal 908.1111, (303.20..908.1111).max
+
+ # raises TypeError when called on an exclusive range and a non Integer value
+ assert_raise(TypeError) { (303.20...908.1111).max }
+
+ # returns nil when the endpoint is less than the start point in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#max given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).max do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).max do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 2], [4, 3], [5, 4]], acc
+
+ # returns the element the block determines to be the maximum
+ assert_equal 1, ((1..3).max { |_a, _b| -3 })
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ((100..10).max { |x, y| x <=> y })
+ assert_equal nil, ((5...5).max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 1, (1..10).min
+ assert_equal 1, (1...10).min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (100..10).min
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ skip unless Object.const_defined?(:Float)
+
+ # returns the minimum value in the Float range when called with no arguments
+ assert_equal 303.20, (303.20..908.1111).min
+
+ # returns nil when the start point is greater than the endpoint in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#min given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).min do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).min do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 1], [4, 1], [5, 1]], acc
+
+ # returns the element the block determines to be the minimum
+ assert_equal 3, ((1..3).min { |_a, _b| -3 })
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ((100..10).min { |x, y| x <=> y })
+ assert_equal nil, ((5...5).min { |x, y| x <=> y })
end
diff --git a/mrbgems/mruby-rational/mrblib/rational.rb b/mrbgems/mruby-rational/mrblib/rational.rb
index 93af72b96..c0b16a6ae 100644
--- a/mrbgems/mruby-rational/mrblib/rational.rb
+++ b/mrbgems/mruby-rational/mrblib/rational.rb
@@ -89,28 +89,29 @@ module Kernel
a, b = b, a % b until b == 0
Rational._new(numerator.div(a), denominator.div(a))
end
-end
-[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
- Fixnum.instance_eval do
- original_operator_name = "__original_operator_#{op}_rational"
- alias_method original_operator_name, op
- define_method op do |rhs|
- if rhs.is_a? Rational
- Rational(self).__send__(op, rhs)
- else
- __send__(original_operator_name, rhs)
+ [:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
+ Fixnum.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ Rational(self).__send__(op, rhs)
+ else
+ __send__(original_operator_name, rhs)
+ end
end
end
- end
- Float.instance_eval do
- original_operator_name = "__original_operator_#{op}_rational"
- alias_method original_operator_name, op
- define_method op do |rhs|
- if rhs.is_a? Rational
- rhs = rhs.to_f
+ Float.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ rhs = rhs.to_f
+ end
+ __send__(original_operator_name, rhs)
end
- __send__(original_operator_name, rhs)
- end
- end if Object.const_defined?(:Float)
+ end if Object.const_defined?(:Float)
+ end
end
+
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
index fa061c0b8..09bd68003 100644
--- a/mrbgems/mruby-rational/src/rational.c
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -1,30 +1,72 @@
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/string.h>
-#include <mruby/istruct.h>
+#include <mruby/numeric.h>
struct mrb_rational {
mrb_int numerator;
mrb_int denominator;
};
+#if MRB_INT_MAX <= INTPTR_MAX
+
+#define RATIONAL_USE_ISTRUCT
+/* use TT_ISTRUCT */
+#include <mruby/istruct.h>
+
+#define rational_ptr(mrb, v) (struct mrb_rational*)mrb_istruct_ptr(v)
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RIStruct *s;
+
+ s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ *p = (struct mrb_rational*)s->inline_data;
+
+ return (struct RBasic*)s;
+}
+
+#else
+/* use TT_DATA */
+#include <mruby/data.h>
+
+static const struct mrb_data_type mrb_rational_type = {"Rational", mrb_free};
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RData *d;
+
+ Data_Make_Struct(mrb, c, struct mrb_rational, &mrb_rational_type, *p, d);
+
+ return (struct RBasic*)d;
+}
+
static struct mrb_rational*
-rational_ptr(mrb_value v)
+rational_ptr(mrb_state *mrb, mrb_value v)
{
- return (struct mrb_rational*)mrb_istruct_ptr(v);
+ struct mrb_rational *p;
+
+ p = DATA_GET_PTR(mrb, v, &mrb_rational_type, struct mrb_rational);
+ if (!p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
+ }
+ return p;
}
+#endif
static mrb_value
rational_numerator(mrb_state *mrb, mrb_value self)
{
- struct mrb_rational *p = rational_ptr(self);
+ struct mrb_rational *p = rational_ptr(mrb, self);
return mrb_fixnum_value(p->numerator);
}
static mrb_value
rational_denominator(mrb_state *mrb, mrb_value self)
{
- struct mrb_rational *p = rational_ptr(self);
+ struct mrb_rational *p = rational_ptr(mrb, self);
return mrb_fixnum_value(p->denominator);
}
@@ -32,13 +74,12 @@ static mrb_value
rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
{
struct RClass *c = mrb_class_get(mrb, "Rational");
- struct RIStruct *s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
- mrb_value rat = mrb_obj_value(s);
- struct mrb_rational *p = rational_ptr(rat);
+ struct mrb_rational *p;
+ struct RBasic *rat = rational_alloc(mrb, c, &p);
p->numerator = numerator;
p->denominator = denominator;
- MRB_SET_FROZEN_FLAG(s);
- return rat;
+ MRB_SET_FROZEN_FLAG(rat);
+ return mrb_obj_value(rat);
}
static mrb_value
@@ -46,7 +87,52 @@ rational_s_new(mrb_state *mrb, mrb_value self)
{
mrb_int numerator, denominator;
+#ifdef MRB_WITHOUT_FLOAT
mrb_get_args(mrb, "ii", &numerator, &denominator);
+#else
+
+#define DROP_PRECISION(cond, num, denom) \
+ do { \
+ while (cond) { \
+ num /= 2; \
+ denom /= 2; \
+ } \
+ } while (0)
+
+ mrb_value numv, denomv;
+
+ mrb_get_args(mrb, "oo", &numv, &denomv);
+ if (mrb_fixnum_p(numv)) {
+ numerator = mrb_fixnum(numv);
+
+ if (mrb_fixnum_p(denomv)) {
+ denominator = mrb_fixnum(denomv);
+ }
+ else {
+ mrb_float denomf = mrb_to_flo(mrb, denomv);
+
+ DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numerator, denomf);
+ denominator = denomf;
+ }
+ }
+ else {
+ mrb_float numf = mrb_to_flo(mrb, numv);
+
+ if (mrb_fixnum_p(denomv)) {
+ denominator = mrb_fixnum(denomv);
+ }
+ else {
+ mrb_float denomf = mrb_to_flo(mrb, denomv);
+
+ DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numf, denomf);
+ denominator = denomf;
+ }
+
+ DROP_PRECISION(numf < MRB_INT_MIN || numf > MRB_INT_MAX, numf, denominator);
+ numerator = numf;
+ }
+#endif
+
return rational_new(mrb, numerator, denominator);
}
@@ -54,7 +140,7 @@ rational_s_new(mrb_state *mrb, mrb_value self)
static mrb_value
rational_to_f(mrb_state *mrb, mrb_value self)
{
- struct mrb_rational *p = rational_ptr(self);
+ struct mrb_rational *p = rational_ptr(mrb, self);
mrb_float f = (mrb_float)p->numerator / (mrb_float)p->denominator;
return mrb_float_value(mrb, f);
@@ -64,7 +150,10 @@ rational_to_f(mrb_state *mrb, mrb_value self)
static mrb_value
rational_to_i(mrb_state *mrb, mrb_value self)
{
- struct mrb_rational *p = rational_ptr(self);
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->denominator == 0) {
+ mrb_raise(mrb, mrb_exc_get(mrb, "StandardError"), "divided by 0");
+ }
return mrb_fixnum_value(p->numerator / p->denominator);
}
@@ -77,7 +166,7 @@ rational_to_r(mrb_state *mrb, mrb_value self)
static mrb_value
rational_negative_p(mrb_state *mrb, mrb_value self)
{
- struct mrb_rational *p = rational_ptr(self);
+ struct mrb_rational *p = rational_ptr(mrb, self);
if (p->numerator < 0) {
return mrb_true_value();
}
@@ -94,9 +183,13 @@ void mrb_mruby_rational_gem_init(mrb_state *mrb)
{
struct RClass *rat;
- mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "Numeric"));
+#ifdef RATIONAL_USE_ISTRUCT
MRB_SET_INSTANCE_TT(rat, MRB_TT_ISTRUCT);
+ mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
+#else
+ MRB_SET_INSTANCE_TT(rat, MRB_TT_DATA);
+#endif
mrb_undef_class_method(mrb, rat, "new");
mrb_define_class_method(mrb, rat, "_new", rational_s_new, MRB_ARGS_REQ(2));
mrb_define_method(mrb, rat, "numerator", rational_numerator, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-rational/test/rational.rb b/mrbgems/mruby-rational/test/rational.rb
index 5e9d9ea48..a8ebb8ea2 100644
--- a/mrbgems/mruby-rational/test/rational.rb
+++ b/mrbgems/mruby-rational/test/rational.rb
@@ -23,17 +23,21 @@ class ComplexLikeNumeric < UserDefinedNumeric
end
def assert_rational(exp, real)
- assert_float exp.numerator, real.numerator
- assert_float exp.denominator, real.denominator
+ assert "assert_rational" do
+ assert_float exp.numerator, real.numerator
+ assert_float exp.denominator, real.denominator
+ end
end
def assert_equal_rational(exp, o1, o2)
- if exp
- assert_operator(o1, :==, o2)
- assert_not_operator(o1, :!=, o2)
- else
- assert_not_operator(o1, :==, o2)
- assert_operator(o1, :!=, o2)
+ assert "assert_equal_rational" do
+ if exp
+ assert_operator(o1, :==, o2)
+ assert_not_operator(o1, :!=, o2)
+ else
+ assert_not_operator(o1, :==, o2)
+ assert_operator(o1, :!=, o2)
+ end
end
end
diff --git a/mrbgems/mruby-sleep/.gitignore b/mrbgems/mruby-sleep/.gitignore
deleted file mode 100644
index b9f9e71df..000000000
--- a/mrbgems/mruby-sleep/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/mruby
-
-*.so
-*.a \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis.yml b/mrbgems/mruby-sleep/.travis.yml
deleted file mode 100644
index ad6b007b6..000000000
--- a/mrbgems/mruby-sleep/.travis.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-dist: trusty
-
-language: c
-compiler:
- - gcc
- - clang
-env:
- - MRUBY_VERSION=1.2.0
- - MRUBY_VERSION=master
-matrix:
- allow_failures:
- - env: MRUBY_VERSION=master
-branches:
- only:
- - master
-addons:
- apt:
- packages:
- - rake
- - bison
- - git
- - gperf
- # - aclocal
- # - automake
- # - autoconf
- # - autotools-dev
-
-script:
- - rake test \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis_build_config.rb b/mrbgems/mruby-sleep/.travis_build_config.rb
deleted file mode 100644
index b32e38a9d..000000000
--- a/mrbgems/mruby-sleep/.travis_build_config.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-MRuby::Build.new do |conf|
- toolchain :gcc
- conf.gembox 'default'
- conf.gem '../mruby-sleep'
- conf.enable_test
-end
diff --git a/mrbgems/mruby-sleep/Rakefile b/mrbgems/mruby-sleep/Rakefile
deleted file mode 100644
index 5e3c46173..000000000
--- a/mrbgems/mruby-sleep/Rakefile
+++ /dev/null
@@ -1,29 +0,0 @@
-MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || ".travis_build_config.rb")
-MRUBY_VERSION=ENV["MRUBY_VERSION"] || "1.2.0"
-
-file :mruby do
- cmd = "git clone --depth=1 git://github.com/mruby/mruby.git"
- if MRUBY_VERSION != 'master'
- cmd << " && cd mruby"
- cmd << " && git fetch --tags && git checkout $(git rev-parse #{MRUBY_VERSION})"
- end
- sh cmd
-end
-
-desc "compile binary"
-task :compile => :mruby do
- sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all"
-end
-
-desc "test"
-task :test => :mruby do
- sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test"
-end
-
-desc "cleanup"
-task :clean do
- exit 0 unless File.directory?('mruby')
- sh "cd mruby && rake deep_clean"
-end
-
-task :default => :compile
diff --git a/mrbgems/mruby-socket/README.md b/mrbgems/mruby-socket/README.md
index 7428cfa6c..ceb50c651 100644
--- a/mrbgems/mruby-socket/README.md
+++ b/mrbgems/mruby-socket/README.md
@@ -20,7 +20,7 @@ Date: Tue, 21 May 2013 04:31:30 GMT
```
## Requirement
-- [iij/mruby-io](https://github.com/iij/mruby-io) mrbgem
+- [mruby-io](https://github.com/mruby/mruby/tree/master/mrbgems/mruby-io) mrbgem
- [iij/mruby-mtest](https://github.com/iij/mruby-mtest) mrgbem to run tests
- system must have RFC3493 basic socket interface
- and some POSIX API...
diff --git a/mrbgems/mruby-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c
index 9b06274dc..53f761617 100644
--- a/mrbgems/mruby-socket/src/socket.c
+++ b/mrbgems/mruby-socket/src/socket.c
@@ -131,7 +131,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags);
if (mrb_string_p(nodename)) {
- hostname = mrb_string_value_cstr(mrb, &nodename);
+ hostname = RSTRING_CSTR(mrb, nodename);
} else if (mrb_nil_p(nodename)) {
hostname = NULL;
} else {
@@ -139,7 +139,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
}
if (mrb_string_p(service)) {
- servname = mrb_string_value_cstr(mrb, &service);
+ servname = RSTRING_CSTR(mrb, service);
} else if (mrb_fixnum_p(service)) {
servname = RSTRING_PTR(mrb_fixnum_to_str(mrb, service, 10));
} else if (mrb_nil_p(service)) {
@@ -171,7 +171,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
error = getaddrinfo(hostname, servname, &hints, &res0);
if (error) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error)));
+ mrb_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %s", gai_strerror(error));
}
mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_cptr_value(mrb, res0));
@@ -206,7 +206,7 @@ mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self)
}
error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags);
if (error) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error)));
+ mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error));
}
ary = mrb_ary_new_capa(mrb, 2);
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
@@ -476,7 +476,7 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0));
optval = mrb_funcall(mrb, so, "data", 0);
} else {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 3)", mrb_fixnum_value(argc));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%i for 3)", argc);
}
s = socket_fd(mrb, self);
@@ -702,7 +702,7 @@ mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "S", &path);
if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %S bytes)", mrb_fixnum_value(sizeof(sunp->sun_path) - 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %d bytes)", (int)sizeof(sunp->sun_path) - 1);
}
s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
sunp = (struct sockaddr_un *)RSTRING_PTR(s);
diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c
index 985ffe276..c8d51962e 100644
--- a/mrbgems/mruby-sprintf/src/sprintf.c
+++ b/mrbgems/mruby-sprintf/src/sprintf.c
@@ -81,7 +81,7 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
char d;
if (base != 2) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %d", base);
}
if (val == 0) {
return mrb_str_new_lit(mrb, "0");
@@ -144,10 +144,10 @@ 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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%d) mixed with numbered", nextarg);
break;
case -2:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%d) mixed with named", nextarg);
break;
default:
break;
@@ -158,26 +158,26 @@ static void
check_pos_arg(mrb_state *mrb, mrb_int posarg, mrb_int n)
{
if (posarg > 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
- mrb_fixnum_value(n), mrb_fixnum_value(posarg));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%i) after unnumbered(%i)",
+ n, posarg);
}
if (posarg == -2) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%i) after named", n);
}
if (n < 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %i$", n);
}
}
static void
-check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
+check_name_arg(mrb_state *mrb, int posarg, const char *name, size_t 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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%l after unnumbered(%d)",
+ name, len, posarg);
}
if (posarg == -1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len)));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%l after numbered", name, len);
}
}
@@ -580,7 +580,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
retry:
switch (*p) {
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p);
break;
case ' ':
@@ -619,7 +619,7 @@ retry:
GETNUM(n, width);
if (*p == '$') {
if (!mrb_undef_p(nextvalue)) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(n));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %i$", n);
}
nextvalue = GETPOSARG(n);
p++;
@@ -639,14 +639,14 @@ retry:
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));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%l after <%n>",
+ start, p - start + 1, id);
}
symname = mrb_str_new(mrb, start + 1, p - start - 1);
id = mrb_intern_str(mrb, symname);
- nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (mrb_int)(p - start + 1));
+ nextvalue = GETNAMEARG(mrb_symbol_value(id), start, 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));
+ mrb_raisef(mrb, E_KEY_ERROR, "key%l not found", start, p - start + 1);
}
if (term == '}') goto format_s;
p++;
@@ -1089,7 +1089,7 @@ retry:
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));
+ if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%s", mesg);
}
#endif
mrb_str_resize(mrb, result, blen);
diff --git a/mrbgems/mruby-string-ext/mrbgem.rake b/mrbgems/mruby-string-ext/mrbgem.rake
index 9812f2cc9..f2df5a783 100644
--- a/mrbgems/mruby-string-ext/mrbgem.rake
+++ b/mrbgems/mruby-string-ext/mrbgem.rake
@@ -2,5 +2,4 @@ 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/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index fdaf2f960..e57d75355 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -414,7 +414,7 @@ class String
e = max.ord
while c <= e
break if exclusive and c == e
- yield c.chr
+ yield c.chr(__ENCODING__)
c += 1
end
return self
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index d9ebb7392..aa6270786 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -5,82 +5,90 @@
#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);
+#define ENC_ASCII_8BIT "ASCII-8BIT"
+#define ENC_BINARY "BINARY"
+#define ENC_UTF8 "UTF-8"
- if (pos < 0)
- pos += RSTRING_LEN(str);
- if (pos < 0 || RSTRING_LEN(str) <= pos)
- return mrb_nil_value();
+#define ENC_COMP_P(enc, enc_lit) \
+ str_casecmp_p(RSTRING_PTR(enc), RSTRING_LEN(enc), enc_lit, sizeof(enc_lit"")-1)
+
+#ifdef MRB_WITHOUT_FLOAT
+# define mrb_float_p(o) FALSE
+#endif
- return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
+static mrb_bool
+str_casecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2)
+{
+ const char *e1, *e2;
+
+ if (len1 != len2) return FALSE;
+ e1 = s1 + len1;
+ e2 = s2 + len2;
+ while (s1 < e1 && s2 < e2) {
+ if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE;
+ ++s1;
+ ++s2;
+ }
+ return TRUE;
}
static mrb_value
-mrb_str_setbyte(mrb_state *mrb, mrb_value str)
+int_chr_binary(mrb_state *mrb, mrb_value num)
{
- mrb_int pos, byte;
- mrb_int 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 out of string", mrb_fixnum_value(pos));
- if (pos < 0)
- pos += len;
+ mrb_int cp = mrb_int(mrb, num);
+ char c;
+ mrb_value str;
- mrb_str_modify(mrb, mrb_str_ptr(str));
- byte &= 0xff;
- RSTRING_PTR(str)[pos] = (unsigned char)byte;
- return mrb_fixnum_value((unsigned char)byte);
+ if (cp < 0 || 0xff < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of char range", num);
+ }
+ c = (char)cp;
+ str = mrb_str_new(mrb, &c, 1);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
+#ifdef MRB_UTF8_STRING
static mrb_value
-mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+int_chr_utf8(mrb_state *mrb, mrb_value num)
{
- mrb_value a1;
+ mrb_int cp = mrb_int(mrb, num);
+ char utf8[4];
mrb_int len;
+ mrb_value str;
+ uint32_t ascii_flag = 0;
- if (mrb_get_argc(mrb) == 2) {
- mrb_int pos;
- mrb_get_args(mrb, "ii", &pos, &len);
- return mrb_str_substr(mrb, str, pos, len);
+ if (cp < 0 || 0x10FFFF < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of char range", num);
}
- mrb_get_args(mrb, "o|i", &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 MRB_RANGE_TYPE_MISMATCH:
- break;
- case MRB_RANGE_OK:
- return mrb_str_substr(mrb, str, beg, len);
- case MRB_RANGE_OUT:
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
- break;
- }
- return mrb_nil_value();
- }
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
- a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
- /* fall through */
-#endif
- 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");
+ if (cp < 0x80) {
+ utf8[0] = (char)cp;
+ len = 1;
+ ascii_flag = MRB_STR_ASCII;
}
- /* not reached */
- return mrb_nil_value();
+ 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;
+ }
+ str = mrb_str_new(mrb, utf8, len);
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ return str;
}
+#endif
/*
* call-seq:
@@ -137,8 +145,6 @@ mrb_str_swapcase(mrb_state *mrb, mrb_value self)
return str;
}
-static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
-
/*
* call-seq:
* str << integer -> str
@@ -148,7 +154,8 @@ static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
*
* 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.
+ * to a character before concatenation
+ * (equivalent to <code>str.concat(integer.chr(__ENCODING__))</code>).
*
* a = "hello "
* a << "world" #=> "hello world"
@@ -160,8 +167,12 @@ 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);
+ if (mrb_fixnum_p(str) || mrb_float_p(str))
+#ifdef MRB_UTF8_STRING
+ str = int_chr_utf8(mrb, str);
+#else
+ str = int_chr_binary(mrb, str);
+#endif
else
str = mrb_ensure_string_type(mrb, str);
mrb_str_concat(mrb, self, str);
@@ -507,8 +518,7 @@ str_tr(mrb_state *mrb, mrb_value str, mrb_value p1, mrb_value p2, mrb_bool squee
continue;
}
if (c > 0x80) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%S) out of range",
- mrb_fixnum_value((mrb_int)c));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%i) out of range", c);
}
lastch = c;
s[i] = (char)c;
@@ -812,7 +822,7 @@ mrb_str_count(mrb_state *mrb, mrb_value str)
tr_parse_pattern(mrb, &pat, v_pat, TRUE);
tr_compile_pattern(&pat, v_pat, bitmap);
tr_free_pattern(mrb, &pat);
-
+
s = RSTRING_PTR(str);
len = RSTRING_LEN(str);
for (i = 0; i < len; i++) {
@@ -848,49 +858,42 @@ mrb_str_chr(mrb_state *mrb, mrb_value self)
return mrb_str_substr(mrb, self, 0, 1);
}
+/*
+ * call-seq:
+ * int.chr([encoding]) -> string
+ *
+ * Returns a string containing the character represented by the +int+'s value
+ * according to +encoding+. +"ASCII-8BIT"+ (+"BINARY"+) and +"UTF-8"+ (only
+ * with +MRB_UTF8_STRING+) can be specified as +encoding+ (default is
+ * +"ASCII-8BIT"+).
+ *
+ * 65.chr #=> "A"
+ * 230.chr #=> "\xE6"
+ * 230.chr("ASCII-8BIT") #=> "\xE6"
+ * 230.chr("UTF-8") #=> "\u00E6"
+ */
static mrb_value
-mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
+mrb_int_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;
+ mrb_value enc;
+ mrb_bool enc_given;
+
+ mrb_get_args(mrb, "|S?", &enc, &enc_given);
+ if (!enc_given ||
+ ENC_COMP_P(enc, ENC_ASCII_8BIT) ||
+ ENC_COMP_P(enc, ENC_BINARY)) {
+ return int_chr_binary(mrb, num);
}
- 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;
+#ifdef MRB_UTF8_STRING
+ else if (ENC_COMP_P(enc, ENC_UTF8)) {
+ return int_chr_utf8(mrb, num);
}
+#endif
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);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown encoding name - %v", enc);
}
- c = (char)cp;
- return mrb_str_new(mrb, &c, 1);
-#endif
+ /* not reached */
+ return mrb_nil_value();
}
/*
@@ -1199,9 +1202,6 @@ 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));
@@ -1231,7 +1231,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "delete_suffix", mrb_str_del_suffix, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "__lines", mrb_str_lines, MRB_ARGS_NONE());
- mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb_module_get(mrb, "Integral"), "chr", mrb_int_chr, MRB_ARGS_OPT(1));
}
void
diff --git a/mrbgems/mruby-string-ext/test/numeric.rb b/mrbgems/mruby-string-ext/test/numeric.rb
new file mode 100644
index 000000000..dfcb9ebf4
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/numeric.rb
@@ -0,0 +1,29 @@
+# coding: utf-8
+
+assert('Integer#chr') do
+ assert_equal("A", 65.chr)
+ assert_equal("B", 0x42.chr)
+ assert_equal("\xab", 171.chr)
+ assert_raise(RangeError) { -1.chr }
+ assert_raise(RangeError) { 256.chr }
+
+ assert_equal("A", 65.chr("ASCII-8BIT"))
+ assert_equal("B", 0x42.chr("BINARY"))
+ assert_equal("\xab", 171.chr("ascii-8bit"))
+ assert_raise(RangeError) { -1.chr("binary") }
+ assert_raise(RangeError) { 256.chr("Ascii-8bit") }
+ assert_raise(ArgumentError) { 65.chr("ASCII") }
+ assert_raise(ArgumentError) { 65.chr("ASCII-8BIT", 2) }
+ assert_raise(TypeError) { 65.chr(:BINARY) }
+
+ if __ENCODING__ == "ASCII-8BIT"
+ assert_raise(ArgumentError) { 65.chr("UTF-8") }
+ else
+ assert_equal("A", 65.chr("UTF-8"))
+ assert_equal("B", 0x42.chr("UTF-8"))
+ assert_equal("«", 171.chr("utf-8"))
+ assert_equal("あ", 12354.chr("Utf-8"))
+ assert_raise(RangeError) { -1.chr("utf-8") }
+ assert_raise(RangeError) { 0x110000.chr.chr("UTF-8") }
+ end
+end
diff --git a/mrbgems/mruby-string-ext/test/range.rb b/mrbgems/mruby-string-ext/test/range.rb
new file mode 100644
index 000000000..80c286850
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/range.rb
@@ -0,0 +1,26 @@
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 'l', ('f'..'l').max
+ assert_equal 'e', ('a'...'f').max
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ('z'..'l').max
+end
+
+assert('Range#max given a block') do
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (('z'..'l').max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 'f', ('f'..'l').min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ('z'..'l').min
+end
+
+assert('Range#min given a block') do
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (('z'..'l').min { |x, y| x <=> y })
+end
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index 02777e594..6914fe31d 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -2,34 +2,12 @@
##
# String(Ext) Test
-UTF8STRING = ("\343\201\202".size == 1)
+UTF8STRING = __ENCODING__ == "UTF-8"
-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#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))
+def assert_upto(exp, receiver, *args)
+ act = []
+ receiver.upto(*args) { |v| act << v }
+ assert_equal exp, act
end
assert('String#dump') do
@@ -116,8 +94,15 @@ end
assert('String#concat') do
assert_equal "Hello World!", "Hello " << "World" << 33
assert_equal "Hello World!", "Hello ".concat("World").concat(33)
-
assert_raise(TypeError) { "".concat(Object.new) }
+
+ if UTF8STRING
+ assert_equal "H«", "H" << 0xab
+ assert_equal "Hは", "H" << 12399
+ else
+ assert_equal "H\xab", "H" << 0xab
+ assert_raise(RangeError) { "H" << 12399 }
+ end
end
assert('String#casecmp') do
@@ -247,12 +232,6 @@ assert('String#oct') do
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
@@ -539,16 +518,15 @@ assert('String#rjust should raise on zero width padding') do
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
+ assert_upto %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8", "b6"
+ assert_upto ["9", "10", "11"], "9", "11"
+ assert_upto [], "25", "5"
+ assert_upto ["07", "08", "09", "10", "11"], "07", "11"
+ assert_upto ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9", "A"
+
+ if UTF8STRING
+ assert_upto %w(あ ぃ い ぅ う ぇ え ぉ お), "あ", "お"
+ end
a = "aa"
start = "aa"
@@ -630,8 +608,11 @@ assert('String#ord(UTF-8)') do
end if UTF8STRING
assert('String#chr') do
+ assert_equal "a", "abcde".chr
assert_equal "h", "hello!".chr
+ assert_equal "", "".chr
end
+
assert('String#chr(UTF-8)') do
assert_equal "こ", "こんにちは世界!".chr
end if UTF8STRING
diff --git a/mrbgems/mruby-struct/mrblib/struct.rb b/mrbgems/mruby-struct/mrblib/struct.rb
index c5b5354be..7682ac033 100644
--- a/mrbgems/mruby-struct/mrblib/struct.rb
+++ b/mrbgems/mruby-struct/mrblib/struct.rb
@@ -46,7 +46,9 @@ if Object.const_defined?(:Struct)
ary
end
- def _inspect
+ def _inspect(recur_list)
+ return "#<struct #{self.class}:...>" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
name = self.class.to_s
if name[0] == "#"
str = "#<struct "
@@ -55,7 +57,7 @@ if Object.const_defined?(:Struct)
end
buf = []
self.each_pair do |k,v|
- buf.push [k.to_s + "=" + v._inspect]
+ buf.push [k.to_s + "=" + v._inspect(recur_list)]
end
str + buf.join(", ") + ">"
end
@@ -70,11 +72,7 @@ if Object.const_defined?(:Struct)
# 15.2.18.4.10(x)
#
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "#<struct #{self.class.to_s}:...>"
- end
+ self._inspect({})
end
##
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index 44822a03e..ebe711ed3 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -66,8 +66,8 @@ struct_members(mrb_state *mrb, mrb_value s)
}
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)));
+ "struct size differs (%i required %i given)",
+ RARRAY_LEN(members), RSTRUCT_LEN(s));
}
}
return members;
@@ -123,18 +123,29 @@ mrb_struct_ref(mrb_state *mrb, mrb_value obj)
static mrb_sym
mrb_id_attrset(mrb_state *mrb, mrb_sym id)
{
+#define ONSTACK_ALLOC_MAX 32
+#define ONSTACK_STRLEN_MAX (ONSTACK_ALLOC_MAX - 1) /* '=' character */
+
const char *name;
char *buf;
mrb_int len;
mrb_sym mid;
+ char onstack[ONSTACK_ALLOC_MAX];
name = mrb_sym2name_len(mrb, id, &len);
- buf = (char *)mrb_malloc(mrb, (size_t)len+1);
+ if (len > ONSTACK_STRLEN_MAX) {
+ buf = (char *)mrb_malloc(mrb, (size_t)len+1);
+ }
+ else {
+ buf = onstack;
+ }
memcpy(buf, name, (size_t)len);
buf[len] = '=';
mid = mrb_intern(mrb, buf, len+1);
- mrb_free(mrb, buf);
+ if (buf != onstack) {
+ mrb_free(mrb, buf);
+ }
return mid;
}
@@ -194,10 +205,10 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *kl
mrb_to_str(mrb, name);
id = mrb_obj_to_sym(mrb, name);
if (!mrb_const_name_p(mrb, RSTRING_PTR(name), RSTRING_LEN(name))) {
- mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
+ mrb_name_error(mrb, id, "identifier %v 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_warn(mrb, "redefining constant Struct::%v", name);
mrb_const_remove(mrb, mrb_obj_value(klass), id);
}
c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
@@ -377,7 +388,7 @@ struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
return ptr[i];
}
}
- mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "no member '%n' in struct", id);
return mrb_nil_value(); /* not reached */
}
@@ -388,12 +399,10 @@ struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
if (idx < 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)));
+ "offset %i too small for struct(size:%i)", i, RSTRUCT_LEN(s));
if (RSTRUCT_LEN(s) <= idx)
mrb_raisef(mrb, E_INDEX_ERROR,
- "offset %S too large for struct(size:%S)",
- mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ "offset %i too large for struct(size:%i)", i, RSTRUCT_LEN(s));
return RSTRUCT_PTR(s)[idx];
}
@@ -426,7 +435,7 @@ mrb_struct_aref(mrb_state *mrb, mrb_value s)
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);
+ mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%v' in struct", idx);
}
idx = sym;
}
@@ -454,7 +463,7 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
return val;
}
}
- mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "no member '%n' in struct", id);
return val; /* not reach */
}
@@ -493,7 +502,7 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s)
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);
+ mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%v' in struct", idx);
}
idx = sym;
}
@@ -505,13 +514,11 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s)
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)));
+ "offset %i too small for struct(size:%i)", i, 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)));
+ "offset %i too large for struct(size:%i)", i, RSTRUCT_LEN(s));
}
mrb_struct_modify(mrb, s);
return RSTRUCT_PTR(s)[i] = val;
diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index 9f2ff1cdc..93930730b 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -116,9 +116,10 @@ assert('struct dup') do
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
+ c = Struct.new(:m1, :m2, :m3, :m4, :m5, :recur)
+ cc = c.new(1,2,3,4,5,nil)
+ cc.recur = cc
+ assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5, recur=#<struct #{cc.class}:...>>", cc.inspect
end
assert('Struct#length, Struct#size') do
diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb
index 61ecad247..db686e5f4 100644
--- a/mrbgems/mruby-symbol-ext/test/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -14,7 +14,7 @@ end
assert("Symbol##{n}") do
assert_equal 5, :hello.__send__(n)
assert_equal 4, :"aA\0b".__send__(n)
- if "あ".size == 1 # enable MRB_UTF8_STRING?
+ if __ENCODING__ == "UTF-8"
assert_equal 8, :"こんにちは世界!".__send__(n)
assert_equal 4, :"aあ\0b".__send__(n)
else
diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c
index 602b76c79..a5f723927 100644
--- a/mrbgems/mruby-test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -21,6 +21,7 @@
extern const uint8_t mrbtest_assert_irep[];
void mrbgemtest_init(mrb_state* mrb);
+void mrb_init_test_vformat(mrb_state* mrb);
/* Print a short remark for the user */
static void
@@ -231,6 +232,8 @@ mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
#endif
#endif
+ mrb_init_test_vformat(mrb);
+
if (verbose) {
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
}
@@ -258,6 +261,7 @@ mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
TEST_COUNT_PASS(ok_test);
TEST_COUNT_PASS(ko_test);
TEST_COUNT_PASS(kill_test);
+ TEST_COUNT_PASS(warning_test);
TEST_COUNT_PASS(skip_test);
#undef TEST_COUNT_PASS
diff --git a/mrbgems/mruby-test/mrbgem.rake b/mrbgems/mruby-test/mrbgem.rake
index dcb7bb719..bf90e0791 100644
--- a/mrbgems/mruby-test/mrbgem.rake
+++ b/mrbgems/mruby-test/mrbgem.rake
@@ -15,8 +15,9 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
mrbtest_lib = libfile("#{build_dir}/mrbtest")
mrbtest_objs = []
- driver_obj = objfile("#{build_dir}/driver")
- # driver = "#{spec.dir}/driver.c"
+ driver_objs = Dir.glob("#{dir}/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
+ objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X"))
+ end
assert_c = "#{build_dir}/assert.c"
assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
@@ -133,7 +134,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
end
unless build.build_mrbtest_lib_only?
- file exec => [driver_obj, mlib, mrbtest_lib, build.libmruby_static] do |t|
+ file exec => [*driver_objs, mlib, mrbtest_lib, build.libmruby_static] 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 }
diff --git a/mrbgems/mruby-test/vformat.c b/mrbgems/mruby-test/vformat.c
new file mode 100644
index 000000000..6984aaeb1
--- /dev/null
+++ b/mrbgems/mruby-test/vformat.c
@@ -0,0 +1,200 @@
+#include <string.h>
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/string.h>
+
+#ifdef MRB_WITHOUT_FLOAT
+typedef mrb_int mrb_float;
+#define mrb_float(o) mrb_fixnum(o)
+#endif
+
+#define NATIVE_TYPES \
+ char c; \
+ int d; \
+ mrb_float f; \
+ mrb_int i; \
+/* size_t l; */\
+ mrb_sym n; \
+ char *s; \
+ struct RClass *C
+
+#define NATIVE_DEFINE_TYPE_FUNC(t) \
+ static mrb_value \
+ native_s_##t(mrb_state *mrb, mrb_value klass) \
+ { \
+ mrb_value obj, type = mrb_fixnum_value(ARG_##t); \
+ mrb_get_args(mrb, "o", &obj); \
+ return mrb_funcall(mrb, klass, "new", 2, type, obj); \
+ }
+
+#define NATIVE_DEFINE_TYPE_METHOD(t) \
+ mrb_define_class_method(mrb, n, #t, native_s_##t, MRB_ARGS_REQ(1))
+
+typedef enum {
+ ARG_c,
+ ARG_d,
+ ARG_f,
+ ARG_i,
+/* ARG_l,*/
+ ARG_n,
+ ARG_s,
+ ARG_C,
+ ARG_v,
+} VFArgumentType;
+
+typedef struct {
+ VFArgumentType type;
+ union { NATIVE_TYPES; };
+} VFNative;
+
+typedef struct {
+ VFArgumentType type;
+ union {
+ NATIVE_TYPES;
+ mrb_value v;
+ };
+} VFArgument;
+
+static void
+native_free(mrb_state *mrb, void *data)
+{
+ VFNative *native = (VFNative*)data;
+ if (native->type == ARG_s) mrb_free(mrb, native->s);
+ mrb_free(mrb, native);
+}
+
+static const struct mrb_data_type native_data_type = {
+ "TestVFormat::Native", native_free
+};
+
+static mrb_value
+native_initialize(mrb_state *mrb, mrb_value self)
+{
+ VFNative data, *datap;
+ mrb_int type;
+ mrb_value obj;
+
+ mrb_get_args(mrb, "io", &type, &obj);
+ data.type = (VFArgumentType)type;
+ switch (data.type) {
+ case ARG_c: data.c = RSTRING_PTR(obj)[0]; break;
+ case ARG_d: data.d = (int)mrb_fixnum(obj); break;
+ case ARG_f: data.f = mrb_float(obj); break;
+ case ARG_i: data.i = mrb_fixnum(obj); break;
+/* case ARG_l: data.l = (size_t)mrb_fixnum(obj); break;*/
+ case ARG_n: data.n = mrb_symbol(obj); break;
+ case ARG_s: data.s = (char*)mrb_malloc(mrb, RSTRING_LEN(obj) + 1);
+ memcpy(data.s, RSTRING_PTR(obj), RSTRING_LEN(obj));
+ data.s[RSTRING_LEN(obj)] = '\0'; break;
+ case ARG_C: data.C = mrb_class_ptr(obj); break;
+ default: mrb_raise(mrb, E_ARGUMENT_ERROR, "unknown type");
+ }
+ datap = (VFNative*)mrb_malloc(mrb, sizeof(VFNative));
+ *datap = data;
+ mrb_data_init(self, datap, &native_data_type);
+ return self;
+}
+
+NATIVE_DEFINE_TYPE_FUNC(c)
+NATIVE_DEFINE_TYPE_FUNC(d)
+NATIVE_DEFINE_TYPE_FUNC(f)
+NATIVE_DEFINE_TYPE_FUNC(i)
+/*NATIVE_DEFINE_TYPE_FUNC(l)*/
+NATIVE_DEFINE_TYPE_FUNC(n)
+NATIVE_DEFINE_TYPE_FUNC(s)
+NATIVE_DEFINE_TYPE_FUNC(C)
+
+static VFArgument*
+arg_from_obj(mrb_state *mrb, mrb_value obj, struct RClass *native_class,
+ VFArgument *vf_arg)
+{
+ if (mrb_obj_is_instance_of(mrb, obj, native_class)) {
+ const VFNative *native = (VFNative*)DATA_PTR(obj);
+ *(VFNative*)vf_arg = *native;
+ }
+ else {
+ vf_arg->v = obj;
+ vf_arg->type = ARG_v;
+ }
+ return vf_arg;
+}
+
+#define VF_FORMAT_INIT(klass) \
+ struct RClass *vf_native_class = \
+ mrb_class_get_under(mrb, mrb_class_ptr(klass), "Native"); \
+ VFArgument vf_args[2];
+
+#define VF_ARG(args, idx) \
+ arg_from_obj(mrb, args[idx], vf_native_class, &vf_args[idx])
+
+#define VF_FORMAT0(fmt) mrb_format(mrb, fmt);
+#define VF_FORMAT1(fmt, args) \
+ (VF_ARG(args, 0), VF_FORMAT_TYPED(fmt, 1, vf_args, NULL))
+#define VF_FORMAT2(fmt, args) ( \
+ VF_ARG(args, 0), VF_ARG(args, 1), \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, c) : \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, d) : \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, f) : \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, i) : \
+/* VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, l) : */\
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, n) : \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, s) : \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, C) : \
+ VF_FORMAT2_COND_EXPR(fmt, vf_args, vf_args+1, v) : \
+ mrb_nil_value() /* not reached */ \
+)
+#define VF_FORMAT2_COND_EXPR(fmt, a1, a2, t) \
+ a1->type == ARG_##t ? VF_FORMAT_TYPED(fmt, 2, a2, (a1)->t)
+#define VF_FORMAT_TYPED(fmt, n_arg, type_a, v1) \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, c) : \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, d) : \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, f) : \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, i) : \
+/* VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, l) : */\
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, n) : \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, s) : \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, C) : \
+ VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, v) : \
+ mrb_nil_value() /* not reached */
+#define VF_FORMAT_TYPED_COND_EXPR(fmt, n_arg, type_a, v1, t) \
+ (type_a)->type == ARG_##t ? n_arg == 1 ? \
+ mrb_format(mrb, fmt, (type_a)->t) : mrb_format(mrb, fmt, v1, (type_a)->t)
+
+static mrb_value
+vf_s_format(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value fmt_str, args[2];
+ mrb_int argc = mrb_get_args(mrb, "S|oo", &fmt_str, args, args+1);
+ const char *fmt = RSTRING_CSTR(mrb, fmt_str);
+
+ VF_FORMAT_INIT(klass);
+
+ switch (argc) {
+ case 1: return VF_FORMAT0(fmt);
+ case 2: return VF_FORMAT1(fmt, args);
+ case 3: return VF_FORMAT2(fmt, args);
+ default: return mrb_nil_value(); /* not reached */
+ }
+}
+
+void
+mrb_init_test_vformat(mrb_state *mrb)
+{
+ struct RClass *vf, *n;
+
+ vf = mrb_define_module(mrb, "TestVFormat");
+ mrb_define_class_method(mrb, vf, "format", vf_s_format, MRB_ARGS_ARG(1,2));
+
+ n = mrb_define_class_under(mrb, vf, "Native", mrb->object_class);
+ MRB_SET_INSTANCE_TT(n, MRB_TT_DATA);
+ NATIVE_DEFINE_TYPE_METHOD(c);
+ NATIVE_DEFINE_TYPE_METHOD(d);
+ NATIVE_DEFINE_TYPE_METHOD(f);
+ NATIVE_DEFINE_TYPE_METHOD(i);
+/* NATIVE_DEFINE_TYPE_METHOD(l);*/
+ NATIVE_DEFINE_TYPE_METHOD(n);
+ NATIVE_DEFINE_TYPE_METHOD(s);
+ NATIVE_DEFINE_TYPE_METHOD(C);
+ mrb_define_method(mrb, n, "initialize", native_initialize, MRB_ARGS_REQ(2));
+}
diff --git a/mrbgems/mruby-time/include/mruby/time.h b/mrbgems/mruby-time/include/mruby/time.h
index d71f4ccd3..1adcfd49c 100644
--- a/mrbgems/mruby-time/include/mruby/time.h
+++ b/mrbgems/mruby-time/include/mruby/time.h
@@ -8,6 +8,7 @@
#define MRUBY_TIME_H
#include "mruby/common.h"
+#include <time.h>
MRB_BEGIN_DECL
@@ -18,7 +19,7 @@ typedef enum mrb_timezone {
MRB_TIMEZONE_LAST = 3
} mrb_timezone;
-MRB_API mrb_value mrb_time_at(mrb_state *mrb, double sec, double usec, mrb_timezone timezone);
+MRB_API mrb_value mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, mrb_timezone timezone);
MRB_END_DECL
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index 34376c286..9b0549bea 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -4,8 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifndef MRB_WITHOUT_FLOAT
#include <math.h>
-#include <time.h>
+#endif
+
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
@@ -17,6 +19,8 @@
#include <string.h>
#endif
+#include <stdlib.h>
+
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define TO_S_FMT "%Y-%m-%d %H:%M:%S "
@@ -26,8 +30,10 @@ double round(double x) {
}
#endif
-#if !defined(__MINGW64__) && defined(_WIN32)
-# define llround(x) round(x)
+#ifdef MRB_WITHOUT_FLOAT
+# if !defined(__MINGW64__) && defined(_WIN32)
+# define llround(x) round(x)
+# endif
#endif
#if defined(__MINGW64__) || defined(__MINGW32__)
@@ -198,6 +204,76 @@ struct mrb_time {
static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
+#ifndef MRB_WITHOUT_FLOAT
+void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
+typedef mrb_float mrb_sec;
+#define mrb_sec_value(mrb, sec) mrb_float_value(mrb, sec)
+#else
+typedef mrb_int mrb_sec;
+#define mrb_sec_value(mrb, sec) mrb_fixnum_value(sec)
+#endif
+
+#ifdef MRB_TIME_T_UINT
+typedef uint64_t mrb_time_int;
+# define MRB_TIME_MIN 0
+# define MRB_TIME_MAX (sizeof(time_t) <= 4 ? UINT32_MAX : UINT64_MAX)
+#else
+typedef int64_t mrb_time_int;
+# define MRB_TIME_MIN (sizeof(time_t) <= 4 ? INT32_MIN : INT64_MIN)
+# define MRB_TIME_MAX (sizeof(time_t) <= 4 ? INT32_MAX : INT64_MAX)
+#endif
+
+static time_t
+mrb_to_time_t(mrb_state *mrb, mrb_value obj, time_t *usec)
+{
+ time_t t;
+
+ switch (mrb_type(obj)) {
+#ifndef MRB_WITHOUT_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float f = mrb_float(obj);
+
+ mrb_check_num_exact(mrb, f);
+ if (f > (mrb_float)MRB_TIME_MAX || (mrb_float)MRB_TIME_MIN > f) {
+ goto out_of_range;
+ }
+
+ if (usec) {
+ t = (time_t)f;
+ *usec = (time_t)llround((f - t) * 1.0e+6);
+ }
+ else {
+ t = (time_t)llround(f);
+ }
+ }
+ break;
+#endif /* MRB_WITHOUT_FLOAT */
+ default:
+ case MRB_TT_FIXNUM:
+ {
+ mrb_int i = mrb_int(mrb, obj);
+
+ if ((mrb_time_int)i > MRB_TIME_MAX || MRB_TIME_MIN > i) {
+ goto out_of_range;
+ }
+
+ t = (time_t)i;
+ if (usec) { *usec = 0; }
+ }
+ break;
+ }
+
+ return t;
+
+out_of_range:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", obj);
+
+ /* not reached */
+ if (usec) { *usec = 0; }
+ return 0;
+}
+
/** Updates the datetime of a mrb_time based on it's timezone and
seconds setting. Returns self on success, NULL of failure.
if `dealloc` is set `true`, it frees `self` on error. */
@@ -214,10 +290,10 @@ time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc)
aid = localtime_r(&t, &self->datetime);
}
if (!aid) {
- mrb_float sec = (mrb_float)t;
+ mrb_sec sec = (mrb_sec)t;
if (dealloc) mrb_free(mrb, self);
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", mrb_sec_value(mrb, sec));
/* not reached */
return NULL;
}
@@ -234,40 +310,15 @@ 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)
+time_alloc_time(mrb_state *mrb, time_t sec, time_t 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);
-#ifndef MRB_TIME_T_UINT
- 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;
- }
-#else
- if (sizeof(time_t) == 4 && (sec > (double)UINT32_MAX || (double)0 > sec)) {
- goto out_of_range;
- }
- if (sizeof(time_t) == 8 && (sec > (double)UINT64_MAX || (double)0 > sec)) {
- goto out_of_range;
- }
-#endif
- 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);
+ tm->sec = sec;
+ tm->usec = usec;
if (tm->usec < 0) {
long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
tm->usec -= sec2 * 1000000;
@@ -284,8 +335,25 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
return tm;
}
+static struct mrb_time*
+time_alloc(mrb_state *mrb, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
+{
+ time_t tsec, tusec;
+
+ tsec = mrb_to_time_t(mrb, sec, &tusec);
+ tusec += mrb_to_time_t(mrb, usec, NULL);
+
+ return time_alloc_time(mrb, tsec, tusec, timezone);
+}
+
static mrb_value
-mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone)
+mrb_time_make_time(mrb_state *mrb, struct RClass *c, time_t sec, time_t usec, enum mrb_timezone timezone)
+{
+ return mrb_time_wrap(mrb, c, time_alloc_time(mrb, sec, usec, timezone));
+}
+
+static mrb_value
+mrb_time_make(mrb_state *mrb, struct RClass *c, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
{
return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone));
}
@@ -347,9 +415,9 @@ mrb_time_now(mrb_state *mrb, mrb_value self)
}
MRB_API mrb_value
-mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
+mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone zone)
{
- return mrb_time_make(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
+ return mrb_time_make_time(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
}
/* 15.2.19.6.1 */
@@ -357,10 +425,12 @@ mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
static mrb_value
mrb_time_at_m(mrb_state *mrb, mrb_value self)
{
- mrb_float f, f2 = 0;
+ mrb_value sec;
+ mrb_value usec = mrb_fixnum_value(0);
+
+ mrb_get_args(mrb, "o|o", &sec, &usec);
- mrb_get_args(mrb, "f|f", &f, &f2);
- return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL);
+ return mrb_time_make(mrb, mrb_class_ptr(self), sec, usec, MRB_TIMEZONE_LOCAL);
}
static struct mrb_time*
@@ -397,7 +467,7 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
}
- return time_alloc(mrb, (double)nowsecs, (double)ausec, timezone);
+ return time_alloc_time(mrb, nowsecs, ausec, timezone);
}
/* 15.2.19.6.2 */
@@ -483,18 +553,19 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_time_plus(mrb_state *mrb, mrb_value self)
{
- mrb_float f;
+ mrb_value o;
struct mrb_time *tm;
+ time_t sec, usec;
- mrb_get_args(mrb, "f", &f);
+ mrb_get_args(mrb, "o", &o);
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);
+ sec = mrb_to_time_t(mrb, o, &usec);
+ return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec+sec, tm->usec+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;
@@ -502,13 +573,22 @@ mrb_time_minus(mrb_state *mrb, mrb_value self)
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;
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_float f;
+ f = (mrb_sec)(tm->sec - tm2->sec)
+ + (mrb_sec)(tm->usec - tm2->usec) / 1.0e6;
return mrb_float_value(mrb, f);
+#else
+ mrb_int f;
+ f = tm->sec - tm2->sec;
+ if (tm->usec < tm2->usec) f--;
+ return mrb_fixnum_value(f);
+#endif
}
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);
+ time_t sec, usec;
+ sec = mrb_to_time_t(mrb, other, &usec);
+ return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec-sec, tm->usec-usec, tm->timezone);
}
}
@@ -765,6 +845,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(tm->datetime.tm_sec);
}
+#ifndef MRB_WITHOUT_FLOAT
/* 15.2.19.7.24 */
/* Returns a Float with the time since the epoch in seconds. */
static mrb_value
@@ -775,32 +856,37 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
}
+#endif
/* 15.2.19.7.25 */
-/* Returns a Fixnum with the time since the epoch in seconds. */
+/* Returns an Integer 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);
+#ifndef MRB_WITHOUT_FLOAT
if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
return mrb_float_value(mrb, (mrb_float)tm->sec);
}
+#endif
return mrb_fixnum_value((mrb_int)tm->sec);
}
/* 15.2.19.7.26 */
-/* Returns a Float with the time since the epoch in microseconds. */
+/* Returns an Integer 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);
+#ifndef MRB_WITHOUT_FLOAT
if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
return mrb_float_value(mrb, (mrb_float)tm->usec);
}
+#endif
return mrb_fixnum_value((mrb_int)tm->usec);
}
@@ -828,13 +914,44 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self)
return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
}
+static size_t
+time_to_s_utc(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len)
+{
+ return strftime(buf, buf_len, TO_S_FMT "UTC", &tm->datetime);
+}
+
+static size_t
+time_to_s_local(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len)
+{
+#if defined(_MSC_VER) && _MSC_VER < 1900 || defined(__MINGW64__) || defined(__MINGW32__)
+ struct tm datetime = {0};
+ time_t utc_sec = timegm(&tm->datetime);
+ size_t len;
+ int offset;
+
+ if (utc_sec == (time_t)-1) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
+ }
+ offset = abs((int)(utc_sec - tm->sec) / 60);
+ datetime.tm_year = 100;
+ datetime.tm_hour = offset / 60;
+ datetime.tm_min = offset % 60;
+ len = strftime(buf, buf_len, TO_S_FMT, &tm->datetime);
+ buf[len++] = utc_sec < tm->sec ? '-' : '+';
+
+ return len + strftime(buf + len, buf_len - len, "%H%M", &datetime);
+#else
+ return strftime(buf, buf_len, TO_S_FMT "%z", &tm->datetime);
+#endif
+}
+
static mrb_value
mrb_time_to_s(mrb_state *mrb, mrb_value self)
{
char buf[64];
struct mrb_time *tm = time_get_ptr(mrb, self);
- const char *fmt = tm->timezone == MRB_TIMEZONE_UTC ? TO_S_FMT "UTC" : TO_S_FMT "%z";
- size_t len = strftime(buf, sizeof(buf), fmt, &tm->datetime);
+ mrb_bool utc = tm->timezone == MRB_TIMEZONE_UTC;
+ size_t len = (utc ? time_to_s_utc : time_to_s_local)(mrb, tm, buf, sizeof(buf));
return mrb_str_new(mrb, buf, len);
}
@@ -878,7 +995,9 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
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 */
+#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */
+#endif
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 */
diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb
index f59e27bc1..be1de7bc6 100644
--- a/mrbgems/mruby-time/test/time.rb
+++ b/mrbgems/mruby-time/test/time.rb
@@ -239,7 +239,8 @@ assert('Time#to_s') do
end
assert('Time#inspect') do
- assert_match("2013-10-28 16:27:48 [^U]*", Time.local(2013,10,28,16,27,48).inspect)
+ assert_match("2013-10-28 16:27:48 [+-][0-9][0-9][0-9][0-9]",
+ Time.local(2013,10,28,16,27,48).inspect)
end
assert('day of week methods') do
diff --git a/mrblib/array.rb b/mrblib/array.rb
index d598efc77..6535d6d83 100644
--- a/mrblib/array.rb
+++ b/mrblib/array.rb
@@ -10,16 +10,16 @@ class Array
# and pass the respective element.
#
# ISO 15.2.12.5.10
- def each(&block)
- return to_enum :each unless block
+ # def each(&block)
+ # return to_enum :each unless block
- idx = 0
- while idx < length
- block.call(self[idx])
- idx += 1
- end
- self
- end
+ # idx = 0
+ # while idx < length
+ # block.call(self[idx])
+ # idx += 1
+ # end
+ # self
+ # end
##
# Calls the given block for each element of +self+
@@ -83,13 +83,15 @@ class Array
self
end
- def _inspect
+ def _inspect(recur_list)
size = self.size
return "[]" if size == 0
+ return "[...]" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
ary=[]
i=0
while i<size
- ary<<self[i].inspect
+ ary<<self[i]._inspect(recur_list)
i+=1
end
"["+ary.join(", ")+"]"
@@ -99,11 +101,7 @@ class Array
#
# ISO 15.2.12.5.31 (x)
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "[...]"
- end
+ self._inspect({})
end
# ISO 15.2.12.5.32 (x)
alias to_s inspect
diff --git a/mrblib/enum.rb b/mrblib/enum.rb
index 9bd74e1c4..15687e159 100644
--- a/mrblib/enum.rb
+++ b/mrblib/enum.rb
@@ -65,22 +65,22 @@ module Enumerable
end
##
- # Call the given block for each element
- # which is yield by +each+. Return
- # +ifnone+ if no block value was true.
- # Otherwise return the first block value
- # which had was true.
+ # Return the first element for which
+ # value from the block is true. If no
+ # object matches, calls +ifnone+ and
+ # returns its result. Otherwise returns
+ # +nil+.
#
# ISO 15.3.2.2.4
def detect(ifnone=nil, &block)
- ret = ifnone
+ return to_enum :detect, ifnone unless block
+
self.each{|*val|
if block.call(*val)
- ret = val.__svalue
- break
+ return val.__svalue
end
}
- ret
+ ifnone.call unless ifnone.nil?
end
##
@@ -284,6 +284,8 @@ module Enumerable
#
# ISO 15.3.2.2.16
def partition(&block)
+ return to_enum :partition unless block
+
ary_T = []
ary_F = []
self.each{|*val|
@@ -304,6 +306,8 @@ module Enumerable
#
# ISO 15.3.2.2.17
def reject(&block)
+ return to_enum :reject unless block
+
ary = []
self.each{|*val|
ary.push(val.__svalue) unless block.call(*val)
diff --git a/mrblib/hash.rb b/mrblib/hash.rb
index 609883ecb..b49e987c7 100644
--- a/mrblib/hash.rb
+++ b/mrblib/hash.rb
@@ -186,15 +186,17 @@ class Hash
end
# internal method for Hash inspection
- def _inspect
+ def _inspect(recur_list)
return "{}" if self.size == 0
+ return "{...}" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
ary=[]
keys=self.keys
size=keys.size
i=0
while i<size
k=keys[i]
- ary<<(k._inspect + "=>" + self[k]._inspect)
+ ary<<(k._inspect(recur_list) + "=>" + self[k]._inspect(recur_list))
i+=1
end
"{"+ary.join(", ")+"}"
@@ -204,11 +206,7 @@ class Hash
#
# ISO 15.2.13.4.30 (x)
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "{...}"
- end
+ self._inspect({})
end
# ISO 15.2.13.4.31 (x)
alias to_s inspect
diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb
index 4700684b6..7c3ea9420 100644
--- a/mrblib/kernel.rb
+++ b/mrblib/kernel.rb
@@ -40,7 +40,7 @@ module Kernel
end
# internal method for inspect
- def _inspect
+ def _inspect(_recur_list)
self.inspect
end
diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb
index 5a3c5eb58..5926518d5 100644
--- a/mrblib/numeric.rb
+++ b/mrblib/numeric.rb
@@ -105,14 +105,14 @@ module Integral
return to_enum(:step, num, step) unless block
i = __coerce_step_counter(num, step)
- if num == nil
+ if num == self || step.infinite?
+ block.call(i) if step > 0 && i <= (num||i) || step < 0 && i >= (num||-i)
+ elsif num == nil
while true
block.call(i)
i += step
end
- return self
- end
- if step > 0
+ elsif step > 0
while i <= num
block.call(i)
i += step
diff --git a/mrblib/string.rb b/mrblib/string.rb
index d72002209..0e7c8dc12 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -202,60 +202,6 @@ class String
self
end
- ##
- # Modify +self+ by replacing the content of +self+.
- # The portion of the string affected is determined using the same criteria as +String#[]+.
- def []=(*args)
- anum = args.size
- if anum == 2
- pos, value = args[0], args[1].__to_str
- case pos
- when String
- posnum = self.index(pos)
- if posnum
- b = self[0, posnum]
- a = self[(posnum + pos.length)..-1]
- self.replace([b, value, a].join(''))
- else
- raise IndexError, "string not matched"
- end
- when Range
- head = pos.begin
- tail = pos.end
- tail += self.length if tail < 0
- unless pos.exclude_end?
- tail += 1
- end
- return self[head, tail-head]=value
- else
- pos = pos.__to_int
- pos += self.length if pos < 0
- if pos < 0 || pos > self.length
- raise IndexError, "index #{args[0]} out of string"
- end
- b = self[0, pos]
- a = self[pos + 1..-1]
- self.replace([b, value, a].join(''))
- end
- return value
- elsif anum == 3
- pos, len, value = args[0].__to_int, args[1].__to_int, args[2].__to_str
- pos += self.length if pos < 0
- if pos < 0 || pos > self.length
- raise IndexError, "index #{args[0]} out of string"
- end
- if len < 0
- raise IndexError, "negative length #{len}"
- end
- b = self[0, pos]
- a = self[pos + len..-1]
- self.replace([b, value, a].join(''))
- return value
- else
- raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)"
- end
- end
-
# those two methods requires Regexp that is optional in mruby
##
# ISO 15.2.10.5.3
diff --git a/src/array.c b/src/array.c
index bd9b4d358..33baceb9c 100644
--- a/src/array.c
+++ b/src/array.c
@@ -9,6 +9,8 @@
#include <mruby/class.h>
#include <mruby/string.h>
#include <mruby/range.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
#include "value_array.h"
#define ARY_DEFAULT_LEN 4
@@ -668,7 +670,7 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
if (n < 0) {
n += len;
if (n < 0) {
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of array", n - len);
}
}
if (len <= n) {
@@ -700,7 +702,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
ary_modify(mrb, a);
/* len check */
- if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len));
+ if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%i)", len);
/* range check */
if (head < 0) {
@@ -734,7 +736,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
}
if (head >= alen) {
if (head > ARY_MAX_SIZE - argc) {
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", head);
}
len = head + argc;
if (len > ARY_CAPA(a)) {
@@ -750,7 +752,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val
mrb_int newlen;
if (alen - len > ARY_MAX_SIZE - argc) {
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(alen + argc - len));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", alen + argc - len);
}
newlen = alen + argc - len;
if (newlen > ARY_CAPA(a)) {
@@ -934,7 +936,7 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self)
mrb_ary_splice(mrb, self, i, len, v2);
break;
case MRB_RANGE_OUT:
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1);
+ mrb_raisef(mrb, E_RANGE_ERROR, "%v out of range", v1);
break;
}
return v2;
@@ -1257,44 +1259,95 @@ mrb_ary_svalue(mrb_state *mrb, mrb_value ary)
}
}
+static const mrb_code each_iseq[] = {
+ OP_ENTER, 0x0, 0x00, 0x1, /* OP_ENTER 0:0:0:0:0:0:1 */
+ OP_JMPIF, 0x1, 0x0, 19, /* OP_JMPIF R1 19 */
+ OP_LOADSELF, 0x3, /* OP_LOADSELF R3 */
+ OP_LOADSYM, 0x4, 0x0, /* OP_LOADSYM R4 :each*/
+ OP_SEND, 0x3, 0x1, 0x1, /* OP_SEND R3 :to_enum 1 */
+ OP_RETURN, 0x3, /* OP_RETURN R3 */
+ OP_LOADI_0, 0x2, /* OP_LOADI_0 R2 */
+ OP_JMP, 0x0, 43, /* OP_JMP 49 */
+ OP_MOVE, 0x3, 0x1, /* OP_MOVE R3 R1 */
+ OP_LOADSELF, 0x4, /* OP_LOADSELF R4 */
+ OP_MOVE, 0x5, 0x2, /* OP_MOVE R5 R2 */
+ OP_SEND, 0x4, 0x2, 0x1, /* OP_SEND R4 :[] 1 */
+ OP_SEND, 0x3, 0x3, 0x1, /* OP_SEND R3 :call 1 */
+ OP_ADDI, 0x2, 1, /* OP_ADDI R3 1 */
+ OP_MOVE, 0x3, 0x2, /* OP_MOVE R3 R2 */
+ OP_LOADSELF, 0x4, /* OP_LOADSELF R4 */
+ OP_SEND, 0x4, 0x4, 0x0, /* OP_SEND R4 :length 0 */
+ OP_LT, 0x3, /* OP_LT R3 */
+ OP_JMPIF, 0x3, 0x0, 24, /* OP_JMPIF R3 24 */
+ OP_RETURN, 0x0 /* OP_RETURN R3 */
+};
+
+static void
+init_ary_each(mrb_state *mrb, struct RClass *ary)
+{
+ struct RProc *p;
+ mrb_method_t m;
+ mrb_irep *each_irep = (mrb_irep*)mrb_malloc(mrb, sizeof(mrb_irep));
+ static const mrb_irep mrb_irep_zero = { 0 };
+
+ *each_irep = mrb_irep_zero;
+ each_irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*5);
+ each_irep->syms[0] = mrb_intern_lit(mrb, "each");
+ each_irep->syms[1] = mrb_intern_lit(mrb, "to_enum");
+ each_irep->syms[2] = mrb_intern_lit(mrb, "[]");
+ each_irep->syms[3] = mrb_intern_lit(mrb, "call");
+ each_irep->syms[4] = mrb_intern_lit(mrb, "length");
+ each_irep->slen = 5;
+ each_irep->flags = MRB_ISEQ_NO_FREE;
+ each_irep->iseq = each_iseq;
+ each_irep->ilen = sizeof(each_iseq);
+ each_irep->nregs = 7;
+ each_irep->nlocals = 3;
+ p = mrb_proc_new(mrb, each_irep);
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, ary, mrb_intern_lit(mrb, "each"), m);
+}
+
void
mrb_init_array(mrb_state *mrb)
{
struct RClass *a;
- mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */
+ mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */
MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY);
- mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */
-
- mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */
- mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */
- mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */
- mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */
- mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */
- mrb_define_method(mrb, a, "clear", mrb_ary_clear_m, MRB_ARGS_NONE()); /* 15.2.12.5.6 */
- mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */
- mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */
- mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */
- mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
- mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
- mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
- mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */
- mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */
- mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */
- mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */
- mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */
- mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
- mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */
- mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
- mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
- mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */
- mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */
- mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */
- mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
+ mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */
+
+ mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */
+ mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */
+ mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */
+ mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.4 */
+ mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ARG(2,1)); /* 15.2.12.5.5 */
+ mrb_define_method(mrb, a, "clear", mrb_ary_clear_m, MRB_ARGS_NONE()); /* 15.2.12.5.6 */
+ mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */
+ mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */
+ mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */
+ mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
+ mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
+ mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
+ mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.17 */
+ mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_OPT(1)); /* 15.2.12.5.18 */
+ mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */
+ mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */
+ mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */
+ mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
+ mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */
+ mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
+ mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
+ mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */
+ mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */
+ mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.29 */
+ mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1));
mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
+ mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
mrb_define_method(mrb, a, "__svalue", mrb_ary_svalue, MRB_ARGS_NONE());
+
+ init_ary_each(mrb, a);
}
diff --git a/src/backtrace.c b/src/backtrace.c
index e4f5a3064..8001849bc 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -16,7 +16,7 @@
#include <mruby/data.h>
struct backtrace_location {
- int lineno;
+ int32_t lineno;
mrb_sym method_id;
const char *filename;
};
@@ -26,7 +26,7 @@ typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*
static const mrb_data_type bt_type = { "Backtrace", mrb_free };
static void
-each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
+each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, const mrb_code *pc0, each_backtrace_func func, void *data)
{
ptrdiff_t i;
@@ -37,7 +37,7 @@ each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_fu
struct backtrace_location loc;
mrb_callinfo *ci;
mrb_irep *irep;
- mrb_code *pc;
+ const mrb_code *pc;
ci = &mrb->c->cibase[i];
@@ -246,9 +246,7 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
mrb_value btline;
if (entry->filename == NULL) continue;
- btline = mrb_format(mrb, "%S:%S",
- mrb_str_new_cstr(mrb, entry->filename),
- mrb_fixnum_value(entry->lineno));
+ btline = mrb_format(mrb, "%s:%d", entry->filename, entry->lineno);
if (entry->method_id != 0) {
mrb_str_cat_lit(mrb, btline, ":in ");
mrb_str_cat_cstr(mrb, btline, mrb_sym2name(mrb, entry->method_id));
diff --git a/src/class.c b/src/class.c
index 373f2aec7..a551c73f6 100644
--- a/src/class.c
+++ b/src/class.c
@@ -15,6 +15,7 @@
#include <mruby/error.h>
#include <mruby/data.h>
#include <mruby/istruct.h>
+#include <mruby/opcode.h>
KHASH_DEFINE(mt, mrb_sym, mrb_method_t, TRUE, kh_int_hash_func, kh_int_hash_equal)
@@ -177,7 +178,7 @@ static void
check_if_class_or_module(mrb_state *mrb, mrb_value obj)
{
if (!class_ptr_p(obj)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj);
}
}
@@ -207,7 +208,7 @@ mrb_define_module(mrb_state *mrb, const char *name)
return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class);
}
-MRB_API struct RClass*
+struct RClass*
mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
{
check_if_class_or_module(mrb, outer);
@@ -215,7 +216,7 @@ mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
mrb_value old = mrb_const_get(mrb, outer, id);
if (mrb_type(old) != MRB_TT_MODULE) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old);
}
return mrb_class_ptr(old);
}
@@ -248,9 +249,8 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *
c = class_from_sym(mrb, outer, name);
MRB_CLASS_ORIGIN(c);
if (super && mrb_class_real(c->super) != super) {
- mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)",
- mrb_sym2str(mrb, name),
- mrb_obj_value(c->super), mrb_obj_value(super));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %n (%C not %C)",
+ name, c->super, super);
}
return c;
}
@@ -265,7 +265,7 @@ MRB_API struct RClass*
mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
{
if (!super) {
- mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name));
+ mrb_warn(mrb, "no super class for '%n', Object assumed", name);
}
return define_class(mrb, name, super, mrb->object_class);
}
@@ -305,7 +305,7 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
}
}
-MRB_API struct RClass*
+struct RClass*
mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id)
{
struct RClass *s;
@@ -313,8 +313,7 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
if (!mrb_nil_p(super)) {
if (mrb_type(super) != MRB_TT_CLASS) {
- mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)",
- mrb_inspect(mrb, super));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%!v given)", super);
}
s = mrb_class_ptr(super);
}
@@ -326,13 +325,13 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
mrb_value old = mrb_const_get(mrb, outer, id);
if (mrb_type(old) != MRB_TT_CLASS) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class", old);
}
c = mrb_class_ptr(old);
if (s) {
/* check super class */
if (mrb_class_real(c->super) != s) {
- mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old);
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %v", old);
}
}
return c;
@@ -410,7 +409,7 @@ mrb_module_get(mrb_state *mrb, const char *name)
/*!
* Defines a class under the namespace of \a outer.
* \param outer a class which contains the new class.
- * \param id name of the new class
+ * \param name name of the new class
* \param super a class from which the new class will derive.
* NULL means \c Object class.
* \return the created class
@@ -431,8 +430,7 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s
#if 0
if (!super) {
- mrb_warn(mrb, "no super class for '%S::%S', Object assumed",
- mrb_obj_value(outer), mrb_sym2str(mrb, id));
+ mrb_warn(mrb, "no super class for '%C::%n', Object assumed", outer, id);
}
#endif
c = define_class(mrb, id, super, outer);
@@ -489,8 +487,7 @@ mrb_notimplement(mrb_state *mrb)
mrb_callinfo *ci = mrb->c->ci;
if (ci->mid) {
- mrb_value str = mrb_sym2str(mrb, ci->mid);
- mrb_raisef(mrb, E_NOTIMP_ERROR, "%S() function is unimplemented on this machine", str);
+ mrb_raisef(mrb, E_NOTIMP_ERROR, "%n() function is unimplemented on this machine", ci->mid);
}
}
@@ -503,30 +500,17 @@ mrb_notimplement_m(mrb_state *mrb, mrb_value self)
return mrb_nil_value();
}
-#define CHECK_TYPE(mrb, val, t, c) do { \
- if (mrb_type(val) != (t)) {\
- mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_lit(mrb, c));\
- }\
-} while (0)
-
-static mrb_value
-to_str(mrb_state *mrb, mrb_value val)
-{
- CHECK_TYPE(mrb, val, MRB_TT_STRING, "String");
- return val;
-}
-
static mrb_value
to_ary(mrb_state *mrb, mrb_value val)
{
- CHECK_TYPE(mrb, val, MRB_TT_ARRAY, "Array");
+ mrb_check_type(mrb, val, MRB_TT_ARRAY);
return val;
}
static mrb_value
to_hash(mrb_state *mrb, mrb_value val)
{
- CHECK_TYPE(mrb, val, MRB_TT_HASH, "Hash");
+ mrb_check_type(mrb, val, MRB_TT_HASH);
return val;
}
@@ -573,20 +557,20 @@ mrb_get_argv(mrb_state *mrb)
string mruby type C type note
----------------------------------------------------------------------------------------------
o: Object [mrb_value]
- C: class/module [mrb_value]
+ C: Class/Module [mrb_value]
S: String [mrb_value] when ! follows, the value may be nil
A: Array [mrb_value] when ! follows, the value may be nil
H: Hash [mrb_value] when ! follows, the value may be nil
s: String [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil
z: String [char*] NUL terminated string; z! gives NULL for nil
a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil
- f: Float [mrb_float]
- i: Integer [mrb_int]
- b: Boolean [mrb_bool]
- n: Symbol [mrb_sym]
- d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
- I: Inline struct [void*]
- &: Block [mrb_value] &! raises exception if no block given
+ f: Fixnum/Float [mrb_float]
+ i: Fixnum/Float [mrb_int]
+ b: boolean [mrb_bool]
+ n: String/Symbol [mrb_sym]
+ d: data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified; when ! follows, the value may be nil
+ I: inline struct [void*]
+ &: block [mrb_value] &! raises exception if no block given
*: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack
|: optional Following arguments are optional
?: optional given [mrb_bool] true if preceding argument (optional) is given
@@ -669,7 +653,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
ss = ARGV[arg_i++];
if (!class_ptr_p(ss)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss);
}
*p = ss;
i++;
@@ -690,7 +674,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
if (i < argc) {
- *p = to_str(mrb, ARGV[arg_i++]);
+ *p = ARGV[arg_i++];
+ mrb_to_str(mrb, *p);
i++;
}
}
@@ -751,7 +736,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
if (i < argc) {
- ss = to_str(mrb, ARGV[arg_i++]);
+ ss = ARGV[arg_i++];
+ mrb_to_str(mrb, ss);
*ps = RSTRING_PTR(ss);
*pl = RSTRING_LEN(ss);
i++;
@@ -773,8 +759,9 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
if (i < argc) {
- ss = to_str(mrb, ARGV[arg_i++]);
- *ps = mrb_string_value_cstr(mrb, &ss);
+ ss = ARGV[arg_i++];
+ mrb_to_str(mrb, ss);
+ *ps = RSTRING_CSTR(mrb, ss);
i++;
}
}
@@ -816,7 +803,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
ss = ARGV[arg_i];
if (mrb_type(ss) != MRB_TT_ISTRUCT)
{
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v is not inline struct", ss);
}
*p = mrb_istruct_ptr(ss);
arg_i++;
@@ -964,7 +951,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
break;
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %c", c);
break;
}
}
@@ -1070,8 +1057,8 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
MRB_API void
mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
{
- int changed = include_module_at(mrb, c, find_origin(c), m, 1);
- if (changed < 0) {
+ mrb_check_frozen(mrb, c);
+ if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
}
}
@@ -1082,6 +1069,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
struct RClass *origin;
int changed = 0;
+ mrb_check_frozen(mrb, c);
if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
@@ -1356,21 +1344,57 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) {
inspect = mrb_any_to_s(mrb, mrb_obj_value(c));
}
- mrb_name_error(mrb, mid, "undefined method '%S' for class %S",
- mrb_sym2str(mrb, mid), inspect);
+ mrb_name_error(mrb, mid, "undefined method '%n' for class %v", mid, inspect);
}
return m;
}
+#define ONSTACK_ALLOC_MAX 32
+
+static mrb_sym
+prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix)
+{
+ char onstack[ONSTACK_ALLOC_MAX];
+ mrb_int sym_len;
+ const char *sym_str = mrb_sym2name_len(mrb, sym, &sym_len);
+ size_t prefix_len = prefix ? strlen(prefix) : 0;
+ size_t suffix_len = suffix ? strlen(suffix) : 0;
+ size_t name_len = sym_len + prefix_len + suffix_len;
+ char *buf = name_len > sizeof(onstack) ? (char *)mrb_alloca(mrb, name_len) : onstack;
+ char *p = buf;
+
+ if (prefix_len > 0) {
+ memcpy(p, prefix, prefix_len);
+ p += prefix_len;
+ }
+
+ memcpy(p, sym_str, sym_len);
+ p += sym_len;
+
+ if (suffix_len > 0) {
+ memcpy(p, suffix, suffix_len);
+ p += suffix_len;
+ }
+
+ return mrb_intern(mrb, buf, name_len);
+}
+
static mrb_value
-attr_reader(mrb_state *mrb, mrb_value obj)
+prepare_ivar_name(mrb_state *mrb, mrb_sym sym)
{
- mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
- return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+ sym = prepare_name_common(mrb, sym, "@", NULL);
+ mrb_iv_name_sym_check(mrb, sym);
+ return mrb_symbol_value(sym);
+}
+
+static mrb_sym
+prepare_writer_name(mrb_state *mrb, mrb_sym sym)
+{
+ return prepare_name_common(mrb, sym, NULL, "=");
}
static mrb_value
-mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state *, mrb_value), mrb_sym (*access_name)(mrb_state *, mrb_sym))
{
struct RClass *c = mrb_class_ptr(mod);
mrb_value *argv;
@@ -1380,20 +1404,18 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "*", &argv, &argc);
ai = mrb_gc_arena_save(mrb);
for (i=0; i<argc; i++) {
- mrb_value name, str;
- mrb_sym method, sym;
+ mrb_value name;
+ mrb_sym method;
struct RProc *p;
mrb_method_t m;
method = to_sym(mrb, argv[i]);
- name = mrb_sym2str(mrb, method);
- str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_name_sym_check(mrb, sym);
- name = mrb_symbol_value(sym);
- p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name);
+ name = prepare_ivar_name(mrb, method);
+ if (access_name) {
+ method = access_name(mrb, method);
+ }
+
+ p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name);
MRB_METHOD_FROM_PROC(m, p);
mrb_define_method_raw(mrb, c, method, m);
mrb_gc_arena_restore(mrb, ai);
@@ -1402,6 +1424,19 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
}
static mrb_value
+attr_reader(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
+ return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+}
+
+static mrb_value
+mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+{
+ return mod_attr_define(mrb, mod, attr_reader, NULL);
+}
+
+static mrb_value
attr_writer(mrb_state *mrb, mrb_value obj)
{
mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
@@ -1415,42 +1450,7 @@ attr_writer(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
{
- struct RClass *c = mrb_class_ptr(mod);
- mrb_value *argv;
- mrb_int argc, i;
- int ai;
-
- mrb_get_args(mrb, "*", &argv, &argc);
- ai = mrb_gc_arena_save(mrb);
- for (i=0; i<argc; i++) {
- mrb_value name, str, attr;
- mrb_sym method, sym;
- struct RProc *p;
- mrb_method_t m;
-
- method = to_sym(mrb, argv[i]);
-
- /* prepare iv name (@name) */
- name = mrb_sym2str(mrb, method);
- str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_name_sym_check(mrb, sym);
- attr = mrb_symbol_value(sym);
-
- /* prepare method name (name=) */
- str = mrb_str_new_capa(mrb, RSTRING_LEN(str));
- mrb_str_cat_str(mrb, str, name);
- mrb_str_cat_lit(mrb, str, "=");
- method = mrb_intern_str(mrb, str);
-
- p = mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr);
- MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, c, method, m);
- mrb_gc_arena_restore(mrb, ai);
- }
- return mrb_nil_value();
+ return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name);
}
static mrb_value
@@ -1465,7 +1465,7 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
if (ttype == 0) ttype = MRB_TT_OBJECT;
if (ttype <= MRB_TT_CPTR) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %S", cv);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %v", cv);
}
o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c);
return mrb_obj_value(o);
@@ -1491,7 +1491,7 @@ mrb_instance_new(mrb_state *mrb, mrb_value cv)
mrb_int argc;
mrb_sym init;
- mrb_get_args(mrb, "*&", &argv, &argc, &blk);
+ mrb_get_args(mrb, "*!&", &argv, &argc, &blk);
obj = mrb_instance_alloc(mrb, cv);
init = mrb_intern_lit(mrb, "initialize");
if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) {
@@ -1540,7 +1540,10 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv)
}
new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super)));
mid = mrb_intern_lit(mrb, "initialize");
- if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) {
+ if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) {
+ mrb_class_initialize(mrb, new_class);
+ }
+ else {
mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk);
}
mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class));
@@ -1692,7 +1695,7 @@ static void
mrb_check_inheritable(mrb_state *mrb, struct RClass *super)
{
if (super->tt != MRB_TT_CLASS) {
- mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_obj_value(super));
+ mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%C given)", super);
}
if (super->tt == MRB_TT_SCLASS) {
mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class");
@@ -1766,6 +1769,7 @@ mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
/*!
* Defines an alias of a method.
+ * \param mrb the mruby state
* \param klass the class which the original method belongs to
* \param name1 a new name for the method
* \param name2 the original name of the method
@@ -1817,24 +1821,28 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod)
return mod;
}
+static void
+undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
+{
+ mrb_method_t m;
+
+ MRB_METHOD_FROM_PROC(m, NULL);
+ mrb_define_method_raw(mrb, c, a, m);
+}
+
void
mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a)
{
if (!mrb_obj_respond_to(mrb, c, a)) {
- mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c));
- }
- else {
- mrb_method_t m;
-
- MRB_METHOD_FROM_PROC(m, NULL);
- mrb_define_method_raw(mrb, c, a, m);
+ mrb_name_error(mrb, a, "undefined method '%n' for class '%C'", a, c);
}
+ undef_method(mrb, c, a);
}
MRB_API void
mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name)
{
- mrb_undef_method_id(mrb, c, mrb_intern_cstr(mrb, name));
+ undef_method(mrb, c, mrb_intern_cstr(mrb, name));
}
MRB_API void
@@ -1864,7 +1872,7 @@ check_const_name_sym(mrb_state *mrb, mrb_sym id)
mrb_int len;
const char *name = mrb_sym2name_len(mrb, id, &len);
if (!mrb_const_name_p(mrb, name, len)) {
- mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "wrong constant name %n", id);
}
}
@@ -1921,7 +1929,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
else {
off = end + 2;
if (off == len) { /* trailing "::" */
- mrb_name_error(mrb, id, "wrong constant name '%S'", path);
+ mrb_name_error(mrb, id, "wrong constant name '%v'", path);
}
}
}
@@ -1951,7 +1959,7 @@ mrb_mod_remove_const(mrb_state *mrb, mrb_value mod)
check_const_name_sym(mrb, id);
val = mrb_iv_remove(mrb, mod, id);
if (mrb_undef_p(val)) {
- mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id));
+ mrb_name_error(mrb, id, "constant %n not defined", id);
}
return val;
}
@@ -1964,13 +1972,10 @@ mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "n", &sym);
if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) {
- mrb_name_error(mrb, sym, "uninitialized constant %S::%S",
- mod,
- mrb_sym2str(mrb, sym));
+ mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym);
}
else {
- mrb_name_error(mrb, sym, "uninitialized constant %S",
- mrb_sym2str(mrb, sym));
+ mrb_name_error(mrb, sym, "uninitialized constant %n", sym);
}
/* not reached */
return mrb_nil_value();
@@ -2031,7 +2036,7 @@ mod_define_method(mrb_state *mrb, mrb_value self)
/* ignored */
break;
default:
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", proc);
break;
}
if (mrb_nil_p(blk)) {
@@ -2064,6 +2069,14 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
}
static mrb_value
+mrb_mod_dup(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mod = mrb_obj_clone(mrb, self);
+ MRB_UNSET_FROZEN_FLAG(mrb_obj_ptr(mod));
+ return mod;
+}
+
+static mrb_value
mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
{
mrb_value *argv;
@@ -2111,6 +2124,40 @@ inspect_main(mrb_state *mrb, mrb_value mod)
return mrb_str_new_lit(mrb, "main");
}
+static const mrb_code new_iseq[] = {
+ OP_ENTER, 0x0, 0x10, 0x1, /* OP_ENTER 0:0:1:0:0:0:1 */
+ OP_LOADSELF, 0x3, /* OP_LOADSELF R3 */
+ OP_SEND, 0x3, 0x0, 0x0, /* OP_SEND R3 :allocate 0 */
+ OP_MOVE, 0x0, 0x3, /* OP_MOVE R0 R3 */
+ OP_MOVE, 0x4, 0x1, /* OP_MOVE R4 R1 */
+ OP_MOVE, 0x5, 0x2, /* OP_MOVE R5 R2 */
+ OP_SENDVB, 0x3, 0x1, /* OP_SENDVB R4 :initialize */
+ OP_RETURN, 0x0 /* OP_RETURN R0 */
+};
+
+static void
+init_class_new(mrb_state *mrb, struct RClass *cls)
+{
+ struct RProc *p;
+ mrb_method_t m;
+ mrb_irep *new_irep = (mrb_irep*)mrb_malloc(mrb, sizeof(mrb_irep));
+ static const mrb_irep mrb_irep_zero = { 0 };
+
+ *new_irep = mrb_irep_zero;
+ new_irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*2);
+ new_irep->syms[0] = mrb_intern_lit(mrb, "allocate");
+ new_irep->syms[1] = mrb_intern_lit(mrb, "initialize");
+ new_irep->slen = 2;
+ new_irep->flags = MRB_ISEQ_NO_FREE;
+ new_irep->iseq = new_iseq;
+ new_irep->ilen = sizeof(new_iseq);
+ new_irep->nregs = 6;
+ new_irep->nlocals = 3;
+ p = mrb_proc_new(mrb, new_irep);
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, cls, mrb_intern_lit(mrb, "new"), m);
+}
+
void
mrb_init_class(mrb_state *mrb)
{
@@ -2133,7 +2180,6 @@ mrb_init_class(mrb_state *mrb)
/* name basic classes */
mrb_define_const(mrb, bob, "BasicObject", mrb_obj_value(bob));
- mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob));
mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj));
mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod));
mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls));
@@ -2153,16 +2199,18 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1));
mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */
- mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.5 */
+ mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */
mrb_define_method(mrb, bob, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
- mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */
+ mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */
- mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, cls, "allocate", mrb_instance_alloc, MRB_ARGS_NONE());
mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */
- mrb_define_method(mrb, cls, "new", mrb_instance_new, MRB_ARGS_ANY()); /* 15.2.3.3.3 */
mrb_define_method(mrb, cls, "initialize", mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */
mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1));
+ init_class_new(mrb, cls);
+
MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE);
mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */
mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */
@@ -2193,6 +2241,7 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1));
mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */
+ mrb_define_method(mrb, mod, "dup", mrb_mod_dup, MRB_ARGS_NONE());
mrb_undef_method(mrb, cls, "append_features");
mrb_undef_method(mrb, cls, "extend_object");
diff --git a/src/codedump.c b/src/codedump.c
index 12d609075..b77a8adb4 100644
--- a/src/codedump.c
+++ b/src/codedump.c
@@ -69,7 +69,7 @@ static void
codedump(mrb_state *mrb, mrb_irep *irep)
{
int ai;
- mrb_code *pc, *pcend;
+ const mrb_code *pc, *pcend;
mrb_code ins;
const char *file = NULL, *next_file;
diff --git a/src/error.c b/src/error.c
index 4c1b67c99..2bb505d85 100644
--- a/src/error.c
+++ b/src/error.c
@@ -151,14 +151,14 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
str = mrb_str_new_cstr(mrb, cname);
if (mrb_string_p(file) && mrb_fixnum_p(line)) {
if (append_mesg) {
- str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str);
+ str = mrb_format(mrb, "%v:%v: %v (%v)", file, line, mesg, str);
}
else {
- str = mrb_format(mrb, "%S:%S: %S", file, line, str);
+ str = mrb_format(mrb, "%v:%v: %v", file, line, str);
}
}
else if (append_mesg) {
- str = mrb_format(mrb, "%S: %S", str, mesg);
+ str = mrb_format(mrb, "%v: %v", str, mesg);
}
return str;
}
@@ -198,11 +198,11 @@ static void
exc_debug_info(mrb_state *mrb, struct RObject *exc)
{
mrb_callinfo *ci = mrb->c->ci;
- mrb_code *pc = ci->pc;
+ const mrb_code *pc = ci->pc;
if (mrb_obj_iv_defined(mrb, exc, mrb_intern_lit(mrb, "file"))) return;
while (ci >= mrb->c->cibase) {
- mrb_code *err = ci->err;
+ const mrb_code *err = ci->err;
if (!err && pc) err = pc - 1;
if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
@@ -260,59 +260,152 @@ mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg)));
}
+/*
+ * <code>vsprintf</code> like formatting.
+ *
+ * The syntax of a format sequence is as follows.
+ *
+ * %[modifier]specifier
+ *
+ * The modifiers are:
+ *
+ * ----------+------------------------------------------------------------
+ * Modifier | Meaning
+ * ----------+------------------------------------------------------------
+ * ! | Convert to string by corresponding `inspect` instead of
+ * | corresponding `to_s`.
+ * ----------+------------------------------------------------------------
+ *
+ * The specifiers are:
+ *
+ * ----------+----------------+--------------------------------------------
+ * Specifier | Argument Type | Note
+ * ----------+----------------+--------------------------------------------
+ * c | char |
+ * d | int |
+ * f | mrb_float |
+ * i | mrb_int |
+ * l | char*, size_t | Arguments are string and length.
+ * n | mrb_sym |
+ * s | char* | Argument is NUL terminated string.
+ * t | mrb_value | Convert to type (class) of object.
+ * v,S | mrb_value |
+ * C | struct RClass* |
+ * T | mrb_value | Convert to real type (class) of object.
+ * Y | mrb_value | Same as `!v` if argument is `true`, `false`
+ * | | or `nil`, otherwise same as `T`.
+ * % | - | Convert to percent sign itself (no argument
+ * | | taken).
+ * ----------+----------------+--------------------------------------------
+ */
MRB_API mrb_value
mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
{
- const char *p = format;
- const char *b = p;
- ptrdiff_t size;
- int ai0 = mrb_gc_arena_save(mrb);
- mrb_value ary = mrb_ary_new_capa(mrb, 4);
+ const char *chars, *p = format, *b = format, *e;
+ char ch;
+ size_t len;
+ mrb_int i;
+ struct RClass *cls;
+ mrb_bool inspect = FALSE;
+ mrb_value result = mrb_str_new_capa(mrb, 128), obj, str;
int ai = mrb_gc_arena_save(mrb);
while (*p) {
const char c = *p++;
-
+ e = p;
if (c == '%') {
- if (*p == 'S') {
- mrb_value val;
-
- size = p - b - 1;
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
- val = va_arg(ap, mrb_value);
- mrb_ary_push(mrb, ary, mrb_obj_as_string(mrb, val));
- b = p + 1;
- }
- }
- else if (c == '\\') {
- if (*p) {
- size = p - b - 1;
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1));
- b = ++p;
+ if (*p == '!') {
+ inspect = TRUE;
+ ++p;
}
- else {
- break;
+ if (!*p) break;
+ switch (*p) {
+ case 'c':
+ ch = (char)va_arg(ap, int);
+ chars = &ch;
+ len = 1;
+ goto L_cat;
+ case 'd': case 'i':
+#if MRB_INT_MAX < INT_MAX
+ i = (mrb_int)va_arg(ap, int);
+#else
+ i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int);
+#endif
+ obj = mrb_fixnum_value(i);
+ goto L_cat_obj;
+#ifndef MRB_WITHOUT_FLOAT
+ case 'f':
+ obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double));
+ goto L_cat_obj;
+#endif
+ case 'l':
+ chars = va_arg(ap, char*);
+ len = va_arg(ap, size_t);
+ L_cat:
+ if (inspect) {
+ obj = mrb_str_new(mrb, chars, len);
+ goto L_cat_obj;
+ }
+ mrb_str_cat(mrb, result, b, e - b - 1);
+ mrb_str_cat(mrb, result, chars, len);
+ b = ++p;
+ mrb_gc_arena_restore(mrb, ai);
+ break;
+ case 'n':
+ obj = mrb_symbol_value(va_arg(ap, mrb_sym));
+ goto L_cat_obj;
+ case 's':
+ chars = va_arg(ap, char*);
+ len = strlen(chars);
+ goto L_cat;
+ case 't':
+ cls = mrb_class(mrb, va_arg(ap, mrb_value));
+ goto L_cat_class;
+ case 'v': case 'S':
+ obj = va_arg(ap, mrb_value);
+ L_cat_obj:
+ str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj);
+ chars = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ inspect = FALSE;
+ goto L_cat;
+ case 'C':
+ cls = va_arg(ap, struct RClass*);
+ L_cat_class:
+ obj = mrb_obj_value(cls);
+ goto L_cat_obj;
+ case 'T':
+ obj = va_arg(ap, mrb_value);
+ L_cat_real_class_of:
+ cls = mrb_obj_class(mrb, obj);
+ goto L_cat_class;
+ case 'Y':
+ obj = va_arg(ap, mrb_value);
+ if (!mrb_test(obj) || mrb_true_p(obj)) {
+ inspect = TRUE;
+ goto L_cat_obj;
+ }
+ else {
+ goto L_cat_real_class_of;
+ }
+ case '%':
+ L_cat_current:
+ chars = p;
+ len = 1;
+ goto L_cat;
+ default:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p);
}
}
- mrb_gc_arena_restore(mrb, ai);
- }
- if (b == format) {
- mrb_gc_arena_restore(mrb, ai0);
- return mrb_str_new_cstr(mrb, format);
- }
- else {
- mrb_value val;
+ else if (c == '\\') {
+ if (!*p) break;
+ goto L_cat_current;
- size = p - b;
- if (size > 0) {
- mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
}
- val = mrb_ary_join(mrb, ary, mrb_nil_value());
- mrb_gc_arena_restore(mrb, ai0);
- mrb_gc_protect(mrb, val);
- return val;
}
+
+ mrb_str_cat(mrb, result, b, p - b);
+ return result;
}
MRB_API mrb_value
@@ -434,7 +527,7 @@ exception_call:
break;
default:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%i for 0..3)", argc);
break;
}
if (argc > 0) {
@@ -487,8 +580,7 @@ mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt,
MRB_API mrb_noreturn void
mrb_frozen_error(mrb_state *mrb, void *frozen_obj)
{
- mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S",
- mrb_obj_value(mrb_class(mrb, mrb_obj_value(frozen_obj))));
+ mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %t", mrb_obj_value(frozen_obj));
}
void
diff --git a/src/etc.c b/src/etc.c
index ac1540f92..bf6586748 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -1,5 +1,5 @@
/*
-** etc.c -
+** etc.c
**
** See Copyright Notice in mruby.h
*/
@@ -8,8 +8,6 @@
#include <mruby/string.h>
#include <mruby/data.h>
#include <mruby/class.h>
-#include <mruby/re.h>
-#include <mruby/irep.h>
MRB_API struct RData*
mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
@@ -33,14 +31,12 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
const mrb_data_type *t2 = DATA_TYPE(obj);
if (t2) {
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)",
- mrb_str_new_cstr(mrb, t2->struct_name), mrb_str_new_cstr(mrb, type->struct_name));
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
+ t2->struct_name, type->struct_name);
}
else {
- struct RClass *c = mrb_class(mrb, obj);
-
- mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %S (expected %S)",
- mrb_obj_value(c), mrb_str_new_cstr(mrb, type->struct_name));
+ mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)",
+ obj, type->struct_name);
}
}
}
@@ -67,24 +63,10 @@ mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
MRB_API mrb_sym
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
{
- mrb_sym id;
-
- switch (mrb_type(name)) {
- default:
- name = mrb_check_string_type(mrb, name);
- if (mrb_nil_p(name)) {
- name = mrb_inspect(mrb, name);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name);
- /* not reached */
- }
- /* fall through */
- case MRB_TT_STRING:
- name = mrb_str_intern(mrb, name);
- /* fall through */
- case MRB_TT_SYMBOL:
- id = mrb_symbol(name);
- }
- return id;
+ if (mrb_symbol_p(name)) return mrb_symbol(name);
+ if (mrb_string_p(name)) return mrb_intern_str(mrb, name);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name);
+ return 0; /* not reached */
}
MRB_API mrb_int
diff --git a/src/gc.c b/src/gc.c
index 94068c0f9..a2a904477 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -506,7 +506,7 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
a = mrb_ary_ptr(table);
mrb_ary_modify(mrb, a);
for (i = 0; i < ARY_LEN(a); i++) {
- if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) {
+ if (mrb_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) {
mrb_int len = ARY_LEN(a)-1;
mrb_value *ptr = ARY_PTR(a);
@@ -521,7 +521,7 @@ MRB_API struct RBasic*
mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
{
struct RBasic *p;
- static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } };
+ static const RVALUE RVALUE_zero = { { { NULL, NULL, MRB_TT_FALSE } } };
mrb_gc *gc = &mrb->gc;
if (cls) {
@@ -542,7 +542,7 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
ttype != MRB_TT_ICLASS &&
ttype != MRB_TT_ENV &&
ttype != tt) {
- mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls));
+ mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls);
}
}
@@ -748,7 +748,7 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
break;
case MRB_TT_STRING:
- if (RSTR_FSHARED_P(obj) && !RSTR_NOFREE_P(obj)) {
+ if (RSTR_FSHARED_P(obj)) {
struct RString *s = (struct RString*)obj;
mrb_gc_mark(mrb, (struct RBasic*)s->as.heap.aux.fshared);
}
diff --git a/src/hash.c b/src/hash.c
index a9367a426..2a0a19363 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -240,7 +240,7 @@ ht_compact(mrb_state *mrb, htable *t)
if (!seg->next && i >= t->last_len) {
goto exit;
}
- if (mrb_undef_p(k)) { /* found delete key */
+ if (mrb_undef_p(k)) { /* found deleted key */
if (seg2 == NULL) {
seg2 = seg;
i2 = i;
@@ -546,6 +546,7 @@ ht_copy(mrb_state *mrb, htable *t)
if ((seg->next == NULL) && (i >= t->last_len)) {
return t2;
}
+ if (mrb_undef_p(key)) continue; /* skip deleted key */
ht_put(mrb, t2, key, val);
}
seg = seg->next;
@@ -1378,10 +1379,21 @@ mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
* values of key objects have changed since they were inserted, this
* method will reindex <i>hsh</i>.
*
- * h = {"AAA" => "b"}
- * h.keys[0].chop!
- * h.rehash #=> {"AA"=>"b"}
- * h["AA"] #=> "b"
+ * keys = (1..17).map{|n| [n]}
+ * k = keys[0]
+ * h = {}
+ * keys.each{|key| h[key] = key[0]}
+ * h #=> { [1]=> 1, [2]=> 2, [3]=> 3, [4]=> 4, [5]=> 5, [6]=> 6, [7]=> 7,
+ * [8]=> 8, [9]=> 9,[10]=>10,[11]=>11,[12]=>12,[13]=>13,[14]=>14,
+ * [15]=>15,[16]=>16,[17]=>17}
+ * h[k] #=> 1
+ * k[0] = keys.size + 1
+ * h #=> {[18]=> 1, [2]=> 2, [3]=> 3, [4]=> 4, [5]=> 5, [6]=> 6, [7]=> 7,
+ * [8]=> 8, [9]=> 9,[10]=>10,[11]=>11,[12]=>12,[13]=>13,[14]=>14,
+ * [15]=>15,[16]=>16,[17]=>17}
+ * h[k] #=> nil
+ * h.rehash
+ * h[k] #=> 1
*/
static mrb_value
mrb_hash_rehash(mrb_state *mrb, mrb_value self)
diff --git a/src/kernel.c b/src/kernel.c
index a3c2d2ec6..f0935a2f8 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -325,7 +325,7 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self)
mrb_value clone;
if (mrb_immediate_p(self)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %v", self);
}
if (mrb_type(self) == MRB_TT_SCLASS) {
mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class");
@@ -366,7 +366,7 @@ mrb_obj_dup(mrb_state *mrb, mrb_value obj)
mrb_value dup;
if (mrb_immediate_p(obj)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %v", obj);
}
if (mrb_type(obj) == MRB_TT_SCLASS) {
mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class");
@@ -641,7 +641,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
mrb_iv_name_sym_check(mrb, sym);
val = mrb_iv_remove(mrb, self, sym);
if (mrb_undef_p(val)) {
- mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym));
+ mrb_name_error(mrb, sym, "instance variable %n not defined", sym);
}
return val;
}
@@ -649,7 +649,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
void
mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
{
- mrb_no_method_error(mrb, name, args, "undefined method '%S'", mrb_sym2str(mrb, name));
+ mrb_no_method_error(mrb, name, args, "undefined method '%n'", name);
}
/* 15.3.1.3.30 */
@@ -805,5 +805,4 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "__to_str", mrb_to_str, MRB_ARGS_NONE()); /* internal */
mrb_include_module(mrb, mrb->object_class, mrb->kernel_module);
- mrb_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */
}
diff --git a/src/load.c b/src/load.c
index 97eafdbb5..2aa2c576f 100644
--- a/src/load.c
+++ b/src/load.c
@@ -102,8 +102,9 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
}
else {
size_t data_len = sizeof(mrb_code) * irep->ilen;
- irep->iseq = (mrb_code *)mrb_malloc(mrb, data_len);
- memcpy(irep->iseq, src, data_len);
+ void *buf = mrb_malloc(mrb, data_len);
+ irep->iseq = (mrb_code *)buf;
+ memcpy(buf, src, data_len);
src += data_len;
}
}
diff --git a/src/numeric.c b/src/numeric.c
index 0d1c23589..638f75fd8 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -172,17 +172,17 @@ integral_div(mrb_state *mrb, mrb_value x)
static mrb_value
integral_coerce_step_counter(mrb_state *mrb, mrb_value self)
{
- mrb_value counter = self, num, step;
+ mrb_value num, step;
mrb_get_args(mrb, "oo", &num, &step);
#ifndef MRB_WITHOUT_FLOAT
if (mrb_float_p(self) || mrb_float_p(num) || mrb_float_p(step)) {
- counter = mrb_funcall(mrb, counter, "to_f", 0);
+ return mrb_Float(mrb, self);
}
#endif
- return counter;
+ return self;
}
#ifndef MRB_WITHOUT_FLOAT
@@ -246,9 +246,6 @@ flo_to_s(mrb_state *mrb, mrb_value flt)
str = mrb_float_to_str(mrb, flt, fmt);
goto insert_dot_zero;
}
- else {
- mrb_str_cat(mrb, str, ".0", 2);
- }
return str;
}
@@ -320,6 +317,8 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
div = (x - mod) / y;
if (modp && divp) div = round(div);
}
+ if (div == 0) div = 0.0;
+ if (mod == 0) mod = 0.0;
if (y*mod < 0) {
mod += y;
div -= 1.0;
@@ -702,6 +701,7 @@ flo_round(mrb_state *mrb, mrb_value num)
f = 1.0;
i = ndigits >= 0 ? ndigits : -ndigits;
+ if (ndigits > DBL_DIG+2) return num;
while (--i >= 0)
f = f*10.0;
@@ -888,22 +888,24 @@ static mrb_value
fix_mod(mrb_state *mrb, mrb_value x)
{
mrb_value y;
- mrb_int a;
+ mrb_int a, b;
mrb_get_args(mrb, "o", &y);
a = mrb_fixnum(x);
- if (mrb_fixnum_p(y)) {
- mrb_int b, mod;
+ if (mrb_fixnum_p(y) && a != MRB_INT_MIN && (b=mrb_fixnum(y)) != MRB_INT_MIN) {
+ mrb_int mod;
- if ((b=mrb_fixnum(y)) == 0) {
+ if (b == 0) {
#ifdef MRB_WITHOUT_FLOAT
/* ZeroDivisionError */
return mrb_fixnum_value(0);
#else
+ if (a > 0) return mrb_float_value(mrb, INFINITY);
+ if (a < 0) return mrb_float_value(mrb, INFINITY);
return mrb_float_value(mrb, NAN);
#endif
}
- fixdivmod(mrb, a, b, 0, &mod);
+ fixdivmod(mrb, a, b, NULL, &mod);
return mrb_fixnum_value(mod);
}
#ifdef MRB_WITHOUT_FLOAT
@@ -912,7 +914,7 @@ fix_mod(mrb_state *mrb, mrb_value x)
else {
mrb_float mod;
- flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod);
+ flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), NULL, &mod);
return mrb_float_value(mrb, mod);
}
#endif
@@ -1118,7 +1120,7 @@ lshift(mrb_state *mrb, mrb_int val, mrb_int width)
}
else {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
- (val < (MRB_INT_MIN >> width))) {
+ (val <= (MRB_INT_MIN >> width))) {
#ifdef MRB_WITHOUT_FLOAT
return mrb_fixnum_value(0);
#else
@@ -1252,7 +1254,7 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
z = (mrb_int)d;
}
else {
- mrb_raisef(mrb, E_RANGE_ERROR, "number (%S) too big for integer", x);
+ mrb_raisef(mrb, E_RANGE_ERROR, "number (%v) too big for integer", x);
}
}
return mrb_fixnum_value(z);
@@ -1384,7 +1386,7 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
mrb_int val = mrb_fixnum(x);
if (base < 2 || 36 < base) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base);
}
if (val == 0) {
@@ -1493,12 +1495,10 @@ integral_cmp(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(n);
}
-static void
+static mrb_noreturn void
cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
{
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %S with %S failed",
- mrb_obj_value(mrb_class(mrb, v1)),
- mrb_obj_value(mrb_class(mrb, v2)));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2);
}
static mrb_value
diff --git a/src/object.c b/src/object.c
index 7c1879019..ee36da320 100644
--- a/src/object.c
+++ b/src/object.c
@@ -297,17 +297,6 @@ mrb_init_object(mrb_state *mrb)
}
static mrb_value
-inspect_type(mrb_state *mrb, mrb_value val)
-{
- if (mrb_type(val) == MRB_TT_FALSE || mrb_type(val) == MRB_TT_TRUE) {
- return mrb_inspect(mrb, val);
- }
- else {
- return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, val));
- }
-}
-
-static mrb_value
convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise)
{
mrb_sym m = 0;
@@ -315,7 +304,7 @@ convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *metho
m = mrb_intern_cstr(mrb, method);
if (!mrb_respond_to(mrb, val, m)) {
if (raise) {
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname));
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into %s", val, tname);
}
return mrb_nil_value();
}
@@ -330,8 +319,7 @@ mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char
if (mrb_type(val) == type) return val;
v = convert_type(mrb, val, tname, method, TRUE);
if (mrb_type(v) != type) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to %S by #%S", val,
- mrb_str_new_cstr(mrb, tname), mrb_str_new_cstr(mrb, method));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be converted to %s by #%s", val, tname, method);
}
return v;
}
@@ -405,13 +393,12 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
else {
etype = mrb_obj_classname(mrb, x);
}
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)",
- mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name));
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
+ etype, type->name);
}
type++;
}
- mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)",
- mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x)));
+ mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %d (%d given)", t, mrb_type(x));
}
}
@@ -499,15 +486,12 @@ mrb_to_int(mrb_state *mrb, mrb_value val)
{
if (!mrb_fixnum_p(val)) {
- mrb_value type;
-
#ifndef MRB_WITHOUT_FLOAT
if (mrb_float_p(val)) {
return mrb_flo_to_fixnum(mrb, val);
}
#endif
- type = inspect_type(mrb, val);
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer", type);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y to Integer", val);
}
return val;
}
@@ -598,8 +582,7 @@ MRB_API mrb_value
mrb_ensure_string_type(mrb_state *mrb, mrb_value str)
{
if (!mrb_string_p(str)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to String",
- inspect_type(mrb, str));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to String", str);
}
return str;
}
@@ -615,8 +598,7 @@ MRB_API mrb_value
mrb_ensure_array_type(mrb_state *mrb, mrb_value ary)
{
if (!mrb_array_p(ary)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Array",
- inspect_type(mrb, ary));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Array", ary);
}
return ary;
}
@@ -632,8 +614,7 @@ MRB_API mrb_value
mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash)
{
if (!mrb_hash_p(hash)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Hash",
- inspect_type(mrb, hash));
+ mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Hash", hash);
}
return hash;
}
diff --git a/src/proc.c b/src/proc.c
index 094fff816..ca398384f 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -9,7 +9,7 @@
#include <mruby/proc.h>
#include <mruby/opcode.h>
-static mrb_code call_iseq[] = {
+static const mrb_code call_iseq[] = {
OP_CALL,
};
@@ -153,8 +153,8 @@ mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
}
if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
- mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
- mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
+ mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)",
+ idx, MRB_ENV_STACK_LEN(e));
}
return e->stack[idx];
@@ -256,7 +256,7 @@ mrb_int
mrb_proc_arity(const struct RProc *p)
{
struct mrb_irep *irep;
- mrb_code *pc;
+ const mrb_code *pc;
mrb_aspec aspec;
int ma, op, ra, pa, arity;
diff --git a/src/range.c b/src/range.c
index 9036ef093..28862d779 100644
--- a/src/range.c
+++ b/src/range.c
@@ -92,7 +92,7 @@ range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, m
if (r) {
if (RANGE_INITIALIZED_P(r)) {
/* Ranges are immutable, so that they should be initialized only once. */
- mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
+ mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "'initialize' called twice");
}
else {
range_ptr_alloc_edges(mrb, r);
@@ -363,7 +363,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
}
}
else {
- mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
+ mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]);
}
}
diff --git a/src/state.c b/src/state.c
index c42df71ba..99b523dd5 100644
--- a/src/state.c
+++ b/src/state.c
@@ -117,7 +117,7 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
int i;
if (!(irep->flags & MRB_ISEQ_NO_FREE))
- mrb_free(mrb, irep->iseq);
+ mrb_free(mrb, (void*)irep->iseq);
if (irep->pool) for (i=0; i<irep->plen; i++) {
if (mrb_type(irep->pool[i]) == MRB_TT_STRING) {
mrb_gc_free_str(mrb, RSTRING(irep->pool[i]));
@@ -141,58 +141,6 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
mrb_free(mrb, irep);
}
-mrb_value
-mrb_str_pool(mrb_state *mrb, mrb_value str)
-{
- struct RString *s = mrb_str_ptr(str);
- struct RString *ns;
- char *ptr;
- mrb_int len;
-
- ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString));
- ns->tt = MRB_TT_STRING;
- ns->c = mrb->string_class;
-
- if (RSTR_NOFREE_P(s)) {
- ns->flags = MRB_STR_NOFREE;
- ns->as.heap.ptr = s->as.heap.ptr;
- ns->as.heap.len = s->as.heap.len;
- ns->as.heap.aux.capa = 0;
- }
- else {
- ns->flags = 0;
- if (RSTR_EMBED_P(s)) {
- ptr = s->as.ary;
- len = RSTR_EMBED_LEN(s);
- }
- else {
- ptr = s->as.heap.ptr;
- len = s->as.heap.len;
- }
-
- if (len < RSTRING_EMBED_LEN_MAX) {
- RSTR_SET_EMBED_FLAG(ns);
- RSTR_SET_EMBED_LEN(ns, len);
- if (ptr) {
- memcpy(ns->as.ary, ptr, len);
- }
- ns->as.ary[len] = '\0';
- }
- else {
- ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
- ns->as.heap.len = len;
- ns->as.heap.aux.capa = len;
- if (ptr) {
- memcpy(ns->as.heap.ptr, ptr, len);
- }
- ns->as.heap.ptr[len] = '\0';
- }
- }
- RSTR_SET_POOL_FLAG(ns);
- MRB_SET_FROZEN_FLAG(ns);
- return mrb_obj_value(ns);
-}
-
void mrb_free_backtrace(mrb_state *mrb);
MRB_API void
@@ -221,10 +169,10 @@ mrb_close(mrb_state *mrb)
}
/* free */
- mrb_gc_free_gv(mrb);
+ mrb_gc_destroy(mrb, &mrb->gc);
mrb_free_context(mrb, mrb->root_c);
+ mrb_gc_free_gv(mrb);
mrb_free_symtbl(mrb);
- mrb_gc_destroy(mrb, &mrb->gc);
mrb_free(mrb, mrb);
}
diff --git a/src/string.c b/src/string.c
index bfe73b359..c1c28ee8b 100644
--- a/src/string.c
+++ b/src/string.c
@@ -21,13 +21,11 @@
#include <mruby/range.h>
#include <mruby/string.h>
#include <mruby/numeric.h>
-#include <mruby/re.h>
typedef struct mrb_shared_string {
- mrb_bool nofree : 1;
int refcnt;
+ mrb_int capa;
char *ptr;
- mrb_int len;
} mrb_shared_string;
const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
@@ -35,55 +33,114 @@ const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
static struct RString*
-str_new_static(mrb_state *mrb, const char *p, size_t len)
+str_init_normal_capa(mrb_state *mrb, struct RString *s,
+ const char *p, size_t len, size_t capa)
{
- struct RString *s;
+ char *dst = (char *)mrb_malloc(mrb, capa + 1);
+ if (p) memcpy(dst, p, len);
+ dst[len] = '\0';
+ s->as.heap.ptr = dst;
+ s->as.heap.len = (mrb_int)len;
+ s->as.heap.aux.capa = (mrb_int)capa;
+ RSTR_UNSET_TYPE_FLAG(s);
+ return s;
+}
- if (len >= MRB_INT_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
- }
- s = mrb_obj_alloc_string(mrb);
+static struct RString*
+str_init_normal(mrb_state *mrb, struct RString *s, const char *p, size_t len)
+{
+ return str_init_normal_capa(mrb, s, p, len, len);
+}
+
+static struct RString*
+str_init_embed(struct RString *s, const char *p, size_t len)
+{
+ if (p) memcpy(RSTR_EMBED_PTR(s), p, len);
+ RSTR_EMBED_PTR(s)[len] = '\0';
+ RSTR_SET_TYPE_FLAG(s, EMBED);
+ RSTR_SET_EMBED_LEN(s, len);
+ return s;
+}
+
+static struct RString*
+str_init_nofree(struct RString *s, const char *p, size_t len)
+{
+ s->as.heap.ptr = (char *)p;
s->as.heap.len = (mrb_int)len;
s->as.heap.aux.capa = 0; /* nofree */
- s->as.heap.ptr = (char *)p;
- s->flags = MRB_STR_NOFREE;
+ RSTR_SET_TYPE_FLAG(s, NOFREE);
+ return s;
+}
+static struct RString*
+str_init_shared(mrb_state *mrb, const struct RString *orig, struct RString *s, mrb_shared_string *shared)
+{
+ if (shared) {
+ shared->refcnt++;
+ }
+ else {
+ shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
+ shared->refcnt = 1;
+ shared->ptr = orig->as.heap.ptr;
+ shared->capa = orig->as.heap.aux.capa;
+ }
+ s->as.heap.ptr = orig->as.heap.ptr;
+ s->as.heap.len = orig->as.heap.len;
+ s->as.heap.aux.shared = shared;
+ RSTR_SET_TYPE_FLAG(s, SHARED);
return s;
}
static struct RString*
-str_new(mrb_state *mrb, const char *p, size_t len)
+str_init_fshared(const struct RString *orig, struct RString *s, struct RString *fshared)
{
- struct RString *s;
+ s->as.heap.ptr = orig->as.heap.ptr;
+ s->as.heap.len = orig->as.heap.len;
+ s->as.heap.aux.fshared = fshared;
+ RSTR_SET_TYPE_FLAG(s, FSHARED);
+ return s;
+}
- if (p && mrb_ro_data_p(p)) {
- return str_new_static(mrb, p, len);
- }
- s = mrb_obj_alloc_string(mrb);
- if (len <= RSTRING_EMBED_LEN_MAX) {
- RSTR_SET_EMBED_FLAG(s);
- RSTR_SET_EMBED_LEN(s, len);
- if (p) {
- memcpy(s->as.ary, p, len);
- }
+static struct RString*
+str_init_modifiable(mrb_state *mrb, struct RString *s, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(s, p, len);
}
else {
- if (len >= MRB_INT_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
- }
- s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1);
- s->as.heap.len = (mrb_int)len;
- s->as.heap.aux.capa = (mrb_int)len;
- if (p) {
- memcpy(s->as.heap.ptr, p, len);
- }
+ return str_init_normal(mrb, s, p, len);
}
- RSTR_PTR(s)[len] = '\0';
- return s;
+}
+
+static struct RString*
+str_new_static(mrb_state *mrb, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(mrb_obj_alloc_string(mrb), p, len);
+ }
+ if (len >= MRB_INT_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ return str_init_nofree(mrb_obj_alloc_string(mrb), p, len);
+}
+
+static struct RString*
+str_new(mrb_state *mrb, const char *p, size_t len)
+{
+ if (RSTR_EMBEDDABLE_P(len)) {
+ return str_init_embed(mrb_obj_alloc_string(mrb), p, len);
+ }
+ if (len >= MRB_INT_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+ }
+ if (p && mrb_ro_data_p(p)) {
+ return str_init_nofree(mrb_obj_alloc_string(mrb), p, len);
+ }
+ return str_init_normal(mrb, mrb_obj_alloc_string(mrb), p, len);
}
static inline void
-str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
+str_with_class(struct RString *s, mrb_value obj)
{
s->c = mrb_str_ptr(obj)->c;
}
@@ -93,7 +150,7 @@ mrb_str_new_empty(mrb_state *mrb, mrb_value str)
{
struct RString *s = str_new(mrb, 0, 0);
- str_with_class(mrb, s, str);
+ str_with_class(s, str);
return mrb_obj_value(s);
}
@@ -102,15 +159,17 @@ mrb_str_new_capa(mrb_state *mrb, size_t capa)
{
struct RString *s;
- s = mrb_obj_alloc_string(mrb);
-
- if (capa >= MRB_INT_MAX) {
+ if (RSTR_EMBEDDABLE_P(capa)) {
+ s = str_init_embed(mrb_obj_alloc_string(mrb), NULL, 0);
+ }
+ else if (capa >= MRB_INT_MAX) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
+ /* not reached */
+ s = NULL;
+ }
+ else {
+ s = str_init_normal_capa(mrb, mrb_obj_alloc_string(mrb), NULL, 0, capa);
}
- s->as.heap.len = 0;
- s->as.heap.aux.capa = (mrb_int)capa;
- s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1);
- RSTR_PTR(s)[0] = '\0';
return mrb_obj_value(s);
}
@@ -135,14 +194,8 @@ resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
mrb_assert(capacity < MRB_INT_MAX);
#endif
if (RSTR_EMBED_P(s)) {
- if (RSTRING_EMBED_LEN_MAX < capacity) {
- char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
- const mrb_int len = RSTR_EMBED_LEN(s);
- memcpy(tmp, s->as.ary, len);
- RSTR_UNSET_EMBED_FLAG(s);
- s->as.heap.ptr = tmp;
- s->as.heap.len = len;
- s->as.heap.aux.capa = (mrb_int)capacity;
+ if (!RSTR_EMBEDDABLE_P(capacity)) {
+ str_init_normal_capa(mrb, s, RSTR_EMBED_PTR(s), RSTR_EMBED_LEN(s), capacity);
}
}
else {
@@ -187,9 +240,7 @@ str_decref(mrb_state *mrb, mrb_shared_string *shared)
{
shared->refcnt--;
if (shared->refcnt == 0) {
- if (!shared->nofree) {
- mrb_free(mrb, shared->ptr);
- }
+ mrb_free(mrb, shared->ptr);
mrb_free(mrb, shared);
}
}
@@ -233,8 +284,10 @@ utf8len(const char* p, const char* e)
mrb_int len;
mrb_int i;
+ if ((unsigned char)*p < 0x80) return 1;
len = utf8len_codepage[(unsigned char)*p];
- if (p + len > e) return 1;
+ if (len == 1) return 1;
+ if (len > e - p) return 1;
for (i = 1; i < len; ++i)
if ((p[i] & 0xc0) != 0x80)
return 1;
@@ -258,14 +311,15 @@ mrb_utf8_len(const char *str, mrb_int byte_len)
static mrb_int
utf8_strlen(mrb_value str)
{
- mrb_int byte_len = RSTRING_LEN(str);
+ struct RString *s = mrb_str_ptr(str);
+ mrb_int byte_len = RSTR_LEN(s);
- if (RSTRING(str)->flags & MRB_STR_NO_UTF) {
+ if (RSTR_ASCII_P(s)) {
return byte_len;
}
else {
- mrb_int utf8_len = mrb_utf8_len(RSTRING_PTR(str), byte_len);
- if (byte_len == utf8_len) RSTRING(str)->flags |= MRB_STR_NO_UTF;
+ mrb_int utf8_len = mrb_utf8_len(RSTR_PTR(s), byte_len);
+ if (byte_len == utf8_len) RSTR_SET_ASCII_FLAG(s);
return utf8_len;
}
}
@@ -290,25 +344,136 @@ chars2bytes(mrb_value s, mrb_int off, mrb_int idx)
/* map byte offset to character index */
static mrb_int
-bytes2chars(char *p, mrb_int bi)
+bytes2chars(char *p, mrb_int len, mrb_int bi)
{
- mrb_int i, b, n;
+ const char *e = p + (size_t)len;
+ const char *pivot = p + bi;
+ mrb_int i;
- for (b=i=0; b<bi; i++) {
- n = utf8len_codepage[(unsigned char)*p];
- b += n;
- p += n;
+ for (i = 0; p < pivot; i ++) {
+ p += utf8len(p, e);
}
- if (b != bi) return -1;
+ if (p != pivot) return -1;
return i;
}
+static const char *
+char_adjust(const char *beg, const char *end, const char *ptr)
+{
+ if ((ptr > beg || ptr < end) && (*ptr & 0xc0) == 0x80) {
+ const int utf8_adjust_max = 3;
+ const char *p;
+
+ if (ptr - beg > utf8_adjust_max) {
+ beg = ptr - utf8_adjust_max;
+ }
+
+ p = ptr;
+ while (p > beg) {
+ p --;
+ if ((*p & 0xc0) != 0x80) {
+ int clen = utf8len(p, end);
+ if (clen > ptr - p) return p;
+ break;
+ }
+ }
+ }
+
+ return ptr;
+}
+
+static const char *
+char_backtrack(const char *ptr, const char *end)
+{
+ if (ptr < end) {
+ const int utf8_bytelen_max = 4;
+ const char *p;
+
+ if (end - ptr > utf8_bytelen_max) {
+ ptr = end - utf8_bytelen_max;
+ }
+
+ p = end;
+ while (p > ptr) {
+ p --;
+ if ((*p & 0xc0) != 0x80) {
+ int clen = utf8len_codepage[(unsigned char)*p];
+ if (clen == end - p) { return p; }
+ break;
+ }
+ }
+ }
+
+ return end - 1;
+}
+
+static mrb_int
+str_index_str_by_char_search(mrb_state *mrb, const char *p, const char *pend, const char *s, const mrb_int slen, mrb_int off)
+{
+ /* Based on Quick Search algorithm (Boyer-Moore-Horspool algorithm) */
+
+ ptrdiff_t qstable[1 << CHAR_BIT];
+
+ /* Preprocessing */
+ {
+ mrb_int i;
+
+ for (i = 0; i < 1 << CHAR_BIT; i ++) {
+ qstable[i] = slen;
+ }
+ for (i = 0; i < slen; i ++) {
+ qstable[(unsigned char)s[i]] = slen - (i + 1);
+ }
+ }
+
+ /* Searching */
+ while (p < pend && pend - p >= slen) {
+ const char *pivot;
+
+ if (memcmp(p, s, slen) == 0) {
+ return off;
+ }
+
+ pivot = p + qstable[(unsigned char)p[slen - 1]];
+ if (pivot > pend || pivot < p /* overflowed */) { return -1; }
+
+ do {
+ p += utf8len(p, pend);
+ off ++;
+ } while (p < pivot);
+ }
+
+ return -1;
+}
+
+static mrb_int
+str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
+{
+ const char *p = RSTRING_PTR(str);
+ const char *pend = p + RSTRING_LEN(str);
+ const char *s = RSTRING_PTR(sub);
+ const mrb_int slen = RSTRING_LEN(sub);
+ mrb_int off = pos;
+
+ for (; pos > 0; pos --) {
+ if (pend - p < 1) { return -1; }
+ p += utf8len(p, pend);
+ }
+
+ if (slen < 1) { return off; }
+
+ return str_index_str_by_char_search(mrb, p, pend, s, slen, off);
+}
+
#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value();
#else
#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
#define chars2bytes(p, off, ci) (ci)
-#define bytes2chars(p, bi) (bi)
+#define bytes2chars(p, end, bi) (bi)
+#define char_adjust(beg, end, ptr) (ptr)
+#define char_backtrack(ptr, end) ((end) - 1)
#define BYTES_ALIGN_CHECK(pos)
+#define str_index_str_by_char(mrb, str, sub, pos) str_index_str(mrb, str, sub, pos)
#endif
static inline mrb_int
@@ -358,108 +523,114 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
static void
str_make_shared(mrb_state *mrb, struct RString *orig, struct RString *s)
{
- mrb_shared_string *shared;
- mrb_int len = RSTR_LEN(orig);
+ size_t len = (size_t)orig->as.heap.len;
mrb_assert(!RSTR_EMBED_P(orig));
- if (RSTR_SHARED_P(orig)) {
- shared = orig->as.heap.aux.shared;
- shared->refcnt++;
- s->as.heap.ptr = orig->as.heap.ptr;
- s->as.heap.len = len;
- s->as.heap.aux.shared = shared;
- RSTR_SET_SHARED_FLAG(s);
- RSTR_UNSET_EMBED_FLAG(s);
+ if (RSTR_NOFREE_P(orig)) {
+ str_init_nofree(s, orig->as.heap.ptr, len);
+ }
+ else if (RSTR_SHARED_P(orig)) {
+ str_init_shared(mrb, orig, s, orig->as.heap.aux.shared);
}
else if (RSTR_FSHARED_P(orig)) {
- struct RString *fs;
-
- fs = orig->as.heap.aux.fshared;
- s->as.heap.ptr = orig->as.heap.ptr;
- s->as.heap.len = len;
- s->as.heap.aux.fshared = fs;
- RSTR_SET_FSHARED_FLAG(s);
- RSTR_UNSET_EMBED_FLAG(s);
+ str_init_fshared(orig, s, orig->as.heap.aux.fshared);
}
else if (MRB_FROZEN_P(orig) && !RSTR_POOL_P(orig)) {
- s->as.heap.ptr = orig->as.heap.ptr;
- s->as.heap.len = len;
- s->as.heap.aux.fshared = orig;
- RSTR_SET_FSHARED_FLAG(s);
- RSTR_UNSET_EMBED_FLAG(s);
+ str_init_fshared(orig, s, orig);
}
else {
- shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
- shared->refcnt = 2;
- shared->nofree = !!RSTR_NOFREE_P(orig);
- if (!shared->nofree && orig->as.heap.aux.capa > orig->as.heap.len) {
- shared->ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1);
- orig->as.heap.ptr = shared->ptr;
- }
- else {
- shared->ptr = orig->as.heap.ptr;
+ if (orig->as.heap.aux.capa > orig->as.heap.len) {
+ orig->as.heap.ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1);
+ orig->as.heap.aux.capa = len;
}
- orig->as.heap.aux.shared = shared;
- RSTR_SET_SHARED_FLAG(orig);
- shared->len = len;
- s->as.heap.aux.shared = shared;
- s->as.heap.ptr = shared->ptr;
- s->as.heap.len = len;
- RSTR_SET_SHARED_FLAG(s);
- RSTR_UNSET_EMBED_FLAG(s);
+ str_init_shared(mrb, orig, s, NULL);
+ str_init_shared(mrb, orig, orig, s->as.heap.aux.shared);
}
}
-static mrb_value
-byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+mrb_value
+mrb_str_pool(mrb_state *mrb, mrb_value str)
+{
+ struct RString *s = (struct RString *)mrb_malloc(mrb, sizeof(struct RString));
+ struct RString *orig = mrb_str_ptr(str);
+ const char *p = RSTR_PTR(orig);
+ size_t len = (size_t)RSTR_LEN(orig);
+
+ s->tt = MRB_TT_STRING;
+ s->c = mrb->string_class;
+ s->flags = 0;
+
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s, p, len);
+ }
+ else if (RSTR_NOFREE_P(orig)) {
+ str_init_nofree(s, p, len);
+ }
+ else {
+ str_init_normal(mrb, s, p, len);
+ }
+ RSTR_SET_POOL_FLAG(s);
+ MRB_SET_FROZEN_FLAG(s);
+ return mrb_obj_value(s);
+}
+
+mrb_value
+mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
{
struct RString *orig, *s;
orig = mrb_str_ptr(str);
- if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0 || len <= RSTRING_EMBED_LEN_MAX) {
- s = str_new(mrb, RSTR_PTR(orig)+beg, len);
+ s = mrb_obj_alloc_string(mrb);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s, RSTR_PTR(orig)+beg, len);
}
else {
- s = mrb_obj_alloc_string(mrb);
str_make_shared(mrb, orig, s);
s->as.heap.ptr += beg;
s->as.heap.len = len;
}
+ RSTR_COPY_ASCII_FLAG(s, orig);
return mrb_obj_value(s);
}
+
+static void
+str_range_to_bytes(mrb_value str, mrb_int *pos, mrb_int *len)
+{
+ *pos = chars2bytes(str, 0, *pos);
+ *len = chars2bytes(str, *pos, *len);
+}
#ifdef MRB_UTF8_STRING
static inline mrb_value
str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
{
- beg = chars2bytes(str, 0, beg);
- len = chars2bytes(str, beg, len);
-
- return byte_subseq(mrb, str, beg, len);
+ str_range_to_bytes(str, &beg, &len);
+ return mrb_str_byte_subseq(mrb, str, beg, len);
}
#else
-#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len)
+#define str_subseq(mrb, str, beg, len) mrb_str_byte_subseq(mrb, str, beg, len)
#endif
-static mrb_value
-str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+mrb_bool
+mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp)
{
- mrb_int clen = RSTRING_CHAR_LEN(str);
-
- if (len < 0) return mrb_nil_value();
- if (clen == 0) {
- len = 0;
+ if (str_len < *begp || *lenp < 0) return FALSE;
+ if (*begp < 0) {
+ *begp += str_len;
+ if (*begp < 0) return FALSE;
}
- if (beg > clen) return mrb_nil_value();
- if (beg < 0) {
- beg += clen;
- if (beg < 0) return mrb_nil_value();
+ if (*lenp > str_len - *begp)
+ *lenp = str_len - *begp;
+ if (*lenp <= 0) {
+ *lenp = 0;
}
- if (len > clen - beg)
- len = clen - beg;
- if (len <= 0) {
- len = 0;
- }
- return str_subseq(mrb, str, beg, len);
+ return TRUE;
+}
+
+static mrb_value
+str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ?
+ str_subseq(mrb, str, beg, len) : mrb_nil_value();
}
MRB_API mrb_int
@@ -502,30 +673,22 @@ str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset)
static mrb_value
str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
{
- mrb_int len;
+ size_t len;
mrb_check_frozen(mrb, s1);
if (s1 == s2) return mrb_obj_value(s1);
- s1->flags &= ~MRB_STR_NO_UTF;
- s1->flags |= s2->flags&MRB_STR_NO_UTF;
- len = RSTR_LEN(s2);
+ RSTR_COPY_ASCII_FLAG(s1, s2);
if (RSTR_SHARED_P(s1)) {
str_decref(mrb, s1->as.heap.aux.shared);
- RSTR_UNSET_SHARED_FLAG(s1);
}
else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1) && !RSTR_FSHARED_P(s1)
&& s1->as.heap.ptr) {
mrb_free(mrb, s1->as.heap.ptr);
}
- RSTR_UNSET_FSHARED_FLAG(s1);
- RSTR_UNSET_NOFREE_FLAG(s1);
- if (len <= RSTRING_EMBED_LEN_MAX) {
- RSTR_UNSET_SHARED_FLAG(s1);
- RSTR_UNSET_FSHARED_FLAG(s1);
- RSTR_SET_EMBED_FLAG(s1);
- memcpy(s1->as.ary, RSTR_PTR(s2), len);
- RSTR_SET_EMBED_LEN(s1, len);
+ len = (size_t)RSTR_LEN(s2);
+ if (RSTR_EMBEDDABLE_P(len)) {
+ str_init_embed(s1, RSTR_PTR(s2), len);
}
else {
str_make_shared(mrb, s2, s1);
@@ -537,7 +700,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
static mrb_int
str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
{
- char *s, *sbeg, *t;
+ const char *s, *sbeg, *t;
struct RString *ps = mrb_str_ptr(str);
mrb_int len = RSTRING_LEN(sub);
@@ -550,11 +713,12 @@ str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
s = RSTR_PTR(ps) + pos;
t = RSTRING_PTR(sub);
if (len) {
+ s = char_adjust(sbeg, sbeg + RSTR_LEN(ps), s);
while (sbeg <= s) {
if (memcmp(s, t, len) == 0) {
return (mrb_int)(s - RSTR_PTR(ps));
}
- s--;
+ s = char_backtrack(sbeg, s);
}
return -1;
}
@@ -642,67 +806,34 @@ mrb_locale_from_utf8(const char *utf8, int len)
#endif
MRB_API void
-mrb_str_modify(mrb_state *mrb, struct RString *s)
+mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s)
{
mrb_check_frozen(mrb, s);
- s->flags &= ~MRB_STR_NO_UTF;
if (RSTR_SHARED_P(s)) {
mrb_shared_string *shared = s->as.heap.aux.shared;
- if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
- s->as.heap.ptr = shared->ptr;
- s->as.heap.aux.capa = shared->len;
- RSTR_PTR(s)[s->as.heap.len] = '\0';
+ if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
+ s->as.heap.aux.capa = shared->capa;
+ s->as.heap.ptr[s->as.heap.len] = '\0';
mrb_free(mrb, shared);
}
else {
- char *ptr, *p;
- mrb_int len;
-
- p = RSTR_PTR(s);
- len = s->as.heap.len;
- if (len < RSTRING_EMBED_LEN_MAX) {
- RSTR_SET_EMBED_FLAG(s);
- RSTR_SET_EMBED_LEN(s, len);
- ptr = RSTR_PTR(s);
- }
- else {
- ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
- s->as.heap.ptr = ptr;
- s->as.heap.aux.capa = len;
- }
- if (p) {
- memcpy(ptr, p, len);
- }
- ptr[len] = '\0';
+ str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len);
str_decref(mrb, shared);
}
- RSTR_UNSET_SHARED_FLAG(s);
- return;
}
- if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) {
- char *p = s->as.heap.ptr;
- mrb_int len = s->as.heap.len;
-
- RSTR_UNSET_FSHARED_FLAG(s);
- RSTR_UNSET_NOFREE_FLAG(s);
- RSTR_UNSET_FSHARED_FLAG(s);
- if (len < RSTRING_EMBED_LEN_MAX) {
- RSTR_SET_EMBED_FLAG(s);
- RSTR_SET_EMBED_LEN(s, len);
- }
- else {
- s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1);
- s->as.heap.aux.capa = len;
- }
- if (p) {
- memcpy(RSTR_PTR(s), p, len);
- }
- RSTR_PTR(s)[len] = '\0';
- return;
+ else if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) {
+ str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len);
}
}
+MRB_API void
+mrb_str_modify(mrb_state *mrb, struct RString *s)
+{
+ mrb_str_modify_keep_ascii(mrb, s);
+ RSTR_UNSET_ASCII_FLAG(s);
+}
+
MRB_API mrb_value
mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
{
@@ -824,7 +955,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
len = RSTRING_LEN(self)*times;
str2 = str_new(mrb, 0, len);
- str_with_class(mrb, str2, self);
+ str_with_class(str2, self);
p = RSTR_PTR(str2);
if (len > 0) {
n = RSTRING_LEN(self);
@@ -836,6 +967,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
memcpy(p + n, p, len-n);
}
p[RSTR_LEN(str2)] = '\0';
+ RSTR_COPY_ASCII_FLAG(str2, mrb_str_ptr(self));
return mrb_obj_value(str2);
}
@@ -973,6 +1105,8 @@ mrb_str_to_str(mrb_state *mrb, mrb_value str)
switch (mrb_type(str)) {
case MRB_TT_STRING:
return str;
+ case MRB_TT_SYMBOL:
+ return mrb_sym2str(mrb, mrb_symbol(str));
case MRB_TT_FIXNUM:
return mrb_fixnum_to_str(mrb, str, 10);
case MRB_TT_CLASS:
@@ -983,6 +1117,7 @@ mrb_str_to_str(mrb_state *mrb, mrb_value str)
}
}
+/* obslete: use RSTRING_PTR() */
MRB_API const char*
mrb_string_value_ptr(mrb_state *mrb, mrb_value str)
{
@@ -990,6 +1125,7 @@ mrb_string_value_ptr(mrb_state *mrb, mrb_value str)
return RSTRING_PTR(str);
}
+/* obslete: use RSTRING_LEN() */
MRB_API mrb_int
mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
{
@@ -1003,55 +1139,95 @@ mrb_str_dup(mrb_state *mrb, mrb_value str)
struct RString *s = mrb_str_ptr(str);
struct RString *dup = str_new(mrb, 0, 0);
- str_with_class(mrb, dup, str);
+ str_with_class(dup, str);
return str_replace(mrb, dup, s);
}
-static mrb_value
-mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
-{
- mrb_int idx;
+enum str_convert_range {
+ /* `beg` and `len` are byte unit in `0 ... str.bytesize` */
+ STR_BYTE_RANGE_CORRECTED = 1,
- switch (mrb_type(indx)) {
- case MRB_TT_FIXNUM:
- idx = mrb_fixnum(indx);
+ /* `beg` and `len` are char unit in any range */
+ STR_CHAR_RANGE = 2,
-num_index:
- str = str_substr(mrb, str, idx, 1);
- if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
- return str;
+ /* `beg` and `len` are char unit in `0 ... str.size` */
+ STR_CHAR_RANGE_CORRECTED = 3,
- case MRB_TT_STRING:
- if (str_index_str(mrb, str, indx, 0) != -1)
- return mrb_str_dup(mrb, indx);
- return mrb_nil_value();
+ /* `beg` is out of range */
+ STR_OUT_OF_RANGE = -1
+};
+
+static enum str_convert_range
+str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len)
+{
+ if (!mrb_undef_p(alen)) {
+ *beg = mrb_int(mrb, indx);
+ *len = mrb_int(mrb, alen);
+ return STR_CHAR_RANGE;
+ }
+ else {
+ switch (mrb_type(indx)) {
+ case MRB_TT_FIXNUM:
+ *beg = mrb_fixnum(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
- case MRB_TT_RANGE:
- goto range_arg;
+ case MRB_TT_STRING:
+ *beg = str_index_str(mrb, str, indx, 0);
+ if (*beg < 0) { break; }
+ *len = RSTRING_LEN(indx);
+ return STR_BYTE_RANGE_CORRECTED;
- default:
- indx = mrb_Integer(mrb, indx);
- if (mrb_nil_p(indx)) {
- range_arg:
- {
- mrb_int beg, len;
-
- len = RSTRING_CHAR_LEN(str);
- switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) {
+ case MRB_TT_RANGE:
+ goto range_arg;
+
+ default:
+ indx = mrb_to_int(mrb, indx);
+ if (mrb_fixnum_p(indx)) {
+ *beg = mrb_fixnum(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
+ }
+range_arg:
+ *len = RSTRING_CHAR_LEN(str);
+ switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) {
case MRB_RANGE_OK:
- return str_subseq(mrb, str, beg, len);
+ return STR_CHAR_RANGE_CORRECTED;
case MRB_RANGE_OUT:
- return mrb_nil_value();
+ return STR_OUT_OF_RANGE;
default:
break;
- }
}
+
mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
+ }
+ }
+ return STR_OUT_OF_RANGE;
+}
+
+static mrb_value
+mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen)
+{
+ mrb_int beg, len;
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_CHAR_RANGE_CORRECTED:
+ return str_subseq(mrb, str, beg, len);
+ case STR_CHAR_RANGE:
+ str = str_substr(mrb, str, beg, len);
+ if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
+ return str;
+ case STR_BYTE_RANGE_CORRECTED:
+ if (mrb_string_p(indx)) {
+ return mrb_str_dup(mrb, indx);
+ }
+ else {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
}
- idx = mrb_fixnum(indx);
- goto num_index;
+ case STR_OUT_OF_RANGE:
+ default:
+ return mrb_nil_value();
}
- return mrb_nil_value(); /* not reached */
}
/* 15.2.10.5.6 */
@@ -1098,16 +1274,118 @@ static mrb_value
mrb_str_aref_m(mrb_state *mrb, mrb_value str)
{
mrb_value a1, a2;
- mrb_int argc;
- argc = mrb_get_args(mrb, "o|o", &a1, &a2);
- if (argc == 2) {
- mrb_int n1, n2;
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 1) {
+ a2 = mrb_undef_value();
+ }
+
+ return mrb_str_aref(mrb, str, a1, a2);
+}
+
+static mrb_noreturn void
+str_out_of_index(mrb_state *mrb, mrb_value index)
+{
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %v out of string", index);
+}
+
+static mrb_value
+str_replace_partial(mrb_state *mrb, mrb_value src, mrb_int pos, mrb_int end, mrb_value rep)
+{
+ const mrb_int shrink_threshold = 256;
+ struct RString *str = mrb_str_ptr(src);
+ mrb_int len = RSTR_LEN(str);
+ mrb_int replen, newlen;
+ char *strp;
- mrb_get_args(mrb, "ii", &n1, &n2);
- return str_substr(mrb, str, n1, n2);
+ if (end > len) { end = len; }
+
+ if (pos < 0 || pos > len) {
+ str_out_of_index(mrb, mrb_fixnum_value(pos));
+ }
+
+ replen = (mrb_nil_p(rep) ? 0 : RSTRING_LEN(rep));
+ newlen = replen + len - (end - pos);
+
+ if (newlen >= MRB_INT_MAX || newlen < replen /* overflowed */) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big");
}
- return mrb_str_aref(mrb, str, a1);
+
+ mrb_str_modify(mrb, str);
+
+ if (len < newlen) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ strp = RSTR_PTR(str);
+
+ memmove(strp + newlen - (len - end), strp + end, len - end);
+ if (!mrb_nil_p(rep)) {
+ memmove(strp + pos, RSTRING_PTR(rep), replen);
+ }
+ RSTR_SET_LEN(str, newlen);
+ strp[newlen] = '\0';
+
+ if (len - newlen >= shrink_threshold) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ return src;
+}
+
+static void
+mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace)
+{
+ mrb_int beg, len, charlen;
+
+ mrb_to_str(mrb, replace);
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_OUT_OF_RANGE:
+ default:
+ mrb_raise(mrb, E_INDEX_ERROR, "string not matched");
+ case STR_CHAR_RANGE:
+ if (len < 0) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "negative length %v", alen);
+ }
+ charlen = RSTRING_CHAR_LEN(str);
+ if (beg < 0) { beg += charlen; }
+ if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); }
+ /* fall through */
+ case STR_CHAR_RANGE_CORRECTED:
+ str_range_to_bytes(str, &beg, &len);
+ /* fall through */
+ case STR_BYTE_RANGE_CORRECTED:
+ str_replace_partial(mrb, str, beg, beg + len, replace);
+ }
+}
+
+/*
+ * call-seq:
+ * str[fixnum] = replace
+ * str[fixnum, fixnum] = replace
+ * str[range] = replace
+ * str[regexp] = replace
+ * str[regexp, fixnum] = replace
+ * str[other_str] = replace
+ *
+ * Modify +self+ by replacing the content of +self+.
+ * The portion of the string affected is determined using the same criteria as +String#[]+.
+ */
+static mrb_value
+mrb_str_aset_m(mrb_state *mrb, mrb_value str)
+{
+ mrb_value indx, alen, replace;
+
+ switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) {
+ case 2:
+ replace = alen;
+ alen = mrb_undef_value();
+ break;
+ case 3:
+ break;
+ }
+ mrb_str_aset(mrb, str, indx, alen, replace);
+ return str;
}
/* 15.2.10.5.8 */
@@ -1130,7 +1408,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value();
p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s);
if (ISLOWER(*p)) {
@@ -1189,7 +1467,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
struct RString *s = mrb_str_ptr(str);
argc = mrb_get_args(mrb, "|S", &rs);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
len = RSTR_LEN(s);
if (argc == 0) {
if (len == 0) return mrb_nil_value();
@@ -1288,7 +1566,7 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
{
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
if (RSTR_LEN(s) > 0) {
mrb_int len;
#ifdef MRB_UTF8_STRING
@@ -1357,7 +1635,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTR_PTR(s);
pend = RSTR_PTR(s) + RSTR_LEN(s);
while (p < pend) {
@@ -1514,31 +1792,26 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_str_index_m(mrb_state *mrb, mrb_value str)
{
- mrb_value *argv;
- mrb_int argc;
mrb_value sub;
- mrb_int pos, clen;
+ mrb_int pos;
- mrb_get_args(mrb, "*!", &argv, &argc);
- if (argc == 2) {
- mrb_get_args(mrb, "oi", &sub, &pos);
- }
- else {
- pos = 0;
- if (argc > 0)
- sub = argv[0];
- else
+ switch (mrb_get_args(mrb, "|oi", &sub, &pos)) {
+ case 0:
sub = mrb_nil_value();
+ /* fall through */
+ case 1:
+ pos = 0;
+ break;
+ case 2:
+ if (pos < 0) {
+ mrb_int clen = RSTRING_CHAR_LEN(str);
+ pos += clen;
+ if (pos < 0) {
+ return mrb_nil_value();
+ }
+ }
+ break;
}
- clen = RSTRING_CHAR_LEN(str);
- if (pos < 0) {
- pos += clen;
- if (pos < 0) {
- return mrb_nil_value();
- }
- }
- if (pos > clen) return mrb_nil_value();
- pos = chars2bytes(str, 0, pos);
switch (mrb_type(sub)) {
default: {
@@ -1546,18 +1819,17 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
tmp = mrb_check_string_type(mrb, sub);
if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
+ mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %v given", sub);
}
sub = tmp;
}
/* fall through */
case MRB_TT_STRING:
- pos = str_index_str(mrb, str, sub, pos);
+ pos = str_index_str_by_char(mrb, str, sub, pos);
break;
}
if (pos == -1) return mrb_nil_value();
- pos = bytes2chars(RSTRING_PTR(str), pos);
BYTES_ALIGN_CHECK(pos);
return mrb_fixnum_value(pos);
}
@@ -1671,6 +1943,18 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
return mrb_obj_value(p_str);
}
+static inline void
+str_reverse(char *p, char *e)
+{
+ char c;
+
+ while (p < e) {
+ c = *p;
+ *p++ = *e;
+ *e-- = c;
+ }
+}
+
/* 15.2.10.5.30 */
/*
* call-seq:
@@ -1681,53 +1965,38 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
static mrb_value
mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
{
+ struct RString *s = mrb_str_ptr(str);
+ char *p, *e;
+
#ifdef MRB_UTF8_STRING
mrb_int utf8_len = RSTRING_CHAR_LEN(str);
- mrb_int len = RSTRING_LEN(str);
-
- if (utf8_len == len) goto bytes;
- if (utf8_len > 1) {
- char *buf;
- char *p, *e, *r;
-
- mrb_str_modify(mrb, mrb_str_ptr(str));
- len = RSTRING_LEN(str);
- buf = (char*)mrb_malloc(mrb, (size_t)len);
- p = buf;
- e = buf + len;
-
- memcpy(buf, RSTRING_PTR(str), len);
- r = RSTRING_PTR(str) + len;
+ mrb_int len = RSTR_LEN(s);
+ if (utf8_len < 2) return str;
+ if (utf8_len < len) {
+ mrb_str_modify(mrb, s);
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s);
while (p<e) {
mrb_int clen = utf8len(p, e);
- r -= clen;
- memcpy(r, p, clen);
+ str_reverse(p, p + clen - 1);
p += clen;
}
- mrb_free(mrb, buf);
+ goto bytes;
}
- return str;
-
- bytes:
#endif
- {
- struct RString *s = mrb_str_ptr(str);
- char *p, *e;
- char c;
+ if (RSTR_LEN(s) > 1) {
mrb_str_modify(mrb, s);
- if (RSTR_LEN(s) > 1) {
- p = RSTR_PTR(s);
- e = p + RSTR_LEN(s) - 1;
- while (p < e) {
- c = *p;
- *p++ = *e;
- *e-- = c;
- }
- }
- return str;
+ goto bytes;
}
+ return str;
+
+ bytes:
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s) - 1;
+ str_reverse(p, e);
+ return str;
}
/* ---------------------------------- */
@@ -1770,28 +2039,25 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str)
static mrb_value
mrb_str_rindex(mrb_state *mrb, mrb_value str)
{
- mrb_value *argv;
- mrb_int argc;
mrb_value sub;
mrb_int pos, len = RSTRING_CHAR_LEN(str);
- mrb_get_args(mrb, "*!", &argv, &argc);
- if (argc == 2) {
- mrb_get_args(mrb, "oi", &sub, &pos);
- if (pos < 0) {
- pos += len;
+ switch (mrb_get_args(mrb, "|oi", &sub, &pos)) {
+ case 0:
+ sub = mrb_nil_value();
+ /* fall through */
+ case 1:
+ pos = len;
+ break;
+ case 2:
if (pos < 0) {
- return mrb_nil_value();
+ pos += len;
+ if (pos < 0) {
+ return mrb_nil_value();
+ }
}
- }
- if (pos > len) pos = len;
- }
- else {
- pos = len;
- if (argc > 0)
- sub = argv[0];
- else
- sub = mrb_nil_value();
+ if (pos > len) pos = len;
+ break;
}
pos = chars2bytes(str, 0, pos);
@@ -1801,7 +2067,7 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
tmp = mrb_check_string_type(mrb, sub);
if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
+ mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %v given", sub);
}
sub = tmp;
}
@@ -1809,7 +2075,7 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
case MRB_TT_STRING:
pos = str_rindex(mrb, str, sub, pos);
if (pos >= 0) {
- pos = bytes2chars(RSTRING_PTR(str), pos);
+ pos = bytes2chars(RSTRING_PTR(str), RSTRING_LEN(str), pos);
BYTES_ALIGN_CHECK(pos);
return mrb_fixnum_value(pos);
}
@@ -1917,7 +2183,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
}
}
else if (ISSPACE(c)) {
- mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg));
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, beg, end-beg));
mrb_gc_arena_restore(mrb, ai);
skip = TRUE;
beg = idx;
@@ -1942,7 +2208,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
else {
end = chars2bytes(str, idx, 1);
}
- mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, idx, end));
mrb_gc_arena_restore(mrb, ai);
idx += end + pat_len;
if (lim_p && lim <= ++i) break;
@@ -1954,7 +2220,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
tmp = mrb_str_new_empty(mrb, str);
}
else {
- tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
+ tmp = mrb_str_byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
}
mrb_ary_push(mrb, result, tmp);
}
@@ -2052,7 +2318,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
break;
default:
if (base < 2 || 36 < base) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base);
}
break;
} /* end of switch (base) { */
@@ -2112,8 +2378,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
else
#endif
{
- mrb_raisef(mrb, E_RANGE_ERROR, "string (%S) too big for integer",
- mrb_str_new(mrb, str, pend-str));
+ mrb_raisef(mrb, E_RANGE_ERROR, "string (%l) too big for integer", str, pend-str);
}
}
}
@@ -2129,8 +2394,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
/* not reached */
bad:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)",
- mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str)));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%!l)", str, pend-str);
/* not reached */
return mrb_fixnum_value(0);
}
@@ -2141,6 +2405,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, mrb_int base, mrb_bool badchec
return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
}
+/* obslete: use RSTRING_CSTR() or mrb_string_cstr() */
MRB_API const char*
mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
{
@@ -2155,16 +2420,23 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
if (p[len] == '\0') {
return p;
}
+ if (MRB_FROZEN_P(ps) || RSTR_CAPA(ps) == len) {
+ ps = str_new(mrb, NULL, len+1);
+ memcpy(RSTR_PTR(ps), p, len);
+ RSTR_SET_LEN(ps, len);
+ *ptr = mrb_obj_value(ps);
+ }
else {
- if (MRB_FROZEN_P(ps)) {
- ps = str_new(mrb, p, len);
- *ptr = mrb_obj_value(ps);
- }
- else {
- mrb_str_modify(mrb, ps);
- }
- return RSTR_PTR(ps);
+ mrb_str_modify(mrb, ps);
}
+ RSTR_PTR(ps)[len] = '\0';
+ return RSTR_PTR(ps);
+}
+
+MRB_API const char*
+mrb_string_cstr(mrb_state *mrb, mrb_value str)
+{
+ return mrb_string_value_cstr(mrb, &str);
}
MRB_API mrb_value
@@ -2173,7 +2445,8 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
const char *s;
mrb_int len;
- s = mrb_string_value_ptr(mrb, str);
+ mrb_to_str(mrb, str);
+ s = RSTRING_PTR(str);
len = RSTRING_LEN(str);
return mrb_str_len_to_inum(mrb, s, len, base, badcheck);
}
@@ -2206,7 +2479,7 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "|i", &base);
if (base < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base);
}
return mrb_str_to_inum(mrb, self, base, FALSE);
}
@@ -2231,7 +2504,7 @@ mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
if (p == end) {
if (badcheck) {
bad:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%s)", p);
/* not reached */
}
return d;
@@ -2278,7 +2551,7 @@ bad:
MRB_API double
mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
{
- return mrb_cstr_to_dbl(mrb, mrb_string_value_cstr(mrb, &str), badcheck);
+ return mrb_cstr_to_dbl(mrb, RSTRING_CSTR(mrb, str), badcheck);
}
/* 15.2.10.5.39 */
@@ -2333,7 +2606,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
char *p, *pend;
mrb_bool modify = FALSE;
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTRING_PTR(str);
pend = RSTRING_END(str);
while (p < pend) {
@@ -2414,7 +2687,7 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
}
result = str_new(mrb, 0, len);
- str_with_class(mrb, result, str);
+ str_with_class(result, str);
p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
q = RSTR_PTR(result);
*q++ = '"';
@@ -2576,6 +2849,9 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
const char *p, *pend;
char buf[CHAR_ESC_LEN + 1];
mrb_value result = mrb_str_new_lit(mrb, "\"");
+#ifdef MRB_UTF8_STRING
+ uint32_t ascii_flag = MRB_STR_ASCII;
+#endif
p = RSTRING_PTR(str); pend = RSTRING_END(str);
for (;p < pend; p++) {
@@ -2592,6 +2868,7 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
}
mrb_str_cat(mrb, result, buf, clen);
p += clen-1;
+ ascii_flag = 0;
continue;
}
#endif
@@ -2633,6 +2910,10 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
}
}
mrb_str_cat_lit(mrb, result, "\"");
+#ifdef MRB_UTF8_STRING
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ mrb_str_ptr(result)->flags |= ascii_flag;
+#endif
return result;
}
@@ -2660,13 +2941,112 @@ mrb_str_bytes(mrb_state *mrb, mrb_value str)
return a;
}
+/*
+ * call-seq:
+ * str.getbyte(index) -> 0 .. 255
+ *
+ * returns the <i>index</i>th byte as an integer.
+ */
+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]);
+}
+
+/*
+ * call-seq:
+ * str.setbyte(index, integer) -> integer
+ *
+ * modifies the <i>index</i>th byte as <i>integer</i>.
+ */
+static mrb_value
+mrb_str_setbyte(mrb_state *mrb, mrb_value str)
+{
+ mrb_int pos, byte;
+ mrb_int len;
+
+ mrb_get_args(mrb, "ii", &pos, &byte);
+
+ len = RSTRING_LEN(str);
+ if (pos < -len || len <= pos)
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of string", pos);
+ if (pos < 0)
+ pos += len;
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ byte &= 0xff;
+ RSTRING_PTR(str)[pos] = (unsigned char)byte;
+ return mrb_fixnum_value((unsigned char)byte);
+}
+
+/*
+ * call-seq:
+ * str.byteslice(integer) -> new_str or nil
+ * str.byteslice(integer, integer) -> new_str or nil
+ * str.byteslice(range) -> new_str or nil
+ *
+ * Byte Reference---If passed a single Integer, returns a
+ * substring of one byte at that position. If passed two Integer
+ * objects, returns a substring starting at the offset given by the first, and
+ * a length given by the second. If given a Range, a substring containing
+ * bytes at offsets given by the range is returned. In all three cases, if
+ * an offset is negative, it is counted from the end of <i>str</i>. Returns
+ * <code>nil</code> if the initial offset falls outside the string, the length
+ * is negative, or the beginning of the range is greater than the end.
+ * The encoding of the resulted string keeps original encoding.
+ *
+ * "hello".byteslice(1) #=> "e"
+ * "hello".byteslice(-1) #=> "o"
+ * "hello".byteslice(1, 2) #=> "el"
+ * "\x80\u3042".byteslice(1, 3) #=> "\u3042"
+ * "\x03\u3042\xff".byteslice(1..3) #=> "\u3042"
+ */
+static mrb_value
+mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+{
+ mrb_value a1, a2;
+ mrb_int str_len = RSTRING_LEN(str), beg, len;
+ mrb_bool empty = TRUE;
+
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 2) {
+ beg = mrb_fixnum(mrb_to_int(mrb, a1));
+ len = mrb_fixnum(mrb_to_int(mrb, a2));
+ }
+ else if (mrb_type(a1) == MRB_TT_RANGE) {
+ if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) != MRB_RANGE_OK) {
+ return mrb_nil_value();
+ }
+ }
+ else {
+ beg = mrb_fixnum(mrb_to_int(mrb, a1));
+ len = 1;
+ empty = FALSE;
+ }
+
+ if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ else {
+ return mrb_nil_value();
+ }
+}
+
/* ---------------------------*/
void
mrb_init_string(mrb_state *mrb)
{
struct RClass *s;
- mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string");
+ mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << MRB_STR_EMBED_LEN_BITSIZE),
+ "pointer size too big for embedded string");
mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */
MRB_SET_INSTANCE_TT(s, MRB_TT_STRING);
@@ -2678,6 +3058,7 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */
mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */
mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */
+ mrb_define_method(mrb, s, "[]=", mrb_str_aset_m, MRB_ARGS_ANY());
mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */
mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */
mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */
@@ -2715,6 +3096,10 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */
mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
mrb_define_method(mrb, s, "bytes", mrb_str_bytes, 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));
}
#ifndef MRB_WITHOUT_FLOAT
diff --git a/src/symbol.c b/src/symbol.c
index b26f2b1fd..aad250f09 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -20,6 +20,22 @@ typedef struct symbol_name {
const char *name;
} symbol_name;
+#define SYMBOL_INLINE_BIT 1
+#define SYMBOL_INLINE_LOWER_BIT 2
+#define SYMBOL_INLINE (1 << (SYMBOL_INLINE_BIT - 1))
+#define SYMBOL_INLINE_LOWER (1 << (SYMBOL_INLINE_LOWER_BIT - 1))
+#define SYMBOL_NORMAL_SHIFT SYMBOL_INLINE_BIT
+#define SYMBOL_INLINE_SHIFT SYMBOL_INLINE_LOWER_BIT
+#ifdef MRB_ENABLE_ALL_SYMBOLS
+# define SYMBOL_INLINE_P(sym) FALSE
+# define SYMBOL_INLINE_LOWER_P(sym) FALSE
+# define sym_inline_pack(name, len) 0
+# define sym_inline_unpack(sym, buf, lenp) NULL
+#else
+# define SYMBOL_INLINE_P(sym) ((sym) & SYMBOL_INLINE)
+# define SYMBOL_INLINE_LOWER_P(sym) ((sym) & SYMBOL_INLINE_LOWER)
+#endif
+
static void
sym_validate_len(mrb_state *mrb, size_t len)
{
@@ -41,7 +57,7 @@ sym_inline_pack(const char *name, uint16_t len)
const char *p;
int i;
mrb_sym sym = 0;
- int lower = 1;
+ mrb_bool lower = TRUE;
if (len > lower_length_max) return 0; /* too long */
for (i=0; i<len; i++) {
@@ -52,9 +68,9 @@ sym_inline_pack(const char *name, uint16_t len)
p = strchr(pack_table, (int)c);
if (p == 0) return 0; /* non alnum char */
bits = (uint32_t)(p - pack_table)+1;
- if (bits > 27) lower = 0;
+ if (bits > 27) lower = FALSE;
if (i >= mix_length_max) break;
- sym |= bits<<(i*6+2);
+ sym |= bits<<(i*6+SYMBOL_INLINE_SHIFT);
}
if (lower) {
sym = 0;
@@ -64,24 +80,24 @@ sym_inline_pack(const char *name, uint16_t len)
c = name[i];
p = strchr(pack_table, (int)c);
bits = (uint32_t)(p - pack_table)+1;
- sym |= bits<<(i*5+2);
+ sym |= bits<<(i*5+SYMBOL_INLINE_SHIFT);
}
- return sym | 3;
+ return sym | SYMBOL_INLINE | SYMBOL_INLINE_LOWER;
}
if (len > mix_length_max) return 0;
- return sym | 1;
+ return sym | SYMBOL_INLINE;
}
static const char*
sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
{
- int bit_per_char = sym&2 ? 5 : 6; /* all lower case if `sym&2` is true */
+ int bit_per_char = SYMBOL_INLINE_LOWER_P(sym) ? 5 : 6;
int i;
- mrb_assert(sym&1);
+ mrb_assert(SYMBOL_INLINE_P(sym));
for (i=0; i<30/bit_per_char; i++) {
- uint32_t bits = sym>>(i*bit_per_char+2) & ((1<<bit_per_char)-1);
+ uint32_t bits = sym>>(i*bit_per_char+SYMBOL_INLINE_SHIFT) & ((1<<bit_per_char)-1);
if (bits == 0) break;
buf[i] = pack_table[bits-1];;
}
@@ -113,25 +129,23 @@ find_symbol(mrb_state *mrb, const char *name, uint16_t len, uint8_t hash)
mrb_sym i;
symbol_name *sname;
-#ifndef MRB_ENABLE_ALL_SYMBOLS
/* inline symbol */
i = sym_inline_pack(name, len);
if (i > 0) return i;
-#endif
i = mrb->symhash[hash];
if (i == 0) return 0;
do {
sname = &mrb->symtbl[i];
if (sname->len == len && memcmp(sname->name, name, len) == 0) {
- return i<<1;
+ return i<<SYMBOL_NORMAL_SHIFT;
}
if (sname->prev == 0xff) {
i -= 0xff;
sname = &mrb->symtbl[i];
while (mrb->symtbl < sname) {
if (sname->len == len && memcmp(sname->name, name, len) == 0) {
- return (mrb_sym)(sname - mrb->symtbl)<<1;
+ return (mrb_sym)(sname - mrb->symtbl)<<SYMBOL_NORMAL_SHIFT;
}
sname--;
}
@@ -186,7 +200,7 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
}
mrb->symhash[hash] = sym;
- return sym<<1;
+ return sym<<SYMBOL_NORMAL_SHIFT;
}
MRB_API mrb_sym
@@ -239,13 +253,9 @@ mrb_check_intern_str(mrb_state *mrb, mrb_value str)
static const char*
sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
{
-#ifndef MRB_ENABLE_ALL_SYMBOLS
- if (sym & 1) { /* inline packed symbol */
- return sym_inline_unpack(sym, buf, lenp);
- }
-#endif
+ if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp);
- sym >>= 1;
+ sym >>= SYMBOL_NORMAL_SHIFT;
if (sym == 0 || mrb->symidx < sym) {
if (lenp) *lenp = 0;
return NULL;
@@ -484,15 +494,18 @@ sym_inspect(mrb_state *mrb, mrb_value sym)
name = mrb_sym2name_len(mrb, id, &len);
str = mrb_str_new(mrb, 0, len+1);
sp = RSTRING_PTR(str);
- RSTRING_PTR(str)[0] = ':';
+ sp[0] = ':';
memcpy(sp+1, name, len);
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
if (!symname_p(name) || strlen(name) != (size_t)len) {
- str = mrb_str_dump(mrb, str);
+ str = mrb_str_inspect(mrb, str);
sp = RSTRING_PTR(str);
sp[0] = ':';
sp[1] = '"';
}
+#ifdef MRB_UTF8_STRING
+ if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+#endif
return str;
}
@@ -503,8 +516,10 @@ mrb_sym2str(mrb_state *mrb, mrb_sym sym)
const char *name = mrb_sym2name_len(mrb, sym, &len);
if (!name) return mrb_undef_value(); /* can't happen */
- if (sym&1) { /* inline symbol */
- return mrb_str_new(mrb, name, len);
+ if (SYMBOL_INLINE_P(sym)) {
+ mrb_value str = mrb_str_new(mrb, name, len);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
}
return mrb_str_new_static(mrb, name, len);
}
@@ -520,13 +535,8 @@ mrb_sym2name(mrb_state *mrb, mrb_sym sym)
return name;
}
else {
- mrb_value str;
- if (sym&1) { /* inline symbol */
- str = mrb_str_new(mrb, name, len);
- }
- else {
- str = mrb_str_new_static(mrb, name, len);
- }
+ mrb_value str = SYMBOL_INLINE_P(sym) ?
+ mrb_str_new(mrb, name, len) : mrb_str_new_static(mrb, name, len);
str = mrb_str_dump(mrb, str);
return RSTRING_PTR(str);
}
diff --git a/src/variable.c b/src/variable.c
index 23d900b7d..06756a69f 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -446,7 +446,7 @@ MRB_API void
mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
{
if (!mrb_iv_name_sym_p(mrb, iv_name)) {
- mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
+ mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name);
}
}
@@ -521,11 +521,11 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
MRB_API mrb_value
mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
- mrb_check_frozen(mrb, mrb_obj_ptr(obj));
if (obj_iv_p(obj)) {
iv_tbl *t = mrb_obj_ptr(obj)->iv;
mrb_value val;
+ mrb_check_frozen(mrb, mrb_obj_ptr(obj));
if (iv_del(mrb, t, sym, &val)) {
return val;
}
@@ -654,8 +654,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
if (given) return v;
}
}
- mrb_name_error(mrb, sym, "uninitialized class variable %S in %S",
- mrb_sym2str(mrb, sym), mrb_obj_value(cls));
+ mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls);
/* not reached */
return mrb_nil_value();
}
@@ -883,7 +882,15 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
ary = *(mrb_value*)p;
s = mrb_sym2name_len(mrb, sym, &len);
if (len >= 1 && ISUPPER(s[0])) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
+ mrb_int i, alen = RARRAY_LEN(ary);
+
+ for (i=0; i<alen; i++) {
+ if (mrb_symbol(RARRAY_PTR(ary)[i]) == sym)
+ break;
+ }
+ if (i==alen) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
+ }
}
return 0;
}
diff --git a/src/vm.c b/src/vm.c
index 0a6d4af8d..8d233f291 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -461,7 +461,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
stack_init(mrb);
}
if (argc < 0) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%i)", argc);
}
c = mrb_class(mrb, self);
m = mrb_method_search_vm(mrb, &c, mid);
@@ -836,8 +836,8 @@ break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
struct RBreak *brk;
brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL);
- brk->proc = p;
- brk->val = val;
+ mrb_break_proc_set(brk, p);
+ mrb_break_value_set(brk, val);
return brk;
}
@@ -878,13 +878,11 @@ argnum_error(mrb_state *mrb, mrb_int num)
}
}
if (mrb->c->ci->mid) {
- str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)",
- mrb_sym2str(mrb, mrb->c->ci->mid),
- mrb_fixnum_value(argc), mrb_fixnum_value(num));
+ str = mrb_format(mrb, "'%n': wrong number of arguments (%i for %i)",
+ mrb->c->ci->mid, argc, num);
}
else {
- str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
- mrb_fixnum_value(argc), mrb_fixnum_value(num));
+ str = mrb_format(mrb, "wrong number of arguments (%i for %i)", argc, num);
}
exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
mrb_exc_set(mrb, exc);
@@ -973,10 +971,10 @@ check_target_class(mrb_state *mrb)
void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
MRB_API mrb_value
-mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
+mrb_vm_exec(mrb_state *mrb, struct RProc *proc, const mrb_code *pc)
{
/* mrb_assert(MRB_PROC_CFUNC_P(proc)) */
- mrb_code *pc0 = pc;
+ const mrb_code *pc0 = pc;
mrb_irep *irep = proc->body.irep;
mrb_value *pool = irep->pool;
mrb_sym *syms = irep->syms;
@@ -1868,7 +1866,7 @@ RETRY_TRY_BLOCK:
mrb_value kdict = regs[mrb->c->ci->argc];
if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) {
- mrb_value str = mrb_format(mrb, "missing keyword: %S", k);
+ mrb_value str = mrb_format(mrb, "missing keyword: %v", k);
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
@@ -1895,7 +1893,7 @@ RETRY_TRY_BLOCK:
if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) {
mrb_value keys = mrb_hash_keys(mrb, kdict);
mrb_value key1 = RARRAY_PTR(keys)[0];
- mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1);
+ mrb_value str = mrb_format(mrb, "unknown keyword: %v", key1);
mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
goto L_RAISE;
}
@@ -2102,8 +2100,8 @@ RETRY_TRY_BLOCK:
}
if (FALSE) {
L_BREAK:
- v = ((struct RBreak*)mrb->exc)->val;
- proc = ((struct RBreak*)mrb->exc)->proc;
+ v = mrb_break_value_get((struct RBreak*)mrb->exc);
+ proc = mrb_break_proc_get((struct RBreak*)mrb->exc);
mrb->exc = NULL;
ci = mrb->c->ci;
}
@@ -2443,7 +2441,12 @@ RETRY_TRY_BLOCK:
CASE(OP_ARYCAT, B) {
mrb_value splat = mrb_ary_splat(mrb, regs[a+1]);
- mrb_ary_concat(mrb, regs[a], splat);
+ if (mrb_nil_p(regs[a])) {
+ regs[a] = splat;
+ }
+ else {
+ mrb_ary_concat(mrb, regs[a], splat);
+ }
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
@@ -2834,6 +2837,7 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta
return mrb_vm_run(mrb, proc, self, stack_keep);
}
if (mrb->c->ci == mrb->c->cibase) {
+ mrb->c->ci->env = NULL;
return mrb_vm_run(mrb, proc, self, stack_keep);
}
ci = cipush(mrb);
diff --git a/tasks/doc.rake b/tasks/doc.rake
new file mode 100644
index 000000000..4aec2d0a1
--- /dev/null
+++ b/tasks/doc.rake
@@ -0,0 +1,48 @@
+desc 'generate document'
+task :doc => [:api_doc, :capi_doc] do
+
+end
+
+desc 'generate yard docs'
+task :api_doc do
+ begin
+ sh "mrbdoc"
+ rescue
+ puts "ERROR: To generate yard documentation, you should install yard-mruby gem."
+ puts " $ gem install yard-mruby yard-coderay"
+ end
+end
+
+desc 'generate doxygen docs'
+task :capi_doc do
+ begin
+ sh "doxygen Doxyfile"
+ rescue
+ puts "ERROR: To generate C API documents, you need Doxygen."
+ puts " $ sudo apt-get install doxygen"
+ end
+end
+
+desc 'clean all built docs'
+task :clean_api_doc do
+ FileUtils.rm_rf 'doc/api'
+end
+
+desc 'clean all built docs'
+task :clean_capi_doc do
+ FileUtils.rm_rf 'doc/capi'
+end
+
+desc 'clean all built docs'
+task :clean_doc => [:clean_api_doc, :clean_capi_doc] do
+end
+
+desc 'clean all built docs'
+task :view_api => [:api_doc] do
+ sh 'xdg-open doc/api/index.html'
+end
+
+desc 'clean all built docs'
+task :view_capi => [:capi_doc] do
+ sh 'xdg-open doc/capi/html/index.html'
+end
diff --git a/test/assert.rb b/test/assert.rb
index 5e2e104b3..9b04f5b48 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -2,17 +2,52 @@ $undefined = Object.new
$ok_test = 0
$ko_test = 0
$kill_test = 0
+$warning_test = 0
$skip_test = 0
$asserts = []
$test_start = Time.now if Object.const_defined?(:Time)
+# For bintest on Ruby
unless RUBY_ENGINE == "mruby"
- # For bintest on Ruby
def t_print(*args)
print(*args)
$stdout.flush
nil
end
+
+ def _str_match?(pattern, str)
+ File.fnmatch?(pattern, str, File::FNM_EXTGLOB|File::FNM_DOTMATCH)
+ end
+end
+
+class Array
+ def _assertion_join
+ join("-")
+ end
+end
+
+class String
+ def _assertion_indent(indent)
+ indent = indent.to_s
+ off = 0
+ str = self
+ while nl = index("\n", off)
+ nl += 1
+ nl += 1 while slice(nl) == "\n"
+ break if nl >= size
+ str = indent.dup if off == 0
+ str += slice(off, nl - off) + indent
+ off = nl
+ end
+
+ if off == 0
+ str = indent + self
+ else
+ str += slice(off..-1)
+ end
+
+ str
+ end
end
##
@@ -22,14 +57,14 @@ def assertion_string(err, str, iso=nil, e=nil, bt=nil)
msg += " [#{iso}]" if iso && !iso.empty?
msg += " => #{e}" if e && !e.to_s.empty?
msg += " (#{GEMNAME == 'mruby-test' ? 'core' : "mrbgems: #{GEMNAME}"})"
- if $mrbtest_assert && $mrbtest_assert.size > 0
+ if $mrbtest_assert
$mrbtest_assert.each do |idx, assert_msg, diff|
msg += "\n - Assertion[#{idx}]"
msg += " #{assert_msg}." if assert_msg && !assert_msg.empty?
msg += "\n#{diff}" if diff && !diff.empty?
end
end
- msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
+ msg += "\nbacktrace:\n #{bt.join("\n ")}" if bt
msg
end
@@ -41,16 +76,42 @@ end
# iso : The ISO reference code of the feature
# which will be tested by this
# assertion
-def assert(str = 'Assertion failed', iso = '')
+def assert(str = 'assert', iso = '')
t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose
begin
+ $mrbtest_child_noassert ||= [0]
+ $mrbtest_child_noassert << 0
+ parent_asserts = $asserts
+ $asserts = []
+ parent_mrbtest_assert = $mrbtest_assert
$mrbtest_assert = []
- $mrbtest_assert_idx = 0
+
+ if $mrbtest_assert_idx && !$mrbtest_assert_idx.empty?
+ $mrbtest_assert_idx[-1] += 1
+ $mrbtest_assert_idx << 0
+ else
+ $mrbtest_assert_idx = [0]
+ class << $mrbtest_assert_idx
+ alias to_s _assertion_join
+ end
+ end
+
yield
- if($mrbtest_assert.size > 0)
- $asserts.push(assertion_string('Fail: ', str, iso))
- $ko_test += 1
- t_print('F')
+ if $mrbtest_assert.size > 0
+ if $mrbtest_assert.size == $mrbtest_child_noassert[-1]
+ $asserts.push(assertion_string('Skip: ', str, iso))
+ $mrbtest_child_noassert[-2] += 1
+ $skip_test += 1
+ t_print('?')
+ else
+ $asserts.push(assertion_string('Fail: ', str, iso))
+ $ko_test += 1
+ t_print('F')
+ end
+ elsif $mrbtest_assert_idx[-1] == 0
+ $asserts.push(assertion_string('Warn: ', str, iso, 'no assertion'))
+ $warning_test += 1
+ t_print('W')
else
$ok_test += 1
t_print('.')
@@ -58,6 +119,7 @@ def assert(str = 'Assertion failed', iso = '')
rescue MRubyTestSkip => e
$asserts.push(assertion_string('Skip: ', str, iso, e))
$skip_test += 1
+ $mrbtest_child_noassert[-2] += 1
t_print('?')
rescue Exception => e
bt = e.backtrace if $mrbtest_verbose
@@ -65,7 +127,25 @@ def assert(str = 'Assertion failed', iso = '')
$kill_test += 1
t_print('X')
ensure
- $mrbtest_assert = nil
+ if $mrbtest_assert_idx.size > 1
+ $asserts.each do |mesg|
+ idx = $mrbtest_assert_idx[0..-2]._assertion_join
+ mesg = mesg._assertion_indent(" ")
+
+ # Give `mesg` as a `diff` argument to avoid adding extra periods.
+ parent_mrbtest_assert << [idx, nil, mesg]
+ end
+ else
+ parent_asserts.concat $asserts
+ end
+ $asserts = parent_asserts
+
+ $mrbtest_assert = parent_mrbtest_assert
+ $mrbtest_assert_idx.pop
+ $mrbtest_assert_idx = nil if $mrbtest_assert_idx.empty?
+ $mrbtest_child_noassert.pop
+
+ nil
end
t_print("\n") if $mrbtest_verbose
end
@@ -76,11 +156,11 @@ def assertion_diff(exp, act)
end
def assert_true(obj, msg = nil, diff = nil)
- if $mrbtest_assert
- $mrbtest_assert_idx += 1
+ if $mrbtest_assert_idx && $mrbtest_assert_idx.size > 0
+ $mrbtest_assert_idx[-1] += 1
unless obj == true
diff ||= " Expected #{obj.inspect} to be true."
- $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
+ $mrbtest_assert.push([$mrbtest_assert_idx.to_s, msg, diff])
end
end
obj
@@ -128,6 +208,13 @@ def assert_nil(obj, msg = nil)
assert_true(ret, msg, diff)
end
+def assert_not_nil(obj, msg = nil)
+ if ret = obj.nil?
+ diff = " Expected #{obj.inspect} to not be nil."
+ end
+ assert_false(ret, msg, diff)
+end
+
def assert_include(*args); _assert_include(true, *args) end
def assert_not_include(*args); _assert_include(false, *args) end
def _assert_include(affirmed, collection, obj, msg = nil)
@@ -180,10 +267,7 @@ end
def assert_match(*args); _assert_match(true, *args) end
def assert_not_match(*args); _assert_match(false, *args) end
def _assert_match(affirmed, pattern, str, msg = nil)
- receiver, *args = RUBY_ENGINE == "mruby" ?
- [self, :_str_match?, pattern, str] :
- [File, :fnmatch?, pattern, str, File::FNM_EXTGLOB|File::FNM_DOTMATCH]
- unless ret = !receiver.__send__(*args) == !affirmed
+ unless ret = _str_match?(pattern, str) == affirmed
diff = " Expected #{pattern.inspect} to #{'not ' unless affirmed}match #{str.inspect}."
end
assert_true(ret, msg, diff)
@@ -217,8 +301,9 @@ def assert_raise(*exc)
exc = exc.empty? ? StandardError : exc.size == 1 ? exc[0] : exc
begin
yield
- rescue *exc
+ rescue *exc => e
pass
+ e
rescue Exception => e
diff = " #{exc} exception expected, not\n" \
" Class: <#{e.class}>\n" \
@@ -243,6 +328,28 @@ def assert_nothing_raised(msg = nil)
end
end
+def assert_raise_with_message(*args, &block)
+ _assert_raise_with_message(:plain, *args, &block)
+end
+def assert_raise_with_message_pattern(*args, &block)
+ _assert_raise_with_message(:pattern, *args, &block)
+end
+def _assert_raise_with_message(type, exc, exp_msg, msg = nil, &block)
+ e = msg ? assert_raise(exc, msg, &block) : assert_raise(exc, &block)
+ e ? ($mrbtest_assert_idx[-1]-=1) : (return e)
+
+ err_msg = e.message
+ unless ret = type == :pattern ? _str_match?(exp_msg, err_msg) : exp_msg == err_msg
+ diff = " Expected Exception(#{exc}) was raised, but the message doesn't match.\n"
+ if type == :pattern
+ diff += " Expected #{exp_msg.inspect} to match #{err_msg.inspect}."
+ else
+ diff += assertion_diff(exp_msg, err_msg)
+ end
+ end
+ assert_true(ret, msg, diff)
+end
+
def pass
assert_true(true)
end
@@ -261,17 +368,18 @@ def report
t_print("#{msg}\n")
end
- $total_test = $ok_test + $ko_test + $kill_test + $skip_test
- t_print("Total: #{$total_test}\n")
+ $total_test = $ok_test + $ko_test + $kill_test + $warning_test + $skip_test
+ t_print(" Total: #{$total_test}\n")
- t_print(" OK: #{$ok_test}\n")
- t_print(" KO: #{$ko_test}\n")
- t_print("Crash: #{$kill_test}\n")
- t_print(" Skip: #{$skip_test}\n")
+ t_print(" OK: #{$ok_test}\n")
+ t_print(" KO: #{$ko_test}\n")
+ t_print(" Crash: #{$kill_test}\n")
+ t_print("Warning: #{$warning_test}\n")
+ t_print(" Skip: #{$skip_test}\n")
if Object.const_defined?(:Time)
t_time = Time.now - $test_start
- t_print(" Time: #{t_time.round(2)} seconds\n")
+ t_print(" Time: #{t_time.round(2)} seconds\n")
end
$ko_test == 0 && $kill_test == 0
diff --git a/test/t/array.rb b/test/t/array.rb
index 3df99056f..eec31d751 100644
--- a/test/t/array.rb
+++ b/test/t/array.rb
@@ -346,11 +346,12 @@ end
assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do
a = [2, 3, 4, 5]
+ a[4] = a
r1 = a.to_s
r2 = a.inspect
assert_equal(r2, r1)
- assert_equal("[2, 3, 4, 5]", r1)
+ assert_equal("[2, 3, 4, 5, [...]]", r1)
end
assert('Array#==', '15.2.12.5.33') do
diff --git a/test/t/enumerable.rb b/test/t/enumerable.rb
index 652c304da..9e7602db7 100644
--- a/test/t/enumerable.rb
+++ b/test/t/enumerable.rb
@@ -45,7 +45,7 @@ end
assert('Enumerable#detect', '15.3.2.2.4') do
assert_equal 1, [1,2,3].detect() { true }
- assert_equal 'a', [1,2,3].detect("a") { false }
+ assert_equal 'a', [1,2,3].detect(->{"a"}) { false }
end
assert('Array#each_with_index', '15.3.2.2.5') do
@@ -64,7 +64,7 @@ end
assert('Enumerable#find', '15.3.2.2.7') do
assert_equal 1, [1,2,3].find() { true }
- assert_equal 'a', [1,2,3].find("a") { false }
+ assert_equal 'a', [1,2,3].find(->{"a"}) { false }
end
assert('Enumerable#find_all', '15.3.2.2.8') do
diff --git a/test/t/float.rb b/test/t/float.rb
index 63bf83f40..dc989636f 100644
--- a/test/t/float.rb
+++ b/test/t/float.rb
@@ -212,10 +212,10 @@ assert('Float#to_s') do
assert_equal("Infinity", Float::INFINITY.to_s)
assert_equal("-Infinity", (-Float::INFINITY).to_s)
assert_equal("NaN", Float::NAN.to_s)
- assert_equal("0.0", 0.0.to_s)
- assert_equal("-0.0", -0.0.to_s)
+ assert_equal("0", 0.0.to_s)
+ assert_equal("-0", -0.0.to_s)
assert_equal("-3.25", -3.25.to_s)
- assert_equal("50.0", 50.0.to_s)
+ assert_equal("50", 50.0.to_s)
assert_equal("0.0125", 0.0125.to_s)
assert_equal("-0.0125", -0.0125.to_s)
assert_equal("1.0e-10", 0.0000000001.to_s)
@@ -224,8 +224,8 @@ assert('Float#to_s') do
assert_equal("-1.0e+20", -1e20.to_s)
assert_equal("1.0e+16", 10000000000000000.0.to_s)
assert_equal("-1.0e+16", -10000000000000000.0.to_s)
- assert_equal("100000.0", 100000.0.to_s)
- assert_equal("-100000.0", -100000.0.to_s)
+ assert_equal("100000", 100000.0.to_s)
+ assert_equal("-100000", -100000.0.to_s)
if uses_float
assert_equal("1.0e+08", 100000000.0.to_s)
assert_equal("-1.0e+08", -100000000.0.to_s)
@@ -234,8 +234,8 @@ assert('Float#to_s') do
else
assert_equal("1.0e+15", 1000000000000000.0.to_s)
assert_equal("-1.0e+15", -1000000000000000.0.to_s)
- assert_equal("100000000000000.0", 100000000000000.0.to_s)
- assert_equal("-100000000000000.0", -100000000000000.0.to_s)
+ assert_equal("100000000000000", 100000000000000.0.to_s)
+ assert_equal("-100000000000000", -100000000000000.0.to_s)
end
end
diff --git a/test/t/hash.rb b/test/t/hash.rb
index 156991f4a..cd47d251d 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -352,11 +352,13 @@ end
assert('Hash#inspect') do
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
+ h["recur"] = h
ret = h.to_s
assert_include ret, '"c"=>300'
assert_include ret, '"a"=>100'
assert_include ret, '"d"=>400'
+ assert_include ret, '"recur"=>{...}'
end
assert('Hash#rehash') do
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index 103660f53..c2eeee1a5 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -240,6 +240,9 @@ assert('Kernel#extend', '15.3.1.3.13') do
assert_true a.respond_to?(:test_method)
assert_false b.respond_to?(:test_method)
+
+ assert_raise(FrozenError) { Object.new.freeze.extend(Test4ExtendModule) }
+ assert_raise(FrozenError, TypeError) { :sym.extend(Test4ExtendModule) }
end
assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
@@ -352,17 +355,15 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
end
end
no_super_test = NoSuperMethodTestClass.new
- begin
+ msg = "undefined method 'no_super_method_named_this'"
+ assert_raise_with_message(NoMethodError, msg) do
no_super_test.no_super_method_named_this
- rescue NoMethodError => e
- assert_equal "undefined method 'no_super_method_named_this'", e.message
end
a = String.new
- begin
+ msg = "undefined method 'no_method_named_this'"
+ assert_raise_with_message(NoMethodError, msg) do
a.no_method_named_this
- rescue NoMethodError => e
- assert_equal "undefined method 'no_method_named_this'", e.message
end
end
@@ -419,6 +420,7 @@ assert('Kernel#remove_instance_variable', '15.3.1.3.41') do
assert_raise(NameError) { tri.remove }
assert_raise(NameError) { tri.remove_instance_variable(:var) }
assert_raise(FrozenError) { tri.freeze.remove }
+ assert_raise(FrozenError, NameError) { :a.remove_instance_variable(:@v) }
end
# Kernel#require is defined in mruby-require. '15.3.1.3.42'
diff --git a/test/t/module.rb b/test/t/module.rb
index 571f4759d..12b7f1344 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -21,6 +21,14 @@ def labeled_class(name, supklass = Object, &block)
end
end
+def assert_uninitialized_const(&block)
+ assert_raise_with_message_pattern(NameError, "uninitialized constant *", &block)
+end
+
+def assert_wrong_const_name(&block)
+ assert_raise_with_message_pattern(NameError, "wrong constant name *", &block)
+end
+
assert('Module', '15.2.2') do
assert_equal Class, Module.class
end
@@ -59,6 +67,7 @@ assert('Module#append_features', '15.2.2.4.10') do
end
assert_equal Test4AppendFeatures2, Test4AppendFeatures2.const_get(:Const4AppendFeatures2)
+ assert_raise(FrozenError) { Module.new.append_features Class.new.freeze }
end
assert('Module#attr NameError') do
@@ -221,7 +230,7 @@ assert('Module#const_defined?', '15.2.2.4.20') do
assert_true Test4ConstDefined.const_defined?(:Const4Test4ConstDefined)
assert_false Test4ConstDefined.const_defined?(:NotExisting)
- assert_raise(NameError){ Test4ConstDefined.const_defined?(:wrong_name) }
+ assert_wrong_const_name{ Test4ConstDefined.const_defined?(:wrong_name) }
end
assert('Module#const_get', '15.2.2.4.21') do
@@ -234,9 +243,9 @@ assert('Module#const_get', '15.2.2.4.21') do
assert_equal 42, Object.const_get("Test4ConstGet::Const4Test4ConstGet")
assert_raise(TypeError){ Test4ConstGet.const_get(123) }
- assert_raise(NameError){ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
- assert_raise(NameError){ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
- assert_raise(NameError){ Test4ConstGet.const_get(:wrong_name) }
+ assert_uninitialized_const{ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
+ assert_uninitialized_const{ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
+ assert_wrong_const_name{ Test4ConstGet.const_get(:wrong_name) }
end
assert('Module#const_set', '15.2.2.4.23') do
@@ -247,7 +256,7 @@ assert('Module#const_set', '15.2.2.4.23') do
assert_equal 23, Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
assert_equal 23, Test4ConstSet.const_get(:Const4Test4ConstSet)
["", "wrongNAME", "Wrong-Name"].each do |n|
- assert_raise(NameError) { Test4ConstSet.const_set(n, 1) }
+ assert_wrong_const_name { Test4ConstSet.const_set(n, 1) }
end
end
@@ -258,9 +267,11 @@ assert('Module#remove_const', '15.2.2.4.40') do
assert_equal 23, Test4RemoveConst.remove_const(:ExistingConst)
assert_false Test4RemoveConst.const_defined?(:ExistingConst)
- assert_raise(NameError) { Test4RemoveConst.remove_const(:NonExistingConst) }
+ assert_raise_with_message_pattern(NameError, "constant * not defined") do
+ Test4RemoveConst.remove_const(:NonExistingConst)
+ end
%i[x X!].each do |n|
- assert_raise(NameError) { Test4RemoveConst.remove_const(n) }
+ assert_wrong_const_name { Test4RemoveConst.remove_const(n) }
end
assert_raise(FrozenError) { Test4RemoveConst.freeze.remove_const(:A) }
end
@@ -275,6 +286,18 @@ assert('Module#const_missing', '15.2.2.4.22') do
assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist)
end
+assert('Module#extend_object', '15.2.2.4.25') do
+ cls = Class.new
+ mod = Module.new { def foo; end }
+ a = cls.new
+ b = cls.new
+ mod.extend_object(b)
+ assert_false a.respond_to?(:foo)
+ assert_true b.respond_to?(:foo)
+ assert_raise(FrozenError) { mod.extend_object(cls.new.freeze) }
+ assert_raise(FrozenError, TypeError) { mod.extend_object(1) }
+end
+
assert('Module#include', '15.2.2.4.27') do
module Test4Include
Const4Include = 42
@@ -288,6 +311,7 @@ assert('Module#include', '15.2.2.4.27') do
assert_equal 42, Test4Include2.const_get(:Const4Include)
assert_equal Test4Include2, Test4Include2.include_result
+ assert_raise(FrozenError) { Module.new.freeze.include Test4Include }
end
assert('Module#include?', '15.2.2.4.28') do
@@ -386,6 +410,29 @@ end
# Not ISO specified
+assert('Module#dup') do
+ module TestModuleDup
+ @@cvar = :cvar
+ class << self
+ attr_accessor :cattr
+ def cmeth; :cmeth end
+ end
+ def cvar; @@cvar end
+ def imeth; :imeth end
+ self.cattr = :cattr
+ end
+
+ m = TestModuleDup.dup
+ o = Object.include(m).new
+ assert_equal(:cattr, m.cattr)
+ assert_equal(:cmeth, m.cmeth)
+ assert_equal(:cvar, o.cvar)
+ assert_equal(:imeth, o.imeth)
+ assert_match("#<Module:0x*>", m.to_s)
+ assert_not_predicate(m, :frozen?)
+ assert_not_predicate(TestModuleDup.freeze.dup, :frozen?)
+end
+
assert('Module#define_method') do
c = Class.new {
define_method(:m1) { :ok }
@@ -398,6 +445,15 @@ assert('Module#define_method') do
end
end
+assert 'Module#prepend_features' do
+ mod = Module.new { def m; :mod end }
+ cls = Class.new { def m; :cls end }
+ assert_equal :cls, cls.new.m
+ mod.prepend_features(cls)
+ assert_equal :mod, cls.new.m
+ assert_raise(FrozenError) { Module.new.prepend_features(Class.new.freeze) }
+end
+
# @!group prepend
assert('Module#prepend') do
module M0
@@ -632,6 +688,10 @@ end
# end
# end;
#end
+
+ assert 'Module#prepend to frozen class' do
+ assert_raise(FrozenError) { Class.new.freeze.prepend Module.new }
+ end
# @!endgroup prepend
assert('Module#to_s') do
diff --git a/test/t/numeric.rb b/test/t/numeric.rb
index d73dfdb61..5b1e79153 100644
--- a/test/t/numeric.rb
+++ b/test/t/numeric.rb
@@ -1,6 +1,19 @@
##
# Numeric ISO Test
+def assert_step(exp, receiver, args, inf: false)
+ act = []
+ ret = receiver.step(*args) do |i|
+ act << i
+ break if inf && exp.size == act.size
+ end
+ expr = "#{receiver.inspect}.step(#{args.map(&:inspect).join(', ')})"
+ assert "assert_step" do
+ assert_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act))
+ assert_same(receiver, ret, "#{expr}: return value") unless inf
+ end
+end
+
assert('Numeric', '15.2.7') do
assert_equal(Class, Numeric.class)
end
@@ -42,31 +55,60 @@ assert('Numeric#**') do
end
assert('Numeric#step') do
- assert_step = ->(exp, receiver, args) do
- inf = !args[0]
- act = []
- ret = receiver.step(*args) do |i|
- act << i
- break if inf && exp.size == act.size
- end
- expr = "#{receiver.inspect}.step(#{args.map(&:inspect).join(', ')})"
- assert_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act))
- assert_same(receiver, ret, "#{expr}: return value") unless inf
- end
-
assert_raise(ArgumentError) { 1.step(2, 0) { break } }
- assert_step.([2, 3, 4], 2, [4])
- assert_step.([10, 8, 6, 4, 2], 10, [1, -2])
- assert_step.([], 2, [1, 3])
- assert_step.([], -2, [-1, -3])
- assert_step.([10, 11, 12, 13], 10, [])
- assert_step.([10, 7, 4], 10, [nil, -3])
+ assert_step([2, 3, 4], 2, [4])
+ assert_step([10, 8, 6, 4, 2], 10, [1, -2])
+ assert_step([], 2, [1, 3])
+ assert_step([], -2, [-1, -3])
+ assert_step([10, 11, 12, 13], 10, [], inf: true)
+ assert_step([10, 7, 4], 10, [nil, -3], inf: true)
skip unless Object.const_defined?(:Float)
+ inf = Float::INFINITY
assert_raise(ArgumentError) { 1.step(2, 0.0) { break } }
- assert_step.([2.0, 3.0, 4.0], 2, [4.0])
- assert_step.([7.0, 4.0, 1.0, -2.0], 7, [-4, -3.0])
- assert_step.([2.0, 3.0, 4.0], 2.0, [4])
- assert_step.([10.0, 11.0, 12.0, 13.0], 10.0, [])
- assert_step.([10.0, 7.0, 4.0], 10, [nil, -3.0])
+ assert_step([2.0, 3.0, 4.0], 2, [4.0])
+ assert_step([7.0, 4.0, 1.0, -2.0], 7, [-4, -3.0])
+ assert_step([2.0, 3.0, 4.0], 2.0, [4])
+ assert_step([10.0, 11.0, 12.0, 13.0], 10.0, [], inf: true)
+ assert_step([10.0, 7.0, 4.0], 10, [nil, -3.0], inf: true)
+ assert_step([1.0], 1, [nil, inf])
+ assert_step([1.0], 1, [nil, -inf])
+ assert_step([1.0], 1, [3, inf])
+ assert_step([], 1, [-3, inf])
+ assert_step([], 1, [3, -inf])
+ assert_step([1.0], 1, [-3, -inf])
+ assert_step([1.0], 1, [inf, inf])
+ assert_step([], 1, [inf, -inf])
+ assert_step([], 1, [-inf, inf])
+ assert_step([1.0], 1, [-inf, -inf])
+ assert_step([], inf, [2])
+ assert_step([], inf, [-2])
+ assert_step([], inf, [2, 3])
+ assert_step([inf, inf, inf], inf, [2, -3], inf: true)
+ assert_step([], inf, [2, inf])
+ assert_step([inf], inf, [2, -inf])
+ assert_step([], inf, [-2, inf])
+ assert_step([inf], inf, [-2, -inf])
+ assert_step([], inf, [-2, 3])
+ assert_step([inf, inf, inf], inf, [-2, -3], inf: true)
+ assert_step([inf], inf, [inf])
+ assert_step([], inf, [-inf])
+ assert_step([inf], inf, [inf, inf])
+ assert_step([inf], inf, [inf, -inf])
+ assert_step([inf], inf, [-inf, -inf])
+ assert_step([-inf, -inf, -inf], -inf, [2], inf: true)
+ assert_step([-inf, -inf, -inf], -inf, [-2], inf: true)
+ assert_step([-inf, -inf, -inf], -inf, [2, 3], inf: true)
+ assert_step([], -inf, [2, -3])
+ assert_step([-inf], -inf, [2, inf])
+ assert_step([], -inf, [2, -inf])
+ assert_step([-inf], -inf, [-2, inf])
+ assert_step([], -inf, [-2, -inf])
+ assert_step([-inf, -inf, -inf], -inf, [-2, 3], inf: true)
+ assert_step([], -inf, [-2, -3])
+ assert_step([-inf, -inf, -inf], -inf, [inf], inf: true)
+ assert_step([-inf], -inf, [-inf])
+ assert_step([-inf], -inf, [inf, inf])
+ assert_step([], -inf, [inf, -inf])
+ assert_step([-inf], -inf, [-inf, -inf])
end
diff --git a/test/t/range.rb b/test/t/range.rb
index d71fe8946..106c2866e 100644
--- a/test/t/range.rb
+++ b/test/t/range.rb
@@ -101,12 +101,12 @@ end
assert('Range#dup') do
r = (1..3).dup
- assert_equal r.begin, 1
- assert_equal r.end, 3
+ assert_equal 1, r.begin
+ assert_equal 3, r.end
assert_false r.exclude_end?
r = ("a"..."z").dup
- assert_equal r.begin, "a"
- assert_equal r.end, "z"
+ assert_equal "a", r.begin
+ assert_equal "z", r.end
assert_true r.exclude_end?
end
diff --git a/test/t/string.rb b/test/t/string.rb
index 9817dd188..c820bfa92 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -2,7 +2,7 @@
##
# String ISO Test
-UTF8STRING = ("\343\201\202".size == 1)
+UTF8STRING = __ENCODING__ == "UTF-8"
assert('String', '15.2.10') do
assert_equal Class, String.class
@@ -37,52 +37,37 @@ end
assert('String#*', '15.2.10.5.5') do
assert_equal 'aaaaa', 'a' * 5
assert_equal '', 'a' * 0
- assert_equal 'aa', 'a' * 2.1
assert_raise(ArgumentError) { 'a' * -1 }
+ assert_raise(TypeError) { 'a' * '1' }
+ assert_raise(TypeError) { 'a' * nil }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal 'aa', 'a' * 2.1
assert_raise(RangeError) { '' * 1e30 }
assert_raise(RangeError) { '' * Float::INFINITY }
assert_raise(RangeError) { '' * Float::NAN }
- assert_raise(TypeError) { 'a' * '1' }
- assert_raise(TypeError) { 'a' * nil }
end
+
assert('String#[]', '15.2.10.5.6') do
# length of args is 1
- a = 'abc'[0]
- b = 'abc'[-1]
- c = 'abc'[10]
- d = 'abc'[-10]
- e = 'abc'[1.1]
+ assert_equal 'a', 'abc'[0]
+ assert_equal 'c', 'abc'[-1]
+ assert_nil 'abc'[10]
+ assert_nil 'abc'[-10]
+ assert_equal 'b', 'abc'[1.1] if Object.const_defined?(:Float)
# length of args is 2
- a1 = 'abc'[0, -1]
- b1 = 'abc'[10, 0]
- c1 = 'abc'[-10, 0]
- d1 = 'abc'[0, 0]
- e1 = 'abc'[1, 2]
-
- # args is RegExp
- # It will be tested in mrbgems.
+ assert_nil 'abc'[0, -1]
+ assert_nil 'abc'[10, 0]
+ assert_nil 'abc'[-10, 0]
+ assert_equal '', 'abc'[0, 0]
+ assert_equal 'bc', 'abc'[1, 2]
# args is String
- a3 = 'abc'['bc']
- b3 = 'abc'['XX']
-
- assert_equal 'a', 'a'
- # assert_equal 'c', b
- # assert_nil c
- # assert_nil d
- # assert_equal 'b', e
- # assert_nil a1
- # assert_nil b1
- # assert_nil c1
- # assert_equal '', d1
- # assert_equal 'bc', e1
- # assert_equal 'bc', a3
- # assert_nil b3
-
- # assert_raise(TypeError) do
- # a[nil]
- # end
+ assert_equal 'bc', 'abc'['bc']
+ assert_nil 'abc'['XX']
+
+ assert_raise(TypeError) { 'abc'[nil] }
end
assert('String#[](UTF-8)', '15.2.10.5.6') do
@@ -209,6 +194,56 @@ assert('String#[]=') do
assert_raise(TypeError) { 'a'[0, 1] = 1 }
end
+assert('String[]=(UTF-8)') do
+ a = "➀➁➂➃➄"
+ a[3] = "⚃"
+ assert_equal "➀➁➂⚃➄", a
+
+ b = "➀➁➂➃➄"
+ b[3, 0] = "⛄"
+ assert_equal "➀➁➂⛄➃➄", b
+
+ c = "➀➁➂➃➄"
+ c[3, 2] = "⚃⚄"
+ assert_equal "➀➁➂⚃⚄", c
+
+ d = "➀➁➂➃➄"
+ d[5] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", d
+
+ e = "➀➁➂➃➄"
+ e[5, 0] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", e
+
+ f = "➀➁➂➃➄"
+ f[5, 2] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", f
+
+ g = "➀➁➂➃➄"
+ assert_raise(IndexError) { g[6] = "⛄" }
+
+ h = "➀➁➂➃➄"
+ assert_raise(IndexError) { h[6, 0] = "⛄" }
+
+ i = "➀➁➂➃➄"
+ assert_raise(IndexError) { i[6, 2] = "⛄" }
+
+ j = "➀➁➂➃➄"
+ j["➃"] = "⚃"
+ assert_equal "➀➁➂⚃➄", j
+
+ k = "➀➁➂➃➄"
+ assert_raise(IndexError) { k["⛄"] = "⛇" }
+
+ l = "➀➁➂➃➄"
+ assert_nothing_raised { l["➂"] = "" }
+ assert_equal "➀➁➃➄", l
+
+ m = "➀➁➂➃➄"
+ assert_raise(TypeError) { m["➂"] = nil }
+ assert_equal "➀➁➂➃➄", m
+end if UTF8STRING
+
assert('String#capitalize', '15.2.10.5.7') do
a = 'abc'
a.capitalize
@@ -418,6 +453,17 @@ assert('String#index', '15.2.10.5.22') do
assert_equal nil, "hello".index("", 6)
end
+assert('String#index(UTF-8)', '15.2.10.5.22') do
+ assert_equal 0, '⓿➊➋➌➍➎'.index('⓿')
+ assert_nil '⓿➊➋➌➍➎'.index('➓')
+ assert_equal 6, '⓿➊➋➌➍➎⓿➊➋➌➍➎'.index('⓿', 1)
+ assert_equal 6, "⓿➊➋➌➍➎".index("", 6)
+ assert_equal nil, "⓿➊➋➌➍➎".index("", 7)
+ assert_equal 0, '⓿➊➋➌➍➎'.index("\xe2")
+ assert_equal nil, '⓿➊➋➌➍➎'.index("\xe3")
+ assert_equal 6, "\xd1\xd1\xd1\xd1\xd1\xd1⓿➊➋➌➍➎".index('⓿')
+end if UTF8STRING
+
assert('String#initialize', '15.2.10.5.23') do
a = ''
a.initialize('abc')
@@ -479,6 +525,7 @@ assert('String#reverse(UTF-8)', '15.2.10.5.29') do
assert_equal 'こんにちは世界!', a
assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse
+ assert_equal 'あ', 'あ'.reverse
end if UTF8STRING
assert('String#reverse!', '15.2.10.5.30') do
@@ -495,6 +542,10 @@ assert('String#reverse!(UTF-8)', '15.2.10.5.30') do
assert_equal '!界世はちにんこ', a
assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse!
+
+ b = 'あ'
+ b.reverse!
+ assert_equal 'あ', b
end if UTF8STRING
assert('String#rindex', '15.2.10.5.31') do
@@ -506,10 +557,16 @@ end
assert('String#rindex(UTF-8)', '15.2.10.5.31') do
str = "こんにちは世界!\nこんにちは世界!"
- assert_nil str.index('さ')
- assert_equal 3, str.index('ち')
- assert_equal 12, str.index('ち', 10)
- assert_equal nil, str.index("さ")
+ assert_nil str.rindex('さ')
+ assert_equal 12, str.rindex('ち')
+ assert_equal 3, str.rindex('ち', 10)
+
+ broken = "\xf0☀\xf1☁\xf2☂\xf3☃\xf0☀\xf1☁\xf2☂\xf3☃"
+ assert_nil broken.rindex("\x81") # "\x81" is a part of "☁" ("\xe2\x98\x81")
+ assert_equal 11, broken.rindex("☁")
+ assert_equal 11, broken.rindex("☁", 12)
+ assert_equal 11, broken.rindex("☁", 11)
+ assert_equal 3, broken.rindex("☁", 10)
end if UTF8STRING
# assert('String#scan', '15.2.10.5.32') do
@@ -727,3 +784,82 @@ assert('String literal concatenation') do
assert_equal 3, ('A' "B" 'C').size
assert_equal 4, (%(A) "B#{?C}" "D").size
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#byteslice') do
+ str1 = "hello"
+ str2 = "\u3042ab" # "\xE3\x81\x82ab"
+
+ assert_equal("h", str1.byteslice(0))
+ assert_equal("e", str1.byteslice(1))
+ assert_equal(nil, str1.byteslice(5))
+ assert_equal("o", str1.byteslice(-1))
+ assert_equal(nil, str1.byteslice(-6))
+ assert_equal("\xE3", str2.byteslice(0))
+ assert_equal("\x81", str2.byteslice(1))
+ assert_equal(nil, str2.byteslice(5))
+ assert_equal("b", str2.byteslice(-1))
+ assert_equal(nil, str2.byteslice(-6))
+
+ assert_equal("", str1.byteslice(0, 0))
+ assert_equal(str1, str1.byteslice(0, 6))
+ assert_equal("el", str1.byteslice(1, 2))
+ assert_equal("", str1.byteslice(5, 1))
+ assert_equal("o", str1.byteslice(-1, 6))
+ assert_equal(nil, str1.byteslice(-6, 1))
+ assert_equal(nil, str1.byteslice(0, -1))
+ assert_equal("", str2.byteslice(0, 0))
+ assert_equal(str2, str2.byteslice(0, 6))
+ assert_equal("\x81\x82", str2.byteslice(1, 2))
+ assert_equal("", str2.byteslice(5, 1))
+ assert_equal("b", str2.byteslice(-1, 6))
+ assert_equal(nil, str2.byteslice(-6, 1))
+ assert_equal(nil, str2.byteslice(0, -1))
+
+ assert_equal("ell", str1.byteslice(1..3))
+ assert_equal("el", str1.byteslice(1...3))
+ assert_equal("h", str1.byteslice(0..0))
+ assert_equal("", str1.byteslice(5..0))
+ assert_equal("o", str1.byteslice(4..5))
+ assert_equal(nil, str1.byteslice(6..0))
+ assert_equal("", str1.byteslice(-1..0))
+ assert_equal("llo", str1.byteslice(-3..5))
+ assert_equal("\x81\x82a", str2.byteslice(1..3))
+ assert_equal("\x81\x82", str2.byteslice(1...3))
+ assert_equal("\xE3", str2.byteslice(0..0))
+ assert_equal("", str2.byteslice(5..0))
+ assert_equal("b", str2.byteslice(4..5))
+ assert_equal(nil, str2.byteslice(6..0))
+ assert_equal("", str2.byteslice(-1..0))
+ assert_equal("\x82ab", str2.byteslice(-3..5))
+
+ assert_raise(ArgumentError) { str1.byteslice }
+ assert_raise(ArgumentError) { str1.byteslice(1, 2, 3) }
+ assert_raise(TypeError) { str1.byteslice("1") }
+ assert_raise(TypeError) { str1.byteslice("1", 2) }
+ assert_raise(TypeError) { str1.byteslice(1, "2") }
+ assert_raise(TypeError) { str1.byteslice(1..2, 3) }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal("o", str1.byteslice(4.0))
+ assert_equal("\x82ab", str2.byteslice(2.0, 3.0))
+end
diff --git a/test/t/vformat.rb b/test/t/vformat.rb
new file mode 100644
index 000000000..679f30407
--- /dev/null
+++ b/test/t/vformat.rb
@@ -0,0 +1,92 @@
+def assert_format(exp, args)
+ assert_equal(exp, TestVFormat.format(*args))
+end
+
+def assert_format_pattern(exp_pattern, args)
+ assert_match(exp_pattern, TestVFormat.format(*args))
+end
+
+# Pass if ArgumentError is raised or return value is +exp+.
+def assert_implementation_dependent(exp, args)
+ begin
+ ret = TestVFormat.format(*args)
+ rescue ArgumentError
+ return pass
+ end
+ if ret == exp
+ pass
+ else
+ flunk "", "Expected ArgumentError is raised or #{ret.inspect} to be #{exp}."
+ end
+end
+
+def sclass(v)
+ class << v
+ self
+ end
+end
+
+assert('mrb_vformat') do
+ n = TestVFormat::Native
+ assert_format '', ['']
+ assert_format 'No specifier!', ['No specifier!']
+ assert_format '`c`: C', ['`c`: %c', n.c(?C)]
+ assert_format '`d`: 123', ['`d`: %d', n.d(123)]
+ assert_format '`d`: -79', ['`d`: %d', n.d(-79)]
+ assert_format '`i`: 514', ['`i`: %i', n.i(514)]
+ assert_format '`i`: -83', ['`i`: %i', n.i(-83)]
+ assert_format '`t`: NilClass', ['`t`: %t', nil]
+ assert_format '`t`: FalseClass', ['`t`: %t', false]
+ assert_format '`t`: TrueClass', ['`t`: %t', true]
+ assert_format '`t`: Fixnum', ['`t`: %t', 0]
+ assert_format '`t`: Hash', ['`t`: %t', k: "value"]
+ assert_format_pattern '#<Class:#<Class:#<Hash:0x*>>>', ['%t', sclass({})]
+# assert_format 'string and length', ['string %l length', n.s('andante'), n.l(3)]
+ assert_format '`n`: sym', ['`n`: %n', n.n(:sym)]
+ assert_format '%C文字列%', ['%s', n.s('%C文字列%')]
+ assert_format '`C`: Kernel module', ['`C`: %C module', n.C(Kernel)]
+ assert_format '`C`: NilClass', ['`C`: %C', n.C(nil.class)]
+ assert_format_pattern '#<Class:#<String:0x*>>', ['%C', n.C(sclass(""))]
+ assert_format '`T`: NilClass', ['`T`: %T', nil]
+ assert_format '`T`: FalseClass', ['`T`: %T', false]
+ assert_format '`T`: TrueClass', ['`T`: %T', true]
+ assert_format '`T`: Fixnum', ['`T`: %T', 0]
+ assert_format '`T`: Hash', ['`T`: %T', k: "value"]
+ assert_format_pattern 'Class', ['%T', sclass({})]
+ assert_format '`Y`: nil', ['`Y`: %Y', nil]
+ assert_format '`Y`: false', ['`Y`: %Y', false]
+ assert_format '`Y`: true', ['`Y`: %Y', true]
+ assert_format '`Y`: Fixnum', ['`Y`: %Y', 0]
+ assert_format '`Y`: Hash', ['`Y`: %Y', k: "value"]
+ assert_format 'Class', ['%Y', sclass({})]
+ assert_format_pattern '#<Class:#<String:0x*>>', ['%v', sclass("")]
+ assert_format '`v`: 1...3', ['`v`: %v', 1...3]
+ assert_format '`S`: {:a=>1, "b"=>"c"}', ['`S`: %S', a: 1, "b" => ?c]
+ assert_format 'percent: %', ['percent: %%']
+ assert_format '"I": inspect char', ['%!c: inspect char', n.c(?I)]
+ assert_format '709: inspect mrb_int', ['%!d: inspect mrb_int', n.i(709)]
+# assert_format '"a\x00b\xff"', ['%!l', n.s("a\000b\xFFc\000d"), n.l(4)]
+ assert_format ':"&.": inspect symbol', ['%!n: inspect symbol', n.n(:'&.')]
+ assert_format 'inspect "String"', ['inspect %!v', 'String']
+ assert_format 'inspect Array: [1, :x, {}]', ['inspect Array: %!v', [1,:x,{}]]
+ assert_format_pattern '`!C`: #<Class:0x*>', ['`!C`: %!C', n.C(Class.new)]
+ assert_format 'to_s -> to_s: ab,cd', ['to_s -> to_s: %n,%v', n.n(:ab), 'cd']
+ assert_format 'to_s -> inspect: x:y', ['to_s -> inspect: %v%!v', 'x', :y]
+ assert_format 'inspect -> to_s: "a"b', ['inspect -> to_s: %!v%n', 'a', n.n(:b)]
+ assert_format 'Y -> to_s: nile', ['Y -> to_s: %Y%v', nil, "e"]
+ assert_format '"abc":Z', ['%!s%!n', n.s('abc'), n.n('Z'.to_sym)]
+ assert_format 'escape: \\%a,b,c,d', ['escape: \\\\\%a,b,\c%v', ',d']
+
+ assert_implementation_dependent 'unknown specifier: %^',
+ ['unknown specifier: %^']
+ assert_implementation_dependent 'unknown specifier with modifier: %!^',
+ ['unknown specifier with modifier: %!^']
+ assert_implementation_dependent 'termination is \\', ['termination is \\']
+ assert_implementation_dependent 'termination is %', ['termination is %']
+ assert_implementation_dependent 'termination is %!', ['termination is %!']
+
+ skip unless Object.const_defined?(:Float)
+ assert_format '`f`: 0.0125', ['`f`: %f', n.f(0.0125)]
+ assert_format '-Infinity', ['%f', n.f(-Float::INFINITY)]
+ assert_format 'NaN: Not a Number', ['%f: Not a Number', n.f(Float::NAN)]
+end