D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
alt
/
ruby32
/
include
/
ruby
/
internal
/
Filename :
scan_args.h
back
Copy
#ifndef RBIMPL_SCAN_ARGS_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_SCAN_ARGS_H /** * @file * @author Ruby developers <ruby-core@ruby-lang.org> * @copyright This file is a part of the programming language Ruby. * Permission is hereby granted, to either redistribute and/or * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are * implementation details. Don't take them as canon. They could * rapidly appear then vanish. The name (path) of this header file * is also an implementation detail. Do not expect it to persist * at the place it is now. Developers are free to move it anywhere * anytime at will. * @note To ruby-core: remember that this header can be possibly * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of * extension libraries. They could be written in C++98. * @brief Compile-time static implementation of ::rb_scan_args(). * * This is a beast. It statically analyses the argument spec string, and * expands the assignment of variables into dedicated codes. */ #include "ruby/assert.h" #include "ruby/internal/attr/diagnose_if.h" #include "ruby/internal/attr/error.h" #include "ruby/internal/attr/forceinline.h" #include "ruby/internal/attr/nonnull.h" #include "ruby/internal/attr/noreturn.h" #include "ruby/internal/config.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/has/attribute.h" #include "ruby/internal/intern/array.h" /* rb_ary_new_from_values */ #include "ruby/internal/intern/error.h" /* rb_error_arity */ #include "ruby/internal/intern/hash.h" /* rb_hash_dup */ #include "ruby/internal/intern/proc.h" /* rb_block_proc */ #include "ruby/internal/iterator.h" /* rb_block_given_p / rb_keyword_given_p */ #include "ruby/internal/static_assert.h" #include "ruby/internal/stdbool.h" #include "ruby/internal/value.h" /** * @name Possible values that you should pass to rb_scan_args_kw(). * @{ */ /** Same behaviour as rb_scan_args(). */ #define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0 /** The final argument should be a hash treated as keywords.*/ #define RB_SCAN_ARGS_KEYWORDS 1 /** * Treat a final argument as keywords if it is a hash, and not as keywords * otherwise. */ #define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3 /** @} */ /** * @name Possible values that you should pass to rb_funcallv_kw(). * @{ */ /** Do not pass keywords. */ #define RB_NO_KEYWORDS 0 /** Pass keywords, final argument should be a hash of keywords. */ #define RB_PASS_KEYWORDS 1 /** * Pass keywords if current method is called with keywords, useful for argument * delegation */ #define RB_PASS_CALLED_KEYWORDS rb_keyword_given_p() /** @} */ /** * @private * * @deprecated This macro once was a thing in the old days, but makes no sense * any longer today. Exists here for backwards compatibility * only. You can safely forget about it. */ #define HAVE_RB_SCAN_ARGS_OPTIONAL_HASH 1 RBIMPL_SYMBOL_EXPORT_BEGIN() RBIMPL_ATTR_NONNULL((2, 3)) /** * Retrieves argument from argc and argv to given ::VALUE references according * to the format string. The format can be described in ABNF as follows: * * ``` * scan-arg-spec := param-arg-spec [keyword-arg-spec] [block-arg-spec] * * param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / * pre-opt-post-arg-spec * pre-arg-spec := num-of-leading-mandatory-args * [num-of-optional-args] * post-arg-spec := sym-for-variable-length-args * [num-of-trailing-mandatory-args] * pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args * num-of-trailing-mandatory-args * keyword-arg-spec := sym-for-keyword-arg * block-arg-spec := sym-for-block-arg * * num-of-leading-mandatory-args := DIGIT ; The number of leading mandatory * ; arguments * num-of-optional-args := DIGIT ; The number of optional arguments * sym-for-variable-length-args := "*" ; Indicates that variable length * ; arguments are captured as a ruby * ; array * num-of-trailing-mandatory-args := DIGIT ; The number of trailing mandatory * ; arguments * sym-for-keyword-arg := ":" ; Indicates that keyword argument * ; captured as a hash. * ; If keyword arguments are not * ; provided, returns nil. * sym-for-block-arg := "&" ; Indicates that an iterator block * ; should be captured if given * ``` * * For example, "12" means that the method requires at least one argument, and * at most receives three (1+2) arguments. So, the format string must be * followed by three variable references, which are to be assigned to captured * arguments. For omitted arguments, variables are set to ::RUBY_Qnil. `NULL` * can be put in place of a variable reference, which means the corresponding * captured argument(s) should be just dropped. * * The number of given arguments, excluding an option hash or iterator block, * is returned. * * @param[in] argc Length of `argv`. * @param[in] argv Pointer to the arguments to parse. * @param[in] fmt Format, in the language described above. * @param[out] ... Variables to fill in. * @exception rb_eFatal Malformed `fmt`. * @exception rb_eArgError Arity mismatch. * @return Actually parsed number of given arguments. * @post Each values passed to `argv` is filled into the variadic * arguments, according to the format. */ int rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...); RBIMPL_ATTR_NONNULL((3, 4)) /** * Identical to rb_scan_args(), except it also accepts `kw_splat`. * * @param[in] kw_splat How to understand the keyword arguments. * - RB_SCAN_ARGS_PASS_CALLED_KEYWORDS: Same behaviour as rb_scan_args(). * - RB_SCAN_ARGS_KEYWORDS: The final argument is a kwarg. * - RB_SCAN_ARGS_LAST_HASH_KEYWORDS: The final argument is a kwarg, iff it * is a hash. * @param[in] argc Length of `argv`. * @param[in] argv Pointer to the arguments to parse. * @param[in] fmt Format, in the language described above. * @param[out] ... Variables to fill in. * @exception rb_eFatal Malformed `fmt`. * @exception rb_eArgError Arity mismatch. * @return Actually parsed number of given arguments. * @post Each values passed to `argv` is filled into the variadic * arguments, according to the format. */ int rb_scan_args_kw(int kw_splat, int argc, const VALUE *argv, const char *fmt, ...); RBIMPL_ATTR_ERROR(("bad scan arg format")) /** * @private * * This is an implementation detail of rb_scan_args(). People don't use it * directly. */ void rb_scan_args_bad_format(const char*); RBIMPL_ATTR_ERROR(("variable argument length doesn't match")) /** * @private * * This is an implementation detail of rb_scan_args(). People don't use it * directly. */ void rb_scan_args_length_mismatch(const char*,int); RBIMPL_SYMBOL_EXPORT_END() /** @cond INTERNAL_MACRO */ /* If we could use constexpr the following macros could be inline functions * ... but sadly we cannot. */ #define rb_scan_args_isdigit(c) (RBIMPL_CAST((unsigned char)((c)-'0'))<10) #define rb_scan_args_count_end(fmt, ofs, vari) \ ((fmt)[ofs] ? -1 : (vari)) #define rb_scan_args_count_block(fmt, ofs, vari) \ ((fmt)[ofs]!='&' ? \ rb_scan_args_count_end(fmt, ofs, vari) : \ rb_scan_args_count_end(fmt, (ofs)+1, (vari)+1)) #define rb_scan_args_count_hash(fmt, ofs, vari) \ ((fmt)[ofs]!=':' ? \ rb_scan_args_count_block(fmt, ofs, vari) : \ rb_scan_args_count_block(fmt, (ofs)+1, (vari)+1)) #define rb_scan_args_count_trail(fmt, ofs, vari) \ (!rb_scan_args_isdigit((fmt)[ofs]) ? \ rb_scan_args_count_hash(fmt, ofs, vari) : \ rb_scan_args_count_hash(fmt, (ofs)+1, (vari)+((fmt)[ofs]-'0'))) #define rb_scan_args_count_var(fmt, ofs, vari) \ ((fmt)[ofs]!='*' ? \ rb_scan_args_count_trail(fmt, ofs, vari) : \ rb_scan_args_count_trail(fmt, (ofs)+1, (vari)+1)) #define rb_scan_args_count_opt(fmt, ofs, vari) \ (!rb_scan_args_isdigit((fmt)[ofs]) ? \ rb_scan_args_count_var(fmt, ofs, vari) : \ rb_scan_args_count_var(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0')) #define rb_scan_args_count_lead(fmt, ofs, vari) \ (!rb_scan_args_isdigit((fmt)[ofs]) ? \ rb_scan_args_count_var(fmt, ofs, vari) : \ rb_scan_args_count_opt(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0')) #define rb_scan_args_count(fmt) rb_scan_args_count_lead(fmt, 0, 0) #if RBIMPL_HAS_ATTRIBUTE(diagnose_if) # /* Assertions done in the attribute. */ # define rb_scan_args_verify(fmt, varc) RBIMPL_ASSERT_NOTHING #else # /* At one sight it _seems_ the expressions below could be written using # * static assertions. The reality is no, they don't. Because fmt is a # * string literal, any operations against fmt cannot produce the "integer # * constant expression"s, as defined in ISO/IEC 9899:2018 section 6.6 # * paragraph #6. Static assertions need such integer constant expressions as # * defined in ISO/IEC 9899:2018 section 6.7.10 paragraph #3. # * # * GCC nonetheless constant-folds this into a no-op, though. */ # define rb_scan_args_verify(fmt, varc) \ (sizeof(char[1-2*(rb_scan_args_count(fmt)<0)])!=1 ? \ rb_scan_args_bad_format(fmt) : \ sizeof(char[1-2*(rb_scan_args_count(fmt)!=(varc))])!=1 ? \ rb_scan_args_length_mismatch(fmt, varc) : \ RBIMPL_ASSERT_NOTHING) #endif static inline bool rb_scan_args_keyword_p(int kw_flag, VALUE last) { switch (kw_flag) { case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS: return !! rb_keyword_given_p(); case RB_SCAN_ARGS_KEYWORDS: return true; case RB_SCAN_ARGS_LAST_HASH_KEYWORDS: return RB_TYPE_P(last, T_HASH); default: return false; } } RBIMPL_ATTR_FORCEINLINE() static bool rb_scan_args_lead_p(const char *fmt) { return rb_scan_args_isdigit(fmt[0]); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_n_lead(const char *fmt) { return (rb_scan_args_lead_p(fmt) ? fmt[0]-'0' : 0); } RBIMPL_ATTR_FORCEINLINE() static bool rb_scan_args_opt_p(const char *fmt) { return (rb_scan_args_lead_p(fmt) && rb_scan_args_isdigit(fmt[1])); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_n_opt(const char *fmt) { return (rb_scan_args_opt_p(fmt) ? fmt[1]-'0' : 0); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_var_idx(const char *fmt) { return (!rb_scan_args_lead_p(fmt) ? 0 : !rb_scan_args_isdigit(fmt[1]) ? 1 : 2); } RBIMPL_ATTR_FORCEINLINE() static bool rb_scan_args_f_var(const char *fmt) { return (fmt[rb_scan_args_var_idx(fmt)]=='*'); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_trail_idx(const char *fmt) { const int idx = rb_scan_args_var_idx(fmt); return idx+(fmt[idx]=='*'); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_n_trail(const char *fmt) { const int idx = rb_scan_args_trail_idx(fmt); return (rb_scan_args_isdigit(fmt[idx]) ? fmt[idx]-'0' : 0); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_hash_idx(const char *fmt) { const int idx = rb_scan_args_trail_idx(fmt); return idx+rb_scan_args_isdigit(fmt[idx]); } RBIMPL_ATTR_FORCEINLINE() static bool rb_scan_args_f_hash(const char *fmt) { return (fmt[rb_scan_args_hash_idx(fmt)]==':'); } RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_block_idx(const char *fmt) { const int idx = rb_scan_args_hash_idx(fmt); return idx+(fmt[idx]==':'); } RBIMPL_ATTR_FORCEINLINE() static bool rb_scan_args_f_block(const char *fmt) { return (fmt[rb_scan_args_block_idx(fmt)]=='&'); } # if 0 RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_end_idx(const char *fmt) { const int idx = rb_scan_args_block_idx(fmt); return idx+(fmt[idx]=='&'); } # endif /* NOTE: Use `char *fmt` instead of `const char *fmt` because of clang's bug*/ /* https://bugs.llvm.org/show_bug.cgi?id=38095 */ # define rb_scan_args0(argc, argv, fmt, varc, vars) \ rb_scan_args_set(RB_SCAN_ARGS_PASS_CALLED_KEYWORDS, argc, argv, \ rb_scan_args_n_lead(fmt), \ rb_scan_args_n_opt(fmt), \ rb_scan_args_n_trail(fmt), \ rb_scan_args_f_var(fmt), \ rb_scan_args_f_hash(fmt), \ rb_scan_args_f_block(fmt), \ (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc) # define rb_scan_args_kw0(kw_flag, argc, argv, fmt, varc, vars) \ rb_scan_args_set(kw_flag, argc, argv, \ rb_scan_args_n_lead(fmt), \ rb_scan_args_n_opt(fmt), \ rb_scan_args_n_trail(fmt), \ rb_scan_args_f_var(fmt), \ rb_scan_args_f_hash(fmt), \ rb_scan_args_f_block(fmt), \ (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc) RBIMPL_ATTR_FORCEINLINE() static int rb_scan_args_set(int kw_flag, int argc, const VALUE *argv, int n_lead, int n_opt, int n_trail, bool f_var, bool f_hash, bool f_block, VALUE *vars[], RB_UNUSED_VAR(const char *fmt), RB_UNUSED_VAR(int varc)) RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) < 0, "bad scan arg format", "error") RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) != varc, "variable argument length doesn't match", "error") { int i, argi = 0, vari = 0; VALUE *var, hash = Qnil; #define rb_scan_args_next_param() vars[vari++] const int n_mand = n_lead + n_trail; /* capture an option hash - phase 1: pop from the argv */ if (f_hash && argc > 0) { VALUE last = argv[argc - 1]; if (rb_scan_args_keyword_p(kw_flag, last)) { hash = rb_hash_dup(last); argc--; } } if (argc < n_mand) { goto argc_error; } /* capture leading mandatory arguments */ for (i = 0; i < n_lead; i++) { var = rb_scan_args_next_param(); if (var) *var = argv[argi]; argi++; } /* capture optional arguments */ for (i = 0; i < n_opt; i++) { var = rb_scan_args_next_param(); if (argi < argc - n_trail) { if (var) *var = argv[argi]; argi++; } else { if (var) *var = Qnil; } } /* capture variable length arguments */ if (f_var) { int n_var = argc - argi - n_trail; var = rb_scan_args_next_param(); if (0 < n_var) { if (var) *var = rb_ary_new_from_values(n_var, &argv[argi]); argi += n_var; } else { if (var) *var = rb_ary_new(); } } /* capture trailing mandatory arguments */ for (i = 0; i < n_trail; i++) { var = rb_scan_args_next_param(); if (var) *var = argv[argi]; argi++; } /* capture an option hash - phase 2: assignment */ if (f_hash) { var = rb_scan_args_next_param(); if (var) *var = hash; } /* capture iterator block */ if (f_block) { var = rb_scan_args_next_param(); if (rb_block_given_p()) { *var = rb_block_proc(); } else { *var = Qnil; } } if (argi == argc) { return argc; } argc_error: rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt); UNREACHABLE_RETURN(-1); #undef rb_scan_args_next_param } /** @endcond */ #if defined(__DOXYGEN__) # /* don't bother */ #elif ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) # /* skip */ #elif ! defined(HAVE_VA_ARGS_MACRO) # /* skip */ #elif ! defined(__OPTIMIZE__) # /* skip */ #elif defined(HAVE___VA_OPT__) # define rb_scan_args(argc, argvp, fmt, ...) \ __builtin_choose_expr( \ __builtin_constant_p(fmt), \ rb_scan_args0( \ argc, argvp, fmt, \ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \ ((VALUE*[]){__VA_ARGS__})), \ (rb_scan_args)(argc, argvp, fmt __VA_OPT__(, __VA_ARGS__))) # define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \ __builtin_choose_expr( \ __builtin_constant_p(fmt), \ rb_scan_args_kw0( \ kw_flag, argc, argvp, fmt, \ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \ ((VALUE*[]){__VA_ARGS__})), \ (rb_scan_args_kw)(kw_flag, argc, argvp, fmt __VA_OPT__(, __VA_ARGS__))) #elif defined(__STRICT_ANSI__) # /* skip */ #elif defined(__GNUC__) # define rb_scan_args(argc, argvp, fmt, ...) \ __builtin_choose_expr( \ __builtin_constant_p(fmt), \ rb_scan_args0( \ argc, argvp, fmt, \ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \ ((VALUE*[]){__VA_ARGS__})), \ (rb_scan_args)(argc, argvp, fmt, __VA_ARGS__)) # define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \ __builtin_choose_expr( \ __builtin_constant_p(fmt), \ rb_scan_args_kw0( \ kw_flag, argc, argvp, fmt, \ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \ ((VALUE*[]){__VA_ARGS__})), \ (rb_scan_args_kw)(kw_flag, argc, argvp, fmt, __VA_ARGS__ /**/)) #endif #endif /* RBIMPL_SCAN_ARGS_H */