parseargs.c
1 
11 #include <kuroko/vm.h>
12 #include <kuroko/util.h>
13 
22 _cold
23 static void raise_TypeError(const char * method_name, const char * expected, KrkValue arg, const char * argName) {
24  krk_runtimeError(vm.exceptions->typeError,
25  "%s()%s%s expects %s, not '%T'",
26  method_name, (argName && *argName) ? " argument " : "", (argName && *argName) ? argName : "",
27  expected, arg);
28 }
29 
40 _cold
41 static const char * methodName(const char * method_name, const char * fmt) {
42  const char * maybeColon = strchr(fmt, ':');
43  return maybeColon ? maybeColon + 1 : method_name;
44 }
45 
46 /* Just to avoid repeating ourselves... */
47 #define _method_name (methodName(orig_method_name, fmt))
48 
62 static int extractKwArg(KrkTable * kwargs, KrkString * argName, KrkValue * out, KrkValueArray * refList) {
63  if (!krk_tableGet_fast(kwargs, argName, out)) return 1;
64  krk_writeValueArray(refList, *out);
65  krk_tableDeleteExact(kwargs, OBJECT_VAL(argName));
66  return 0;
67 }
68 
86  const char * orig_method_name,
87  int argc, const KrkValue argv[], int hasKw,
88  const char * fmt, const char ** names, va_list args) {
89  int iarg = 0;
90  int oarg = 0;
91  int required = 1;
92  int acceptextrakws = 0;
94  if (*fmt == '.') {
101  argv++;
102  argc--;
103  fmt++;
104  }
105 
106  /* Required args */
107  while (*fmt) {
108  if (*fmt == ':') break;
109  if (*fmt == '|') {
115  if (!required) {
116  krk_runtimeError(vm.exceptions->typeError, "format string has multiple |s");
117  return 1;
118  }
119  required = 0;
120  fmt++;
121  continue;
122  }
123  if (*fmt == '*') {
133  int * out_c = va_arg(args, int *);
134  const KrkValue ** out_v = va_arg(args, const KrkValue **);
135  *out_c = argc - iarg;
136  *out_v = &argv[iarg];
137  iarg = argc;
138  required = 0;
139  fmt++;
140  continue;
141  }
142  if (*fmt == '$') {
150  if (required) {
151  krk_runtimeError(vm.exceptions->typeError, "$ must be after | or * in format string");
152  return 1;
153  }
154  if (iarg < argc) break;
155  fmt++;
156  continue;
157  }
158  if (*fmt == '~') {
166  acceptextrakws = 1;
167  fmt++;
168  continue;
169  }
170 
171  KrkValue arg = KWARGS_VAL(0);
172 
173  if (iarg < argc) {
174  /* Positional arguments are pretty straightforward. */
175  arg = argv[iarg];
176  iarg++;
177  } else if ((required && !hasKw) || (hasKw && extractKwArg(AS_DICT(argv[argc]), krk_copyString(names[oarg],strlen(names[oarg])), &arg, AS_LIST(argv[argc+1])) && required)) {
178  /* If keyword argument lookup failed and this is not an optional argument, raise an exception. */
179  krk_runtimeError(vm.exceptions->typeError, "%s() missing required positional argument: '%s'",
180  _method_name, names[oarg]);
181  goto _error;
182  }
183 
184  char argtype = *fmt++;
185 
186  if (*fmt == '?') {
187  /* "is present", useful for things where relying on a default isn't useful but you
188  * still want to have all the type checking and automatic parsing. */
189  fmt++;
190  int * out = va_arg(args, int*);
191  *out = !krk_valuesSame(arg, KWARGS_VAL(0));
192  }
193 
194  if (*fmt == '!') {
195  /* "of type", thrown an exception if the argument was present but was not
196  * an instance of a given class. Originally just for @c O and @c V but
197  * now available anywhere, though likely not useful for other types.
198  * Maybe if you want @c p to only be a bool this could be useful? */
199  fmt++;
200  KrkClass * type = va_arg(args, KrkClass*);
201  if (!krk_valuesSame(arg, KWARGS_VAL(0)) && !krk_isInstanceOf(arg, type)) {
202  raise_TypeError(_method_name, type ? type->name->chars : "unknown type", arg, names[oarg]);
203  goto _error;
204  }
205  }
206 
207  switch (argtype) {
217  case 'O': {
218  KrkObj ** out = va_arg(args, KrkObj**);
219  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
220  if (IS_NONE(arg)) {
221  *out = NULL;
222  } else if (!IS_OBJECT(arg)) {
223  raise_TypeError(_method_name, "heap object", arg, names[oarg]);
224  goto _error;
225  } else {
226  *out = AS_OBJECT(arg);
227  }
228  }
229  break;
230  }
231 
241  case 'V': {
242  KrkValue * out = va_arg(args, KrkValue*);
243  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
244  *out = arg;
245  }
246  break;
247  }
248 
256  case 'z': {
257  char ** out = va_arg(args, char **);
258  size_t * size = NULL;
259  if (*fmt == '#') {
260  fmt++;
261  size = va_arg(args, size_t*);
262  }
263  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
264  if (IS_NONE(arg)) {
265  *out = NULL;
266  if (size) *size = 0;
267  } else if (IS_STRING(arg)) {
268  *out = AS_CSTRING(arg);
269  if (size) *size = AS_STRING(arg)->length;
270  } else {
271  raise_TypeError(_method_name, "str or None", arg, names[oarg]);
272  goto _error;
273  }
274  }
275  break;
276  }
277 
281  case 's': {
282  char ** out = va_arg(args, char **);
283  size_t * size = NULL;
284  if (*fmt == '#') {
285  fmt++;
286  size = va_arg(args, size_t*);
287  }
288  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
289  if (IS_STRING(arg)) {
290  *out = AS_CSTRING(arg);
291  if (size) *size = AS_STRING(arg)->length;
292  } else {
293  raise_TypeError(_method_name, "str", arg, names[oarg]);
294  goto _error;
295  }
296  }
297  break;
298  }
299 
309 #define NUMERIC(c,type) case c: { type * out = va_arg(args, type*); if (!krk_valuesSame(arg, KWARGS_VAL(0))) { if (!krk_long_to_int(arg, sizeof(type), out)) goto _error; } break; }
310  NUMERIC('b',unsigned char)
311  NUMERIC('h',short)
312  NUMERIC('H',unsigned short)
313  NUMERIC('i',int)
314  NUMERIC('I',unsigned int)
315  NUMERIC('l',long)
316  NUMERIC('k',unsigned long)
317  NUMERIC('L',long long)
318  NUMERIC('K',unsigned long long)
319  NUMERIC('n',ssize_t)
320  NUMERIC('N',size_t)
321 
322 
326  case 'C': {
327  int * out = va_arg(args, int*);
328  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
329  if (!IS_STRING(arg) || AS_STRING(arg)->codesLength != 1) {
330  raise_TypeError(_method_name, "str of length 1", arg, names[oarg]);
331  goto _error;
332  }
333  *out = krk_unicodeCodepoint(AS_STRING(arg),0);
334  }
335  break;
336  }
337 
338 #ifndef KRK_NO_FLOAT
342  case 'f': {
343  float * out = va_arg(args, float*);
344  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
345  if (!IS_FLOATING(arg)) {
346  KrkClass * type = krk_getType(arg);
347  krk_push(arg);
348  if (!krk_bindMethod(type, S("__float__"))) {
349  krk_pop();
350  raise_TypeError(_method_name, "float", arg, names[oarg]);
351  goto _error;
352  }
353  arg = krk_callStack(0);
354  }
355  *out = AS_FLOATING(arg);
356  }
357  break;
358  }
359 
363  case 'd': {
364  double * out = va_arg(args, double*);
365  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
366  if (!IS_FLOATING(arg)) {
367  KrkClass * type = krk_getType(arg);
368  krk_push(arg);
369  if (!krk_bindMethod(type, S("__float__"))) {
370  krk_pop();
371  raise_TypeError(_method_name, "float", arg, names[oarg]);
372  goto _error;
373  }
374  arg = krk_callStack(0);
375  }
376  *out = AS_FLOATING(arg);
377  }
378  break;
379  }
380 #else
381  case 'f':
382  case 'd':
383  krk_runtimeError(vm.exceptions->typeError, "no float support");
384  goto _error;
385 #endif
386 
393  case 'p': {
394  int * out = va_arg(args, int*);
395  if (!krk_valuesSame(arg, KWARGS_VAL(0))) {
396  *out = !krk_isFalsey(arg);
397  if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _error;
398  }
399  break;
400  }
401 
402  default: {
403  krk_runtimeError(vm.exceptions->typeError, "unrecognized directive '%c' in format string", argtype);
404  goto _error;
405  }
406  }
407 
408  oarg++;
409  }
410 
411  if (iarg < argc) {
416  krk_runtimeError(vm.exceptions->argumentError, "%s() takes %s %d argument%s (%d given)",
417  _method_name, required ? "exactly" : "at most", oarg, oarg == 1 ? "" : "s", argc);
418  return 0;
419  }
420 
421  if (!acceptextrakws && hasKw && AS_DICT(argv[argc])->count) {
428  for (size_t i = 0; i < AS_DICT(argv[argc])->capacity; ++i) {
429  KrkTableEntry * entry = &AS_DICT(argv[argc])->entries[i];
430  if (IS_STRING(entry->key)) {
431  /* See if this was the name of an argument, which means it was already provided as a positional argument. */
432  for (int j = 0; j < oarg; ++j) {
433  if (*names[j] && strlen(names[j]) == AS_STRING(entry->key)->length && !strcmp(names[j], AS_CSTRING(entry->key))) {
434  krk_runtimeError(vm.exceptions->typeError, "%s() got multiple values for argument '%s'",
435  _method_name, names[j]);
436  return 0;
437  }
438  }
439  /* Otherwise just say it was unexpected. */
440  krk_runtimeError(vm.exceptions->typeError, "%s() got an unexpected keyword argument '%S'",
441  _method_name, AS_STRING(entry->key));
442  return 0;
443  }
444  }
445  }
446 
447  return 1;
448 
449 _error:
450  return 0;
451 }
452 
457  const char * method_name,
458  int argc, const KrkValue argv[], int hasKw,
459  const char * format, const char ** names, ...) {
460  va_list args;
461  va_start(args, names);
462  int result = krk_parseVArgs(method_name,argc,argv,hasKw,format,names,args);
463  va_end(args);
464  return result;
465 }
466 
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
Type object.
Definition: object.h:215
KrkString * name
Name of the class.
Definition: object.h:219
int krk_bindMethod(KrkClass *_class, KrkString *name)
Perform method binding on the stack.
Definition: vm.c:1655
The most basic object type.
Definition: object.h:41
Immutable sequence of Unicode codepoints.
Definition: object.h:93
uint32_t krk_unicodeCodepoint(KrkString *string, size_t index)
Obtain the codepoint at a given index in a string.
Definition: object.c:162
KrkString * krk_copyString(const char *chars, size_t length)
Obtain a string object representation of the given C string.
Definition: object.c:224
char * chars
UTF8 canonical data.
Definition: object.h:97
One (key,value) pair in a table.
Definition: table.h:20
Simple hash table of arbitrary keys to values.
Definition: table.h:28
int krk_tableDeleteExact(KrkTable *table, KrkValue key)
Remove a key from a hash table, with identity lookup.
Definition: table.c:249
int krk_tableGet_fast(KrkTable *table, struct KrkString *str, KrkValue *value)
Obtain the value associated with a string key in a table.
Definition: table.c:219
int flags
Definition: vm.h:165
Flexible vector of stack references.
Definition: value.h:75
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Definition: value.c:17
Stack reference or primative value.
int krk_isInstanceOf(KrkValue obj, const KrkClass *type)
Determine if a class is an instance or subclass of a given type.
Definition: vm.c:282
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:240
static int krk_valuesSame(KrkValue a, KrkValue b)
Compare two values by identity.
Definition: value.h:141
int krk_isFalsey(KrkValue value)
Determine the truth of a value.
Definition: vm.c:821
Utilities for creating native bindings.
int krk_parseArgs_impl(const char *_method_name, int argc, const KrkValue argv[], int hasKw, const char *format, const char **names,...)
Variable argument version of krk_parseVArgs.
Definition: parseargs.c:456
int krk_parseVArgs(const char *_method_name, int argc, const KrkValue argv[], int hasKw, const char *fmt, const char **names, va_list args)
Validate and parse arguments to a function similar to how managed function arguments are handled.
Definition: parseargs.c:85
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
KrkValue krk_callStack(int argCount)
Call a callable on the stack with argCount arguments.
Definition: vm.c:732
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:131
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118