kuroko.c
1 
7 #define _DEFAULT_SOURCE
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <signal.h>
14 #include <errno.h>
15 
16 #ifdef __toaru__
17 #include <toaru/rline.h>
18 #else
19 #ifndef NO_RLINE
20 #include "vendor/rline.h"
21 #endif
22 #endif
23 
24 #include <kuroko/kuroko.h>
25 #include <kuroko/chunk.h>
26 #include <kuroko/debug.h>
27 #include <kuroko/vm.h>
28 #include <kuroko/memory.h>
29 #include <kuroko/scanner.h>
30 #include <kuroko/compiler.h>
31 #include <kuroko/util.h>
32 
33 #define PROMPT_MAIN ">>> "
34 #define PROMPT_BLOCK " > "
35 
36 #define CALLGRIND_TMP_FILE "/tmp/kuroko.callgrind.tmp"
37 
38 static int enableRline = 1;
39 static int exitRepl = 0;
40 static int pasteEnabled = 0;
41 
42 KRK_Function(exit) {
43  FUNCTION_TAKES_NONE();
44  exitRepl = 1;
45  return NONE_VAL();
46 }
47 
48 KRK_Function(paste) {
49  int enabled = !pasteEnabled;
50  if (!krk_parseArgs("|p",(const char*[]){"enabled"},&enabled)) return NONE_VAL();
51  pasteEnabled = enabled;
52  fprintf(stderr, "Pasting is %s.\n", pasteEnabled ? "enabled" : "disabled");
53  return NONE_VAL();
54 }
55 
56 static int doRead(char * buf, size_t bufSize) {
57 #ifndef NO_RLINE
58  if (enableRline)
59  return rline(buf, bufSize);
60  else
61 #endif
62  return read(STDIN_FILENO, buf, bufSize);
63 }
64 
65 static KrkValue readLine(char * prompt, int promptWidth, char * syntaxHighlighter) {
66  struct StringBuilder sb = {0};
67 
68 #ifndef NO_RLINE
69  if (enableRline) {
70  rline_exit_string = "";
71  rline_exp_set_prompts(prompt, "", promptWidth, 0);
72  rline_exp_set_syntax(syntaxHighlighter);
73  rline_exp_set_tab_complete_func(NULL);
74  } else
75 #endif
76  {
77  fprintf(stdout, "%s", prompt);
78  fflush(stdout);
79  }
80 
81  /* Read a line of input using a method that we can guarantee will be
82  * interrupted by signal delivery. */
83  while (1) {
84  char buf[4096];
85  ssize_t bytesRead = doRead(buf, 4096);
86  if (krk_currentThread.flags & KRK_THREAD_SIGNALLED) goto _exit;
87  if (bytesRead < 0) {
88  krk_runtimeError(vm.exceptions->ioError, "%s", strerror(errno));
89  goto _exit;
90  } else if (bytesRead == 0 && !sb.length) {
91  krk_runtimeError(vm.exceptions->baseException, "EOF");
92  goto _exit;
93  } else {
94  pushStringBuilderStr(&sb, buf, bytesRead);
95  }
96  /* Was there a linefeed? Then we can exit. */
97  if (sb.length && sb.bytes[sb.length-1] == '\n') {
98  sb.length--;
99  break;
100  }
101  }
102 
103  return finishStringBuilder(&sb);
104 
105 _exit:
106  discardStringBuilder(&sb);
107  return NONE_VAL();
108 }
109 
116 KRK_Function(input) {
117  char * prompt = "";
118  int promptwidth = 0;
119  char * syntax = NULL;
120 
121  if (!krk_parseArgs("|siz", (const char*[]){"prompt","promptwidth","syntax"},
122  &prompt, &promptwidth, &syntax)) return NONE_VAL();
123 
124  if (promptwidth == 0 && *prompt) {
125  promptwidth = strlen(prompt);
126  }
127 
128  return readLine(prompt, promptwidth, syntax);
129 }
130 
131 #ifndef NO_RLINE
140 static KrkValue findFromProperty(KrkValue current, KrkToken next) {
141  KrkValue member = OBJECT_VAL(krk_copyString(next.start, next.literalWidth));
142  krk_push(member);
143  KrkValue value = krk_valueGetAttribute_default(current, AS_CSTRING(member), NONE_VAL());
144  krk_pop();
145  return value;
146 }
147 
148 static char * syn_krk_keywords[] = {
149  "and","class","def","else","for","if","in","import","del",
150  "let","not","or","return","while","try","except","raise",
151  "continue","break","as","from","elif","lambda","with","is",
152  "pass","assert","yield","finally","async","await",
153  "True","False","None",
154  NULL
155 };
156 
157 static void tab_complete_func(rline_context_t * c) {
158  /* Figure out where the cursor is and if we should be completing anything. */
159  if (c->offset) {
161  /* Copy up to the cursor... */
162  char * tmp = malloc(c->offset + 1);
163  memcpy(tmp, c->buffer, c->offset);
164  tmp[c->offset] = '\0';
165  /* and pass it to the scanner... */
166  KrkScanner scanner = krk_initScanner(tmp);
167  /* Logically, there can be at most (offset) tokens, plus some wiggle room. */
168  KrkToken * space = malloc(sizeof(KrkToken) * (c->offset + 2));
169  int count = 0;
170  do {
171  space[count++] = krk_scanToken(&scanner);
172  } while (space[count-1].type != TOKEN_EOF && space[count-1].type != TOKEN_ERROR);
173 
174  /* If count == 1, it was EOF or an error and we have nothing to complete. */
175  if (count == 1) {
176  goto _cleanup;
177  }
178 
179  /* Otherwise we want to see if we're on an identifier or a dot. */
180  int base = 2;
181  int n = base;
182  if (space[count-base].type == TOKEN_DOT) {
183  /* Dots we need to look back at the previous tokens for */
184  n--;
185  base--;
186  } else if (space[count-base].type >= TOKEN_IDENTIFIER && space[count-base].type <= TOKEN_WITH) {
187  /* Something alphanumeric; only for the last element */
188  } else {
189  /* Some other symbol */
190  goto _cleanup;
191  }
192 
193  /* Work backwards to find the start of this chain of identifiers */
194  while (n < count) {
195  if (space[count-n-1].type != TOKEN_DOT) break;
196  n++;
197  if (n == count) break;
198  if (space[count-n-1].type != TOKEN_IDENTIFIER) break;
199  n++;
200  }
201 
202  if (n <= count) {
203  /* Now work forwards, starting from the current globals. */
204  KrkValue root = OBJECT_VAL(krk_currentThread.module);
205  int isGlobal = 1;
206  while (n > base) {
207  /* And look at the potential fields for instances/classes */
208  KrkValue next = findFromProperty(root, space[count-n]);
209  if (IS_NONE(next)) {
210  /* If we hit None, we found something invalid (or literally hit a None
211  * object, but really the difference is minimal in this case: Nothing
212  * useful to tab complete from here. */
213  if (!isGlobal) goto _cleanup;
214  /* Does this match a builtin? */
215  if (!krk_tableGet_fast(&vm.builtins->fields,
216  krk_copyString(space[count-n].start,space[count-n].literalWidth), &next) || IS_NONE(next)) {
217  goto _cleanup;
218  }
219  }
220  isGlobal = 0;
221  root = next;
222  n -= 2; /* To skip every other dot. */
223  }
224 
225  if (isGlobal && n < count && (space[count-n-1].type == TOKEN_IMPORT || space[count-n-1].type == TOKEN_FROM)) {
226  KrkInstance * modules = krk_newInstance(vm.baseClasses->objectClass);
227  root = OBJECT_VAL(modules);
228  krk_push(root);
229  for (size_t i = 0; i < vm.modules.capacity; ++i) {
230  KrkTableEntry * entry = &vm.modules.entries[i];
231  if (IS_KWARGS(entry->key)) continue;
232  krk_attachNamedValue(&modules->fields, AS_CSTRING(entry->key), NONE_VAL());
233  }
234  }
235 
236  /* Now figure out what we're completing - did we already have a partial symbol name? */
237  int length = (space[count-base].type == TOKEN_DOT) ? 0 : (space[count-base].length);
238  isGlobal = isGlobal && (length != 0);
239 
240  /* Collect up to 256 of those that match */
241  char * matches[256];
242  int matchCount = 0;
243 
244  /* Take the last symbol name from the chain and get its member list from dir() */
245 
246  for (;;) {
247  KrkValue dirList = krk_dirObject(1,(KrkValue[]){root},0);
248  krk_push(dirList);
249  if (!IS_INSTANCE(dirList)) {
250  fprintf(stderr,"\nInternal error while tab completting.\n");
251  goto _cleanup;
252  }
253 
254  for (size_t i = 0; i < AS_LIST(dirList)->count; ++i) {
255  KrkString * s = AS_STRING(AS_LIST(dirList)->values[i]);
256  krk_push(OBJECT_VAL(s));
257  KrkToken asToken = {.start = s->chars, .literalWidth = s->length};
258  KrkValue thisValue = findFromProperty(root, asToken);
259  krk_push(thisValue);
260  if (IS_CLOSURE(thisValue) || IS_BOUND_METHOD(thisValue) || IS_NATIVE(thisValue)) {
261  size_t allocSize = s->length + 2;
262  char * tmp = malloc(allocSize);
263  size_t len = snprintf(tmp, allocSize, "%s(", s->chars);
264  s = krk_takeString(tmp, len);
265  krk_pop();
266  krk_push(OBJECT_VAL(s));
267  } else {
268  krk_pop();
269  }
270 
271  /* If this symbol is shorter than the current submatch, skip it. */
272  if (length && (int)s->length < length) continue;
273 
274  /* See if it's already in the matches */
275  int found = 0;
276  for (int i = 0; i < matchCount; ++i) {
277  if (!strcmp(matches[i], s->chars)) {
278  found = 1;
279  break;
280  }
281  }
282  if (found) continue;
283 
284  if (!memcmp(s->chars, space[count-base].start, length)) {
285  matches[matchCount] = s->chars;
286  matchCount++;
287  if (matchCount == 255) goto _toomany;
288  }
289  }
290 
291  /*
292  * If the object we were scanning was the current module,
293  * then we should also throw the builtins into the ring.
294  */
295  if (isGlobal && AS_OBJECT(root) == (KrkObj*)krk_currentThread.module) {
296  root = OBJECT_VAL(vm.builtins);
297  continue;
298  } else if (isGlobal && AS_OBJECT(root) == (KrkObj*)vm.builtins) {
299  KrkInstance * fakeKeywordsObject = krk_newInstance(vm.baseClasses->objectClass);
300  root = OBJECT_VAL(fakeKeywordsObject);
301  krk_push(root);
302  for (char ** keyword = syn_krk_keywords; *keyword; keyword++) {
303  krk_attachNamedValue(&fakeKeywordsObject->fields, *keyword, NONE_VAL());
304  }
305  continue;
306  } else {
307  break;
308  }
309  }
310 _toomany:
311 
312  /* Now we can do things with the matches. */
313  if (matchCount == 1) {
314  /* If there was only one, just fill it. */
315  rline_insert(c, matches[0] + length);
316  rline_place_cursor();
317  } else if (matchCount) {
318  /* Otherwise, try to find a common substring among them... */
319  int j = length;
320  while (1) {
321  char m = matches[0][j];
322  if (!m) break;
323  int diff = 0;
324  for (int i = 1; i < matchCount; ++i) {
325  if (matches[i][j] != m) {
326  diff = 1;
327  break;
328  }
329  }
330  if (diff) break;
331  j++;
332  }
333  /* If no common sub string could be filled in, we print the list. */
334  if (j == length) {
335  /* First find the maximum width of an entry */
336  int maxWidth = 0;
337  for (int i = 0; i < matchCount; ++i) {
338  if ((int)strlen(matches[i]) > maxWidth) maxWidth = strlen(matches[i]);
339  }
340  /* Now how many can we fit in a screen */
341  int colsPerLine = rline_terminal_width / (maxWidth + 2); /* +2 for the spaces */
342  fprintf(stderr, "\n");
343  int column = 0;
344  for (int i = 0; i < matchCount; ++i) {
345  fprintf(stderr, "%-*s ", maxWidth, matches[i]);
346  column += 1;
347  if (column >= colsPerLine) {
348  fprintf(stderr, "\n");
349  column = 0;
350  }
351  }
352  if (column != 0) fprintf(stderr, "\n");
353  } else {
354  /* If we do have a common sub string, fill in those characters. */
355  for (int i = length; i < j; ++i) {
356  char tmp[2] = {matches[0][i], '\0'};
357  rline_insert(c, tmp);
358  }
359  }
360  }
361  }
362 _cleanup:
363  free(tmp);
364  free(space);
366  return;
367  }
368 }
369 #endif
370 
371 #ifndef KRK_DISABLE_DEBUG
372 static char * lastDebugCommand = NULL;
373 static int debuggerHook(KrkCallFrame * frame) {
374 
375  /* File information */
376  fprintf(stderr, "At offset 0x%04lx of function '%s' from '%s' on line %lu:\n",
377  (unsigned long)(frame->ip - frame->closure->function->chunk.code),
378  frame->closure->function->name->chars,
379  frame->closure->function->chunk.filename->chars,
380  (unsigned long)krk_lineNumber(&frame->closure->function->chunk,
381  (unsigned long)(frame->ip - frame->closure->function->chunk.code)));
382 
383  /* Opcode trace */
385  stderr,
386  frame->closure->function,
387  (size_t)(frame->ip - frame->closure->function->chunk.code));
388 
389  krk_debug_dumpStack(stderr, frame);
390 
391  while (1) {
392  char buf[4096] = {0};
393 #ifndef NO_RLINE
394  if (enableRline) {
395  rline_exit_string="";
396  rline_exp_set_prompts("(dbg) ", "", 6, 0);
397  rline_exp_set_syntax("krk-dbg");
398  rline_exp_set_tab_complete_func(NULL);
399  if (rline(buf, 4096) == 0) goto _dbgQuit;
400  } else {
401 #endif
402  fprintf(stderr, "(dbg) ");
403  fflush(stderr);
404  char * out = fgets(buf, 4096, stdin);
405  if (!out || !strlen(buf)) {
406  fprintf(stdout, "^D\n");
407  goto _dbgQuit;
408  }
409 #ifndef NO_RLINE
410  }
411 #endif
412 
413  char * nl = strstr(buf,"\n");
414  if (nl) *nl = '\0';
415 
416  if (!strlen(buf)) {
417  if (lastDebugCommand) {
418  strcpy(buf, lastDebugCommand);
419  } else {
420  continue;
421  }
422  } else {
423 #ifndef NO_RLINE
424  if (enableRline) {
425  rline_history_insert(strdup(buf));
426  rline_scroll = 0;
427  }
428 #endif
429  if (lastDebugCommand) free(lastDebugCommand);
430  lastDebugCommand = strdup(buf);
431  }
432 
433  /* Try to tokenize the first bit */
434  char * arg = NULL;
435  char * sp = strstr(buf," ");
436  if (sp) {
437  *sp = '\0';
438  arg = sp + 1;
439  }
440  /* Now check commands */
441  if (!strcmp(buf, "c") || !strcmp(buf,"continue")) {
442  return KRK_DEBUGGER_CONTINUE;
443  } else if (!strcmp(buf, "s") || !strcmp(buf, "step")) {
444  return KRK_DEBUGGER_STEP;
445  } else if (!strcmp(buf, "abort")) {
446  return KRK_DEBUGGER_ABORT;
447  } else if (!strcmp(buf, "q") || !strcmp(buf, "quit")) {
448  return KRK_DEBUGGER_QUIT;
449  } else if (!strcmp(buf, "bt") || !strcmp(buf, "backtrace")) {
451  } else if (!strcmp(buf, "p") || !strcmp(buf, "print")) {
452  if (!arg) {
453  fprintf(stderr, "print requires an argument\n");
454  } else {
455  size_t frameCount = krk_currentThread.frameCount;
456  /* Compile statement */
457  KrkCodeObject * expression = krk_compile(arg,"<debugger>");
458  if (expression) {
459  /* Make sure stepping is disabled first. */
461  /* Turn our compiled expression into a callable. */
462  krk_push(OBJECT_VAL(expression));
463  krk_push(OBJECT_VAL(krk_newClosure(expression, OBJECT_VAL(krk_currentThread.module))));
464  krk_swap(1);
465  krk_pop();
466  /* Stack silliness, don't ask. */
467  krk_push(NONE_VAL());
468  krk_pop();
469  /* Call the compiled expression with no args. */
471  fprintf(stderr, "\033[1;30m=> ");
472  krk_printValue(stderr, krk_peek(0));
473  fprintf(stderr, "\033[0m\n");
474  krk_pop();
475  }
476  if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
478  krk_currentThread.flags &= ~(KRK_THREAD_HAS_EXCEPTION);
479  }
480  krk_currentThread.frameCount = frameCount;
481  }
482  } else if (!strcmp(buf, "break") || !strcmp(buf, "b")) {
483  char * filename = arg;
484  if (!filename) {
485  fprintf(stderr, "usage: break FILE LINE [type]\n");
486  continue;
487  }
488 
489  char * lineno = strstr(filename, " ");
490  if (!lineno) {
491  fprintf(stderr, "usage: break FILE LINE [type]\n");
492  continue;
493  }
494 
495  /* Advance whitespace */
496  *lineno = '\0';
497  lineno++;
498 
499  /* collect optional type */
500  int flags = KRK_BREAKPOINT_NORMAL;
501  char * type = strstr(lineno, " ");
502  if (type) {
503  *type = '\0'; type++;
504  if (!strcmp(type, "repeat") || !strcmp(type,"r")) {
505  flags = KRK_BREAKPOINT_REPEAT;
506  } else if (!strcmp(type, "once") || !strcmp(type,"o")) {
507  flags = KRK_BREAKPOINT_ONCE;
508  } else {
509  fprintf(stderr, "Unrecognized breakpoint type: %s\n", type);
510  continue;
511  }
512  }
513 
514  int lineInt = atoi(lineno);
515  int result = krk_debug_addBreakpointFileLine(krk_copyString(filename, strlen(filename)), lineInt, flags);
516 
517  if (result == -1) {
518  fprintf(stderr, "Sorry, couldn't add breakpoint.\n");
519  } else {
520  fprintf(stderr, "Breakpoint %d enabled.\n", result);
521  }
522 
523  } else if (!strcmp(buf, "i") || !strcmp(buf, "info")) {
524  if (!arg) {
525  fprintf(stderr, " info breakpoints - Show breakpoints.\n");
526  continue;
527  }
528 
529  if (!strcmp(arg,"breakpoints")) {
530  KrkCodeObject * codeObject = NULL;
531  size_t offset = 0;
532  int flags = 0;
533  int enabled = 0;
534  int breakIndex = 0;
535  while (1) {
536  int result = krk_debug_examineBreakpoint(breakIndex, &codeObject, &offset, &flags, &enabled);
537  if (result == -1) break;
538  if (result == -2) continue;
539 
540  fprintf(stderr, "%-4d in %s+%d %s %s\n",
541  breakIndex,
542  codeObject->name->chars,
543  (int)offset,
544  flags == KRK_BREAKPOINT_NORMAL ? "normal":
545  flags == KRK_BREAKPOINT_REPEAT ? "repeat" :
546  flags == KRK_BREAKPOINT_ONCE ? "once" : "?",
547  enabled ? "enabled" : "disabled");
548 
549  breakIndex++;
550  }
551  } else {
552  fprintf(stderr, "Unrecognized info object: %s\n", arg);
553  }
554 
555  } else if (!strcmp(buf, "e") || !strcmp(buf, "enable")) {
556  if (!arg) {
557  fprintf(stderr, "enable requires an argument\n");
558  continue;
559  }
560 
561  int breakIndex = atoi(arg);
562 
563  if (krk_debug_enableBreakpoint(breakIndex)) {
564  fprintf(stderr, "Invalid breakpoint handle.\n");
565  } else {
566  fprintf(stderr, "Breakpoint %d enabled.\n", breakIndex);
567  }
568  } else if (!strcmp(buf, "d") || !strcmp(buf, "disable")) {
569  if (!arg) {
570  fprintf(stderr, "disable requires an argument\n");
571  continue;
572  }
573 
574  int breakIndex = atoi(arg);
575 
576  if (krk_debug_disableBreakpoint(breakIndex)) {
577  fprintf(stderr, "Invalid breakpoint handle.\n");
578  } else {
579  fprintf(stderr, "Breakpoint %d disabled.\n", breakIndex);
580  }
581  } else if (!strcmp(buf, "r") || !strcmp(buf, "remove")) {
582  if (!arg) {
583  fprintf(stderr, "remove requires an argument\n");
584  continue;
585  }
586 
587  int breakIndex = atoi(arg);
588 
589  if (krk_debug_removeBreakpoint(breakIndex)) {
590  fprintf(stderr, "Invalid breakpoint handle.\n");
591  } else {
592  fprintf(stderr, "Breakpoint %d removed.\n", breakIndex);
593  }
594  } else if (!strcmp(buf, "help")) {
595  fprintf(stderr,
596  "Kuroko Interactive Debugger\n"
597  " c continue - Continue until the next breakpoint.\n"
598  " s step - Execute this instruction and return to the debugger.\n"
599  " bt backtrace - Print a backtrace.\n"
600  " q quit - Exit the interpreter.\n"
601  " abort - Abort the interpreter (may create a core dump).\n"
602  " b break ... - Set a breakpoint.\n"
603  " e enable N - Enable breakpoint 'N'.\n"
604  " d disable N - Disable breakpoint 'N'.\n"
605  " r remove N - Remove breakpoint 'N'.\n"
606  " i info ... - See information about breakpoints.\n"
607  "\n"
608  "Empty input lines will repeat the last command.\n"
609  );
610  } else {
611  fprintf(stderr, "Unrecognized command: %s\n", buf);
612  }
613 
614  }
615 
616  return KRK_DEBUGGER_CONTINUE;
617 _dbgQuit:
618  return KRK_DEBUGGER_QUIT;
619 }
620 #endif
621 
622 static void handleSigint(int sigNum) {
623  /* Don't set the signal flag if the VM is not running */
624  krk_currentThread.flags |= KRK_THREAD_SIGNALLED;
625 }
626 
627 #ifndef _WIN32
628 static void handleSigtrap(int sigNum) {
629  krk_currentThread.flags |= KRK_THREAD_SINGLE_STEP;
630 }
631 #endif
632 
633 static void bindSignalHandlers(void) {
634 #if !defined(_WIN32)
635  struct sigaction sigIntAction;
636  sigIntAction.sa_handler = handleSigint;
637  sigemptyset(&sigIntAction.sa_mask);
638  sigIntAction.sa_flags = 0; /* Do not restore the default, do not restart syscalls, do not pass go, do not collect $500 */
639  sigaction(
640  SIGINT, /* ^C for keyboard interrupts */
641  &sigIntAction,
642  NULL);
643 
644  struct sigaction sigTrapAction;
645  sigTrapAction.sa_handler = handleSigtrap;
646  sigemptyset(&sigTrapAction.sa_mask);
647  sigTrapAction.sa_flags = 0;
648  sigaction(
649  SIGTRAP,
650  &sigTrapAction,
651  NULL);
652 #else
653  signal(SIGINT, handleSigint);
654 #endif
655 }
656 
657 static void findInterpreter(char * argv[]) {
658 #ifdef _WIN32
659  vm.binpath = strdup(_pgmptr);
660 #else
661  /* Try asking /proc */
662  char tmp[4096];
663  char * binpath = realpath("/proc/self/exe", NULL);
664  if (!binpath || (access(binpath, X_OK) != 0)) {
665  if (binpath) {
666  free(binpath);
667  binpath = NULL;
668  }
669  if (strchr(argv[0], '/')) {
670  binpath = realpath(argv[0], NULL);
671  } else {
672  /* Search PATH for argv[0] */
673  char * p = getenv("PATH");
674  if (!p) return;
675  char * _path = strdup(p);
676  char * path = _path;
677  while (path) {
678  char * next = strchr(path,':');
679  if (next) *next++ = '\0';
680 
681  snprintf(tmp, 4096, "%s/%s", path, argv[0]);
682  if (access(tmp, X_OK) == 0) {
683  binpath = strdup(tmp);
684  break;
685  }
686  path = next;
687  }
688  free(_path);
689  }
690  }
691  if (binpath) {
692  vm.binpath = binpath;
693  } /* Else, give up at this point and just don't attach it at all. */
694 #endif
695 }
696 
697 static int runString(char * argv[], int flags, char * string) {
698  findInterpreter(argv);
699  krk_initVM(flags);
700  krk_startModule("__main__");
701  krk_attachNamedValue(&krk_currentThread.module->fields,"__doc__", NONE_VAL());
702  krk_interpret(string, "<stdin>");
703  krk_freeVM();
704  return 0;
705 }
706 
707 static int compileFile(char * argv[], int flags, char * fileName) {
708  findInterpreter(argv);
709  krk_initVM(flags);
710 
711  /* Open the file. */
712  FILE * f = fopen(fileName,"r");
713  if (!f) {
714  fprintf(stderr, "%s: could not read file '%s': %s\n", argv[0], fileName, strerror(errno));
715  return 1;
716  }
717 
718  /* Read it like we normally do... */
719  fseek(f, 0, SEEK_END);
720  size_t size = ftell(f);
721  fseek(f, 0, SEEK_SET);
722  char * buf = malloc(size+1);
723  if (fread(buf, 1, size, f) == 0) return 2;
724  fclose(f);
725  buf[size] = '\0';
726 
727  /* Set up a module scope */
728  krk_startModule("__main__");
729 
730  /* Call the compiler directly. */
731  KrkCodeObject * func = krk_compile(buf, fileName);
732 
733  /* See if there was an exception. */
734  if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
736  }
737 
738  /* Free the source string */
739  free(buf);
740 
741  /* Close out the compiler */
742  krk_freeVM();
743 
744  return func == NULL;
745 }
746 
747 #ifdef BUNDLE_LIBS
748 #define BUNDLED(name) do { \
749  extern KrkValue krk_module_onload_ ## name (); \
750  KrkValue moduleOut = krk_module_onload_ ## name (); \
751  krk_attachNamedValue(&vm.modules, # name, moduleOut); \
752  krk_attachNamedObject(&AS_INSTANCE(moduleOut)->fields, "__name__", (KrkObj*)krk_copyString(#name, sizeof(#name)-1)); \
753  krk_attachNamedValue(&AS_INSTANCE(moduleOut)->fields, "__file__", NONE_VAL()); \
754 } while (0)
755 #endif
756 
757 int main(int argc, char * argv[]) {
758 #ifdef _WIN32
759  SetConsoleOutputCP(65001);
760  SetConsoleCP(65001);
761 #endif
762  char * runCmd = NULL;
763  int flags = 0;
764  int moduleAsMain = 0;
765  int inspectAfter = 0;
766  int opt;
767  int maxDepth = -1;
768  while ((opt = getopt(argc, argv, "+:c:C:dgGim:rR:tTMSV-:")) != -1) {
769  switch (opt) {
770  case 'c':
771  runCmd = optarg;
772  goto _finishArgs;
773  case 'd':
774  /* Disassemble code blocks after compilation. */
775  flags |= KRK_THREAD_ENABLE_DISASSEMBLY;
776  break;
777  case 'g':
778  /* Always garbage collect during an allocation. */
779  flags |= KRK_GLOBAL_ENABLE_STRESS_GC;
780  break;
781  case 'G':
782  flags |= KRK_GLOBAL_REPORT_GC_COLLECTS;
783  break;
784  case 'S':
785  flags |= KRK_THREAD_SINGLE_STEP;
786  break;
787  case 't':
788  /* Disassemble instructions as they are executed. */
789  flags |= KRK_THREAD_ENABLE_TRACING;
790  break;
791  case 'T': {
792  flags |= KRK_GLOBAL_CALLGRIND;
793  vm.callgrindFile = fopen(CALLGRIND_TMP_FILE,"w");
794  break;
795  }
796  case 'i':
797  inspectAfter = 1;
798  break;
799  case 'm':
800  moduleAsMain = 1;
801  optind--; /* to get us back to optarg */
802  goto _finishArgs;
803  case 'r':
804  enableRline = 0;
805  break;
806  case 'R':
807  maxDepth = atoi(optarg);
808  break;
809  case 'M':
810  return runString(argv,0,"import kuroko; print(kuroko.module_paths)\n");
811  case 'V':
812  return runString(argv,0,"import kuroko; print('Kuroko',kuroko.version)\n");
813  case 'C':
814  return compileFile(argv,flags,optarg);
815  case ':':
816  fprintf(stderr, "%s: option '%c' requires an argument\n", argv[0], optopt);
817  return 1;
818  case '?':
819  if (optopt != '-') {
820  fprintf(stderr, "%s: unrecognized option '%c'\n", argv[0], optopt);
821  return 1;
822  }
823  optarg = argv[optind]+2;
824  /* fall through */
825  case '-':
826  if (!strcmp(optarg,"version")) {
827  return runString(argv,0,"import kuroko; print('Kuroko',kuroko.version)\n");
828  } else if (!strcmp(optarg,"help")) {
829 #ifndef KRK_NO_DOCUMENTATION
830  fprintf(stderr,"usage: %s [flags] [FILE...]\n"
831  "\n"
832  "Interpreter options:\n"
833  " -d Debug output from the bytecode compiler.\n"
834  " -g Collect garbage on every allocation.\n"
835  " -G Report GC collections.\n"
836  " -i Enter repl after a running -c, -m, or FILE.\n"
837  " -m mod Run a module as a script.\n"
838  " -r Disable complex line editing in the REPL.\n"
839  " -R depth Set maximum recursion depth.\n"
840  " -t Disassemble instructions as they are exceuted.\n"
841  " -T Write call trace file.\n"
842  " -C file Compile 'file', but do not execute it.\n"
843  " -M Print the default module import paths.\n"
844  " -S Enable single-step debugging.\n"
845  " -V Print version information.\n"
846  "\n"
847  " --version Print version information.\n"
848  " --help Show this help text.\n"
849  "\n"
850  "If no files are provided, the interactive REPL will run.\n",
851  argv[0]);
852 #endif
853  return 0;
854  } else {
855  fprintf(stderr,"%s: unrecognized option '--%s'\n",
856  argv[0], optarg);
857  return 1;
858  }
859  }
860  }
861 
862 _finishArgs:
863  findInterpreter(argv);
864  krk_initVM(flags);
865 
866  if (maxDepth != -1) {
868  }
869 
870 #ifndef KRK_DISABLE_DEBUG
871  krk_debug_registerCallback(debuggerHook);
872 #endif
873 
874  /* Attach kuroko.argv - argv[0] will be set to an empty string for the repl */
875  if (argc == optind) krk_push(OBJECT_VAL(krk_copyString("",0)));
876  for (int arg = optind; arg < argc; ++arg) {
877  krk_push(OBJECT_VAL(krk_copyString(argv[arg],strlen(argv[arg]))));
878  }
879  KrkValue argList = krk_callNativeOnStack(argc - optind + (optind == argc), &krk_currentThread.stackTop[-(argc - optind + (optind == argc))], 0, krk_list_of);
880  krk_push(argList);
881  krk_attachNamedValue(&vm.system->fields, "argv", argList);
882  krk_pop();
883  for (int arg = optind; arg < argc + (optind == argc); ++arg) krk_pop();
884 
885  /* Bind interrupt signal */
886  bindSignalHandlers();
887 
888 #ifdef BUNDLE_LIBS
889  /* Add any other modules you want to include that are normally built as shared objects. */
890  BUNDLED(math);
891  BUNDLED(socket);
892  BUNDLED(timeit);
893 #endif
894 
895  KrkValue result = INTEGER_VAL(0);
896 
897  char * _KUROKOPATH = getenv("KUROKOPATH");
898 
899  if (_KUROKOPATH) {
900  /* Build a path by splitting */
901  krk_push(OBJECT_VAL(krk_copyString(_KUROKOPATH,strlen(_KUROKOPATH))));
902  krk_push(OBJECT_VAL(S(":")));
903 
904  /* Split into list */
905  KrkValue list = krk_string_split(2,(KrkValue[]){krk_peek(1),krk_peek(0)},0);
906  krk_push(list);
907  krk_swap(2);
908  krk_pop(); /* colon */
909  krk_pop(); /* path */
910 
911  /* Extend with current module_paths */
912  krk_push(krk_valueGetAttribute(OBJECT_VAL(vm.system), "module_paths"));
913 
914  extern FUNC_SIG(list,extend);
915  FUNC_NAME(list,extend)(2,(KrkValue[]){krk_peek(1),krk_peek(0)},0);
916 
917  /* Store */
918  krk_attachNamedValue(&vm.system->fields, "module_paths", list);
919 
920  /* Clean up our mess */
921  krk_pop();
922  krk_pop(); /* list */
923  }
924 
929  KRK_DOC(BIND_FUNC(vm.builtins,input), "@brief Read a line of input.\n"
930  "@arguments [prompt], promptwidth=None, syntax=None\n\n"
931  "Read a line of input from @c stdin. If the @c rline library is available, "
932  "it will be used to gather input. Input reading stops on end-of file or when "
933  "a read ends with a line feed, which will be removed from the returned string. "
934  "If a prompt is provided, it will be printed without a line feed before requesting "
935  "input. If @c rline is available, the prompt will be passed to the library as the "
936  "left-hand prompt string. If not provided, @p promptwidth will default to the width "
937  "of @p prompt in codepoints; if you are providing a prompt with escape characters or "
938  "characters with multi-column East-Asian Character Width be sure to pass a value "
939  "for @p promptwidth that reflects the display width of your prompt. "
940  "If provided, @p syntax specifies the name of an @c rline syntax module to "
941  "provide color highlighting of the input line.");
942 
943  if (moduleAsMain) {
944  krk_push(OBJECT_VAL(krk_copyString("__main__",8)));
945  int out = !krk_importModule(
946  AS_STRING(AS_LIST(argList)->values[0]),
947  AS_STRING(krk_peek(0)));
948  if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) {
950  krk_resetStack();
951  }
952  if (!inspectAfter) return out;
953  if (IS_INSTANCE(krk_peek(0))) {
954  krk_currentThread.module = AS_INSTANCE(krk_peek(0));
955  }
956  } else if (optind != argc) {
957  krk_startModule("__main__");
958  result = krk_runfile(argv[optind],argv[optind]);
959  if (IS_NONE(result) && krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) result = INTEGER_VAL(1);
960  }
961 
962  if (!krk_currentThread.module) {
963  /* The repl runs in the context of a top-level module so each input
964  * line can share a globals state with the others. */
965  krk_startModule("__main__");
966  krk_attachNamedValue(&krk_currentThread.module->fields,"__doc__", NONE_VAL());
967  }
968 
969  if (runCmd) {
970  result = krk_interpret(runCmd, "<stdin>");
971  }
972 
973  if ((!moduleAsMain && !runCmd && optind == argc) || inspectAfter) {
974  /* Add builtins for the repl, but hide them from the globals() list. */
975  KRK_DOC(BIND_FUNC(vm.builtins,exit), "@brief Exit the interactive repl.\n\n"
976  "Only available from the interactive interpreter; exits the repl.");
977  KRK_DOC(BIND_FUNC(vm.builtins,paste), "@brief Toggle paste mode.\n"
978  "@arguments enabled=None\n\n"
979  "Toggles paste-safe mode, disabling automatic indentation in the repl. "
980  "If @p enabled is specified, the mode can be directly specified, otherwise "
981  "it will be set to the opposite of the current mode. The new mode will be "
982  "printed to stderr.");
983 
993  KrkValue systemModule;
994  if (krk_tableGet(&vm.modules, OBJECT_VAL(krk_copyString("kuroko",6)), &systemModule)) {
995  KrkValue version, buildenv, builddate;
996  krk_tableGet(&AS_INSTANCE(systemModule)->fields, OBJECT_VAL(krk_copyString("version",7)), &version);
997  krk_tableGet(&AS_INSTANCE(systemModule)->fields, OBJECT_VAL(krk_copyString("buildenv",8)), &buildenv);
998  krk_tableGet(&AS_INSTANCE(systemModule)->fields, OBJECT_VAL(krk_copyString("builddate",9)), &builddate);
999 
1000  fprintf(stdout, "Kuroko %s (%s) with %s\n",
1001  AS_CSTRING(version), AS_CSTRING(builddate), AS_CSTRING(buildenv));
1002  }
1003 
1004  fprintf(stdout, "Type `help` for guidance, `paste()` to toggle automatic indentation, `license` for copyright information.\n");
1005 
1006  while (!exitRepl) {
1007  size_t lineCapacity = 8;
1008  size_t lineCount = 0;
1009  char ** lines = ALLOCATE(char *, lineCapacity);
1010  size_t totalData = 0;
1011  int valid = 1;
1012  char * allData = NULL;
1013  int inBlock = 0;
1014  int blockWidth = 0;
1015 
1016 #ifndef NO_RLINE
1017  /* Main prompt is >>> like in Python */
1018  rline_exp_set_prompts(PROMPT_MAIN, "", 4, 0);
1019  /* Set ^D to send EOF */
1020  rline_exit_string="";
1021  /* Enable syntax highlight for Kuroko */
1022  rline_exp_set_syntax("krk");
1023  /* Bind a callback for \t */
1024  rline_exp_set_tab_complete_func(tab_complete_func);
1025 #endif
1026 
1027  while (1) {
1028  /* This would be a nice place for line editing */
1029  char buf[4096] = {0};
1030 
1031 #ifndef NO_RLINE
1032  if (inBlock) {
1033  /* When entering multiple lines, the additional lines
1034  * will show a single > (and keep the left side aligned) */
1035  rline_exp_set_prompts(PROMPT_BLOCK, "", 4, 0);
1036  /* Also add indentation as necessary */
1037  if (!pasteEnabled) {
1038  rline_preload = malloc(blockWidth + 1);
1039  for (int i = 0; i < blockWidth; ++i) {
1040  rline_preload[i] = ' ';
1041  }
1042  rline_preload[blockWidth] = '\0';
1043  }
1044  }
1045 
1046  if (!enableRline) {
1047 #else
1048  if (1) {
1049 #endif
1050  fprintf(stdout, "%s", inBlock ? PROMPT_BLOCK : PROMPT_MAIN);
1051  fflush(stdout);
1052  }
1053 
1054 #ifndef NO_RLINE
1055  rline_scroll = 0;
1056  if (enableRline) {
1057  if (rline(buf, 4096) == 0) {
1058  valid = 0;
1059  exitRepl = 1;
1060  break;
1061  }
1062  } else {
1063 #endif
1064  char * out = fgets(buf, 4096, stdin);
1065  if (krk_currentThread.flags & KRK_THREAD_SIGNALLED) {
1066  fprintf(stdout, "\n");
1067  } else if ((!out || !strlen(buf))) {
1068  fprintf(stdout, "^D\n");
1069  valid = 0;
1070  exitRepl = 1;
1071  break;
1072  }
1073 #ifndef NO_RLINE
1074  }
1075 #endif
1076  if (krk_currentThread.flags & KRK_THREAD_SIGNALLED) {
1077  /* We should actually be properly raising the interrupt and printing it with an empty traceback, but whatever. */
1078  krk_currentThread.flags &= ~(KRK_THREAD_SIGNALLED); /* Clear signal flag */
1079  fprintf(stderr, "KeyboardInterrupt\n");
1080  valid = 0;
1081  break;
1082  }
1083 
1084  if (buf[strlen(buf)-1] != '\n') {
1085  /* rline shouldn't allow this as it doesn't accept ^D to submit input
1086  * unless the line is empty, but just in case... */
1087  fprintf(stderr, "Expected end of line in repl input. Did you ^D early?\n");
1088  valid = 0;
1089  break;
1090  }
1091 
1092  if (lineCapacity < lineCount + 1) {
1093  /* If we need more space, grow as needed... */
1094  size_t old = lineCapacity;
1095  lineCapacity = GROW_CAPACITY(old);
1096  lines = GROW_ARRAY(char *,lines,old,lineCapacity);
1097  }
1098 
1099  int i = lineCount++;
1100  lines[i] = strdup(buf);
1101 
1102  size_t lineLength = strlen(lines[i]);
1103  totalData += lineLength;
1104 
1105  /* Figure out indentation */
1106  int isSpaces = 1;
1107  int countSpaces = 0;
1108  for (size_t j = 0; j < lineLength; ++j) {
1109  if (lines[i][j] != ' ' && lines[i][j] != '\n') {
1110  isSpaces = 0;
1111  break;
1112  }
1113  countSpaces += 1;
1114  }
1115 
1116  /* Naively detect the start of a new block so we can
1117  * continue to accept input. Our compiler isn't really
1118  * set up to let us compile "on the fly" so we can't just
1119  * run lines through it and see if it wants more... */
1120  if (lineLength > 1 && lines[i][lineLength-2] == ':') {
1121  inBlock = 1;
1122  blockWidth = countSpaces + 4;
1123  continue;
1124  } else if (lineLength > 1 && lines[i][lineLength-2] == '\\') {
1125  inBlock = 1;
1126  continue;
1127  } else if (inBlock && lineLength != 1) {
1128  if (isSpaces) {
1129  free(lines[i]);
1130  totalData -= lineLength;
1131  lineCount--;
1132  break;
1133  }
1134  blockWidth = countSpaces;
1135  continue;
1136  } else if (lineLength > 1 && lines[i][countSpaces] == '@') {
1137  inBlock = 1;
1138  blockWidth = countSpaces;
1139  continue;
1140  }
1141 
1142  /* Ignore blank lines. */
1143  if (isSpaces && !i) valid = 0;
1144 
1145  /* If we're not in a block, or have entered a blank line,
1146  * we can stop reading new lines and jump to execution. */
1147  break;
1148  }
1149 
1150  if (valid) {
1151  allData = malloc(totalData + 1);
1152  allData[0] = '\0';
1153  }
1154 
1155  for (size_t i = 0; i < lineCount; ++i) {
1156  if (valid) strcat(allData, lines[i]);
1157 #ifndef NO_RLINE
1158  if (enableRline) {
1159  rline_history_insert(strdup(lines[i]));
1160  rline_scroll = 0;
1161  }
1162 #endif
1163  free(lines[i]);
1164  }
1165  FREE_ARRAY(char *, lines, lineCapacity);
1166 
1167  if (valid) {
1168  KrkValue result = krk_interpret(allData, "<stdin>");
1169  if (!IS_NONE(result)) {
1170  krk_attachNamedValue(&vm.builtins->fields, "_", result);
1171  KrkClass * type = krk_getType(result);
1172  const char * formatStr = " \033[1;90m=> %s\033[0m\n";
1173  if (type->_reprer) {
1174  krk_push(result);
1175  result = krk_callDirect(type->_reprer, 1);
1176  } else if (type->_tostr) {
1177  krk_push(result);
1178  result = krk_callDirect(type->_tostr, 1);
1179  }
1180  if (!IS_STRING(result)) {
1181  fprintf(stdout, " \033[1;91m=> Unable to produce representation for value.\033[0m\n");
1182  } else {
1183  fprintf(stdout, formatStr, AS_CSTRING(result));
1184  }
1185  }
1186  krk_resetStack();
1187  free(allData);
1188  }
1189 
1190  (void)blockWidth;
1191  }
1192  }
1193 
1194  if (vm.globalFlags & KRK_GLOBAL_CALLGRIND) {
1195  fclose(vm.callgrindFile);
1196  vm.globalFlags &= ~(KRK_GLOBAL_CALLGRIND);
1197 
1198  krk_resetStack();
1199  krk_startModule("<callgrind>");
1200  krk_attachNamedObject(&krk_currentThread.module->fields, "filename", (KrkObj*)S(CALLGRIND_TMP_FILE));
1201  krk_interpret(
1202  "from callgrind import processFile\n"
1203  "import kuroko\n"
1204  "import os\n"
1205  "processFile(filename, os.getpid(), ' '.join(kuroko.argv))","<callgrind>");
1206  }
1207 
1208  krk_freeVM();
1209 
1210  if (IS_INTEGER(result)) return AS_INTEGER(result);
1211 
1212  return 0;
1213 }
Structures and enums for bytecode chunks.
KrkCodeObject * krk_compile(const char *src, char *fileName)
Compile a source string to bytecode.
Definition: compiler.c:3986
Exported methods for the source compiler.
Functions for debugging bytecode execution.
#define KRK_BREAKPOINT_ONCE
Definition: debug.h:227
void krk_debug_disableSingleStep(void)
Disable single stepping in the current thread.
Definition: debug.c:513
int krk_debug_examineBreakpoint(int breakIndex, KrkCodeObject **funcOut, size_t *offsetOut, int *flagsOut, int *enabledOut)
Retreive information on a breakpoint.
Definition: debug.c:569
int krk_debug_enableBreakpoint(int breakpointId)
Enable a breakpoint.
Definition: debug.c:388
void krk_debug_dumpTraceback(void)
Safely dump a traceback to stderr.
Definition: debug.c:497
int krk_debug_removeBreakpoint(int breakpointId)
Remove a breakpoint from the breakpoint table.
Definition: debug.c:418
int krk_debug_registerCallback(KrkDebugCallback hook)
Register a debugger callback.
Definition: debug.c:563
int krk_debug_disableBreakpoint(int breakpointId)
Disable a breakpoint.
Definition: debug.c:401
size_t krk_disassembleInstruction(FILE *f, KrkCodeObject *func, size_t offset)
Print a disassembly of a single opcode instruction.
Definition: debug.c:248
int krk_debug_addBreakpointFileLine(KrkString *filename, size_t line, int flags)
Add a breakpoint to the given line of a file.
Definition: debug.c:348
#define KRK_BREAKPOINT_REPEAT
Definition: debug.h:228
#define KRK_BREAKPOINT_NORMAL
Definition: debug.h:226
void krk_debug_dumpStack(FILE *f, KrkCallFrame *frame)
Print the elements on the stack.
Definition: debug.c:22
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:445
void krk_dumpTraceback(void)
If there is an active exception, print a traceback to stderr.
Definition: exceptions.c:351
Top-level header with configuration macros.
Functions for dealing with garbage collection and memory allocation.
Definitions used by the token scanner.
KrkToken krk_scanToken(KrkScanner *)
Read the next token from the scanner.
Definition: scanner.c:294
KrkScanner krk_initScanner(const char *src)
Initialize the compiler to scan tokens from 'src'.
Definition: scanner.c:10
Represents a managed call state in a VM thread.
Definition: vm.h:46
KrkClosure * closure
Definition: vm.h:47
uint8_t * ip
Definition: vm.h:48
size_t krk_lineNumber(KrkChunk *chunk, size_t offset)
Obtain the line number for a byte offset into a bytecode chunk.
Definition: chunk.c:74
Type object.
Definition: object.h:189
KrkObj * _tostr
__str__ Called to produce a string from an instance
Definition: object.h:204
KrkObj * _reprer
__repr__ Called to create a reproducible string representation of an instance
Definition: object.h:203
KrkCodeObject * function
The codeobject containing the bytecode run when this function is called.
Definition: object.h:171
KrkClosure * krk_newClosure(KrkCodeObject *function, KrkValue globals)
Create a new function object.
Definition: object.c:286
Code object.
Definition: object.h:144
KrkChunk chunk
Bytecode data.
Definition: object.h:151
KrkString * name
Name of the function.
Definition: object.h:152
An object of a class.
Definition: object.h:255
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:339
KrkTable fields
Attributes table.
Definition: object.h:258
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
Definition: obj_list.c:30
The most basic object type.
Definition: object.h:41
Token scanner state.
Definition: scanner.h:138
Immutable sequence of Unicode codepoints.
Definition: object.h:93
KrkString * krk_copyString(const char *chars, size_t length)
Obtain a string object representation of the given C string.
Definition: object.c:221
KrkString * krk_takeString(char *chars, size_t length)
Yield ownership of a C string to the GC and obtain a string object.
Definition: object.c:205
char * chars
UTF8 canonical data.
Definition: object.h:97
size_t length
String length in bytes.
Definition: object.h:95
One (key,value) pair in a table.
Definition: table.h:20
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
Definition: table.c:178
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
Definition: vm.c:839
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:825
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:189
size_t frameCount
Definition: vm.h:165
KrkValue * stack
Definition: vm.h:167
KrkValue * stackTop
Definition: vm.h:168
int flags
Definition: vm.h:174
KrkInstance * module
Definition: vm.h:172
A token from the scanner.
Definition: scanner.h:122
void krk_freeVM(void)
Release resources from the VM.
Definition: vm.c:992
void krk_initVM(int flags)
Initialize the VM at program startup.
Definition: vm.c:904
Stack reference or primative value.
void krk_printValue(FILE *f, KrkValue value)
Print a string representation of a value.
Definition: value.c:33
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:275
KrkValue krk_valueGetAttribute(KrkValue value, char *name)
Obtain a property of an object by name.
Definition: vm.c:1805
Inline flexible string array.
Definition: util.h:150
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
Definition: util.h:348
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:292
Core API for the bytecode virtual machine.
int krk_importModule(KrkString *name, KrkString *runAs)
Load the dotted name name with the final element as runAs.
Definition: vm.c:1416
void krk_resetStack(void)
Reset the current thread's stack state to the top level.
Definition: vm.c:124
KrkValue krk_callStack(int argCount)
Call a callable on the stack with argCount arguments.
Definition: vm.c:763
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
KrkValue krk_callNativeOnStack(size_t argCount, const KrkValue *stackArgs, int hasKw, NativeFn native)
Call a native function using a reference to stack arguments safely.
Definition: vm.c:637
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:170
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
KrkValue krk_valueGetAttribute_default(KrkValue value, char *name, KrkValue defaultVal)
See krk_valueGetAttribute.
Definition: vm.c:1816
void krk_setMaximumRecursionDepth(size_t maxDepth)
Set the maximum recursion call depth.
Definition: vm.c:894
void krk_swap(int distance)
Swap the top of the stack of the value distance slots down.
Definition: vm.c:184
KrkValue krk_runfile(const char *fileName, char *fromFile)
Load and run a source file and return when execution completes.
Definition: vm.c:3246
KrkValue krk_dirObject(int argc, const KrkValue argv[], int hasKw)
Obtain a list of properties for an object.
Definition: builtins.c:13
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:157
KrkInstance * krk_startModule(const char *name)
Set up a new module object in the current thread.
Definition: vm.c:3219
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
Definition: vm.c:771
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:178
KrkValue krk_interpret(const char *src, char *fromFile)
Compile and execute a source code input.
Definition: vm.c:3229