mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-30 23:23:07 +00:00 
			
		
		
		
	Add get and set instructions. GC is still buggy and currently
crashes everything all the time. :(
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # TIL | ||||
|  | ||||
| CFLAGS=-std=c99 -Wall -Wextra -g | ||||
| CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g | ||||
|  | ||||
| TARGET=interp | ||||
| PREFIX=/usr/local | ||||
|   | ||||
							
								
								
									
										81
									
								
								compile.c
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								compile.c
									
									
									
									
									
								
							| @@ -87,11 +87,11 @@ static FormOptions FormOptionsDefault() { | ||||
| /* Create some helpers that allows us to push more than just raw bytes | ||||
|  * to the byte buffer. This helps us create the byte code for the compiled | ||||
|  * functions. */ | ||||
| BufferDefine(UInt32, uint32_t); | ||||
| BufferDefine(Int32, int32_t); | ||||
| BufferDefine(Number, Number); | ||||
| BufferDefine(UInt16, uint16_t); | ||||
| BufferDefine(Int16, int16_t); | ||||
| BufferDefine(UInt32, uint32_t) | ||||
| BufferDefine(Int32, int32_t) | ||||
| BufferDefine(Number, Number) | ||||
| BufferDefine(UInt16, uint16_t) | ||||
| BufferDefine(Int16, int16_t) | ||||
|  | ||||
| /* If there is an error during compilation, | ||||
|  * jump back to start */ | ||||
| @@ -553,7 +553,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) { | ||||
|  * called with n arguments, the number of arguments is written | ||||
|  * after the op code, followed by those arguments. | ||||
|  * | ||||
|  * This makes a few assumptions about the opertors. One, no side | ||||
|  * This makes a few assumptions about the operators. One, no side | ||||
|  * effects. With this assumptions, if the result of the operator | ||||
|  * is unused, it's calculation can be ignored (the evaluation of | ||||
|  * its argument is still carried out, but their results can | ||||
| @@ -576,7 +576,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, | ||||
|         if (form->count < 2) { | ||||
|             if (op0 < 0) { | ||||
|                 if (opn < 0) CError(c, "This operator does not take 0 arguments."); | ||||
|                 /* Use multiple form */ | ||||
|                 /* Use multiple form of op */ | ||||
|                 BufferPushUInt16(c->vm, buffer, opn); | ||||
|                 BufferPushUInt16(c->vm, buffer, ret.index); | ||||
|                 BufferPushUInt16(c->vm, buffer, 0); | ||||
| @@ -587,7 +587,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, | ||||
|         } else if (form->count == 2) { | ||||
|             if (op1 < 0) { | ||||
|                 if (opn < 0) CError(c, "This operator does not take 1 argument."); | ||||
|                 /* Use multiple form */ | ||||
|                 /* Use multiple form of op */ | ||||
|                 BufferPushUInt16(c->vm, buffer, opn); | ||||
|                 BufferPushUInt16(c->vm, buffer, ret.index); | ||||
|                 BufferPushUInt16(c->vm, buffer, 1); | ||||
| @@ -642,6 +642,39 @@ static Slot CompileGreaterThanOrEqual(Compiler * c, FormOptions opts, Array * fo | ||||
| static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) { | ||||
|     return CompileOperator(c, opts, form, VM_OP_FLS, VM_OP_NOT, -1, -1, 0); | ||||
| } | ||||
| static Slot CompileGet(Compiler * c, FormOptions opts, Array * form) { | ||||
| 	return CompileOperator(c, opts, form, -1, -1, VM_OP_GET, -1, 0); | ||||
| } | ||||
|  | ||||
| /* Associative set */ | ||||
| static Slot CompileSet(Compiler * c, FormOptions opts, Array * form) { | ||||
|     Buffer * buffer = c->buffer; | ||||
|     FormOptions subOpts = FormOptionsDefault(); | ||||
|     Slot ds, key, val; | ||||
|     if (form->count != 4) CError(c, "Set expects 4 arguments"); | ||||
|     if (opts.resultUnused) { | ||||
|         ds = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[1])); | ||||
|     } else { | ||||
|         subOpts = opts; | ||||
|         subOpts.isTail = 0; | ||||
|         ds = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[1])); | ||||
|         subOpts = FormOptionsDefault(); | ||||
|     } | ||||
|     key = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[2])); | ||||
|    	val = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[3])); | ||||
|     BufferPushUInt16(c->vm, buffer, VM_OP_SET); | ||||
|     BufferPushUInt16(c->vm, buffer, ds.index); | ||||
|     BufferPushUInt16(c->vm, buffer, key.index); | ||||
|     BufferPushUInt16(c->vm, buffer,	val.index); | ||||
|     CompilerDropSlot(c, c->tail, key); | ||||
|     CompilerDropSlot(c, c->tail, val); | ||||
|     if (opts.resultUnused) { | ||||
|         CompilerDropSlot(c, c->tail, ds); | ||||
|         return NilSlot(); | ||||
|     } else { | ||||
| 		return ds; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Compile an assignment operation */ | ||||
| static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value right) { | ||||
| @@ -939,7 +972,7 @@ static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) { | ||||
| } | ||||
|  | ||||
| /* Assignment special */ | ||||
| static Slot CompileSet(Compiler * c, FormOptions opts, Array * form) { | ||||
| static Slot CompileVar(Compiler * c, FormOptions opts, Array * form) { | ||||
|     if (form->count != 3) | ||||
|         CError(c, "Assignment expects 2 arguments"); | ||||
|     return CompileAssign(c, opts, form->data[1], form->data[2]); | ||||
| @@ -968,7 +1001,6 @@ static SpecialFormHelper GetSpecial(Array * form) { | ||||
|             case '>': return CompileGreaterThan; | ||||
|             case '<': return CompileLessThan; | ||||
|             case '=': return CompileEquals; | ||||
|             case '\'': return CompileQuote; | ||||
|             default: | ||||
|                        break; | ||||
|         } | ||||
| @@ -991,6 +1023,14 @@ static SpecialFormHelper GetSpecial(Array * form) { | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case 'g': | ||||
|             { | ||||
| 				if (VStringSize(name) == 3 && | ||||
|     				    name[1] == 'e' && | ||||
|     				    name[2] == 't') { | ||||
| 					return CompileGet; | ||||
| 			    } | ||||
|             } | ||||
|         case 'd': | ||||
|             { | ||||
|                 if (VStringSize(name) == 2 && | ||||
| @@ -1053,6 +1093,15 @@ static SpecialFormHelper GetSpecial(Array * form) { | ||||
|                     return CompileWhile; | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case ':': | ||||
|             { | ||||
|                 if (VStringSize(name) == 2 && | ||||
|                         name[1] == '=') { | ||||
|                     return CompileVar; | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| @@ -1152,7 +1201,7 @@ void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) { | ||||
|     Value func; | ||||
|     func.type = TYPE_CFUNCTION; | ||||
|     func.data.cfunction = f; | ||||
|     return CompilerAddGlobal(c, name, func); | ||||
|     CompilerAddGlobal(c, name, func); | ||||
| } | ||||
|  | ||||
| /* Compile interface. Returns a function that evaluates the | ||||
| @@ -1175,8 +1224,12 @@ Func * CompilerCompile(Compiler * c, Value form) { | ||||
|         uint32_t envSize = c->env->count; | ||||
|         FuncEnv * env = VMAlloc(c->vm, sizeof(FuncEnv)); | ||||
|         Func * func = VMAlloc(c->vm, sizeof(Func)); | ||||
|         env->values = VMAlloc(c->vm, sizeof(Value) * envSize); | ||||
|         memcpy(env->values, c->env->data, envSize * sizeof(Value)); | ||||
|         if (envSize) { | ||||
|         	env->values = VMAlloc(c->vm, sizeof(Value) * envSize); | ||||
|         	memcpy(env->values, c->env->data, envSize * sizeof(Value)); | ||||
|         } else { | ||||
| 			env->values = NULL; | ||||
|         } | ||||
|         env->stackOffset = envSize; | ||||
|         env->thread = NULL; | ||||
|         func->parent = NULL; | ||||
| @@ -1201,7 +1254,7 @@ int CompileMacroExpand(VM * vm, Value x, Dictionary * macros, Value * out) { | ||||
|         VMLoad(vm, macroFn); | ||||
|         if (VMStart(vm)) { | ||||
|             /* We encountered an error during parsing */         | ||||
|             return 1;; | ||||
|             return 1; | ||||
|         } else { | ||||
|             x = vm->ret; | ||||
|         } | ||||
|   | ||||
							
								
								
									
										16
									
								
								datatypes.h
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								datatypes.h
									
									
									
									
									
								
							| @@ -43,6 +43,7 @@ typedef struct Parser Parser; | ||||
| typedef struct ParseState ParseState; | ||||
| typedef struct Scope Scope; | ||||
| typedef struct Compiler Compiler; | ||||
| typedef struct StackFrame StackFrame; | ||||
|  | ||||
| union ValueData { | ||||
|     Boolean boolean; | ||||
| @@ -116,6 +117,15 @@ struct DictBucket { | ||||
|     DictBucket * next; | ||||
| }; | ||||
|  | ||||
| struct StackFrame { | ||||
|     Value callee; | ||||
|     uint16_t size; | ||||
|     uint16_t prevSize; | ||||
|     uint16_t ret; | ||||
|     FuncEnv * env; | ||||
|     uint16_t * pc; | ||||
| }; | ||||
|  | ||||
| struct VM { | ||||
|     /* Garbage collection */ | ||||
|     void * blocks; | ||||
| @@ -127,7 +137,7 @@ struct VM { | ||||
|     uint16_t * pc; | ||||
|     Array * thread; | ||||
|     Value * base; | ||||
|     Value root; /* Global state - prevents GC cleanup */ | ||||
|     StackFrame * frame; | ||||
|     /* Return state */ | ||||
|     const char * error; | ||||
|     jmp_buf jump; | ||||
| @@ -206,7 +216,7 @@ enum OpCode { | ||||
|     VM_OP_DVM,        /* 0x001f */ | ||||
|     VM_OP_RTN,        /* 0x0020 */ | ||||
|     VM_OP_SET,        /* 0x0021 */ | ||||
|     VM_OP_GET,        /* 0x0022 */ | ||||
|     VM_OP_GET         /* 0x0022 */ | ||||
| }; | ||||
|  | ||||
| #endif /* end of include guard: DATATYPES_H_PJJ035NT */ | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										8
									
								
								disasm.c
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								disasm.c
									
									
									
									
									
								
							| @@ -67,7 +67,7 @@ void dasm(FILE * out, uint16_t *byteCode, uint32_t len) { | ||||
| 	uint16_t *current = byteCode; | ||||
| 	uint16_t *end = byteCode + len; | ||||
|  | ||||
| 	fprintf(out, "----- ASM BYTECODE AT %p -----\n", byteCode); | ||||
| 	fprintf(out, "----- ASM BYTECODE START -----\n"); | ||||
|  | ||||
| 	while (current < end) { | ||||
| 		switch (*current) { | ||||
| @@ -196,6 +196,12 @@ void dasm(FILE * out, uint16_t *byteCode, uint32_t len) { | ||||
|         case VM_OP_RTN: | ||||
| 			current += dasmPrintFixedOp(out, current, "returnNil", 0); | ||||
|             break; | ||||
|         case VM_OP_GET: | ||||
|             current += dasmPrintFixedOp(out, current, "get", 3); | ||||
|             break; | ||||
|         case VM_OP_SET: | ||||
|             current += dasmPrintFixedOp(out, current, "set", 3); | ||||
|             break; | ||||
| 		} | ||||
| 		fprintf(out, "\n"); | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										2
									
								
								ds.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								ds.h
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ uint8_t * BufferToString(VM * vm, Buffer * buffer); | ||||
| #define BufferDefine(name, type) \ | ||||
| static void BufferPush##name (VM * vm, Buffer * buffer, type x) { \ | ||||
|     union { type t; uint8_t bytes[sizeof(type)]; } u; \ | ||||
|     u.t = x; return BufferAppendData(vm, buffer, u.bytes, sizeof(type)); \ | ||||
|     u.t = x; BufferAppendData(vm, buffer, u.bytes, sizeof(type)); \ | ||||
| } | ||||
|  | ||||
| /****/ | ||||
|   | ||||
							
								
								
									
										8
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								main.c
									
									
									
									
									
								
							| @@ -37,7 +37,7 @@ void debugRepl() { | ||||
|   for (;;) { | ||||
|  | ||||
|     /* Run garbage collection */ | ||||
|     VMMaybeCollect(&vm); | ||||
| /*    VMMaybeCollect(&vm);*/ | ||||
|  | ||||
|     /* Reset state */ | ||||
|     ParserInit(&p, &vm); | ||||
| @@ -85,6 +85,11 @@ void debugRepl() { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     /* Print asm */ | ||||
|     printf("\n"); | ||||
|     dasmFunc(stdout, func.data.func); | ||||
|     printf("\n"); | ||||
|  | ||||
|     /* Execute function */ | ||||
|     VMLoad(&vm, func); | ||||
|     if (VMStart(&vm)) { | ||||
| @@ -103,4 +108,5 @@ void debugRepl() { | ||||
| int main() { | ||||
|   printf("Super cool interpreter v0.0\n"); | ||||
|   debugRepl(); | ||||
|   return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								parse.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								parse.c
									
									
									
									
									
								
							| @@ -130,7 +130,7 @@ static int isWhitespace(uint8_t c) { | ||||
| static int isSymbolChar(uint8_t c) { | ||||
|     if (c >= 'a' && c <= 'z') return 1; | ||||
|     if (c >= 'A' && c <= 'Z') return 1; | ||||
|     if (c >= '0' && c <= '9') return 1; | ||||
|     if (c >= '0' && c <= ':') return 1; | ||||
|     if (c >= '<' && c <= '@') return 1; | ||||
|     if (c >= '*' && c <= '/') return 1; | ||||
|     if (c >= '#' && c <= '&') return 1; | ||||
|   | ||||
							
								
								
									
										142
									
								
								value.c
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								value.c
									
									
									
									
									
								
							| @@ -148,26 +148,26 @@ uint8_t * ValueToString(VM * vm, Value x) { | ||||
|         case TYPE_NUMBER: | ||||
|             return NumberToString(vm, x.data.number); | ||||
|         case TYPE_ARRAY: | ||||
|             return StringDescription(vm, "array", 5, x.data.array); | ||||
|             return StringDescription(vm, "array", 5, x.data.pointer); | ||||
|         case TYPE_FORM: | ||||
|             return StringDescription(vm, "form", 4, x.data.array); | ||||
|             return StringDescription(vm, "form", 4, x.data.pointer); | ||||
|         case TYPE_STRING: | ||||
|         case TYPE_SYMBOL: | ||||
|             return x.data.string; | ||||
|         case TYPE_BYTEBUFFER: | ||||
|             return StringDescription(vm, "buffer", 6, x.data.buffer); | ||||
|             return StringDescription(vm, "buffer", 6, x.data.pointer); | ||||
|         case TYPE_CFUNCTION: | ||||
|             return StringDescription(vm, "cfunction", 9, x.data.cfunction); | ||||
|             return StringDescription(vm, "cfunction", 9, x.data.pointer); | ||||
|         case TYPE_FUNCTION: | ||||
|             return StringDescription(vm, "function", 8, x.data.func); | ||||
|             return StringDescription(vm, "function", 8, x.data.pointer); | ||||
|         case TYPE_DICTIONARY: | ||||
|             return StringDescription(vm, "dictionary", 10, x.data.dict); | ||||
|             return StringDescription(vm, "dictionary", 10, x.data.pointer); | ||||
|         case TYPE_FUNCDEF: | ||||
|             return StringDescription(vm, "funcdef", 7, x.data.funcdef); | ||||
|             return StringDescription(vm, "funcdef", 7, x.data.pointer); | ||||
|         case TYPE_FUNCENV: | ||||
|             return StringDescription(vm, "funcenv", 7, x.data.funcenv); | ||||
|             return StringDescription(vm, "funcenv", 7, x.data.pointer); | ||||
|         case TYPE_THREAD: | ||||
|             return StringDescription(vm, "thread", 6, x.data.array); | ||||
|             return StringDescription(vm, "thread", 6, x.data.pointer); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
| @@ -352,23 +352,115 @@ int ValueCompare(Value x, Value y) { | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| /* Get a value out af an associated data structure. Can throw VM error. */ | ||||
| Value ValueGet(VM * vm, Value ds, Value key) { | ||||
| 	switch (ds.type) { | ||||
| 	case TYPE_ARRAY: | ||||
|     case TYPE_FORM: | ||||
|     case TYPE_BYTEBUFFER: | ||||
|     case TYPE_SYMBOL: | ||||
|     case TYPE_STRING: | ||||
|     case TYPE_DICTIONARY: | ||||
|    	case TYPE_FUNCENV: | ||||
|     default: | ||||
|         VMError(vm, "Cannot get."); | ||||
|         break; | ||||
| /* Allow negative indexing to get from end of array like structure */ | ||||
| /* This probably isn't very fast - look at Lua conversion function. | ||||
|  * I would like to keep this standard C for as long as possible, though. */ | ||||
| static int32_t ToIndex(Number raw, int64_t len) { | ||||
| 	int32_t toInt = raw; | ||||
| 	if ((Number) toInt == raw) { | ||||
| 		/* We were able to convert */ | ||||
| 		if (toInt < 0) {	 | ||||
|     		/* Index from end */ | ||||
| 			if (toInt < -len) return -1;	 | ||||
| 			return len + toInt; | ||||
| 		} else {	 | ||||
|     		/* Normal indexing */ | ||||
|     		if (toInt >= len) return -1; | ||||
|         	return toInt; | ||||
| 		} | ||||
| 	} else { | ||||
|         return -1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Set a value in an associative data structure. Can throw VM error. */ | ||||
| int ValueSet(VM * vm, Value ds, Value key, Value value) { | ||||
|  | ||||
| /* Convert a number into a byte. */ | ||||
| static uint8_t NumberToByte(Number raw) { | ||||
| 	if (raw > 255) return 255; | ||||
| 	if (raw < 0) return 0; | ||||
| 	return (uint8_t) raw; | ||||
| } | ||||
|  | ||||
| /* Get a value out af an associated data structure. Can throw VM error. */ | ||||
| Value ValueGet(VM * vm, Value ds, Value key) { | ||||
|     int32_t index; | ||||
|     Value ret; | ||||
| 	switch (ds.type) { | ||||
| 	case TYPE_ARRAY: | ||||
|     case TYPE_FORM: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
| 		index = ToIndex(key.data.number, ds.data.array->count); | ||||
| 		if (index == -1) VMError(vm, "Invalid array access"); | ||||
| 		return ds.data.array->data[index]; | ||||
|     case TYPE_BYTEBUFFER: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
|         index = ToIndex(key.data.number, ds.data.buffer->count); | ||||
| 		if (index == -1) VMError(vm, "Invalid buffer access"); | ||||
| 		ret.type = TYPE_NUMBER; | ||||
| 		ret.data.number = ds.data.buffer->data[index]; | ||||
| 		break; | ||||
|     case TYPE_SYMBOL: | ||||
|     case TYPE_STRING: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
|         index = ToIndex(key.data.number, VStringSize(ds.data.string)); | ||||
| 		if (index == -1) VMError(vm, "Invalid string access"); | ||||
| 		ret.type = TYPE_NUMBER; | ||||
| 		ret.data.number = ds.data.string[index]; | ||||
| 		break; | ||||
|     case TYPE_DICTIONARY: | ||||
|         return DictGet(ds.data.dict, key); | ||||
|    	case TYPE_FUNCENV: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
|         if (ds.data.funcenv->thread) { | ||||
|            	Array * thread = ds.data.funcenv->thread; | ||||
| 			index = ToIndex(key.data.number, vm->frame->size); | ||||
|     		if (index == -1) VMError(vm, "Invalid funcenv access"); | ||||
|     		return thread->data[thread->count + index]; | ||||
|         } else { | ||||
|     		index = ToIndex(key.data.number, ds.data.funcenv->stackOffset); | ||||
|     		if (index == -1) VMError(vm, "Invalid funcenv access"); | ||||
|     		return ds.data.funcenv->values[index]; | ||||
|         } | ||||
|     default: | ||||
|         VMError(vm, "Cannot get."); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| /* Set a value in an associative data structure. Can throw VM error. */ | ||||
| void ValueSet(VM * vm, Value ds, Value key, Value value) { | ||||
|     int32_t index; | ||||
| 	switch (ds.type) { | ||||
| 	case TYPE_ARRAY: | ||||
|     case TYPE_FORM: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
| 		index = ToIndex(key.data.number, ds.data.array->count); | ||||
| 		if (index == -1) VMError(vm, "Invalid array access"); | ||||
| 		ds.data.array->data[index] = value; | ||||
| 		break; | ||||
|     case TYPE_BYTEBUFFER: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
|         VMAssertType(vm, value, TYPE_NUMBER); | ||||
|         index = ToIndex(key.data.number, ds.data.buffer->count); | ||||
| 		if (index == -1) VMError(vm, "Invalid buffer access"); | ||||
| 		ds.data.buffer->data[index] = NumberToByte(value.data.number); | ||||
| 		break; | ||||
|     case TYPE_DICTIONARY: | ||||
|         DictPut(vm, ds.data.dict, key, value); | ||||
|         break; | ||||
|    	case TYPE_FUNCENV: | ||||
|         VMAssertType(vm, key, TYPE_NUMBER); | ||||
|         if (ds.data.funcenv->thread) { | ||||
|            	Array * thread = ds.data.funcenv->thread; | ||||
| 			index = ToIndex(key.data.number, vm->frame->size); | ||||
|     		if (index == -1) VMError(vm, "Invalid funcenv access"); | ||||
|     		thread->data[thread->count + index] = value; | ||||
|         } else { | ||||
|     		index = ToIndex(key.data.number, ds.data.funcenv->stackOffset); | ||||
|     		if (index == -1) VMError(vm, "Invalid funcenv access"); | ||||
|     		ds.data.funcenv->values[index] = value; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         VMError(vm, "Cannot set."); | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								value.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								value.h
									
									
									
									
									
								
							| @@ -11,7 +11,7 @@ int ValueEqual(Value x, Value y); | ||||
|  | ||||
| Value ValueGet(VM * vm, Value ds, Value key); | ||||
|  | ||||
| int ValueSet(VM * vm, Value ds, Value key, Value value); | ||||
| void ValueSet(VM * vm, Value ds, Value key, Value value); | ||||
|  | ||||
| Value ValueLoadCString(VM * vm, const char * string); | ||||
|  | ||||
|   | ||||
							
								
								
									
										184
									
								
								vm.c
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								vm.c
									
									
									
									
									
								
							| @@ -11,17 +11,6 @@ static const char EXPECTED_FUNCTION[] = "Expected function"; | ||||
| static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number"; | ||||
| static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number"; | ||||
|  | ||||
| /* The stack frame data */ | ||||
| typedef struct StackFrame StackFrame; | ||||
| struct StackFrame { | ||||
|     Value callee; | ||||
|     uint16_t size; | ||||
|     uint16_t prevSize; | ||||
|     uint16_t ret; | ||||
|     FuncEnv * env; | ||||
|     uint16_t * pc; | ||||
| }; | ||||
|  | ||||
| /* The size of a StackFrame in units of Values. */ | ||||
| #define FRAME_SIZE ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value)) | ||||
|  | ||||
| @@ -37,7 +26,7 @@ static StackFrame * ThreadFrame(Array * thread) { | ||||
| typedef struct GCMemoryHeader GCMemoryHeader; | ||||
| struct GCMemoryHeader { | ||||
|     GCMemoryHeader * next; | ||||
|     uint32_t color; | ||||
|     uint32_t color : 1; | ||||
| }; | ||||
|  | ||||
| /* Forward declaration */ | ||||
| @@ -52,17 +41,43 @@ static void VMMarkFuncEnv(VM * vm, FuncEnv * env) { | ||||
|             temp.type = TYPE_THREAD; | ||||
|             temp.data.array = env->thread; | ||||
|             VMMark(vm, &temp); | ||||
|         } else { | ||||
|         } else if (env->values) { | ||||
|             uint32_t count = env->stackOffset; | ||||
|             uint32_t i; | ||||
|             GCHeader(env->values)->color = vm->black; | ||||
|             for (i = 0; i < count; ++i) { | ||||
|             for (i = 0; i < count; ++i) | ||||
|                 VMMark(vm, env->values + i); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* GC helper to mark a FuncDef */ | ||||
| static void VMMarkFuncDef(VM * vm, FuncDef * def) { | ||||
|     if (GCHeader(def)->color != vm->black) { | ||||
|         GCHeader(def)->color = vm->black; | ||||
|         GCHeader(def->byteCode)->color = vm->black; | ||||
|         uint32_t count, i; | ||||
|         if (def->literals) { | ||||
|             count = def->literalsLen; | ||||
|             GCHeader(def->literals)->color = vm->black; | ||||
|             for (i = 0; i < count; ++i) | ||||
|                 VMMark(vm, def->literals + i); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Helper to mark a stack frame. Returns the next frame. */ | ||||
| static StackFrame * VMMarkStackFrame(VM * vm, StackFrame * frame) { | ||||
|     uint32_t i; | ||||
|     Value * stack = (Value *)frame + FRAME_SIZE; | ||||
|     VMMark(vm, &frame->callee); | ||||
|     if (frame->env) | ||||
|         VMMarkFuncEnv(vm, frame->env); | ||||
|     for (i = 0; i < frame->size; ++i) | ||||
|         VMMark(vm, stack + i); | ||||
|     return (StackFrame *)(stack + frame->size); | ||||
| } | ||||
|  | ||||
| /* Mark allocated memory associated with a value. This is | ||||
|  * the main function for doing garbage collection. */ | ||||
| static void VMMark(VM * vm, Value * x) { | ||||
| @@ -97,22 +112,13 @@ static void VMMark(VM * vm, Value * x) { | ||||
|  | ||||
|         case TYPE_THREAD: | ||||
|             if (GCHeader(x->data.array)->color != vm->black) { | ||||
|                 uint32_t i; | ||||
|                 Array * thread = x->data.array; | ||||
|                 StackFrame * frame = (StackFrame *)thread->data; | ||||
|                 StackFrame * end = (StackFrame *)(thread->data + thread->count - FRAME_SIZE); | ||||
|                 StackFrame * end = ThreadFrame(thread); | ||||
|                 GCHeader(thread)->color = vm->black; | ||||
|                 GCHeader(thread->data)->color = vm->black; | ||||
|                 while (frame <= end) { | ||||
|                     Value * stack = (Value *)frame + FRAME_SIZE; | ||||
|                     VMMark(vm, &frame->callee); | ||||
|                     if (frame->env) | ||||
|                         VMMarkFuncEnv(vm, frame->env); | ||||
|                     for (i = 0; i < frame->size; ++i) { | ||||
|                         VMMark(vm, stack + i); | ||||
|                     } | ||||
|                     frame = (StackFrame *)(stack + frame->size); | ||||
|                 } | ||||
|                 while (frame <= end) | ||||
|                     frame = VMMarkStackFrame(vm, frame); | ||||
|             } | ||||
|             break; | ||||
|  | ||||
| @@ -121,16 +127,12 @@ static void VMMark(VM * vm, Value * x) { | ||||
|                 Func * f = x->data.func; | ||||
|                 GCHeader(f)->color = vm->black; | ||||
|                 VMMarkFuncEnv(vm, f->env); | ||||
|                 { | ||||
|                 VMMarkFuncDef(vm, f->def); | ||||
|                 if (f->parent) { | ||||
|                     Value temp; | ||||
|                     temp.type = TYPE_FUNCDEF; | ||||
|                     temp.data.funcdef = x->data.funcdef; | ||||
|                     temp.type = TYPE_FUNCTION; | ||||
|                     temp.data.func = f->parent; | ||||
|                     VMMark(vm, &temp); | ||||
|                     if (f->parent) { | ||||
|                         temp.type = TYPE_FUNCTION; | ||||
|                         temp.data.func = f->parent; | ||||
|                         VMMark(vm, &temp); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
| @@ -151,16 +153,7 @@ static void VMMark(VM * vm, Value * x) { | ||||
|             break; | ||||
|  | ||||
|         case TYPE_FUNCDEF: | ||||
|             if (GCHeader(x->data.funcdef)->color != vm->black) { | ||||
|                 GCHeader(x->data.funcdef->byteCode)->color = vm->black; | ||||
|                 uint32_t count, i; | ||||
|                 count = x->data.funcdef->literalsLen; | ||||
|                 if (x->data.funcdef->literals) { | ||||
|                     GCHeader(x->data.funcdef->literals)->color = vm->black; | ||||
|                     for (i = 0; i < count; ++i) | ||||
|                         VMMark(vm, x->data.funcdef->literals + i); | ||||
|                 } | ||||
|             } | ||||
| 			VMMarkFuncDef(vm, x->data.funcdef); | ||||
|             break; | ||||
|  | ||||
|         case TYPE_FUNCENV: | ||||
| @@ -223,14 +216,11 @@ void * VMZalloc(VM * vm, uint32_t size) { | ||||
| void VMCollect(VM * vm) { | ||||
|     if (vm->lock > 0) return; | ||||
|     /* Thread can be null */ | ||||
|     if (vm->thread) { | ||||
|         Value thread; | ||||
|         thread.type = TYPE_THREAD; | ||||
|         thread.data.array = vm->thread; | ||||
|         VMMark(vm, &thread); | ||||
|     } | ||||
|     Value thread; | ||||
|     thread.type = TYPE_THREAD; | ||||
|     thread.data.array = vm->thread; | ||||
|     VMMark(vm, &thread); | ||||
|     VMMark(vm, &vm->ret); | ||||
|     VMMark(vm, &vm->root); | ||||
|     VMSweep(vm); | ||||
|     vm->nextCollection = 0; | ||||
| } | ||||
| @@ -259,22 +249,23 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) { | ||||
|      * the garabage collector */ | ||||
|     for (i = nextCount; i < nextCount + size; ++i) | ||||
|         thread->data[i].type = TYPE_NIL; | ||||
|     frame = ThreadFrame(thread); | ||||
|     vm->base = thread->data + thread->count; | ||||
|     vm->frame = frame = (StackFrame *)(vm->base - FRAME_SIZE); | ||||
|     /* Set up the new stack frame */ | ||||
|     frame->prevSize = oldSize; | ||||
|     frame->size = size; | ||||
|     frame->env = NULL; | ||||
|     frame->callee = callee; | ||||
|     vm->base = thread->data + thread->count; | ||||
| } | ||||
|  | ||||
| /* Copy the current function stack to the current closure | ||||
|    environment */ | ||||
| static void VMThreadSplitStack(VM * vm, Array * thread) { | ||||
|     StackFrame * frame = ThreadFrame(thread); | ||||
| static void VMThreadSplitStack(VM * vm) { | ||||
|     StackFrame * frame = vm->frame; | ||||
|     FuncEnv * env = frame->env; | ||||
|     /* Check for closures */ | ||||
|     if (env) { | ||||
|         Array * thread = vm->thread; | ||||
|         uint32_t size = frame->size; | ||||
|         env->thread = NULL; | ||||
|         env->stackOffset = size; | ||||
| @@ -284,16 +275,18 @@ static void VMThreadSplitStack(VM * vm, Array * thread) { | ||||
| } | ||||
|  | ||||
| /* Pop the top-most stack frame from stack */ | ||||
| static void VMThreadPop(VM * vm, Array * thread) { | ||||
|     StackFrame * frame = ThreadFrame(thread); | ||||
| static void VMThreadPop(VM * vm) { | ||||
|     Array * thread = vm->thread; | ||||
|     StackFrame * frame = vm->frame; | ||||
|     uint32_t delta = FRAME_SIZE + frame->prevSize; | ||||
|     if (thread->count) { | ||||
|         VMThreadSplitStack(vm, thread); | ||||
|         VMThreadSplitStack(vm); | ||||
|     } else { | ||||
|         VMError(vm, "Nothing to pop from stack."); | ||||
|     } | ||||
|     thread->count -= delta; | ||||
|     vm->base -= delta; | ||||
|     vm->frame = (StackFrame *)(vm->base - FRAME_SIZE); | ||||
| } | ||||
|  | ||||
| /* Get an upvalue */ | ||||
| @@ -329,28 +322,24 @@ static int truthy(Value v) { | ||||
|  | ||||
| /* Return from the vm */ | ||||
| static void VMReturn(VM * vm, Value ret) { | ||||
|     Array * thread = vm->thread; | ||||
|     StackFrame * frame = ThreadFrame(thread); | ||||
|     VMThreadPop(vm, thread); | ||||
|     if (thread->count == 0) { | ||||
|     VMThreadPop(vm); | ||||
|     if (vm->thread->count == 0) { | ||||
|         VMExit(vm, ret); | ||||
|     } | ||||
|     frame = ThreadFrame(thread); | ||||
|     vm->pc = frame->pc; | ||||
|     vm->base[frame->ret] = ret; | ||||
|     vm->pc = vm->frame->pc; | ||||
|     vm->base[vm->frame->ret] = ret; | ||||
| } | ||||
|  | ||||
| /* Implementation of the opcode for function calls */ | ||||
| static void VMCallOp(VM * vm) { | ||||
|     Array * thread = vm->thread; | ||||
|     StackFrame * frame = ThreadFrame(thread); | ||||
|     Value callee = vm->base[vm->pc[1]]; | ||||
|     uint32_t arity = vm->pc[3]; | ||||
|     uint32_t oldCount = thread->count; | ||||
|     uint32_t i; | ||||
|     Value * oldBase; | ||||
|     frame->pc = vm->pc + 4 + arity; | ||||
|     frame->ret = vm->pc[2]; | ||||
|     vm->frame->pc = vm->pc + 4 + arity; | ||||
|     vm->frame->ret = vm->pc[2]; | ||||
|     if (callee.type == TYPE_FUNCTION) { | ||||
|         Func * fn = callee.data.func; | ||||
|         VMThreadPush(vm, thread, callee, fn->def->locals); | ||||
| @@ -375,19 +364,17 @@ static void VMCallOp(VM * vm) { | ||||
|             vm->base[i].type = TYPE_NIL; | ||||
|         vm->pc = f->def->byteCode; | ||||
|     } | ||||
|     VMMaybeCollect(vm); | ||||
| } | ||||
|  | ||||
| /* Implementation of the opcode for tail calls */ | ||||
| static void VMTailCallOp(VM * vm) { | ||||
|     Array * thread = vm->thread; | ||||
|     StackFrame * frame = ThreadFrame(thread); | ||||
|     Value callee = vm->base[vm->pc[1]]; | ||||
|     uint32_t arity = vm->pc[2]; | ||||
|     uint16_t newFrameSize, currentFrameSize; | ||||
|     uint32_t i; | ||||
|     /* Check for closures */ | ||||
|     VMThreadSplitStack(vm, thread); | ||||
|     VMThreadSplitStack(vm); | ||||
|     if (callee.type == TYPE_CFUNCTION) { | ||||
|         newFrameSize = arity; | ||||
|     } else if (callee.type == TYPE_FUNCTION) { | ||||
| @@ -397,9 +384,8 @@ static void VMTailCallOp(VM * vm) { | ||||
|         VMError(vm, EXPECTED_FUNCTION); | ||||
|     } | ||||
|     /* Ensure stack has enough space for copies of arguments */ | ||||
|     currentFrameSize = frame->size; | ||||
|     currentFrameSize = vm->frame->size; | ||||
|     ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity); | ||||
|     frame = ThreadFrame(thread); | ||||
|     vm->base = thread->data + thread->count; | ||||
|     /* Copy the arguments into the extra space */ | ||||
|     for (i = 0; i < arity; ++i) { | ||||
| @@ -412,9 +398,9 @@ static void VMTailCallOp(VM * vm) { | ||||
|         vm->base[i].type = TYPE_NIL; | ||||
|     } | ||||
|     /* Update the stack frame */ | ||||
|     frame->size = newFrameSize; | ||||
|     frame->callee = callee; | ||||
|     frame->env = NULL; | ||||
|     vm->frame->size = newFrameSize; | ||||
|     vm->frame->callee = callee; | ||||
|     vm->frame->env = NULL; | ||||
|     if (callee.type == TYPE_CFUNCTION) { | ||||
|         ++vm->lock; | ||||
|         VMReturn(vm, callee.data.cfunction(vm)); | ||||
| @@ -423,27 +409,25 @@ static void VMTailCallOp(VM * vm) { | ||||
|         Func * f = callee.data.func; | ||||
|         vm->pc = f->def->byteCode; | ||||
|     } | ||||
|     VMMaybeCollect(vm); | ||||
| } | ||||
|  | ||||
| /* Instantiate a closure */ | ||||
| static Value VMMakeClosure(VM * vm, uint16_t literal) { | ||||
|     Array * thread = vm->thread; | ||||
|     StackFrame * frame = ThreadFrame(thread); | ||||
|     if (frame->callee.type != TYPE_FUNCTION) { | ||||
|     if (vm->frame->callee.type != TYPE_FUNCTION) { | ||||
|         VMError(vm, EXPECTED_FUNCTION); | ||||
|     } else { | ||||
|         Value constant, ret; | ||||
|         Func * fn, * current; | ||||
|         FuncEnv * env = frame->env; | ||||
|         FuncEnv * env = vm->frame->env; | ||||
|         if (!env) { | ||||
|             env = VMAlloc(vm, sizeof(FuncEnv)); | ||||
|             env->thread = thread; | ||||
|             env->stackOffset = thread->count; | ||||
|             env->values = NULL; | ||||
|             frame->env = env; | ||||
|             vm->frame->env = env; | ||||
|         } | ||||
|         current = frame->callee.data.func; | ||||
|         current = vm->frame->callee.data.func; | ||||
|         constant = LoadConstant(vm, current, literal); | ||||
|         if (constant.type != TYPE_FUNCDEF) { | ||||
|             VMError(vm, EXPECTED_FUNCTION); | ||||
| @@ -500,10 +484,10 @@ int VMStart(VM * vm) { | ||||
|                 DO_BINARY_MATH(-) | ||||
|  | ||||
|             case VM_OP_MUL: /* Multiplication */ | ||||
|                     DO_BINARY_MATH(*) | ||||
|                 DO_BINARY_MATH(*) | ||||
|  | ||||
|             case VM_OP_DIV: /* Division */ | ||||
|                         DO_BINARY_MATH(/) | ||||
|                 DO_BINARY_MATH(/) | ||||
|  | ||||
|             #undef DO_BINARY_MATH | ||||
|  | ||||
| @@ -556,7 +540,7 @@ int VMStart(VM * vm) { | ||||
|                 break; | ||||
|  | ||||
|             case VM_OP_UPV: /* Load Up Value */ | ||||
|                 temp = ThreadFrame(vm->thread)->callee; | ||||
|                 temp = vm->frame->callee; | ||||
|                 VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION); | ||||
|                 vm->base[vm->pc[1]] = *GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]); | ||||
|                 vm->pc += 4; | ||||
| @@ -583,14 +567,14 @@ int VMStart(VM * vm) { | ||||
|                 break; | ||||
|  | ||||
|             case VM_OP_SUV: /* Set Up Value */ | ||||
|                 temp = ThreadFrame(vm->thread)->callee; | ||||
|                 temp = vm->frame->callee; | ||||
|                 VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION); | ||||
|                 *GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]]; | ||||
|                 vm->pc += 4; | ||||
|                 break; | ||||
|  | ||||
|             case VM_OP_CST: /* Load constant value */ | ||||
|                 temp = ThreadFrame(vm->thread)->callee; | ||||
|                 temp = vm->frame->callee; | ||||
|                 VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION); | ||||
|                 vm->base[vm->pc[1]] = LoadConstant(vm, temp.data.func, vm->pc[2]); | ||||
|                 vm->pc += 3; | ||||
| @@ -653,7 +637,6 @@ int VMStart(VM * vm) { | ||||
|                     temp.data.array = array; | ||||
|                     vm->base[vm->pc[1]] = temp; | ||||
|                     vm->pc += 3 + arrayLen; | ||||
|                     VMMaybeCollect(vm); | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
| @@ -661,7 +644,7 @@ int VMStart(VM * vm) { | ||||
|                 { | ||||
|                     uint32_t i = 3; | ||||
|                     uint32_t kvs = vm->pc[2]; | ||||
|                     Dictionary * dict = DictNew(vm, kvs); | ||||
|                     Dictionary * dict = DictNew(vm, kvs + 2); | ||||
|                     kvs = kvs + 3; | ||||
|                     while (i < kvs) { | ||||
|                         v1 = vm->base[vm->pc[i++]]; | ||||
| @@ -672,7 +655,6 @@ int VMStart(VM * vm) { | ||||
|                     temp.data.dict = dict; | ||||
|                     vm->base[vm->pc[1]] = temp; | ||||
|                     vm->pc += kvs; | ||||
|                     VMMaybeCollect(vm); | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
| @@ -718,40 +700,48 @@ int VMStart(VM * vm) { | ||||
|                 break; | ||||
|  | ||||
|             case VM_OP_GET: | ||||
| 				temp = ValueGet(vm, vm->base[vm->pc[2]], vm->base[vm->pc[3]]); | ||||
| 				vm->base[vm->pc[1]] = temp; | ||||
| 				vm->pc += 4; | ||||
|                 break; | ||||
|  | ||||
|             case VM_OP_SET: | ||||
| 				ValueSet(vm, vm->base[vm->pc[1]], vm->base[vm->pc[2]], vm->base[vm->pc[3]]); | ||||
| 				vm->pc += 4; | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 VMError(vm, "Unknown opcode"); | ||||
|                 break; | ||||
|         } | ||||
|         VMMaybeCollect(vm); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Get an argument from the stack */ | ||||
| Value VMGetArg(VM * vm, uint16_t index) { | ||||
|     uint16_t frameSize = ThreadFrame(vm->thread)->size; | ||||
|     uint16_t frameSize = vm->frame->size; | ||||
|     VMAssert(vm, frameSize > index, "Cannot get arg out of stack bounds"); | ||||
|     return vm->base[index]; | ||||
| } | ||||
|  | ||||
| /* Put a value on the stack */ | ||||
| void VMSetArg(VM * vm, uint16_t index, Value x) { | ||||
|     uint16_t frameSize = ThreadFrame(vm->thread)->size; | ||||
|     uint16_t frameSize = vm->frame->size; | ||||
|     VMAssert(vm, frameSize > index, "Cannot set arg out of stack bounds"); | ||||
|     vm->base[index] = x; | ||||
| } | ||||
|  | ||||
| /* Get the size of the VMStack */ | ||||
| uint16_t VMCountArgs(VM * vm) { | ||||
|     return ThreadFrame(vm->thread)->size; | ||||
|     return vm->frame->size; | ||||
| } | ||||
|  | ||||
| /* Initialize the VM */ | ||||
| void VMInit(VM * vm) { | ||||
|     vm->ret.type = TYPE_NIL; | ||||
|     vm->root.type = TYPE_NIL; | ||||
|     vm->base = NULL; | ||||
|     vm->frame = NULL; | ||||
|     vm->pc = NULL; | ||||
|     vm->error = NULL; | ||||
|     /* Garbage collection */ | ||||
| @@ -760,8 +750,8 @@ void VMInit(VM * vm) { | ||||
|     vm->memoryInterval = 0; | ||||
|     vm->black = 0; | ||||
|     vm->lock = 0; | ||||
|     /* Set to empty thread */ | ||||
|     vm->thread = NULL; | ||||
|     /* Add thread */ | ||||
|     vm->thread = ArrayNew(vm, 20); | ||||
| } | ||||
|  | ||||
| /* Load a function into the VM. The function will be called with | ||||
|   | ||||
							
								
								
									
										4
									
								
								vm.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								vm.h
									
									
									
									
									
								
							| @@ -17,8 +17,8 @@ | ||||
|     { if (!(cond)) { VMError((vm), (e)); } } while (0) | ||||
|  | ||||
| /* Type assertion */ | ||||
| #define VMAssertType(vm, f, type) \ | ||||
|     VMAssert(vm, (f).type == (type), "Expected type " type) | ||||
| #define VMAssertType(vm, f, t) \ | ||||
|     VMAssert((vm), (f).type == (t), "Expected type,") | ||||
|  | ||||
| /* Initialize the VM */ | ||||
| void VMInit(VM * vm); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose