mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 07:33:01 +00:00 
			
		
		
		
	More work on compiler.
* Fix up while special form * Change Value functions to pass-by-value
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -32,6 +32,6 @@ debug: $(TARGET) | |||||||
| 	gdb $(TARGET) | 	gdb $(TARGET) | ||||||
|  |  | ||||||
| valgrind: $(TARGET) | valgrind: $(TARGET) | ||||||
| 	valgrind ./$(TARGET) | 	valgrind ./$(TARGET) --leak-check=full | ||||||
|  |  | ||||||
| .PHONY: clean install run debug valgrind | .PHONY: clean install run debug valgrind | ||||||
|   | |||||||
							
								
								
									
										177
									
								
								compile.c
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								compile.c
									
									
									
									
									
								
							| @@ -63,6 +63,7 @@ struct SlotTracker { | |||||||
|  * points to the parent Scope, and its current child |  * points to the parent Scope, and its current child | ||||||
|  * Scope. */ |  * Scope. */ | ||||||
| struct Scope { | struct Scope { | ||||||
|  |     uint32_t level; | ||||||
|     uint16_t nextLocal; |     uint16_t nextLocal; | ||||||
|     uint32_t heapCapacity; |     uint32_t heapCapacity; | ||||||
|     uint32_t heapSize; |     uint32_t heapSize; | ||||||
| @@ -118,8 +119,12 @@ static Scope * CompilerPushScope(Compiler * c, int sameFunction) { | |||||||
|     scope->heapCapacity = 10; |     scope->heapCapacity = 10; | ||||||
|     scope->nextScope = NULL; |     scope->nextScope = NULL; | ||||||
|     scope->previousScope = c->tail; |     scope->previousScope = c->tail; | ||||||
|     if (c->tail) |     if (c->tail) { | ||||||
|         c->tail->nextScope = scope; |         c->tail->nextScope = scope; | ||||||
|  |         scope->level = c->tail->level + (sameFunction ? 0 : 1); | ||||||
|  |     } else { | ||||||
|  | 		scope->level = 0; | ||||||
|  |     } | ||||||
|     if (sameFunction) { |     if (sameFunction) { | ||||||
|         if (!c->tail) { |         if (!c->tail) { | ||||||
| 			CError(c, "Cannot inherit scope when root scope"); | 			CError(c, "Cannot inherit scope when root scope"); | ||||||
| @@ -256,12 +261,17 @@ static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) { | |||||||
|  * Useful for dictionary literals, array literals, function calls, etc. */ |  * Useful for dictionary literals, array literals, function calls, etc. */ | ||||||
| static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToBuffer) { | static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToBuffer) { | ||||||
|     uint32_t i; |     uint32_t i; | ||||||
|     if (writeToBuffer) { |     Buffer * buffer = c->buffer; | ||||||
|         Buffer * buffer = c->buffer; |     if (writeToBuffer == 1) { | ||||||
|         for (i = 0; i < tracker->count; ++i) { |         for (i = 0; i < tracker->count; ++i) { | ||||||
|             Slot * s = tracker->slots + i; |             Slot * s = tracker->slots + i; | ||||||
|             BufferPushUInt16(c->vm, buffer, s->index); |             BufferPushUInt16(c->vm, buffer, s->index); | ||||||
|         } |         } | ||||||
|  |     } else if (writeToBuffer == 2) { /* Write in reverse */ | ||||||
|  |         for (i = 0; i < tracker->count; ++i) { | ||||||
|  |             Slot * s = tracker->slots + tracker->count - 1 - i; | ||||||
|  |             BufferPushUInt16(c->vm, buffer, s->index); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     /* Free in reverse order */ |     /* Free in reverse order */ | ||||||
|     for (i = tracker->count - 1; i < tracker->count; --i) { |     for (i = tracker->count - 1; i < tracker->count; --i) { | ||||||
| @@ -286,7 +296,7 @@ static void CompilerTrackerPush(Compiler * c, SlotTracker * tracker, Slot slot) | |||||||
|  * that one instead of creating a new literal. This allows for some reuse |  * that one instead of creating a new literal. This allows for some reuse | ||||||
|  * of things like string constants.*/ |  * of things like string constants.*/ | ||||||
| static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) { | static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) { | ||||||
|     Value checkDup = DictGet(scope->literals, &x); |     Value checkDup = DictGet(scope->literals, x); | ||||||
|     uint16_t literalIndex = 0; |     uint16_t literalIndex = 0; | ||||||
|     if (checkDup.type != TYPE_NIL) { |     if (checkDup.type != TYPE_NIL) { | ||||||
|         /* An equal literal is already registered in the current scope */ |         /* An equal literal is already registered in the current scope */ | ||||||
| @@ -297,7 +307,7 @@ static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) { | |||||||
|         valIndex.type = TYPE_NUMBER; |         valIndex.type = TYPE_NUMBER; | ||||||
|         literalIndex = scope->literalsArray->count; |         literalIndex = scope->literalsArray->count; | ||||||
|         valIndex.data.number = literalIndex; |         valIndex.data.number = literalIndex; | ||||||
|         DictPut(c->vm, scope->literals, &x, &valIndex); |         DictPut(c->vm, scope->literals, x, valIndex); | ||||||
|         ArrayPush(c->vm, scope->literalsArray, x); |         ArrayPush(c->vm, scope->literalsArray, x); | ||||||
|     } |     } | ||||||
|     return literalIndex; |     return literalIndex; | ||||||
| @@ -312,7 +322,7 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) { | |||||||
|     uint16_t target = CompilerGetLocal(c, scope); |     uint16_t target = CompilerGetLocal(c, scope); | ||||||
|     x.type = TYPE_NUMBER; |     x.type = TYPE_NUMBER; | ||||||
|     x.data.number = target; |     x.data.number = target; | ||||||
|     DictPut(c->vm, scope->locals, &sym, &x); |     DictPut(c->vm, scope->locals, sym, x); | ||||||
|     return target; |     return target; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -320,15 +330,14 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) { | |||||||
|  * pass back the level and index by reference. */ |  * pass back the level and index by reference. */ | ||||||
| static int ScopeSymbolResolve(Scope * scope, Value x, | static int ScopeSymbolResolve(Scope * scope, Value x, | ||||||
|         uint16_t * level, uint16_t * index) { |         uint16_t * level, uint16_t * index) { | ||||||
|     uint16_t levelTest = 0; |     uint32_t currentLevel = scope->level; | ||||||
|     while (scope) { |     while (scope) { | ||||||
|         Value check = DictGet(scope->locals, &x); |         Value check = DictGet(scope->locals, x); | ||||||
|         if (check.type != TYPE_NIL) { |         if (check.type != TYPE_NIL) { | ||||||
|             *level = levelTest; |             *level = currentLevel - scope->level; | ||||||
|             *index = (uint16_t) check.data.number; |             *index = (uint16_t) check.data.number; | ||||||
|             return 1; |             return 1; | ||||||
|         } |         } | ||||||
|         ++levelTest; |  | ||||||
|         scope = scope->previousScope; |         scope = scope->previousScope; | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| @@ -559,7 +568,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) { | |||||||
|  * its argument is still carried out, but their results can |  * its argument is still carried out, but their results can | ||||||
|  * also be ignored). */ |  * also be ignored). */ | ||||||
| static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, | static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, | ||||||
|         int16_t op0, int16_t op1, int16_t op2, int16_t opn) { |         int16_t op0, int16_t op1, int16_t op2, int16_t opn, int reverseOperands) { | ||||||
|     Scope * scope = c->tail; |     Scope * scope = c->tail; | ||||||
|     Buffer * buffer = c->buffer; |     Buffer * buffer = c->buffer; | ||||||
|     Slot ret = SlotDefault(); |     Slot ret = SlotDefault(); | ||||||
| @@ -602,22 +611,63 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form, | |||||||
|         BufferPushUInt16(c->vm, buffer, ret.index); |         BufferPushUInt16(c->vm, buffer, ret.index); | ||||||
|     } |     } | ||||||
|     /* Write the location of all of the arguments */ |     /* Write the location of all of the arguments */ | ||||||
|     CompilerTrackerFree(c, scope, &tracker, 1); |     CompilerTrackerFree(c, scope, &tracker, reverseOperands ? 2 : 1); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Math specials */ | /* Math specials */ | ||||||
| static Slot CompileAddition(Compiler * c, FormOptions opts, Array * form) { | static Slot CompileAddition(Compiler * c, FormOptions opts, Array * form) { | ||||||
|     return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_ADM, VM_OP_ADD, VM_OP_ADM); |     return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_ADM, VM_OP_ADD, VM_OP_ADM, 0); | ||||||
| } | } | ||||||
| static Slot CompileSubtraction(Compiler * c, FormOptions opts, Array * form) { | static Slot CompileSubtraction(Compiler * c, FormOptions opts, Array * form) { | ||||||
|     return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_SBM, VM_OP_SUB, VM_OP_SBM); |     return CompileOperator(c, opts, form, VM_OP_LD0, VM_OP_SBM, VM_OP_SUB, VM_OP_SBM, 0); | ||||||
| } | } | ||||||
| static Slot CompileMultiplication(Compiler * c, FormOptions opts, Array * form) { | static Slot CompileMultiplication(Compiler * c, FormOptions opts, Array * form) { | ||||||
|     return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_MUM, VM_OP_MUL, VM_OP_MUM); |     return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_MUM, VM_OP_MUL, VM_OP_MUM, 0); | ||||||
| } | } | ||||||
| static Slot CompileDivision(Compiler * c, FormOptions opts, Array * form) { | static Slot CompileDivision(Compiler * c, FormOptions opts, Array * form) { | ||||||
|     return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_DVM, VM_OP_DIV, VM_OP_DVM); |     return CompileOperator(c, opts, form, VM_OP_LD1, VM_OP_DVM, VM_OP_DIV, VM_OP_DVM, 0); | ||||||
|  | } | ||||||
|  | static Slot CompileEquals(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_EQL, -1, 0); | ||||||
|  | } | ||||||
|  | static Slot CompileLessThan(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTN, -1, 0); | ||||||
|  | } | ||||||
|  | static Slot CompileLessThanOrEqual(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTE, -1, 0); | ||||||
|  | } | ||||||
|  | static Slot CompileGreaterThan(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTN, -1, 1); | ||||||
|  | } | ||||||
|  | static Slot CompileGreaterThanOrEqual(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     return CompileOperator(c, opts, form, VM_OP_TRU, VM_OP_TRU, VM_OP_LTE, -1, 1); | ||||||
|  | } | ||||||
|  | static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     return CompileOperator(c, opts, form, VM_OP_FLS, VM_OP_NOT, -1, -1, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Helper function to return nil from a form that doesn't do anything  | ||||||
|  |  (set, while, etc.) */ | ||||||
|  | static Slot CompileReturnNil(Compiler * c, FormOptions opts) { | ||||||
|  |     Slot ret; | ||||||
|  |     /* If we need a return value, we just use nil */ | ||||||
|  |     if (opts.isTail) { | ||||||
|  | 		ret.isDud = 1; | ||||||
|  |         BufferPushUInt16(c->vm, c->buffer, VM_OP_RTN); | ||||||
|  |     } else if (!opts.canDrop) { | ||||||
|  |         ret.isDud = 0; | ||||||
|  |         if (opts.canChoose) { | ||||||
|  |             ret.isTemp = 1; | ||||||
|  |             ret.index = CompilerGetLocal(c, c->tail); | ||||||
|  |         } else { | ||||||
|  |             ret.isTemp = 0; | ||||||
|  |             ret.index = opts.target; | ||||||
|  |         } | ||||||
|  |         BufferPushUInt16(c->vm, c->buffer, VM_OP_NIL); | ||||||
|  |         BufferPushUInt16(c->vm, c->buffer, ret.index); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Compile an assignment operation */ | /* Compile an assignment operation */ | ||||||
| @@ -625,7 +675,6 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ | |||||||
|     Scope * scope = c->tail; |     Scope * scope = c->tail; | ||||||
|     Buffer * buffer = c->buffer; |     Buffer * buffer = c->buffer; | ||||||
|     FormOptions subOpts = FormOptionsDefault(); |     FormOptions subOpts = FormOptionsDefault(); | ||||||
|     Slot ret = SlotDefault(); |  | ||||||
|     uint16_t target = 0; |     uint16_t target = 0; | ||||||
|     uint16_t level = 0; |     uint16_t level = 0; | ||||||
|     if (ScopeSymbolResolve(scope, left, &level, &target)) { |     if (ScopeSymbolResolve(scope, left, &level, &target)) { | ||||||
| @@ -641,7 +690,12 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ | |||||||
|             BufferPushUInt16(c->vm, buffer, target); |             BufferPushUInt16(c->vm, buffer, target); | ||||||
|             /* Drop the possibly temporary slot if it is indeed temporary */ |             /* Drop the possibly temporary slot if it is indeed temporary */ | ||||||
|             CompilerDropSlot(c, scope, slot); |             CompilerDropSlot(c, scope, slot); | ||||||
|             return ret; |         } else { | ||||||
|  |             Slot slot; | ||||||
|  |             subOpts.canChoose = 0; | ||||||
|  |             subOpts.target = target; | ||||||
|  |             slot = CompileValue(c, subOpts, right); | ||||||
|  |             CompilerDropSlot(c, scope, slot); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         /* We need to declare a new symbol */ |         /* We need to declare a new symbol */ | ||||||
| @@ -649,18 +703,7 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ | |||||||
|         subOpts.canChoose = 0; |         subOpts.canChoose = 0; | ||||||
|         CompileValue(c, subOpts, right); |         CompileValue(c, subOpts, right); | ||||||
|     } |     } | ||||||
|     /* If we need a return value, we just use nil */ | 	return CompileReturnNil(c, opts); | ||||||
|     if (!opts.canDrop) { |  | ||||||
|         if (opts.canChoose) { |  | ||||||
|             ret.isTemp = 1; |  | ||||||
|             ret.index = CompilerGetLocal(c, scope); |  | ||||||
|         } else { |  | ||||||
|             ret.index = opts.target; |  | ||||||
|         } |  | ||||||
|         BufferPushUInt16(c->vm, buffer, VM_OP_NIL); |  | ||||||
|         BufferPushUInt16(c->vm, buffer, ret.index); |  | ||||||
|     } |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Writes bytecode to return a slot */ | /* Writes bytecode to return a slot */ | ||||||
| @@ -683,7 +726,7 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t | |||||||
|     uint32_t current = startIndex; |     uint32_t current = startIndex; | ||||||
|     /* Compile the body */ |     /* Compile the body */ | ||||||
|     while (current < form->count) { |     while (current < form->count) { | ||||||
|         subOpts.canDrop = current != form->count - 1; |         subOpts.canDrop = (current != form->count - 1) || opts.canDrop; | ||||||
|         subOpts.isTail = opts.isTail && !subOpts.canDrop; |         subOpts.isTail = opts.isTail && !subOpts.canDrop; | ||||||
|         ret = CompileValue(c, subOpts, form->data[current]); |         ret = CompileValue(c, subOpts, form->data[current]); | ||||||
|         if (subOpts.canDrop) { |         if (subOpts.canDrop) { | ||||||
| @@ -692,7 +735,7 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t | |||||||
|         } |         } | ||||||
|         ++current; |         ++current; | ||||||
|     } |     } | ||||||
|     if (opts.isTail && !ret.isDud) { | 	if (opts.isTail && !ret.isDud) { | ||||||
|         CompilerReturnSlot(c, ret); |         CompilerReturnSlot(c, ret); | ||||||
|         ret.isDud = 1; |         ret.isDud = 1; | ||||||
|     } |     } | ||||||
| @@ -891,6 +934,39 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* While special */ | ||||||
|  | static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) { | ||||||
|  |     Slot cond; | ||||||
|  | 	uint32_t countAtStart = c->buffer->count; | ||||||
|  | 	uint32_t countAtJumpDelta; | ||||||
|  | 	uint32_t countAtFinish; | ||||||
|  | 	FormOptions subOpts = FormOptionsDefault(); | ||||||
|  | 	subOpts.canDrop = 1; | ||||||
|  |     CompilerPushScope(c, 1); | ||||||
|  |     /* Compile condition */ | ||||||
|  | 	cond = CompileValue(c, FormOptionsDefault(), form->data[1]); | ||||||
|  | 	/* Leave space for jump later */ | ||||||
|  | 	countAtJumpDelta = c->buffer->count; | ||||||
|  | 	c->buffer->count += sizeof(uint16_t) * 2 + sizeof(int32_t); | ||||||
|  | 	/* Compile loop body */ | ||||||
|  |     CompilerDropSlot(c, c->tail, CompileBlock(c, subOpts, form, 2)); | ||||||
|  |     /* Jump back to the loop start */ | ||||||
|  |     countAtFinish = c->buffer->count; | ||||||
|  |     BufferPushUInt16(c->vm, c->buffer, VM_OP_JMP); | ||||||
|  |     BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtStart) / -2); | ||||||
|  |     countAtFinish = c->buffer->count; | ||||||
|  |     /* Set the jump to the correct length */ | ||||||
|  |     c->buffer->count = countAtJumpDelta; | ||||||
|  | 	BufferPushUInt16(c->vm, c->buffer, VM_OP_JIF); | ||||||
|  | 	BufferPushUInt16(c->vm, c->buffer, cond.index); | ||||||
|  |     BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtJumpDelta) / 2); | ||||||
|  | 	/* Pop scope */ | ||||||
|  | 	c->buffer->count = countAtFinish; | ||||||
|  |     CompilerPopScope(c); | ||||||
|  | 	/* Return dud */ | ||||||
|  |     return CompileReturnNil(c, opts); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Do special */ | /* Do special */ | ||||||
| static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) { | static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) { | ||||||
|     Slot ret; |     Slot ret; | ||||||
| @@ -960,6 +1036,9 @@ static SpecialFormHelper GetSpecial(Array * form) { | |||||||
|             case '-': return CompileSubtraction; |             case '-': return CompileSubtraction; | ||||||
|             case '*': return CompileMultiplication; |             case '*': return CompileMultiplication; | ||||||
|             case '/': return CompileDivision; |             case '/': return CompileDivision; | ||||||
|  |             case '>': return CompileGreaterThan; | ||||||
|  |             case '<': return CompileLessThan; | ||||||
|  |            	case '=': return CompileEquals; | ||||||
|             case '\'': return CompileQuote; |             case '\'': return CompileQuote; | ||||||
|             default: |             default: | ||||||
|                 break; |                 break; | ||||||
| @@ -967,6 +1046,22 @@ static SpecialFormHelper GetSpecial(Array * form) { | |||||||
|     } |     } | ||||||
|     /* Multi character specials. Mostly control flow. */ |     /* Multi character specials. Mostly control flow. */ | ||||||
|     switch (name[0]) { |     switch (name[0]) { | ||||||
|  |         case '>': | ||||||
|  |         	{ | ||||||
|  | 				if (VStringSize(name) == 2 && | ||||||
|  | 				    	name[1] == '=') { | ||||||
|  | 					return CompileGreaterThanOrEqual; | ||||||
|  | 		    	} | ||||||
|  |         	} | ||||||
|  |         	break; | ||||||
|  |         case '<': | ||||||
|  |         	{ | ||||||
|  | 				if (VStringSize(name) == 2 && | ||||||
|  | 				    	name[1] == '=') { | ||||||
|  | 					return CompileLessThanOrEqual; | ||||||
|  | 		    	} | ||||||
|  |         	} | ||||||
|  |         	break; | ||||||
|         case 'd': |         case 'd': | ||||||
|             { |             { | ||||||
|                 if (VStringSize(name) == 2 && |                 if (VStringSize(name) == 2 && | ||||||
| @@ -991,6 +1086,14 @@ static SpecialFormHelper GetSpecial(Array * form) { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|  |        	case 'n': | ||||||
|  |            	{ | ||||||
|  | 				if (VStringSize(name) == 3 && | ||||||
|  | 				    	name[1] == 'o' && | ||||||
|  | 				    	name[2] == 't') { | ||||||
|  | 					return CompileNot; | ||||||
|  | 		    	} | ||||||
|  |            	} | ||||||
|         case 'q': |         case 'q': | ||||||
|             { |             { | ||||||
|                 if (VStringSize(name) == 5 && |                 if (VStringSize(name) == 5 && | ||||||
| @@ -1011,6 +1114,16 @@ static SpecialFormHelper GetSpecial(Array * form) { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|  |         case 'w': | ||||||
|  |         	{ | ||||||
|  | 				if (VStringSize(name) == 5 && | ||||||
|  | 				    	name[1] == 'h' && | ||||||
|  | 				    	name[2] == 'i' && | ||||||
|  | 				    	name[3] == 'l' && | ||||||
|  | 				    	name[4] == 'e') { | ||||||
|  | 					return CompileWhile; | ||||||
|  | 		    	} | ||||||
|  |         	} | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -139,6 +139,7 @@ struct VM { | |||||||
|     uint32_t memoryInterval; |     uint32_t memoryInterval; | ||||||
|     uint32_t nextCollection; |     uint32_t nextCollection; | ||||||
|     uint32_t black : 1; |     uint32_t black : 1; | ||||||
|  |     uint32_t lock : 31; | ||||||
|     /* Thread */ |     /* Thread */ | ||||||
|     uint16_t * pc; |     uint16_t * pc; | ||||||
|     Array * thread; |     Array * thread; | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								ds.c
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								ds.c
									
									
									
									
									
								
							| @@ -166,7 +166,7 @@ static void DictReHash(VM * vm, Dictionary * dict, uint32_t size) { | |||||||
|         while (bucket) { |         while (bucket) { | ||||||
|             uint32_t index; |             uint32_t index; | ||||||
|             DictBucket * next = bucket->next; |             DictBucket * next = bucket->next; | ||||||
|             index = ValueHash(&bucket->key) % size; |             index = ValueHash(bucket->key) % size; | ||||||
|             bucket->next = newBuckets[index]; |             bucket->next = newBuckets[index]; | ||||||
|             newBuckets[index] = bucket; |             newBuckets[index] = bucket; | ||||||
|             bucket = next; |             bucket = next; | ||||||
| @@ -177,11 +177,11 @@ static void DictReHash(VM * vm, Dictionary * dict, uint32_t size) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Find the bucket that contains the given key */ | /* Find the bucket that contains the given key */ | ||||||
| static DictBucket * DictFind(Dictionary * dict, Value * key) { | static DictBucket * DictFind(Dictionary * dict, Value key) { | ||||||
|     uint32_t index = ValueHash(key) % dict->capacity; |     uint32_t index = ValueHash(key) % dict->capacity; | ||||||
|     DictBucket * bucket = dict->buckets[index]; |     DictBucket * bucket = dict->buckets[index]; | ||||||
|     while (bucket) { |     while (bucket) { | ||||||
|         if (ValueEqual(&bucket->key, key)) |         if (ValueEqual(bucket->key, key)) | ||||||
|             return bucket; |             return bucket; | ||||||
|         bucket = bucket->next; |         bucket = bucket->next; | ||||||
|     } |     } | ||||||
| @@ -189,7 +189,7 @@ static DictBucket * DictFind(Dictionary * dict, Value * key) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Get a value out of the dictionary */ | /* Get a value out of the dictionary */ | ||||||
| Value DictGet(Dictionary * dict, Value * key) { | Value DictGet(Dictionary * dict, Value key) { | ||||||
|     DictBucket * bucket = DictFind(dict, key); |     DictBucket * bucket = DictFind(dict, key); | ||||||
|     if (bucket) { |     if (bucket) { | ||||||
|         return bucket->value; |         return bucket->value; | ||||||
| @@ -201,13 +201,13 @@ Value DictGet(Dictionary * dict, Value * key) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Remove an entry from the dictionary */ | /* Remove an entry from the dictionary */ | ||||||
| Value DictRemove(VM * vm, Dictionary * dict, Value * key) { | Value DictRemove(VM * vm, Dictionary * dict, Value key) { | ||||||
|     DictBucket * bucket, * previous; |     DictBucket * bucket, * previous; | ||||||
|     uint32_t index = ValueHash(key) % dict->capacity; |     uint32_t index = ValueHash(key) % dict->capacity; | ||||||
|     bucket = dict->buckets[index]; |     bucket = dict->buckets[index]; | ||||||
|     previous = (DictBucket *)0; |     previous = (DictBucket *)0; | ||||||
|     while (bucket) { |     while (bucket) { | ||||||
|         if (ValueEqual(&bucket->key, key)) { |         if (ValueEqual(bucket->key, key)) { | ||||||
|             if (previous) { |             if (previous) { | ||||||
|                 previous->next = bucket->next; |                 previous->next = bucket->next; | ||||||
|             } else { |             } else { | ||||||
| @@ -232,16 +232,16 @@ Value DictRemove(VM * vm, Dictionary * dict, Value * key) { | |||||||
|  |  | ||||||
| /* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory. | /* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory. | ||||||
|  * The VM pointer is needed for memory allocation. */ |  * The VM pointer is needed for memory allocation. */ | ||||||
| void DictPut(VM * vm, Dictionary * dict, Value * key, Value * value) { | void DictPut(VM * vm, Dictionary * dict, Value key, Value value) { | ||||||
|     DictBucket * bucket, * previous; |     DictBucket * bucket, * previous; | ||||||
|     uint32_t index = ValueHash(key) % dict->capacity; |     uint32_t index = ValueHash(key) % dict->capacity; | ||||||
|     if (key->type == TYPE_NIL) return; |     if (key.type == TYPE_NIL) return; | ||||||
|     /* Do a removal if value is nil */ |     /* Do a removal if value is nil */ | ||||||
|     if (value->type == TYPE_NIL) { |     if (value.type == TYPE_NIL) { | ||||||
|         bucket = dict->buckets[index]; |         bucket = dict->buckets[index]; | ||||||
|         previous = (DictBucket *)0; |         previous = (DictBucket *)0; | ||||||
|         while (bucket) { |         while (bucket) { | ||||||
|             if (ValueEqual(&bucket->key, key)) { |             if (ValueEqual(bucket->key, key)) { | ||||||
|                 if (previous) { |                 if (previous) { | ||||||
|                     previous->next = bucket->next; |                     previous->next = bucket->next; | ||||||
|                 } else { |                 } else { | ||||||
| @@ -259,15 +259,15 @@ void DictPut(VM * vm, Dictionary * dict, Value * key, Value * value) { | |||||||
|     } else { |     } else { | ||||||
|         bucket = DictFind(dict, key); |         bucket = DictFind(dict, key); | ||||||
|         if (bucket) { |         if (bucket) { | ||||||
|             bucket->value = *value; |             bucket->value = value; | ||||||
|         } else { |         } else { | ||||||
|             if (dict->count >= 2 * dict->capacity) { |             if (dict->count >= 2 * dict->capacity) { | ||||||
|                 DictReHash(vm, dict, 2 * dict->capacity); |                 DictReHash(vm, dict, 2 * dict->capacity); | ||||||
|             } |             } | ||||||
|             bucket = VMAlloc(vm, sizeof(DictBucket)); |             bucket = VMAlloc(vm, sizeof(DictBucket)); | ||||||
|             bucket->next = dict->buckets[index]; |             bucket->next = dict->buckets[index]; | ||||||
|             bucket->value = *value; |             bucket->value = value; | ||||||
|             bucket->key = *key; |             bucket->key = key; | ||||||
|             dict->buckets[index] = bucket; |             dict->buckets[index] = bucket; | ||||||
|             ++dict->count; |             ++dict->count; | ||||||
|         } |         } | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								ds.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								ds.h
									
									
									
									
									
								
							| @@ -68,15 +68,15 @@ Value ArrayPeek(Array * array); | |||||||
| Dictionary * DictNew(VM * vm, uint32_t capacity); | Dictionary * DictNew(VM * vm, uint32_t capacity); | ||||||
|  |  | ||||||
| /* Get a value out of the dictionary */ | /* Get a value out of the dictionary */ | ||||||
| Value DictGet(Dictionary * dict, Value * key); | Value DictGet(Dictionary * dict, Value key); | ||||||
|  |  | ||||||
| /* Get a Value from the dictionary, but remove it at the same | /* Get a Value from the dictionary, but remove it at the same | ||||||
|  * time. */ |  * time. */ | ||||||
| Value DictRemove(VM * vm, Dictionary * dict, Value * key); | Value DictRemove(VM * vm, Dictionary * dict, Value key); | ||||||
|  |  | ||||||
| /* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory. | /* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory. | ||||||
|  * The VM pointer is needed for memory allocation. */ |  * The VM pointer is needed for memory allocation. */ | ||||||
| void DictPut(VM * vm, Dictionary * dict, Value * key, Value * value); | void DictPut(VM * vm, Dictionary * dict, Value key, Value value); | ||||||
|  |  | ||||||
| /* Begin iteration through a dictionary */ | /* Begin iteration through a dictionary */ | ||||||
| void DictIterate(Dictionary * dict, DictionaryIterator * iterator); | void DictIterate(Dictionary * dict, DictionaryIterator * iterator); | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								main.c
									
									
									
									
									
								
							| @@ -7,6 +7,18 @@ | |||||||
| #include "compile.h" | #include "compile.h" | ||||||
| #include "value.h" | #include "value.h" | ||||||
|  |  | ||||||
|  | Value print(VM * vm) { | ||||||
|  |     uint32_t i; | ||||||
|  |     Value nil; | ||||||
|  | 	uint8_t * string = ValueToString(vm, VMGetArg(vm, 0)); | ||||||
|  | 	uint32_t len = VStringSize(string); | ||||||
|  | 	for (i = 0; i < len; ++i) | ||||||
|  |     	fputc(string[i], stdout); | ||||||
|  |     fputc('\n', stdout); | ||||||
|  | 	nil.type = TYPE_NIL; | ||||||
|  | 	return nil; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* A simple repl for debugging */ | /* A simple repl for debugging */ | ||||||
| void debugRepl() { | void debugRepl() { | ||||||
|     char buffer[128] = {0}; |     char buffer[128] = {0}; | ||||||
| @@ -30,7 +42,7 @@ void debugRepl() { | |||||||
|         while (p.status == PARSER_PENDING) { |         while (p.status == PARSER_PENDING) { | ||||||
|             /* Get some input if we are done */ |             /* Get some input if we are done */ | ||||||
|             if (*reader == '\0') { |             if (*reader == '\0') { | ||||||
|                 printf(">> "); |                 printf("> "); | ||||||
|                 if (!fgets(buffer, sizeof(buffer), stdin)) { |                 if (!fgets(buffer, sizeof(buffer), stdin)) { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| @@ -75,7 +87,7 @@ void debugRepl() { | |||||||
|             buffer[0] = 0; |             buffer[0] = 0; | ||||||
|             continue; |             continue; | ||||||
|         } else { |         } else { | ||||||
|             ValuePrint(&vm.tempRoot, 0); |             ValuePrint(vm.tempRoot, 0); | ||||||
|             printf("\n"); |             printf("\n"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								parse.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								parse.c
									
									
									
									
									
								
							| @@ -109,7 +109,7 @@ static void ParserTopAppend(Parser * p, Value x) { | |||||||
|             break; |             break; | ||||||
|         case PTYPE_DICTIONARY: |         case PTYPE_DICTIONARY: | ||||||
|             if (top->buf.dictState.keyFound) { |             if (top->buf.dictState.keyFound) { | ||||||
|                 DictPut(p->vm, top->buf.dictState.dict, &top->buf.dictState.key, &x); |                 DictPut(p->vm, top->buf.dictState.dict, top->buf.dictState.key, x); | ||||||
|             } else { |             } else { | ||||||
|                 top->buf.dictState.key = x; |                 top->buf.dictState.key = x; | ||||||
|             } |             } | ||||||
|   | |||||||
							
								
								
									
										132
									
								
								value.c
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								value.c
									
									
									
									
									
								
							| @@ -20,42 +20,42 @@ static void FuncDefBytecodePrint(FuncDef * def) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Print a value recursively. Used for debugging */ | /* Print a value recursively. Used for debugging */ | ||||||
| void ValuePrint(Value * x, uint32_t indent) { | void ValuePrint(Value x, uint32_t indent) { | ||||||
|     uint32_t i; |     uint32_t i; | ||||||
|     for (i = 0; i < indent; ++i) |     for (i = 0; i < indent; ++i) | ||||||
|         fputc(' ', stdout); |         fputc(' ', stdout); | ||||||
|     switch (x->type) { |     switch (x.type) { | ||||||
|         case TYPE_NIL: |         case TYPE_NIL: | ||||||
|             printf("<nil>"); |             printf("<nil>"); | ||||||
|             break; |             break; | ||||||
|         case TYPE_BOOLEAN: |         case TYPE_BOOLEAN: | ||||||
|             printf(x->data.boolean ? "<true>" : "<false>"); |             printf(x.data.boolean ? "<true>" : "<false>"); | ||||||
|             break; |             break; | ||||||
|         case TYPE_NUMBER: |         case TYPE_NUMBER: | ||||||
|             printf("%f", x->data.number); |             printf("%f", x.data.number); | ||||||
|             break; |             break; | ||||||
|         case TYPE_FORM: |         case TYPE_FORM: | ||||||
|         case TYPE_ARRAY: |         case TYPE_ARRAY: | ||||||
|             if (x->type == TYPE_ARRAY) printf("  [\n"); else printf("  (\n"); |             if (x.type == TYPE_ARRAY) printf("  [\n"); else printf("  (\n"); | ||||||
|             for (i = 0; i < x->data.array->count; ++i) { |             for (i = 0; i < x.data.array->count; ++i) { | ||||||
|                 ValuePrint(x->data.array->data + i, indent + 4); |                 ValuePrint(x.data.array->data[i], indent + 4); | ||||||
|                 printf("\n"); |                 printf("\n"); | ||||||
|             } |             } | ||||||
|             for (i = 0; i < indent; ++i) fputc(' ', stdout); |             for (i = 0; i < indent; ++i) fputc(' ', stdout); | ||||||
|             if (x->type == TYPE_ARRAY) printf("  ]\n"); else printf("  )\n"); |             if (x.type == TYPE_ARRAY) printf("  ]\n"); else printf("  )\n"); | ||||||
|             break; |             break; | ||||||
|         case TYPE_STRING: |         case TYPE_STRING: | ||||||
|             printf("\"%.*s\"", VStringSize(x->data.string), (char *) x->data.string); |             printf("\"%.*s\"", VStringSize(x.data.string), (char *) x.data.string); | ||||||
|             break; |             break; | ||||||
|         case TYPE_SYMBOL: |         case TYPE_SYMBOL: | ||||||
|             printf("%.*s", VStringSize(x->data.string), (char *) x->data.string); |             printf("%.*s", VStringSize(x.data.string), (char *) x.data.string); | ||||||
|             break; |             break; | ||||||
|         case TYPE_CFUNCTION: |         case TYPE_CFUNCTION: | ||||||
|             printf("<cfunction>"); |             printf("<cfunction>"); | ||||||
|             break; |             break; | ||||||
|         case TYPE_FUNCTION: |         case TYPE_FUNCTION: | ||||||
|             printf("<function "); |             printf("<function "); | ||||||
|            	FuncDefBytecodePrint(x->data.func->def); |            	FuncDefBytecodePrint(x.data.func->def); | ||||||
|            	printf(">"); |            	printf(">"); | ||||||
|             break; |             break; | ||||||
|         case TYPE_DICTIONARY: |         case TYPE_DICTIONARY: | ||||||
| @@ -66,7 +66,7 @@ void ValuePrint(Value * x, uint32_t indent) { | |||||||
|             break; |             break; | ||||||
|         case TYPE_FUNCDEF: |         case TYPE_FUNCDEF: | ||||||
|             printf("<funcdef "); |             printf("<funcdef "); | ||||||
|            	FuncDefBytecodePrint(x->data.funcdef); |            	FuncDefBytecodePrint(x.data.funcdef); | ||||||
|            	printf(">"); |            	printf(">"); | ||||||
|             break; |             break; | ||||||
|         case TYPE_FUNCENV: |         case TYPE_FUNCENV: | ||||||
| @@ -135,39 +135,39 @@ static uint8_t * StringDescription(VM * vm, const char * title, uint32_t titlele | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Returns a string pointer or NULL if could not allocate memory. */ | /* Returns a string pointer or NULL if could not allocate memory. */ | ||||||
| uint8_t * ValueToString(VM * vm, Value * x) { | uint8_t * ValueToString(VM * vm, Value x) { | ||||||
|     switch (x->type) { |     switch (x.type) { | ||||||
|         case TYPE_NIL: |         case TYPE_NIL: | ||||||
|             return LoadCString(vm, "nil", 3); |             return LoadCString(vm, "nil", 3); | ||||||
|         case TYPE_BOOLEAN: |         case TYPE_BOOLEAN: | ||||||
|             if (x->data.boolean) { |             if (x.data.boolean) { | ||||||
|                 return LoadCString(vm, "true", 4); |                 return LoadCString(vm, "true", 4); | ||||||
|             } else { |             } else { | ||||||
|                 return LoadCString(vm, "false", 5); |                 return LoadCString(vm, "false", 5); | ||||||
|             } |             } | ||||||
|         case TYPE_NUMBER: |         case TYPE_NUMBER: | ||||||
|             return NumberToString(vm, x->data.number); |             return NumberToString(vm, x.data.number); | ||||||
|         case TYPE_ARRAY: |         case TYPE_ARRAY: | ||||||
|             return StringDescription(vm, "array", 5, x->data.array); |             return StringDescription(vm, "array", 5, x.data.array); | ||||||
|         case TYPE_FORM: |         case TYPE_FORM: | ||||||
|             return StringDescription(vm, "form", 4, x->data.array); |             return StringDescription(vm, "form", 4, x.data.array); | ||||||
|         case TYPE_STRING: |         case TYPE_STRING: | ||||||
|         case TYPE_SYMBOL: |         case TYPE_SYMBOL: | ||||||
|             return x->data.string; |             return x.data.string; | ||||||
|         case TYPE_BYTEBUFFER: |         case TYPE_BYTEBUFFER: | ||||||
|             return StringDescription(vm, "buffer", 6, x->data.buffer); |             return StringDescription(vm, "buffer", 6, x.data.buffer); | ||||||
|         case TYPE_CFUNCTION: |         case TYPE_CFUNCTION: | ||||||
|             return StringDescription(vm, "cfunction", 9, x->data.cfunction); |             return StringDescription(vm, "cfunction", 9, x.data.cfunction); | ||||||
|         case TYPE_FUNCTION: |         case TYPE_FUNCTION: | ||||||
|             return StringDescription(vm, "function", 8, x->data.func); |             return StringDescription(vm, "function", 8, x.data.func); | ||||||
|         case TYPE_DICTIONARY: |         case TYPE_DICTIONARY: | ||||||
|             return StringDescription(vm, "dictionary", 10, x->data.dict); |             return StringDescription(vm, "dictionary", 10, x.data.dict); | ||||||
|         case TYPE_FUNCDEF: |         case TYPE_FUNCDEF: | ||||||
|             return StringDescription(vm, "funcdef", 7, x->data.funcdef); |             return StringDescription(vm, "funcdef", 7, x.data.funcdef); | ||||||
|         case TYPE_FUNCENV: |         case TYPE_FUNCENV: | ||||||
|             return StringDescription(vm, "funcenv", 7, x->data.funcenv); |             return StringDescription(vm, "funcenv", 7, x.data.funcenv); | ||||||
|         case TYPE_THREAD: |         case TYPE_THREAD: | ||||||
|             return StringDescription(vm, "thread", 6, x->data.array); |             return StringDescription(vm, "thread", 6, x.data.array); | ||||||
|     } |     } | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
| @@ -182,44 +182,36 @@ uint32_t djb2(const uint8_t * str) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Check if two values are equal. This is strict equality with no conversion. */ | /* Check if two values are equal. This is strict equality with no conversion. */ | ||||||
| int ValueEqual(Value * x, Value * y) { | int ValueEqual(Value x, Value y) { | ||||||
|     int result = 0; |     int result = 0; | ||||||
|     if (x->type != y->type) { |     if (x.type != y.type) { | ||||||
|         result = 0; |         result = 0; | ||||||
|     } else { |     } else { | ||||||
|         switch (x->type) { |         switch (x.type) { | ||||||
|             case TYPE_NIL: |             case TYPE_NIL: | ||||||
|                 result = 1; |                 result = 1; | ||||||
|                 break; |                 break; | ||||||
|             case TYPE_BOOLEAN: |             case TYPE_BOOLEAN: | ||||||
|                 result = x->data.boolean == y->data.boolean; |                 result = x.data.boolean == y.data.boolean; | ||||||
|                 break; |                 break; | ||||||
|             case TYPE_NUMBER: |             case TYPE_NUMBER: | ||||||
|                 result = x->data.number == y->data.number; |                 result = x.data.number == y.data.number; | ||||||
|                 break; |                 break; | ||||||
|             /* Assume that when strings are created, equal strings |             /* Assume that when strings are created, equal strings | ||||||
|              * are set to the same string */ |              * are set to the same string */ | ||||||
|             case TYPE_STRING: |             case TYPE_STRING: | ||||||
|  |             /* TODO: keep cache to for symbols to get quick equality. */ | ||||||
|             case TYPE_SYMBOL: |             case TYPE_SYMBOL: | ||||||
|                 if (x->data.string == y->data.string) { |                 if (x.data.string == y.data.string) { | ||||||
|                     result = 1; |                     result = 1; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 if (ValueHash(x) != ValueHash(y) || |                 if (ValueHash(x) != ValueHash(y) || | ||||||
|                         VStringSize(x->data.string) != VStringSize(y->data.string)) { |                         VStringSize(x.data.string) != VStringSize(y.data.string)) { | ||||||
|                     result = 0; |                     result = 0; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 /* If two different strings are equal, merge them to share the same data */ |                 if (!strncmp((char *) x.data.string, (char *) y.data.string, VStringSize(x.data.string))) { | ||||||
|                 if (!strncmp((char *) x->data.string, (char *) y->data.string, VStringSize(x->data.string))) { |  | ||||||
|                     /* Use the lower pointer in memory. This means that in long running |  | ||||||
|                      * programs, repeated string compares will eventually all use identical |  | ||||||
|                      * pointers for identical strings. */ |  | ||||||
|                     if (x->data.string < y->data.string) { |  | ||||||
|                         y->data.string = x->data.string; |  | ||||||
|                     } else { |  | ||||||
|                         x->data.string = y->data.string; |  | ||||||
|                     } |  | ||||||
|                     result = 1; |                     result = 1; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
| @@ -235,7 +227,7 @@ int ValueEqual(Value * x, Value * y) { | |||||||
|             case TYPE_FUNCENV: |             case TYPE_FUNCENV: | ||||||
|             case TYPE_THREAD: |             case TYPE_THREAD: | ||||||
|                 /* compare pointers */ |                 /* compare pointers */ | ||||||
|                 result = x->data.array == y->data.array; |                 result = x.data.array == y.data.array; | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -243,14 +235,14 @@ int ValueEqual(Value * x, Value * y) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Computes a hash value for a function */ | /* Computes a hash value for a function */ | ||||||
| uint32_t ValueHash(Value * x) { | uint32_t ValueHash(Value x) { | ||||||
|     uint32_t hash = 0; |     uint32_t hash = 0; | ||||||
|     switch (x->type) { |     switch (x.type) { | ||||||
|         case TYPE_NIL: |         case TYPE_NIL: | ||||||
|             hash = 0; |             hash = 0; | ||||||
|             break; |             break; | ||||||
|         case TYPE_BOOLEAN: |         case TYPE_BOOLEAN: | ||||||
|             hash = x->data.boolean; |             hash = x.data.boolean; | ||||||
|             break; |             break; | ||||||
|         case TYPE_NUMBER: |         case TYPE_NUMBER: | ||||||
|             { |             { | ||||||
| @@ -258,7 +250,7 @@ uint32_t ValueHash(Value * x) { | |||||||
|                     uint32_t hash; |                     uint32_t hash; | ||||||
|                     Number number; |                     Number number; | ||||||
|                 } u; |                 } u; | ||||||
|                 u.number = x->data.number; |                 u.number = x.data.number; | ||||||
|                 hash = u.hash; |                 hash = u.hash; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| @@ -266,10 +258,10 @@ uint32_t ValueHash(Value * x) { | |||||||
|         case TYPE_SYMBOL: |         case TYPE_SYMBOL: | ||||||
|         case TYPE_STRING: |         case TYPE_STRING: | ||||||
|             /* Assume 0 is not hashed. */ |             /* Assume 0 is not hashed. */ | ||||||
|             if (VStringHash(x->data.string)) |             if (VStringHash(x.data.string)) | ||||||
|                 hash = VStringHash(x->data.string); |                 hash = VStringHash(x.data.string); | ||||||
|             else |             else | ||||||
|                 hash = VStringHash(x->data.string) = djb2(x->data.string); |                 hash = VStringHash(x.data.string) = djb2(x.data.string); | ||||||
|             break; |             break; | ||||||
|         case TYPE_ARRAY: |         case TYPE_ARRAY: | ||||||
|         case TYPE_FORM: |         case TYPE_FORM: | ||||||
| @@ -286,7 +278,7 @@ uint32_t ValueHash(Value * x) { | |||||||
| 					void * pointer; | 					void * pointer; | ||||||
| 					uint32_t hash; | 					uint32_t hash; | ||||||
| 				} u; | 				} u; | ||||||
| 				u.pointer = x->data.pointer; | 				u.pointer = x.data.pointer; | ||||||
| 				hash = u.hash; | 				hash = u.hash; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| @@ -297,49 +289,43 @@ uint32_t ValueHash(Value * x) { | |||||||
| /* Compares x to y. If they are equal retuns 0. If x is less, returns -1. | /* Compares x to y. If they are equal retuns 0. If x is less, returns -1. | ||||||
|  * If y is less, returns 1. All types are comparable |  * If y is less, returns 1. All types are comparable | ||||||
|  * and should have strict ordering. */ |  * and should have strict ordering. */ | ||||||
| int ValueCompare(Value * x, Value * y) { | int ValueCompare(Value x, Value y) { | ||||||
|     if (x->type == y->type) { |     if (x.type == y.type) { | ||||||
|         switch (x->type) { |         switch (x.type) { | ||||||
|             case TYPE_NIL: |             case TYPE_NIL: | ||||||
|                 return 0; |                 return 0; | ||||||
|             case TYPE_BOOLEAN: |             case TYPE_BOOLEAN: | ||||||
|                 if (x->data.boolean == y->data.boolean) { |                 if (x.data.boolean == y.data.boolean) { | ||||||
|                     return 0; |                     return 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     return x->data.boolean ? 1 : -1; |                     return x.data.boolean ? 1 : -1; | ||||||
|                 } |                 } | ||||||
|             case TYPE_NUMBER: |             case TYPE_NUMBER: | ||||||
|                 /* TODO: define behavior for NaN and infinties. */ |                 /* TODO: define behavior for NaN and infinties. */ | ||||||
|                 if (x->data.number == y->data.number) { |                 if (x.data.number == y.data.number) { | ||||||
|                     return 0; |                     return 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     return x->data.number > y->data.number ? 1 : -1; |                     return x.data.number > y.data.number ? 1 : -1; | ||||||
|                 } |                 } | ||||||
|             case TYPE_STRING: |             case TYPE_STRING: | ||||||
|             case TYPE_SYMBOL: |             case TYPE_SYMBOL: | ||||||
|                 if (x->data.string == y->data.string) { |                 if (x.data.string == y.data.string) { | ||||||
|                     return 0; |                     return 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     uint32_t xlen = VStringSize(x->data.string); |                     uint32_t xlen = VStringSize(x.data.string); | ||||||
|                     uint32_t ylen = VStringSize(y->data.string); |                     uint32_t ylen = VStringSize(y.data.string); | ||||||
|                     uint32_t len = xlen > ylen ? ylen : xlen; |                     uint32_t len = xlen > ylen ? ylen : xlen; | ||||||
|                     uint32_t i; |                     uint32_t i; | ||||||
|                     for (i = 0; i < len; ++i) { |                     for (i = 0; i < len; ++i) { | ||||||
|                         if (x->data.string[i] == y->data.string[i]) { |                         if (x.data.string[i] == y.data.string[i]) { | ||||||
|                             continue; |                             continue; | ||||||
|                         } else if (x->data.string[i] < y->data.string[i]) { |                         } else if (x.data.string[i] < y.data.string[i]) { | ||||||
|                             return 1; /* x is less then y */ |                             return 1; /* x is less then y */ | ||||||
|                         } else { |                         } else { | ||||||
|                             return -1; /* y is less than x */ |                             return -1; /* y is less than x */ | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     if (xlen == ylen) { |                     if (xlen == ylen) { | ||||||
|                         /* Merge the two strings */ |  | ||||||
|                         if (x->data.string < y->data.string) { |  | ||||||
|                             y->data.string = x->data.string; |  | ||||||
|                         } else { |  | ||||||
|                             x->data.string = y->data.string; |  | ||||||
|                         } |  | ||||||
|                         return 0; |                         return 0; | ||||||
|                     } else { |                     } else { | ||||||
|                         return xlen < ylen ? -1 : 1; |                         return xlen < ylen ? -1 : 1; | ||||||
| @@ -354,13 +340,13 @@ int ValueCompare(Value * x, Value * y) { | |||||||
|             case TYPE_FUNCDEF: |             case TYPE_FUNCDEF: | ||||||
|             case TYPE_FUNCENV: |             case TYPE_FUNCENV: | ||||||
|             case TYPE_THREAD: |             case TYPE_THREAD: | ||||||
|                 if (x->data.string == y->data.string) { |                 if (x.data.string == y.data.string) { | ||||||
|                     return 0; |                     return 0; | ||||||
|                 } else { |                 } else { | ||||||
|                     return x->data.string > y->data.string ? 1 : -1; |                     return x.data.string > y.data.string ? 1 : -1; | ||||||
|                 } |                 } | ||||||
|         } |         } | ||||||
|     } else if (x->type < y->type) { |     } else if (x.type < y.type) { | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     return 1; |     return 1; | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								value.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								value.h
									
									
									
									
									
								
							| @@ -3,16 +3,16 @@ | |||||||
|  |  | ||||||
| #include "datatypes.h" | #include "datatypes.h" | ||||||
|  |  | ||||||
| void ValuePrint(Value * x, uint32_t indent); | void ValuePrint(Value x, uint32_t indent); | ||||||
|  |  | ||||||
| int ValueCompare(Value * x, Value * y); | int ValueCompare(Value x, Value y); | ||||||
|  |  | ||||||
| int ValueEqual(Value * x, Value * y); | int ValueEqual(Value x, Value y); | ||||||
|  |  | ||||||
| Value ValueLoadCString(VM * vm, const char * string); | Value ValueLoadCString(VM * vm, const char * string); | ||||||
|  |  | ||||||
| uint8_t * ValueToString(VM * vm, Value * x); | uint8_t * ValueToString(VM * vm, Value x); | ||||||
|  |  | ||||||
| uint32_t ValueHash(Value * x); | uint32_t ValueHash(Value x); | ||||||
|  |  | ||||||
| #endif /* end of include guard: VALUE_H_1RJPQKFM */ | #endif /* end of include guard: VALUE_H_1RJPQKFM */ | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								vm.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								vm.c
									
									
									
									
									
								
							| @@ -198,6 +198,7 @@ void * VMZalloc(VM * vm, uint32_t size) { | |||||||
|  |  | ||||||
| /* Run garbage collection */ | /* Run garbage collection */ | ||||||
| void VMCollect(VM * vm) { | void VMCollect(VM * vm) { | ||||||
|  |     if (vm->lock > 0) return; | ||||||
|     Value thread; |     Value thread; | ||||||
|     thread.type = TYPE_THREAD; |     thread.type = TYPE_THREAD; | ||||||
|     thread.data.array = vm->thread; |     thread.data.array = vm->thread; | ||||||
| @@ -336,7 +337,9 @@ static void VMCallOp(VM * vm) { | |||||||
|     if (callee.type == TYPE_CFUNCTION) { |     if (callee.type == TYPE_CFUNCTION) { | ||||||
|         for (i = 0; i < arity; ++i) |         for (i = 0; i < arity; ++i) | ||||||
|             *(argWriter++) = *VMOpArg(4 + i); |             *(argWriter++) = *VMOpArg(4 + i); | ||||||
|  |         ++vm->lock; | ||||||
|         VMReturn(vm, callee.data.cfunction(vm)); |         VMReturn(vm, callee.data.cfunction(vm)); | ||||||
|  |         --vm->lock; | ||||||
|         VMMaybeCollect(vm); |         VMMaybeCollect(vm); | ||||||
| 	} else if (callee.type == TYPE_FUNCTION) { | 	} else if (callee.type == TYPE_FUNCTION) { | ||||||
|     	Func * f = callee.data.func; |     	Func * f = callee.data.func; | ||||||
| @@ -399,7 +402,9 @@ static void VMTailCallOp(VM * vm) { | |||||||
| 	FrameSize(thread) = newFrameSize; | 	FrameSize(thread) = newFrameSize; | ||||||
|     FrameCallee(thread) = callee; |     FrameCallee(thread) = callee; | ||||||
|     if (callee.type == TYPE_CFUNCTION) { |     if (callee.type == TYPE_CFUNCTION) { | ||||||
|  |         ++vm->lock; | ||||||
|         VMReturn(vm, callee.data.cfunction(vm)); |         VMReturn(vm, callee.data.cfunction(vm)); | ||||||
|  |         --vm->lock; | ||||||
|         VMMaybeCollect(vm); |         VMMaybeCollect(vm); | ||||||
| 	} else { | 	} else { | ||||||
|     	Func * f = callee.data.func; |     	Func * f = callee.data.func; | ||||||
| @@ -630,7 +635,7 @@ int VMStart(VM * vm) { | |||||||
|             case VM_OP_EQL: /* Equality */ |             case VM_OP_EQL: /* Equality */ | ||||||
|                 vRet = VMOpArg(1); |                 vRet = VMOpArg(1); | ||||||
|                 vRet->type = TYPE_BOOLEAN; |                 vRet->type = TYPE_BOOLEAN; | ||||||
|                 vRet->data.boolean = ValueEqual(VMOpArg(2), VMOpArg(3)); |                 vRet->data.boolean = ValueEqual(*VMOpArg(2), *VMOpArg(3)); | ||||||
|                 vm->pc += 4; |                 vm->pc += 4; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -639,7 +644,7 @@ int VMStart(VM * vm) { | |||||||
|                 v1 = VMOpArg(2); |                 v1 = VMOpArg(2); | ||||||
|                 v2 = VMOpArg(3); |                 v2 = VMOpArg(3); | ||||||
|                 vRet->type = TYPE_BOOLEAN; |                 vRet->type = TYPE_BOOLEAN; | ||||||
|                 vRet->data.boolean = (ValueCompare(VMOpArg(2), VMOpArg(3)) == -1); |                 vRet->data.boolean = (ValueCompare(*VMOpArg(2), *VMOpArg(3)) == -1); | ||||||
|                 vm->pc += 4; |                 vm->pc += 4; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -648,7 +653,7 @@ int VMStart(VM * vm) { | |||||||
|                 v1 = VMOpArg(2); |                 v1 = VMOpArg(2); | ||||||
|                 v2 = VMOpArg(3); |                 v2 = VMOpArg(3); | ||||||
|                 vRet->type = TYPE_BOOLEAN; |                 vRet->type = TYPE_BOOLEAN; | ||||||
|                 vRet->data.boolean = (ValueCompare(VMOpArg(2), VMOpArg(3)) != 1); |                 vRet->data.boolean = (ValueCompare(*VMOpArg(2), *VMOpArg(3)) != 1); | ||||||
|                 vm->pc += 4; |                 vm->pc += 4; | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
| @@ -678,7 +683,7 @@ int VMStart(VM * vm) { | |||||||
| 					while (i < kvs) { | 					while (i < kvs) { | ||||||
|                         v1 = VMOpArg(i++); |                         v1 = VMOpArg(i++); | ||||||
|                         v2 = VMOpArg(i++); |                         v2 = VMOpArg(i++); | ||||||
| 					    DictPut(vm, dict, v1, v2); | 					    DictPut(vm, dict, *v1, *v2); | ||||||
|                     } |                     } | ||||||
| 					vRet->type = TYPE_DICTIONARY; | 					vRet->type = TYPE_DICTIONARY; | ||||||
| 					vRet->data.dict = dict; | 					vRet->data.dict = dict; | ||||||
| @@ -740,6 +745,20 @@ int VMStart(VM * vm) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Get an argument from the stack */ | ||||||
|  | Value VMGetArg(VM * vm, uint16_t index) { | ||||||
|  |     uint16_t frameSize = FrameSize(vm->thread); | ||||||
|  |     VMAssert(vm, frameSize > index, "Cannot get arg out of stack bounds"); | ||||||
|  | 	return *VMArg(index); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Put a value on the stack */ | ||||||
|  | void VMSetArg(VM * vm, uint16_t index, Value x) { | ||||||
|  |     uint16_t frameSize = FrameSize(vm->thread); | ||||||
|  |     VMAssert(vm, frameSize > index, "Cannot set arg out of stack bounds"); | ||||||
|  | 	*VMArg(index) = x; | ||||||
|  | } | ||||||
|  |  | ||||||
| #undef VMOpArg | #undef VMOpArg | ||||||
| #undef VMArg | #undef VMArg | ||||||
|  |  | ||||||
| @@ -749,12 +768,14 @@ void VMInit(VM * vm) { | |||||||
|     vm->base = NULL; |     vm->base = NULL; | ||||||
|     vm->pc = NULL; |     vm->pc = NULL; | ||||||
|     vm->error = NULL; |     vm->error = NULL; | ||||||
|     vm->thread = ArrayNew(vm, 20); |  | ||||||
| 	/* Garbage collection */ | 	/* Garbage collection */ | ||||||
|     vm->blocks = NULL; |     vm->blocks = NULL; | ||||||
|     vm->nextCollection = 0; |     vm->nextCollection = 0; | ||||||
|     vm->memoryInterval = 1024 * 256; |     vm->memoryInterval = 1024 * 256; | ||||||
|     vm->black = 0; |     vm->black = 0; | ||||||
|  |     vm->lock = 0; | ||||||
|  |     /* Create new thread */ | ||||||
|  |     vm->thread = ArrayNew(vm, 20); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Load a function into the VM. The function will be called with | /* Load a function into the VM. The function will be called with | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								vm.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								vm.h
									
									
									
									
									
								
							| @@ -44,4 +44,10 @@ void * VMAlloc(VM * vm, uint32_t amount); | |||||||
| /* Allocate zeroed memory */ | /* Allocate zeroed memory */ | ||||||
| void * VMZalloc(VM * vm, uint32_t amount); | void * VMZalloc(VM * vm, uint32_t amount); | ||||||
|  |  | ||||||
|  | /* Get an argument from the stack */ | ||||||
|  | Value VMGetArg(VM * vm, uint16_t index); | ||||||
|  |  | ||||||
|  | /* Put a value on the stack */ | ||||||
|  | void VMSetArg(VM * vm, uint16_t index, Value x); | ||||||
|  |  | ||||||
| #endif /* end of include guard: VM_H_C4OZU8CQ */ | #endif /* end of include guard: VM_H_C4OZU8CQ */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose