17 #include "simple-repl.h"
20 #define DEBUGOUT(...) fprintf(stderr, __VA_ARGS__)
28 } __attribute__((packed));
44 } __attribute__((packed));
49 } __attribute__((packed));
53 NativeFn ListContains;
59 static void _initListFunctions(
void) {
64 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"pop")), &_list_pop);
65 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"append")), &_list_append);
66 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"__contains__")), &_list_contains);
67 krk_tableGet(&
vm.baseClasses->listClass->methods, OBJECT_VAL(S(
"index")), &_list_index);
68 ListPop = AS_NATIVE(_list_pop)->function;
69 ListAppend = AS_NATIVE(_list_append)->function;
70 ListContains = AS_NATIVE(_list_contains)->function;
71 ListIndex = AS_NATIVE(_list_index)->function;
74 static void findInterpreter(
char * argv[]) {
76 vm.binpath = strdup(_pgmptr);
79 char * binpath = realpath(
"/proc/self/exe", NULL);
80 if (!binpath || (access(binpath, X_OK) != 0)) {
81 if (strchr(argv[0],
'/')) {
82 binpath = realpath(argv[0], NULL);
85 char * _path = strdup(getenv(
"PATH"));
88 char * next = strchr(path,
':');
89 if (next) *next++ =
'\0';
92 snprintf(tmp, 4096,
"%s/%s", path, argv[0]);
93 if (access(tmp, X_OK) == 0) {
94 binpath = strdup(tmp);
103 vm.binpath = binpath;
109 static size_t available = 0;
110 static size_t count = 0;
111 static size_t internString(
KrkString * str) {
112 for (
size_t i = 0; i < count; ++i) {
113 if (myStrings[i] == str)
return i;
116 if (count + 1 > available) {
117 available = (available == 0) ? 8 : (available * 2);
118 myStrings = realloc(myStrings,available *
sizeof(
KrkString*));
121 myStrings[count] = str;
125 static int doStringTable(FILE * out) {
126 uint32_t stringCount = count;
127 fwrite(&stringCount, 1,
sizeof(uint32_t), out);
129 for (
size_t i = 0; i < count; ++i) {
130 uint32_t strLen = myStrings[i]->
length;
131 fwrite(&strLen, 1,
sizeof(uint32_t), out);
132 fwrite(myStrings[i]->chars, 1, strLen, out);
138 #define WRITE_INTEGER(i) _writeInteger(out, i)
139 static void _writeInteger(FILE* out, krk_integer_type i) {
140 if (i >= 0 && i < 256) { \
141 fwrite((uint8_t[]){
'i',i}, 1, 2, out);
146 memcpy(&data[1], &value,
sizeof(int64_t));
147 fwrite(data, 1, 9, out);
151 #define WRITE_FLOATING(f) _writeFloating(out, f)
152 static void _writeFloating(FILE * out,
double f) {
154 memcpy(&doubleOut, &f,
sizeof(
double));
155 fwrite(
"d", 1, 1, out);
156 fwrite(&doubleOut, 1,
sizeof(uint64_t), out);
159 #define WRITE_KWARGS(k) fwrite("k",1,1,out);
161 #define WRITE_STRING(s) _writeString(out, s)
162 static void _writeString(FILE * out,
KrkString * s) {
163 uint32_t ind = internString(s);
165 fwrite((uint8_t[]){
's',(uint8_t)ind}, 1, 2, out);
168 fwrite(&ind,1,
sizeof(uint32_t),out);
172 #define WRITE_BYTES(b) _writeBytes(out,b)
173 static void _writeBytes(FILE * out,
KrkBytes * b) {
175 fwrite((uint8_t[]){
'b', (uint8_t)b->
length}, 1, 2, out);
180 fwrite(&len, 1,
sizeof(uint32_t), out);
185 #define WRITE_FUNCTION(f) _writeFunction(out,f)
190 if (!IS_INTEGER(index)) {
191 fprintf(stderr,
"Internal error: Expected int from list.index, got '%s'\n",
krk_typeName(index));
194 krk_integer_type i = AS_INTEGER(index);
196 fprintf(stderr,
"Internal error: expected an index, not %ld\n", (
unsigned long)i);
200 fwrite((uint8_t[]){
'f',(uint8_t)i},1,2,out);
204 fwrite(&val,1,
sizeof(uint32_t),out);
208 static int doFirstPass(FILE * out) {
211 while (AS_LIST(UnseenFunctions)->count) {
212 KrkValue nextFunc = ListPop(2,(
KrkValue[]){UnseenFunctions,INTEGER_VAL(0)},0);
214 ListAppend(2,(
KrkValue[]){SeenFunctions,nextFunc},0);
219 if (func->name) internString(func->name);
220 if (func->docstring) internString(func->docstring);
221 if (func->qualname) internString(func->qualname);
223 for (
size_t i = 0; i < (size_t)func->potentialPositionals + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); ++i) {
224 internString(AS_STRING(func->positionalArgNames.values[i]));
227 for (
size_t i = 0; i < (size_t)func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) {
228 internString(AS_STRING(func->keywordArgNames.values[i]));
231 for (
size_t i = 0; i < func->chunk.constants.count; ++i) {
232 KrkValue value = func->chunk.constants.values[i];
233 if (IS_OBJECT(value)) {
234 if (IS_STRING(value)) {
235 internString(AS_STRING(value));
236 }
else if (IS_codeobject(value)) {
240 if (IS_BOOLEAN(boolResult) && AS_BOOLEAN(boolResult) == 0) {
241 ListAppend(2,(
KrkValue[]){UnseenFunctions,value},0);
254 static int doSecondPass(FILE * out) {
257 uint32_t functionCount = AS_LIST(SeenFunctions)->count;
258 fwrite(&functionCount, 1,
sizeof(uint32_t), out);
260 for (
size_t funcIndex = 0; funcIndex < AS_LIST(SeenFunctions)->count; ++funcIndex) {
261 KrkCodeObject * func = AS_codeobject(AS_LIST(SeenFunctions)->values[funcIndex]);
266 func->
name ? internString(func->
name) : UINT32_MAX,
267 func->docstring ? internString(func->docstring) : UINT32_MAX,
268 func->qualname ? internString(func->qualname) : UINT32_MAX,
271 func->potentialPositionals,
273 func->localNameCount,
275 func->chunk.linesCount,
276 func->chunk.constants.count,
287 for (
size_t i = 0; i < (size_t)func->
keywordArgs + !!(func->
obj.
flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); ++i) {
292 fwrite(func->
chunk.code, 1, func->
chunk.count, out);
295 for (
size_t i = 0; i < func->
chunk.linesCount; ++i) {
297 func->
chunk.lines[i].startOffset,
298 func->
chunk.lines[i].line
303 for (
size_t i = 0; i < func->
chunk.constants.
count; ++i) {
305 switch (KRK_VAL_TYPE(*val)) {
307 switch (AS_OBJECT(*val)->type) {
309 WRITE_STRING(AS_STRING(*val));
312 WRITE_BYTES(AS_BYTES(*val));
314 case KRK_OBJ_CODEOBJECT:
315 WRITE_FUNCTION(AS_codeobject(*val));
319 "Invalid object found in constants table,"
320 "this marashal format can not store '%s'\n",
326 WRITE_KWARGS(AS_INTEGER(*val));
328 case KRK_VAL_INTEGER:
329 WRITE_INTEGER(AS_INTEGER(*val));
332 if (IS_FLOATING(*val)) {
333 WRITE_FLOATING(AS_FLOATING(*val));
337 "Invalid value found in constants table,"
338 "this marashal format can not store '%s'\n",
348 static int compileFile(
char * fileName) {
350 FILE * f = fopen(fileName,
"r");
352 fprintf(stderr,
"%s: %s\n", fileName, strerror(errno));
356 fseek(f, 0, SEEK_END);
357 size_t size = ftell(f);
358 fseek(f, 0, SEEK_SET);
359 char * buf = malloc(size + 1);
360 if (fread(buf, 1, size, f) != size) {
361 fprintf(stderr,
"%s: %s\n", fileName, strerror(errno));
367 FILE * out = fopen(
"out.kbc",
"w");
374 fprintf(stderr,
"%s: exception during compilation:\n", fileName);
385 fwrite(&header, 1,
sizeof(header), out);
393 if (doFirstPass(out))
return 1;
394 if (doStringTable(out))
return 1;
395 if (doSecondPass(out))
return 1;
403 static KrkValue valueFromConstant(
int i, FILE * inFile) {
404 uint8_t c = fgetc(inFile);
405 DEBUGOUT(
" %4lu: ", (
unsigned long)i);
409 int64_t inVal = (c ==
'i') ? fgetc(inFile) : 0;
410 if (c ==
'I') assert(fread(&inVal, 1,
sizeof(int64_t), inFile) ==
sizeof(int64_t));
411 DEBUGOUT(
"int %lld\n", (
long long)inVal);
412 return INTEGER_VAL(inVal);
416 uint32_t ind = (c ==
's') ? fgetc(inFile) : 0;
417 if (c ==
'S') assert(fread(&ind, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
418 KrkValue valOut = AS_LIST(StringTable)->values[ind];
420 fprintf(stderr,
"str #%lu ", (
unsigned long)ind);
422 fprintf(stderr,
"\n");
428 assert(fread(&val, 1,
sizeof(
double), inFile) ==
sizeof(
double));
429 DEBUGOUT(
"float %g\n", val);
430 return FLOATING_VAL(val);
434 uint32_t ind = (c ==
'f') ? fgetc(inFile) : 0;
435 if (c ==
'F') assert(fread(&ind, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
436 DEBUGOUT(
"function #%lu\n", (
unsigned long)ind);
437 return AS_LIST(SeenFunctions)->values[ind];
440 return KWARGS_VAL(0);
443 fprintf(stderr,
"Unknown type '%c'.\n", c);
449 static int readFile(
char * fileName) {
451 FILE * inFile = fopen(fileName,
"r");
453 fprintf(stderr,
"%s: %s\n", fileName, strerror(errno));
466 assert(fread(&header, 1,
sizeof(header), inFile) ==
sizeof(header));
468 if (memcmp(header.magic,(uint8_t[]){
'K',
'R',
'K',
'B'},4) != 0)
469 return fprintf(stderr,
"Invalid header.\n"), 1;
471 if (memcmp(header.version,(uint8_t[]){
'1',
'0',
'1',
'2'},4) != 0)
472 return fprintf(stderr,
"Bytecode is for a different version.\n"), 2;
475 uint32_t stringCount;
476 assert(fread(&stringCount, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
478 DEBUGOUT(
"[String Table (count=%lu)]\n", (
unsigned long)stringCount);
479 for (
size_t i = 0; i < (size_t)stringCount; ++i) {
481 assert(fread(&strLen, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
483 char * strVal = malloc(strLen+1);
484 assert(fread(strVal, 1, strLen, inFile) == strLen);
485 strVal[strLen] =
'\0';
491 fprintf(stderr,
"%04lu: ", (
unsigned long)i);
493 fprintf(stderr,
" (len=%lu)\n", (
unsigned long)strLen);
498 uint32_t functionCount;
499 assert(fread(&functionCount, 1,
sizeof(uint32_t), inFile) ==
sizeof(uint32_t));
501 DEBUGOUT(
"[Code Objects (count=%lu)]\n", (
unsigned long)functionCount);
503 for (
size_t i = 0; i < (size_t)functionCount; ++i) {
509 for (
size_t i = 0; i < (size_t)functionCount; ++i) {
511 KrkCodeObject *
self = AS_codeobject(AS_LIST(SeenFunctions)->values[i]);
514 assert(fread(&
function, 1,
sizeof(
function), inFile) ==
sizeof(
function));
516 if (
function.nameInd != UINT32_MAX) {
517 self->name = AS_STRING(AS_LIST(StringTable)->values[
function.nameInd]);
519 self->name = S(
"__main__");
523 fprintf(stderr,
"<");
525 fprintf(stderr,
">\n");
528 if (
function.docInd != UINT32_MAX) {
529 self->docstring = AS_STRING(AS_LIST(StringTable)->values[
function.docInd]);
532 if (
function.qualInd != UINT32_MAX) {
533 self->qualname = AS_STRING(AS_LIST(StringTable)->values[
function.qualInd]);
537 fprintf(stderr,
" Required arguments: %lu\n", (
unsigned long)
function.reqArgs);
538 fprintf(stderr,
" Keyword arguments: %lu\n", (
unsigned long)
function.kwArgs);
539 fprintf(stderr,
" Named locals: %lu\n", (
unsigned long)
function.locals);
540 fprintf(stderr,
" Bytes of bytecode: %lu\n", (
unsigned long)
function.bcSize);
541 fprintf(stderr,
" Line mappings: %lu\n", (
unsigned long)
function.lmSize);
542 fprintf(stderr,
" Constants: %lu\n", (
unsigned long)
function.ctSize);
545 self->requiredArgs =
function.reqArgs;
546 self->keywordArgs =
function.kwArgs;
547 self->obj.flags =
function.flags;
548 self->upvalueCount =
function.upvalues;
549 self->potentialPositionals =
function.posArgs;
551 self->totalArguments =
self->potentialPositionals + !!(
self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS);
554 DEBUGOUT(
" [Positional Arguments]\n");
555 for (
size_t i = 0; i < (size_t)
function.posArgs + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS); i++) {
559 DEBUGOUT(
" [Keyword Arguments]\n");
560 for (
size_t i = 0; i < (size_t)
function.kwArgs + !!(self->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS); i++) {
565 self->chunk.capacity =
function.bcSize;
566 self->chunk.code = malloc(self->chunk.capacity);
567 assert(fread(self->chunk.code, 1, self->chunk.capacity, inFile) == self->chunk.capacity);
568 self->chunk.count =
self->chunk.capacity;
570 self->chunk.linesCapacity =
function.lmSize;
571 self->chunk.lines = malloc(
sizeof(
KrkLineMap) *
function.lmSize);
573 DEBUGOUT(
" [Line Mapping]\n");
574 for (
size_t i = 0; i <
function.lmSize; ++i) {
579 DEBUGOUT(
" %4lu = 0x%04lx\n", (
unsigned long)entry.line, (
unsigned long)entry.startOffset);
581 self->chunk.lines[i].startOffset = entry.startOffset;
582 self->chunk.lines[i].line = entry.line;
584 self->chunk.linesCount =
self->chunk.linesCapacity;
587 DEBUGOUT(
" [Constants Table]\n");
588 for (
size_t i = 0; i <
function.ctSize; i++) {
596 krk_push(AS_LIST(SeenFunctions)->values[0]);
606 if (IS_INTEGER(result))
return AS_INTEGER(result);
608 return runSimpleRepl();
612 int main(
int argc,
char * argv[]) {
614 fprintf(stderr,
"usage: %s path-to-file.krk\n"
615 " %s -r path-to-file.kbc\n",
621 findInterpreter(argv);
623 _initListFunctions();
626 return compileFile(argv[1]);
627 }
else if (argc == 3 && !strcmp(argv[1],
"-r")) {
628 return readFile(argv[2]);
KrkCodeObject * krk_compile(const char *src, 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.
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.
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.
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.
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
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.