17 #include "simple-repl.h"
20 #define DEBUGOUT(...) fprintf(stderr, __VA_ARGS__)
28 } __attribute__((packed));
44 } __attribute__((packed));
49 } __attribute__((packed));
53 NativeFn ListContains;
60 static void _initListFunctions(
void) {
65 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"pop")), &_list_pop);
66 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"append")), &_list_append);
67 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"__contains__")), &_list_contains);
68 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"index")), &_list_index);
69 ListPop = AS_NATIVE(_list_pop)->function;
70 ListAppend = AS_NATIVE(_list_append)->function;
71 ListContains = AS_NATIVE(_list_contains)->function;
72 ListIndex = AS_NATIVE(_list_index)->function;
77 static void findInterpreter(
char * argv[]) {
79 vm.binpath = strdup(_pgmptr);
82 char * binpath = realpath(
"/proc/self/exe", NULL);
83 if (!binpath || (access(binpath, X_OK) != 0)) {
84 if (strchr(argv[0],
'/')) {
85 binpath = realpath(argv[0], NULL);
88 char * _path = strdup(getenv(
"PATH"));
91 char * next = strchr(path,
':');
92 if (next) *next++ =
'\0';
95 snprintf(tmp, 4096,
"%s/%s", path, argv[0]);
96 if (access(tmp, X_OK) == 0) {
97 binpath = strdup(tmp);
106 vm.binpath = binpath;
112 static size_t available = 0;
113 static size_t count = 0;
114 static size_t internString(
KrkString * str) {
115 for (
size_t i = 0; i < count; ++i) {
116 if (myStrings[i] == str)
return i;
119 if (count + 1 > available) {
120 available = (available == 0) ? 8 : (available * 2);
121 myStrings = realloc(myStrings,available *
sizeof(
KrkString*));
124 myStrings[count] = str;
128 static int doStringTable(FILE * out) {
129 uint32_t stringCount = count;
130 fwrite(&stringCount, 1,
sizeof(uint32_t), out);
132 for (
size_t i = 0; i < count; ++i) {
133 uint32_t strLen = myStrings[i]->
length;
134 fwrite(&strLen, 1,
sizeof(uint32_t), out);
135 fwrite(myStrings[i]->chars, 1, strLen, out);
141 #define WRITE_INTEGER(i) _writeInteger(out, i)
142 static void _writeInteger(FILE* out, krk_integer_type i) {
143 if (i >= 0 && i < 256) { \
144 fwrite((uint8_t[]){
'i',i}, 1, 2, out);
149 memcpy(&data[1], &value,
sizeof(int64_t));
150 fwrite(data, 1, 9, out);
154 #define WRITE_FLOATING(f) _writeFloating(out, f)
155 static void _writeFloating(FILE * out,
double f) {
157 memcpy(&doubleOut, &f,
sizeof(
double));
158 fwrite(
"d", 1, 1, out);
159 fwrite(&doubleOut, 1,
sizeof(uint64_t), out);
162 #define WRITE_KWARGS(k) fwrite("k",1,1,out);
164 #define WRITE_STRING(s) _writeString(out, s)
165 static void _writeString(FILE * out,
KrkString * s) {
166 uint32_t ind = internString(s);
168 fwrite((uint8_t[]){
's',(uint8_t)ind}, 1, 2, out);
171 fwrite(&ind,1,
sizeof(uint32_t),out);
175 #define WRITE_BYTES(b) _writeBytes(out,b)
176 static void _writeBytes(FILE * out,
KrkBytes * b) {
178 fwrite((uint8_t[]){
'b', (uint8_t)b->
length}, 1, 2, out);
183 fwrite(&len, 1,
sizeof(uint32_t), out);
188 #define WRITE_FUNCTION(f) _writeFunction(out,f)
193 if (!IS_INTEGER(index)) {
194 fprintf(stderr,
"Internal error: Expected int from list.index, got '%s'\n",
krk_typeName(index));
197 krk_integer_type i = AS_INTEGER(index);
199 fprintf(stderr,
"Internal error: expected an index, not %ld\n", (
unsigned long)i);
203 fwrite((uint8_t[]){
'f',(uint8_t)i},1,2,out);
207 fwrite(&val,1,
sizeof(uint32_t),out);
211 static int doFirstPass(FILE * out) {
214 while (AS_LIST(UnseenFunctions)->count) {
215 KrkValue nextFunc = ListPop(2,(
KrkValue[]){UnseenFunctions,INTEGER_VAL(0)},0);
217 ListAppend(2,(
KrkValue[]){SeenFunctions,nextFunc},0);
222 if (func->name) internString(func->name);
223 if (func->docstring) internString(func->docstring);
224 if (func->qualname) internString(func->qualname);
226 for (
size_t i = 0; i < (size_t)func->potentialPositionals + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); ++i) {
227 internString(AS_STRING(func->positionalArgNames.values[i]));
230 for (
size_t i = 0; i < (size_t)func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) {
231 internString(AS_STRING(func->keywordArgNames.values[i]));
234 for (
size_t i = 0; i < func->chunk.constants.count; ++i) {
235 KrkValue value = func->chunk.constants.values[i];
236 if (IS_OBJECT(value)) {
237 if (IS_STRING(value)) {
238 internString(AS_STRING(value));
239 }
else if (IS_codeobject(value)) {
243 if (IS_BOOLEAN(boolResult) && AS_BOOLEAN(boolResult) == 0) {
244 ListAppend(2,(
KrkValue[]){UnseenFunctions,value},0);
257 static int doSecondPass(FILE * out) {
260 uint32_t functionCount = AS_LIST(SeenFunctions)->count;
261 fwrite(&functionCount, 1,
sizeof(uint32_t), out);
263 for (
size_t funcIndex = 0; funcIndex < AS_LIST(SeenFunctions)->count; ++funcIndex) {
264 KrkCodeObject * func = AS_codeobject(AS_LIST(SeenFunctions)->values[funcIndex]);
269 func->
name ? internString(func->
name) : UINT32_MAX,
270 func->docstring ? internString(func->docstring) : UINT32_MAX,
271 func->qualname ? internString(func->qualname) : UINT32_MAX,
274 func->potentialPositionals,
276 func->localNameCount,
278 func->chunk.linesCount,
279 func->chunk.constants.count,
290 for (
size_t i = 0; i < (size_t)func->
keywordArgs + !!(func->
obj.
flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) {
295 fwrite(func->
chunk.code, 1, func->
chunk.count, out);
298 for (
size_t i = 0; i < func->
chunk.linesCount; ++i) {
300 func->
chunk.lines[i].startOffset,
301 func->
chunk.lines[i].line
306 for (
size_t i = 0; i < func->
chunk.constants.
count; ++i) {
308 switch (KRK_VAL_TYPE(*val)) {
310 switch (AS_OBJECT(*val)->type) {
312 WRITE_STRING(AS_STRING(*val));
315 WRITE_BYTES(AS_BYTES(*val));
317 case KRK_OBJ_CODEOBJECT:
318 WRITE_FUNCTION(AS_codeobject(*val));
326 "Invalid object found in constants table, "
327 "this marashal format can not store '%s'\n",
333 WRITE_KWARGS(AS_INTEGER(*val));
335 case KRK_VAL_INTEGER:
336 WRITE_INTEGER(AS_INTEGER(*val));
339 if (IS_FLOATING(*val)) {
340 WRITE_FLOATING(AS_FLOATING(*val));
344 "Invalid value found in constants table, "
345 "this marashal format can not store '%s'\n",
355 static int compileFile(
char * fileName) {
357 FILE * f = fopen(fileName,
"r");
359 fprintf(stderr,
"%s: %s\n", fileName, strerror(errno));
363 fseek(f, 0, SEEK_END);
364 size_t size = ftell(f);
365 fseek(f, 0, SEEK_SET);
366 char * buf = malloc(size + 1);
367 if (fread(buf, 1, size, f) != size) {
368 fprintf(stderr,
"%s: %s\n", fileName, strerror(errno));
374 FILE * out = fopen(
"out.kbc",
"w");
381 fprintf(stderr,
"%s: exception during compilation:\n", fileName);
392 fwrite(&header, 1,
sizeof(header), out);
400 if (doFirstPass(out))
return 1;
401 if (doStringTable(out))
return 1;
402 if (doSecondPass(out))
return 1;
410 static KrkValue valueFromConstant(
int i, FILE * inFile) {
411 uint8_t c = fgetc(inFile);
412 DEBUGOUT(
" %4lu: ", (
unsigned long)i);
416 int64_t inVal = (c ==
'i') ? fgetc(inFile) : 0;
417 if (c ==
'I') assert(fread(&inVal, 1,
sizeof(int64_t), inFile) ==
sizeof(int64_t));
418 DEBUGOUT(
"int %lld\n", (
long long)inVal);
419 return INTEGER_VAL(inVal);
423 uint32_t ind = (c ==
's') ? fgetc(inFile) : 0;
424 if (c ==
'S') assert(fread(&ind, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
425 KrkValue valOut = AS_LIST(StringTable)->values[ind];
427 fprintf(stderr,
"str #%lu ", (
unsigned long)ind);
429 fprintf(stderr,
"\n");
435 assert(fread(&val, 1,
sizeof(
double), inFile) ==
sizeof(
double));
436 DEBUGOUT(
"float %g\n", val);
437 return FLOATING_VAL(val);
441 uint32_t ind = (c ==
'f') ? fgetc(inFile) : 0;
442 if (c ==
'F') assert(fread(&ind, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
443 DEBUGOUT(
"function #%lu\n", (
unsigned long)ind);
444 return AS_LIST(SeenFunctions)->values[ind];
447 return KWARGS_VAL(0);
453 fprintf(stderr,
"Unknown type '%c'.\n", c);
459 static int readFile(
char * fileName) {
461 FILE * inFile = fopen(fileName,
"r");
463 fprintf(stderr,
"%s: %s\n", fileName, strerror(errno));
476 assert(fread(&header, 1,
sizeof(header), inFile) ==
sizeof(header));
478 if (memcmp(header.magic,(uint8_t[]){
'K',
'R',
'K',
'B'},4) != 0)
479 return fprintf(stderr,
"Invalid header.\n"), 1;
481 if (memcmp(header.version,(uint8_t[]){
'1',
'0',
'1',
'2'},4) != 0)
482 return fprintf(stderr,
"Bytecode is for a different version.\n"), 2;
485 uint32_t stringCount;
486 assert(fread(&stringCount, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
488 DEBUGOUT(
"[String Table (count=%lu)]\n", (
unsigned long)stringCount);
489 for (
size_t i = 0; i < (size_t)stringCount; ++i) {
491 assert(fread(&strLen, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
493 char * strVal = malloc(strLen+1);
494 assert(fread(strVal, 1, strLen, inFile) == strLen);
495 strVal[strLen] =
'\0';
501 fprintf(stderr,
"%04lu: ", (
unsigned long)i);
503 fprintf(stderr,
" (len=%lu)\n", (
unsigned long)strLen);
508 uint32_t functionCount;
509 assert(fread(&functionCount, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
511 DEBUGOUT(
"[Code Objects (count=%lu)]\n", (
unsigned long)functionCount);
513 for (
size_t i = 0; i < (size_t)functionCount; ++i) {
519 for (
size_t i = 0; i < (size_t)functionCount; ++i) {
521 KrkCodeObject *
self = AS_codeobject(AS_LIST(SeenFunctions)->values[i]);
524 assert(fread(&
function, 1,
sizeof(
function), inFile) ==
sizeof(
function));
526 if (
function.nameInd != UINT32_MAX) {
527 self->name = AS_STRING(AS_LIST(StringTable)->values[
function.nameInd]);
529 self->name = S(
"__main__");
533 fprintf(stderr,
"<");
535 fprintf(stderr,
">\n");
538 if (
function.docInd != UINT32_MAX) {
539 self->docstring = AS_STRING(AS_LIST(StringTable)->values[
function.docInd]);
542 if (
function.qualInd != UINT32_MAX) {
543 self->qualname = AS_STRING(AS_LIST(StringTable)->values[
function.qualInd]);
547 fprintf(stderr,
" Required arguments: %lu\n", (
unsigned long)
function.reqArgs);
548 fprintf(stderr,
" Keyword arguments: %lu\n", (
unsigned long)
function.kwArgs);
549 fprintf(stderr,
" Named locals: %lu\n", (
unsigned long)
function.locals);
550 fprintf(stderr,
" Bytes of bytecode: %lu\n", (
unsigned long)
function.bcSize);
551 fprintf(stderr,
" Line mappings: %lu\n", (
unsigned long)
function.lmSize);
552 fprintf(stderr,
" Constants: %lu\n", (
unsigned long)
function.ctSize);
555 self->requiredArgs =
function.reqArgs;
556 self->keywordArgs =
function.kwArgs;
557 self->obj.flags =
function.flags;
558 self->upvalueCount =
function.upvalues;
559 self->potentialPositionals =
function.posArgs;
561 self->totalArguments =
self->potentialPositionals + !!(
self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
564 DEBUGOUT(
" [Positional Arguments]\n");
565 for (
size_t i = 0; i < (size_t)
function.posArgs + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); i++) {
569 DEBUGOUT(
" [Keyword Arguments]\n");
570 for (
size_t i = 0; i < (size_t)
function.kwArgs + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); i++) {
575 self->chunk.capacity =
function.bcSize;
576 self->chunk.code = malloc(self->chunk.capacity);
577 assert(fread(self->chunk.code, 1, self->chunk.capacity, inFile) == self->chunk.capacity);
578 self->chunk.count =
self->chunk.capacity;
580 self->chunk.linesCapacity =
function.lmSize;
581 self->chunk.lines = malloc(
sizeof(
KrkLineMap) *
function.lmSize);
583 DEBUGOUT(
" [Line Mapping]\n");
584 for (
size_t i = 0; i <
function.lmSize; ++i) {
589 DEBUGOUT(
" %4lu = 0x%04lx\n", (
unsigned long)entry.line, (
unsigned long)entry.startOffset);
591 self->chunk.lines[i].startOffset = entry.startOffset;
592 self->chunk.lines[i].line = entry.line;
594 self->chunk.linesCount =
self->chunk.linesCapacity;
597 DEBUGOUT(
" [Constants Table]\n");
598 for (
size_t i = 0; i <
function.ctSize; i++) {
606 krk_push(AS_LIST(SeenFunctions)->values[0]);
616 if (IS_INTEGER(result))
return AS_INTEGER(result);
618 return runSimpleRepl();
622 int main(
int argc,
char * argv[]) {
624 fprintf(stderr,
"usage: %s path-to-file.krk\n"
625 " %s -r path-to-file.kbc\n",
631 findInterpreter(argv);
633 _initListFunctions();
636 return compileFile(argv[1]);
637 }
else if (argc == 3 && !strcmp(argv[1],
"-r")) {
638 return readFile(argv[2]);
KrkCodeObject * krk_compile(const char *src, const char *fileName)
Compile a source string to bytecode.
Exported methods for the source compiler.
void krk_dumpTraceback(void)
If there is an active exception, print a traceback to stderr.
Top-level header with configuration macros.
Immutable sequence of bytes.
size_t length
Length of data in bytes.
uint8_t * bytes
Pointer to separately-stored bytes data.
KrkClosure * krk_newClosure(KrkCodeObject *function, KrkValue globals)
Create a new function object.
unsigned short potentialPositionals
Precalculated positional arguments for complex argument processing.
KrkChunk chunk
Bytecode data.
KrkValueArray positionalArgNames
Array of names for positional arguments (and *args)
unsigned short keywordArgs
Arity of keyword (default) arguments.
KrkValueArray keywordArgNames
Array of names for keyword-only arguments (and **kwargs)
KrkCodeObject * krk_newCodeObject(void)
Create a new, uninitialized code object.
KrkString * name
Name of the function.
Map entry of instruction offsets to line numbers.
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
uint16_t flags
General object flags, mostly related to garbage collection.
Immutable sequence of Unicode codepoints.
KrkString * krk_takeString(char *chars, size_t length)
Yield ownership of a C string to the GC and obtain a string object.
size_t length
String length in bytes.
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
int krk_tableGet_fast(KrkTable *table, struct KrkString *str, KrkValue *value)
Obtain the value associated with a string key in a table.
void krk_initVM(int flags)
Initialize the VM at program startup.
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Stack reference or primative value.
const char * krk_typeName(KrkValue value)
Get the name of the type of a value.
int krk_callValue(KrkValue callee, int argCount, int callableOnStack)
Call a callable value in the current stack context.
static int krk_valuesSame(KrkValue a, KrkValue b)
Compare two values by identity.
void krk_printValueSafe(FILE *f, KrkValue value)
Print a value without calling the VM.
Utilities for creating native bindings.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
KrkValue krk_runNext(void)
Continue VM execution until the next exit trigger.
#define vm
Convenience macro for namespacing.
KrkValue krk_pop(void)
Pop the top of the stack.
void krk_push(KrkValue value)
Push a stack value.
KrkInstance * krk_startModule(const char *name)
Set up a new module object in the current thread.
KrkValue krk_peek(int distance)
Peek down from the top of the stack.