Ruby  3.4.0dev (2024-12-06 revision 892c46283a5ea4179500d951c9d4866c0051f27b)
scan_args.h
Go to the documentation of this file.
1 #ifndef RBIMPL_SCAN_ARGS_H /*-*-C++-*-vi:se ft=cpp:*/
2 #define RBIMPL_SCAN_ARGS_H
26 #include "ruby/assert.h"
32 #include "ruby/internal/config.h"
35 #include "ruby/internal/intern/array.h" /* rb_ary_new_from_values */
36 #include "ruby/internal/intern/error.h" /* rb_error_arity */
37 #include "ruby/internal/intern/hash.h" /* rb_hash_dup */
38 #include "ruby/internal/intern/proc.h" /* rb_block_proc */
39 #include "ruby/internal/iterator.h" /* rb_block_given_p / rb_keyword_given_p */
41 #include "ruby/internal/stdbool.h"
42 #include "ruby/internal/value.h"
43 
50 #define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0
51 
53 #define RB_SCAN_ARGS_KEYWORDS 1
54 
59 #define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3
60 
69 #define RB_NO_KEYWORDS 0
70 
72 #define RB_PASS_KEYWORDS 1
73 
78 #define RB_PASS_CALLED_KEYWORDS !!rb_keyword_given_p()
79 
89 #define HAVE_RB_SCAN_ARGS_OPTIONAL_HASH 1
90 
92 RBIMPL_ATTR_NONNULL((2, 3))
147 int rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...);
148 
149 RBIMPL_ATTR_NONNULL((3, 4))
168 int rb_scan_args_kw(int kw_splat, int argc, const VALUE *argv, const char *fmt, ...);
169 
170 RBIMPL_ATTR_ERROR(("bad scan arg format"))
177 void rb_scan_args_bad_format(const char*);
178 
179 RBIMPL_ATTR_ERROR(("variable argument length doesn't match"))
186 void rb_scan_args_length_mismatch(const char*,int);
187 
189 
192 /* If we could use constexpr the following macros could be inline functions
193  * ... but sadly we cannot. */
194 
195 #define rb_scan_args_isdigit(c) (RBIMPL_CAST((unsigned char)((c)-'0'))<10)
196 
197 #define rb_scan_args_count_end(fmt, ofs, vari) \
198  ((fmt)[ofs] ? -1 : (vari))
199 
200 #define rb_scan_args_count_block(fmt, ofs, vari) \
201  ((fmt)[ofs]!='&' ? \
202  rb_scan_args_count_end(fmt, ofs, vari) : \
203  rb_scan_args_count_end(fmt, (ofs)+1, (vari)+1))
204 
205 #define rb_scan_args_count_hash(fmt, ofs, vari) \
206  ((fmt)[ofs]!=':' ? \
207  rb_scan_args_count_block(fmt, ofs, vari) : \
208  rb_scan_args_count_block(fmt, (ofs)+1, (vari)+1))
209 
210 #define rb_scan_args_count_trail(fmt, ofs, vari) \
211  (!rb_scan_args_isdigit((fmt)[ofs]) ? \
212  rb_scan_args_count_hash(fmt, ofs, vari) : \
213  rb_scan_args_count_hash(fmt, (ofs)+1, (vari)+((fmt)[ofs]-'0')))
214 
215 #define rb_scan_args_count_var(fmt, ofs, vari) \
216  ((fmt)[ofs]!='*' ? \
217  rb_scan_args_count_trail(fmt, ofs, vari) : \
218  rb_scan_args_count_trail(fmt, (ofs)+1, (vari)+1))
219 
220 #define rb_scan_args_count_opt(fmt, ofs, vari) \
221  (!rb_scan_args_isdigit((fmt)[ofs]) ? \
222  rb_scan_args_count_var(fmt, ofs, vari) : \
223  rb_scan_args_count_var(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0'))
224 
225 #define rb_scan_args_count_lead(fmt, ofs, vari) \
226  (!rb_scan_args_isdigit((fmt)[ofs]) ? \
227  rb_scan_args_count_var(fmt, ofs, vari) : \
228  rb_scan_args_count_opt(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0'))
229 
230 #define rb_scan_args_count(fmt) rb_scan_args_count_lead(fmt, 0, 0)
231 
232 #if RBIMPL_HAS_ATTRIBUTE(diagnose_if)
233 # /* Assertions done in the attribute. */
234 # define rb_scan_args_verify(fmt, varc) RBIMPL_ASSERT_NOTHING
235 #else
236 # /* At one sight it _seems_ the expressions below could be written using
237 # * static assertions. The reality is no, they don't. Because fmt is a
238 # * string literal, any operations against fmt cannot produce the "integer
239 # * constant expression"s, as defined in ISO/IEC 9899:2018 section 6.6
240 # * paragraph #6. Static assertions need such integer constant expressions as
241 # * defined in ISO/IEC 9899:2018 section 6.7.10 paragraph #3.
242 # *
243 # * GCC nonetheless constant-folds this into a no-op, though. */
244 # define rb_scan_args_verify(fmt, varc) \
245  (sizeof(char[1-2*(rb_scan_args_count(fmt)<0)])!=1 ? \
246  rb_scan_args_bad_format(fmt) : \
247  sizeof(char[1-2*(rb_scan_args_count(fmt)!=(varc))])!=1 ? \
248  rb_scan_args_length_mismatch(fmt, varc) : \
249  RBIMPL_ASSERT_NOTHING)
250 #endif
251 
252 static inline bool
253 rb_scan_args_keyword_p(int kw_flag, VALUE last)
254 {
255  switch (kw_flag) {
257  return !! rb_keyword_given_p();
259  return true;
261  return RB_TYPE_P(last, T_HASH);
262  default:
263  return false;
264  }
265 }
266 
268 static bool
269 rb_scan_args_lead_p(const char *fmt)
270 {
271  return rb_scan_args_isdigit(fmt[0]);
272 }
273 
275 static int
276 rb_scan_args_n_lead(const char *fmt)
277 {
278  return (rb_scan_args_lead_p(fmt) ? fmt[0]-'0' : 0);
279 }
280 
282 static bool
283 rb_scan_args_opt_p(const char *fmt)
284 {
285  return (rb_scan_args_lead_p(fmt) && rb_scan_args_isdigit(fmt[1]));
286 }
287 
289 static int
290 rb_scan_args_n_opt(const char *fmt)
291 {
292  return (rb_scan_args_opt_p(fmt) ? fmt[1]-'0' : 0);
293 }
294 
296 static int
297 rb_scan_args_var_idx(const char *fmt)
298 {
299  return (!rb_scan_args_lead_p(fmt) ? 0 : !rb_scan_args_isdigit(fmt[1]) ? 1 : 2);
300 }
301 
303 static bool
304 rb_scan_args_f_var(const char *fmt)
305 {
306  return (fmt[rb_scan_args_var_idx(fmt)]=='*');
307 }
308 
310 static int
311 rb_scan_args_trail_idx(const char *fmt)
312 {
313  const int idx = rb_scan_args_var_idx(fmt);
314  return idx+(fmt[idx]=='*');
315 }
316 
318 static int
319 rb_scan_args_n_trail(const char *fmt)
320 {
321  const int idx = rb_scan_args_trail_idx(fmt);
322  return (rb_scan_args_isdigit(fmt[idx]) ? fmt[idx]-'0' : 0);
323 }
324 
326 static int
327 rb_scan_args_hash_idx(const char *fmt)
328 {
329  const int idx = rb_scan_args_trail_idx(fmt);
330  return idx+rb_scan_args_isdigit(fmt[idx]);
331 }
332 
334 static bool
335 rb_scan_args_f_hash(const char *fmt)
336 {
337  return (fmt[rb_scan_args_hash_idx(fmt)]==':');
338 }
339 
341 static int
342 rb_scan_args_block_idx(const char *fmt)
343 {
344  const int idx = rb_scan_args_hash_idx(fmt);
345  return idx+(fmt[idx]==':');
346 }
347 
349 static bool
350 rb_scan_args_f_block(const char *fmt)
351 {
352  return (fmt[rb_scan_args_block_idx(fmt)]=='&');
353 }
354 
355 # if 0
357 static int
358 rb_scan_args_end_idx(const char *fmt)
359 {
360  const int idx = rb_scan_args_block_idx(fmt);
361  return idx+(fmt[idx]=='&');
362 }
363 # endif
364 
365 /* NOTE: Use `char *fmt` instead of `const char *fmt` because of clang's bug*/
366 /* https://bugs.llvm.org/show_bug.cgi?id=38095 */
367 # define rb_scan_args0(argc, argv, fmt, varc, vars) \
368  rb_scan_args_set(RB_SCAN_ARGS_PASS_CALLED_KEYWORDS, argc, argv, \
369  rb_scan_args_n_lead(fmt), \
370  rb_scan_args_n_opt(fmt), \
371  rb_scan_args_n_trail(fmt), \
372  rb_scan_args_f_var(fmt), \
373  rb_scan_args_f_hash(fmt), \
374  rb_scan_args_f_block(fmt), \
375  (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
376 # define rb_scan_args_kw0(kw_flag, argc, argv, fmt, varc, vars) \
377  rb_scan_args_set(kw_flag, argc, argv, \
378  rb_scan_args_n_lead(fmt), \
379  rb_scan_args_n_opt(fmt), \
380  rb_scan_args_n_trail(fmt), \
381  rb_scan_args_f_var(fmt), \
382  rb_scan_args_f_hash(fmt), \
383  rb_scan_args_f_block(fmt), \
384  (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
385 
387 static int
388 rb_scan_args_set(int kw_flag, int argc, const VALUE *argv,
389  int n_lead, int n_opt, int n_trail,
390  bool f_var, bool f_hash, bool f_block,
391  VALUE *vars[], RB_UNUSED_VAR(const char *fmt), RB_UNUSED_VAR(int varc))
392  RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) < 0, "bad scan arg format", "error")
393  RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) != varc, "variable argument length doesn't match", "error")
394 {
395  int i, argi = 0, vari = 0;
396  VALUE *var, hash = Qnil;
397 #define rb_scan_args_next_param() vars[vari++]
398  const int n_mand = n_lead + n_trail;
399 
400  /* capture an option hash - phase 1: pop from the argv */
401  if (f_hash && argc > 0) {
402  VALUE last = argv[argc - 1];
403  if (rb_scan_args_keyword_p(kw_flag, last)) {
404  hash = rb_hash_dup(last);
405  argc--;
406  }
407  }
408 
409  if (argc < n_mand) {
410  goto argc_error;
411  }
412 
413  /* capture leading mandatory arguments */
414  for (i = 0; i < n_lead; i++) {
415  var = rb_scan_args_next_param();
416  if (var) *var = argv[argi];
417  argi++;
418  }
419 
420  /* capture optional arguments */
421  for (i = 0; i < n_opt; i++) {
422  var = rb_scan_args_next_param();
423  if (argi < argc - n_trail) {
424  if (var) *var = argv[argi];
425  argi++;
426  }
427  else {
428  if (var) *var = Qnil;
429  }
430  }
431 
432  /* capture variable length arguments */
433  if (f_var) {
434  int n_var = argc - argi - n_trail;
435 
436  var = rb_scan_args_next_param();
437  if (0 < n_var) {
438  if (var) *var = rb_ary_new_from_values(n_var, &argv[argi]);
439  argi += n_var;
440  }
441  else {
442  if (var) *var = rb_ary_new();
443  }
444  }
445 
446  /* capture trailing mandatory arguments */
447  for (i = 0; i < n_trail; i++) {
448  var = rb_scan_args_next_param();
449  if (var) *var = argv[argi];
450  argi++;
451  }
452 
453  /* capture an option hash - phase 2: assignment */
454  if (f_hash) {
455  var = rb_scan_args_next_param();
456  if (var) *var = hash;
457  }
458 
459  /* capture iterator block */
460  if (f_block) {
461  var = rb_scan_args_next_param();
462  if (rb_block_given_p()) {
463  *var = rb_block_proc();
464  }
465  else {
466  *var = Qnil;
467  }
468  }
469 
470  if (argi == argc) {
471  return argc;
472  }
473 
474  argc_error:
475  rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt);
476  UNREACHABLE_RETURN(-1);
477 #undef rb_scan_args_next_param
478 }
479 
482 #if defined(__DOXYGEN__)
483 # /* don't bother */
484 
485 #elif ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
486 # /* skip */
487 
488 #elif ! defined(HAVE_VA_ARGS_MACRO)
489 # /* skip */
490 
491 #elif ! defined(__OPTIMIZE__)
492 # /* skip */
493 
494 #elif defined(HAVE___VA_OPT__)
495 # define rb_scan_args(argc, argvp, fmt, ...) \
496  __builtin_choose_expr( \
497  __builtin_constant_p(fmt), \
498  rb_scan_args0( \
499  argc, argvp, fmt, \
500  (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
501  ((VALUE*[]){__VA_ARGS__})), \
502  (rb_scan_args)(argc, argvp, fmt __VA_OPT__(, __VA_ARGS__)))
503 # define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \
504  __builtin_choose_expr( \
505  __builtin_constant_p(fmt), \
506  rb_scan_args_kw0( \
507  kw_flag, argc, argvp, fmt, \
508  (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
509  ((VALUE*[]){__VA_ARGS__})), \
510  (rb_scan_args_kw)(kw_flag, argc, argvp, fmt __VA_OPT__(, __VA_ARGS__)))
511 
512 #elif defined(__STRICT_ANSI__)
513 # /* skip */
514 
515 #elif defined(__GNUC__)
516 # define rb_scan_args(argc, argvp, fmt, ...) \
517  __builtin_choose_expr( \
518  __builtin_constant_p(fmt), \
519  rb_scan_args0( \
520  argc, argvp, fmt, \
521  (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
522  ((VALUE*[]){__VA_ARGS__})), \
523  (rb_scan_args)(argc, argvp, fmt, __VA_ARGS__))
524 # define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \
525  __builtin_choose_expr( \
526  __builtin_constant_p(fmt), \
527  rb_scan_args_kw0( \
528  kw_flag, argc, argvp, fmt, \
529  (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
530  ((VALUE*[]){__VA_ARGS__})), \
531  (rb_scan_args_kw)(kw_flag, argc, argvp, fmt, __VA_ARGS__ ))
532 #endif
533 
534 #endif /* RBIMPL_SCAN_ARGS_H */
Defines RBIMPL_HAS_ATTRIBUTE.
Defines RBIMPL_ATTR_DIAGNOSE_IF.
#define RBIMPL_ATTR_DIAGNOSE_IF(_, __, ___)
Wraps (or simulates) __attribute__((diagnose_if))
Definition: diagnose_if.h:32
Tweaking visibility of C variables/functions.
#define RBIMPL_SYMBOL_EXPORT_END()
Counterpart of RBIMPL_SYMBOL_EXPORT_BEGIN.
Definition: dllexport.h:74
#define RBIMPL_SYMBOL_EXPORT_BEGIN()
Shortcut macro equivalent to RUBY_SYMBOL_EXPORT_BEGIN extern "C" {.
Definition: dllexport.h:65
Defines RBIMPL_ATTR_FORCEINLINE.
#define RBIMPL_ATTR_FORCEINLINE()
Wraps (or simulates) __forceinline.
Definition: forceinline.h:35
int rb_scan_args_kw(int kw_splat, int argc, const VALUE *argv, const char *fmt,...)
Identical to rb_scan_args(), except it also accepts kw_splat.
Definition: class.c:2648
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition: class.c:2635
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
Definition: eval.c:929
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:916
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition: assume.h:29
#define T_HASH
Old name of RUBY_T_HASH.
Definition: value_type.h:65
#define Qnil
Old name of RUBY_Qnil.
Defines RBIMPL_ATTR_ERROR.
#define RBIMPL_ATTR_ERROR(msg)
Wraps (or simulates) __attribute__((error))
Definition: error.h:27
Public APIs related to rb_cArray.
VALUE rb_ary_new_from_values(long n, const VALUE *elts)
Identical to rb_ary_new_from_args(), except how objects are passed.
Definition: array.c:780
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition: array.c:741
Public APIs related to rb_eException.
#define UNLIMITED_ARGUMENTS
This macro is used in conjunction with rb_check_arity().
Definition: error.h:35
Public APIs related to rb_cHash.
VALUE rb_hash_dup(VALUE hash)
Duplicates a hash.
Definition: hash.c:1563
Public APIs related to rb_cProc.
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition: proc.c:813
Defines RBIMPL_STATIC_ASSERT.
Block related APIs.
Defines RBIMPL_ATTR_NONNULL.
#define RBIMPL_ATTR_NONNULL(list)
Wraps (or simulates) __attribute__((nonnull))
Definition: nonnull.h:27
Defines RBIMPL_ATTR_NORETURN.
#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
Same behaviour as rb_scan_args().
Definition: scan_args.h:50
#define RB_SCAN_ARGS_KEYWORDS
The final argument should be a hash treated as keywords.
Definition: scan_args.h:53
#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS
Treat a final argument as keywords if it is a hash, and not as keywords otherwise.
Definition: scan_args.h:59
C99 shim for <stdbool.h>
Defines VALUE and ID.
uintptr_t VALUE
Type that represents a Ruby object.
Definition: value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition: value_type.h:376