Ruby: include/ruby/internal/scan_args.h Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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/internal/attr/diagnose_if.h"
27#include "ruby/internal/attr/error.h"
28#include "ruby/internal/attr/forceinline.h"
29#include "ruby/internal/attr/noreturn.h"
30#include "ruby/internal/config.h"
31#include "ruby/internal/dllexport.h"
32#include "ruby/internal/has/attribute.h"
33#include "ruby/internal/intern/array.h" /* rb_ary_new_from_values */
34#include "ruby/internal/intern/error.h" /* rb_error_arity */
35#include "ruby/internal/intern/hash.h" /* rb_hash_dup */
36#include "ruby/internal/intern/proc.h" /* rb_block_proc */
37#include "ruby/internal/iterator.h" /* rb_block_given_p / rb_keyword_given_p */
38#include "ruby/internal/static_assert.h"
39#include "ruby/internal/stdbool.h"
40#include "ruby/internal/value.h"
41#include "ruby/assert.h"
42
43#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0
44#define RB_SCAN_ARGS_KEYWORDS 1
45#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3
46#define RB_NO_KEYWORDS 0
47#define RB_PASS_KEYWORDS 1
48#define RB_PASS_CALLED_KEYWORDS rb_keyword_given_p()
49/* rb_scan_args() format allows ':' for optional hash */
50#define HAVE_RB_SCAN_ARGS_OPTIONAL_HASH 1
51
52 RBIMPL_SYMBOL_EXPORT_BEGIN()
53 int rb_scan_args(int, const VALUE*, const char*, ...);
54int rb_scan_args_kw(int, int, const VALUE*, const char*, ...);
55
56 RBIMPL_ATTR_ERROR(("bad scan arg format"))
57 void rb_scan_args_bad_format(const char*);
58
59 RBIMPL_ATTR_ERROR(("variable argument length doesn't match"))
60 void rb_scan_args_length_mismatch(const char*,int);
61
62 RBIMPL_SYMBOL_EXPORT_END()
63
64/* If we could use constexpr the following macros could be inline functions
65 * ... but sadly we cannot. */
66
67#define rb_scan_args_isdigit(c) (RBIMPL_CAST((unsigned char)((c)-'0'))<10)
68
69#define rb_scan_args_count_end(fmt, ofs, vari) \
70 ((fmt)[ofs] ? -1 : (vari))
71
72#define rb_scan_args_count_block(fmt, ofs, vari) \
73 ((fmt)[ofs]!='&' ? \
74 rb_scan_args_count_end(fmt, ofs, vari) : \
75 rb_scan_args_count_end(fmt, (ofs)+1, (vari)+1))
76
77#define rb_scan_args_count_hash(fmt, ofs, vari) \
78 ((fmt)[ofs]!=':' ? \
79 rb_scan_args_count_block(fmt, ofs, vari) : \
80 rb_scan_args_count_block(fmt, (ofs)+1, (vari)+1))
81
82#define rb_scan_args_count_trail(fmt, ofs, vari) \
83 (!rb_scan_args_isdigit((fmt)[ofs]) ? \
84 rb_scan_args_count_hash(fmt, ofs, vari) : \
85 rb_scan_args_count_hash(fmt, (ofs)+1, (vari)+((fmt)[ofs]-'0')))
86
87#define rb_scan_args_count_var(fmt, ofs, vari) \
88 ((fmt)[ofs]!='*' ? \
89 rb_scan_args_count_trail(fmt, ofs, vari) : \
90 rb_scan_args_count_trail(fmt, (ofs)+1, (vari)+1))
91
92#define rb_scan_args_count_opt(fmt, ofs, vari) \
93 (!rb_scan_args_isdigit((fmt)[ofs]) ? \
94 rb_scan_args_count_var(fmt, ofs, vari) : \
95 rb_scan_args_count_var(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0'))
96
97#define rb_scan_args_count_lead(fmt, ofs, vari) \
98 (!rb_scan_args_isdigit((fmt)[ofs]) ? \
99 rb_scan_args_count_var(fmt, ofs, vari) : \
100 rb_scan_args_count_opt(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0'))
101
102#define rb_scan_args_count(fmt) rb_scan_args_count_lead(fmt, 0, 0)
103
104#if RBIMPL_HAS_ATTRIBUTE(diagnose_if)
105# /* Assertions done in the attribute. */
106# define rb_scan_args_verify(fmt, varc) RBIMPL_ASSERT_NOTHING
107#else
108# /* At one sight it _seems_ the expressions below could be written using
109# * static assrtions. The reality is no, they don't. Because fmt is a string
110# * literal, any operations against fmt cannot produce the "integer constant
111# * expression"s, as defined in ISO/IEC 9899:2018 section 6.6 paragraph #6.
112# * Static assertions need such integer constant expressions as defined in
113# * ISO/IEC 9899:2018 section 6.7.10 paragraph #3.
114# *
115# * GCC nonetheless constant-folds this into no-op, though. */
116# define rb_scan_args_verify(fmt, varc) \
117 (sizeof(char[1-2*(rb_scan_args_count(fmt)<0)])!=1 ? \
118 rb_scan_args_bad_format(fmt) : \
119 sizeof(char[1-2*(rb_scan_args_count(fmt)!=(varc))])!=1 ? \
120 rb_scan_args_length_mismatch(fmt, varc) : \
121 RBIMPL_ASSERT_NOTHING)
122#endif
123
124static inline bool
125rb_scan_args_keyword_p(int kw_flag, VALUE last)
126{
127 switch (kw_flag) {
128 case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS:
129 return !! rb_keyword_given_p();
130 case RB_SCAN_ARGS_KEYWORDS:
131 return true;
132 case RB_SCAN_ARGS_LAST_HASH_KEYWORDS:
133 return RB_TYPE_P(last, T_HASH);
134 default:
135 return false;
136 }
137}
138
139 RBIMPL_ATTR_FORCEINLINE()
140static bool
141rb_scan_args_lead_p(const char *fmt)
142{
143 return rb_scan_args_isdigit(fmt[0]);
144}
145
146 RBIMPL_ATTR_FORCEINLINE()
147static int
148rb_scan_args_n_lead(const char *fmt)
149{
150 return (rb_scan_args_lead_p(fmt) ? fmt[0]-'0' : 0);
151}
152
153 RBIMPL_ATTR_FORCEINLINE()
154static bool
155rb_scan_args_opt_p(const char *fmt)
156{
157 return (rb_scan_args_lead_p(fmt) && rb_scan_args_isdigit(fmt[1]));
158}
159
160 RBIMPL_ATTR_FORCEINLINE()
161static int
162rb_scan_args_n_opt(const char *fmt)
163{
164 return (rb_scan_args_opt_p(fmt) ? fmt[1]-'0' : 0);
165}
166
167 RBIMPL_ATTR_FORCEINLINE()
168static int
169rb_scan_args_var_idx(const char *fmt)
170{
171 return (!rb_scan_args_lead_p(fmt) ? 0 : !rb_scan_args_isdigit(fmt[1]) ? 1 : 2);
172}
173
174 RBIMPL_ATTR_FORCEINLINE()
175static bool
176rb_scan_args_f_var(const char *fmt)
177{
178 return (fmt[rb_scan_args_var_idx(fmt)]=='*');
179}
180
181 RBIMPL_ATTR_FORCEINLINE()
182static int
183rb_scan_args_trail_idx(const char *fmt)
184{
185 const int idx = rb_scan_args_var_idx(fmt);
186 return idx+(fmt[idx]=='*');
187}
188
189 RBIMPL_ATTR_FORCEINLINE()
190static int
191rb_scan_args_n_trail(const char *fmt)
192{
193 const int idx = rb_scan_args_trail_idx(fmt);
194 return (rb_scan_args_isdigit(fmt[idx]) ? fmt[idx]-'0' : 0);
195}
196
197 RBIMPL_ATTR_FORCEINLINE()
198static int
199rb_scan_args_hash_idx(const char *fmt)
200{
201 const int idx = rb_scan_args_trail_idx(fmt);
202 return idx+rb_scan_args_isdigit(fmt[idx]);
203}
204
205 RBIMPL_ATTR_FORCEINLINE()
206static bool
207rb_scan_args_f_hash(const char *fmt)
208{
209 return (fmt[rb_scan_args_hash_idx(fmt)]==':');
210}
211
212 RBIMPL_ATTR_FORCEINLINE()
213static int
214rb_scan_args_block_idx(const char *fmt)
215{
216 const int idx = rb_scan_args_hash_idx(fmt);
217 return idx+(fmt[idx]==':');
218}
219
220 RBIMPL_ATTR_FORCEINLINE()
221static bool
222rb_scan_args_f_block(const char *fmt)
223{
224 return (fmt[rb_scan_args_block_idx(fmt)]=='&');
225}
226
227# if 0
228 RBIMPL_ATTR_FORCEINLINE()
229static int
230rb_scan_args_end_idx(const char *fmt)
231{
232 const int idx = rb_scan_args_block_idx(fmt);
233 return idx+(fmt[idx]=='&');
234}
235# endif
236
237/* NOTE: Use `char *fmt` instead of `const char *fmt` because of clang's bug*/
238/* https://bugs.llvm.org/show_bug.cgi?id=38095 */
239# define rb_scan_args0(argc, argv, fmt, varc, vars) \
240 rb_scan_args_set(RB_SCAN_ARGS_PASS_CALLED_KEYWORDS, argc, argv, \
241 rb_scan_args_n_lead(fmt), \
242 rb_scan_args_n_opt(fmt), \
243 rb_scan_args_n_trail(fmt), \
244 rb_scan_args_f_var(fmt), \
245 rb_scan_args_f_hash(fmt), \
246 rb_scan_args_f_block(fmt), \
247 (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
248# define rb_scan_args_kw0(kw_flag, argc, argv, fmt, varc, vars) \
249 rb_scan_args_set(kw_flag, argc, argv, \
250 rb_scan_args_n_lead(fmt), \
251 rb_scan_args_n_opt(fmt), \
252 rb_scan_args_n_trail(fmt), \
253 rb_scan_args_f_var(fmt), \
254 rb_scan_args_f_hash(fmt), \
255 rb_scan_args_f_block(fmt), \
256 (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
257
258 RBIMPL_ATTR_FORCEINLINE()
259static int
260rb_scan_args_set(int kw_flag, int argc, const VALUE *argv,
261 int n_lead, int n_opt, int n_trail,
262 bool f_var, bool f_hash, bool f_block,
263 VALUE *vars[], RB_UNUSED_VAR(const char *fmt), RB_UNUSED_VAR(int varc))
264 RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) < 0, "bad scan arg format", "error")
265 RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) != varc, "variable argument length doesn't match", "error")
266{
267 int i, argi = 0, vari = 0;
268 VALUE *var, hash = Qnil;
269#define rb_scan_args_next_param() vars[vari++]
270 const int n_mand = n_lead + n_trail;
271
272 /* capture an option hash - phase 1: pop from the argv */
273 if (f_hash && argc > 0) {
274 VALUE last = argv[argc - 1];
275 if (rb_scan_args_keyword_p(kw_flag, last)) {
276 hash = rb_hash_dup(last);
277 argc--;
278 }
279 }
280
281 if (argc < n_mand) {
282 goto argc_error;
283 }
284
285 /* capture leading mandatory arguments */
286 for (i = 0; i < n_lead; i++) {
287 var = rb_scan_args_next_param();
288 if (var) *var = argv[argi];
289 argi++;
290 }
291
292 /* capture optional arguments */
293 for (i = 0; i < n_opt; i++) {
294 var = rb_scan_args_next_param();
295 if (argi < argc - n_trail) {
296 if (var) *var = argv[argi];
297 argi++;
298 }
299 else {
300 if (var) *var = Qnil;
301 }
302 }
303
304 /* capture variable length arguments */
305 if (f_var) {
306 int n_var = argc - argi - n_trail;
307
308 var = rb_scan_args_next_param();
309 if (0 < n_var) {
310 if (var) *var = rb_ary_new_from_values(n_var, &argv[argi]);
311 argi += n_var;
312 }
313 else {
314 if (var) *var = rb_ary_new();
315 }
316 }
317
318 /* capture trailing mandatory arguments */
319 for (i = 0; i < n_trail; i++) {
320 var = rb_scan_args_next_param();
321 if (var) *var = argv[argi];
322 argi++;
323 }
324
325 /* capture an option hash - phase 2: assignment */
326 if (f_hash) {
327 var = rb_scan_args_next_param();
328 if (var) *var = hash;
329 }
330
331 /* capture iterator block */
332 if (f_block) {
333 var = rb_scan_args_next_param();
334 if (rb_block_given_p()) {
335 *var = rb_block_proc();
336 }
337 else {
338 *var = Qnil;
339 }
340 }
341
342 if (argi == argc) {
343 return argc;
344 }
345
346 argc_error:
347 rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt);
348 UNREACHABLE_RETURN(-1);
349#undef rb_scan_args_next_param
350}
351
352#if ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
353# /* skip */
354
355#elif ! defined(HAVE_VA_ARGS_MACRO)
356# /* skip */
357
358#elif ! defined(__OPTIMIZE__)
359# /* skip */
360
361#elif defined(HAVE___VA_OPT__)
362# define rb_scan_args(argc, argvp, fmt, ...) \
363 __builtin_choose_expr( \
364 __builtin_constant_p(fmt), \
365 rb_scan_args0( \
366 argc, argvp, fmt, \
367 (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
368 ((VALUE*[]){__VA_ARGS__})), \
369 (rb_scan_args)(argc, argvp, fmt __VA_OPT__(, __VA_ARGS__)))
370# define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \
371 __builtin_choose_expr( \
372 __builtin_constant_p(fmt), \
373 rb_scan_args_kw0( \
374 kw_flag, argc, argvp, fmt, \
375 (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
376 ((VALUE*[]){__VA_ARGS__})), \
377 (rb_scan_args_kw)(kw_flag, argc, argvp, fmt __VA_OPT__(, __VA_ARGS__)))
378
379#elif defined(__STRICT_ANSI__)
380# /* skip */
381
382#elif defined(__GNUC__)
383# define rb_scan_args(argc, argvp, fmt, ...) \
384 __builtin_choose_expr( \
385 __builtin_constant_p(fmt), \
386 rb_scan_args0( \
387 argc, argvp, fmt, \
388 (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
389 ((VALUE*[]){__VA_ARGS__})), \
390 (rb_scan_args)(argc, argvp, fmt, __VA_ARGS__))
391# define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \
392 __builtin_choose_expr( \
393 __builtin_constant_p(fmt), \
394 rb_scan_args_kw0( \
395 kw_flag, argc, argvp, fmt, \
396 (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
397 ((VALUE*[]){__VA_ARGS__})), \
398 (rb_scan_args_kw)(kw_flag, argc, argvp, fmt, __VA_ARGS__ ))
399#endif
400
401#endif /* RBIMPL_SCAN_ARGS_H */
rb_ary_new_from_values
VALUE rb_ary_new_from_values(long n, const VALUE *elts)
Definition: array.c:788
rb_ary_new
VALUE rb_ary_new(void)
Definition: array.c:749
assert.h
attribute.h
Defines RBIMPL_HAS_ATTRIBUTE.
RB_UNUSED_VAR
#define RB_UNUSED_VAR(x)
Definition: attributes.h:168
UNREACHABLE_RETURN
#define UNREACHABLE_RETURN
Definition: assume.h:31
diagnose_if.h
Defines RBIMPL_ATTR_DIAGNOSE_IF.
RBIMPL_ATTR_DIAGNOSE_IF
#define RBIMPL_ATTR_DIAGNOSE_IF(_, __, ___)
Wraps (or simulates) __attribute__((diagnose_if))
Definition: diagnose_if.h:39
dllexport.h
Tewaking visibility of C variables/functions.
RBIMPL_SYMBOL_EXPORT_END
#define RBIMPL_SYMBOL_EXPORT_END()
Counterpart of RBIMPL_SYMBOL_EXPORT_BEGIN.
Definition: dllexport.h:86
RBIMPL_SYMBOL_EXPORT_BEGIN
#define RBIMPL_SYMBOL_EXPORT_BEGIN()
Shortcut macro equivalent to RUBY_SYMBOL_EXPORT_BEGIN extern "C" {.
Definition: dllexport.h:77
rb_keyword_given_p
int rb_keyword_given_p(void)
Definition: eval.c:948
forceinline.h
Defines RBIMPL_ATTR_FORCEINLINE.
RBIMPL_ATTR_FORCEINLINE
#define RBIMPL_ATTR_FORCEINLINE()
Wraps (or simulates) __forceinline.
Definition: forceinline.h:37
rb_scan_args_kw
int rb_scan_args_kw(int, int, const VALUE *, const char *,...)
Definition: class.c:2309
rb_scan_args
int rb_scan_args(int, const VALUE *, const char *,...)
Definition: class.c:2296
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:935
match
unsigned char match[65280+2]
Definition: gun.c:165
rb_hash_dup
VALUE rb_hash_dup(VALUE hash)
Definition: hash.c:1579
error.h
Defines RBIMPL_ATTR_ERROR.
RBIMPL_ATTR_ERROR
#define RBIMPL_ATTR_ERROR(msg)
Wraps (or simulates) __attribute__((error))
Definition: error.h:29
config.h
Thin wrapper to ruby/config.h.
array.h
Public APIs related to rb_cArray.
error.h
Public APIs related to rb_eException.
UNLIMITED_ARGUMENTS
#define UNLIMITED_ARGUMENTS
Definition: error.h:29
rb_error_arity
void rb_error_arity(int, int, int)
Definition: vm_insnhelper.c:442
hash.h
Public APIs related to rb_cHash.
proc.h
Public APIs related to rb_cProc.
rb_block_proc
VALUE rb_block_proc(void)
Definition: proc.c:826
static_assert.h
Defines RBIMPL_STATIC_ASSERT.
int
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
iterator.h
Block related APIs.
last
unsigned int last
Definition: nkf.c:4324
noreturn.h
Defines RBIMPL_ATTR_NORETURN.
argc
int argc
Definition: ruby.c:240
argv
char ** argv
Definition: ruby.c:241
rb_scan_args_length_mismatch
void rb_scan_args_length_mismatch(const char *, int)
RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
Definition: scan_args.h:43
RB_SCAN_ARGS_KEYWORDS
#define RB_SCAN_ARGS_KEYWORDS
Definition: scan_args.h:44
RB_SCAN_ARGS_LAST_HASH_KEYWORDS
#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS
Definition: scan_args.h:45
rb_scan_args_bad_format
void rb_scan_args_bad_format(const char *)
rb_scan_args_count
#define rb_scan_args_count(fmt)
Definition: scan_args.h:102
rb_scan_args_isdigit
#define rb_scan_args_isdigit(c)
Definition: scan_args.h:67
rb_scan_args_next_param
#define rb_scan_args_next_param()
Qnil
#define Qnil
Definition: special_consts.h:51
stdbool.h
C99 shim for <stdbool.h>
const
#define const
Definition: strftime.c:108
t
#define t
Definition: symbol.c:253
error
void error(const char *msg)
Definition: untgz.c:593
value.h
Defines VALUE and ID.
VALUE
unsigned long VALUE
Definition: value.h:38
T_HASH
#define T_HASH
Definition: value_type.h:64