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