diff options
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 @@ -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 @@ -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 Binary files differnew file mode 100644 index 000000000..9006c84eb --- /dev/null +++ b/doc/mruby_logo_red_icon.png 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. | - * | | | 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 @@ -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 ======== -[](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 @@ -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 @@ -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; } @@ -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 |
