瀏覽代碼

Removed separate unittest command line tool, replaced by gravity -t option.

Marco Bambini 7 年之前
父節點
當前提交
8499492057

+ 3 - 0
.gitignore

@@ -313,3 +313,6 @@ xcuserdata/
 *.xcworkspacedata
 .DS_Store
 gravity.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+api/api.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+*.xcscheme
+gravity.xcodeproj/xcuserdata/marco.xcuserdatad/xcschemes/xcschememanagement.plist

+ 0 - 5
CMakeLists.txt

@@ -6,7 +6,6 @@ SET(SHARED_DIR src/shared/)
 SET(UTILS_DIR src/utils/)
 SET(OPT_DIR src/optionals/)
 
-SET(UNITTEST_SRC src/cli/unittest.c)
 SET(GRAVITY_SRC src/cli/gravity.c)
 
 AUX_SOURCE_DIRECTORY(${COMPILER_DIR} COMPILER_FILES)
@@ -33,9 +32,5 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
     LIST(APPEND LIBS "m" "rt")
 endif()
 
-ADD_EXECUTABLE(unittest ${UNITTEST_SRC} $<TARGET_OBJECTS:libgravity>)
-TARGET_LINK_LIBRARIES(unittest ${LIBS})
-
-
 ADD_EXECUTABLE(gravity ${GRAVITY_SRC} $<TARGET_OBJECTS:libgravity>)
 TARGET_LINK_LIBRARIES(gravity ${LIBS})

+ 3 - 7
Makefile

@@ -3,7 +3,6 @@ RUNTIME_DIR = src/runtime/
 SHARED_DIR = src/shared/
 UTILS_DIR = src/utils/
 OPT_DIR = src/optionals/
-UNITTEST_SRC = src/cli/unittest.c
 GRAVITY_SRC = src/cli/gravity.c
 
 CC ?= gcc
@@ -44,18 +43,15 @@ else
 	CFLAGS += -O2
 endif
 
-all: unittest gravity
-
-unittest:	$(OBJ) $(UNITTEST_SRC)
-	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+all: gravity
 
 gravity:	$(OBJ) $(GRAVITY_SRC)
 	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
 
-.PHONY: all clean unittest gravity
+.PHONY: all clean gravity
 
 lib: gravity
 	$(CC) -shared -o $(LIBTARGET) $(OBJ) $(LDFLAGS)
 
 clean:
-	rm -f $(OBJ) unittest gravity libgravity.so gravity.dll
+	rm -f $(OBJ) gravity libgravity.so gravity.dll

+ 37 - 1
api/api.xcodeproj/project.pbxproj

@@ -27,6 +27,8 @@
 		A922BB051E6E0F5C00D9FAD0 /* gravity_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = A922BAEC1E6E0F5C00D9FAD0 /* gravity_debug.c */; };
 		A922BB061E6E0F5C00D9FAD0 /* gravity_json.c in Sources */ = {isa = PBXBuildFile; fileRef = A922BAEE1E6E0F5C00D9FAD0 /* gravity_json.c */; };
 		A922BB071E6E0F5C00D9FAD0 /* gravity_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = A922BAF01E6E0F5C00D9FAD0 /* gravity_utils.c */; };
+		A961617720E4AFDF00A39833 /* gravity_math.c in Sources */ = {isa = PBXBuildFile; fileRef = A961617320E4AFDF00A39833 /* gravity_math.c */; };
+		A961617820E4AFDF00A39833 /* gravity_math.c in Sources */ = {isa = PBXBuildFile; fileRef = A961617320E4AFDF00A39833 /* gravity_math.c */; };
 		A9BA7D451E74180100259798 /* exec_gravity.c in Sources */ = {isa = PBXBuildFile; fileRef = A9BA7D441E74180100259798 /* exec_gravity.c */; };
 		A9FE6A1A1E753077007FC86B /* gravity_hash.c in Sources */ = {isa = PBXBuildFile; fileRef = A922BAE31E6E0F5C00D9FAD0 /* gravity_hash.c */; };
 		A9FE6A1B1E753077007FC86B /* exec_c.c in Sources */ = {isa = PBXBuildFile; fileRef = A9FE6A151E753041007FC86B /* exec_c.c */; };
@@ -120,6 +122,10 @@
 		A922BAEF1E6E0F5C00D9FAD0 /* gravity_json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_json.h; sourceTree = "<group>"; };
 		A922BAF01E6E0F5C00D9FAD0 /* gravity_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_utils.c; sourceTree = "<group>"; };
 		A922BAF11E6E0F5C00D9FAD0 /* gravity_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_utils.h; sourceTree = "<group>"; };
+		A961617320E4AFDF00A39833 /* gravity_math.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_math.c; sourceTree = "<group>"; };
+		A961617420E4AFDF00A39833 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
+		A961617520E4AFDF00A39833 /* gravity_optionals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_optionals.h; sourceTree = "<group>"; };
+		A961617620E4AFDF00A39833 /* gravity_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_math.h; sourceTree = "<group>"; };
 		A9BA7D441E74180100259798 /* exec_gravity.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exec_gravity.c; sourceTree = "<group>"; };
 		A9FE6A151E753041007FC86B /* exec_c.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exec_c.c; sourceTree = "<group>"; };
 		A9FE6A341E753077007FC86B /* exec_c */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = exec_c; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -167,6 +173,7 @@
 			children = (
 				A922BAC01E6E0F5C00D9FAD0 /* compiler */,
 				A922BADA1E6E0F5C00D9FAD0 /* runtime */,
+				A961617220E4AFDF00A39833 /* optionals */,
 				A922BAE01E6E0F5C00D9FAD0 /* shared */,
 				A922BAEB1E6E0F5C00D9FAD0 /* utils */,
 			);
@@ -248,6 +255,17 @@
 			path = utils;
 			sourceTree = "<group>";
 		};
+		A961617220E4AFDF00A39833 /* optionals */ = {
+			isa = PBXGroup;
+			children = (
+				A961617320E4AFDF00A39833 /* gravity_math.c */,
+				A961617420E4AFDF00A39833 /* README.md */,
+				A961617520E4AFDF00A39833 /* gravity_optionals.h */,
+				A961617620E4AFDF00A39833 /* gravity_math.h */,
+			);
+			path = optionals;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -291,7 +309,7 @@
 		A922BAA81E6E0F2600D9FAD0 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 0820;
+				LastUpgradeCheck = 0940;
 				ORGANIZATIONNAME = Creolabs;
 				TargetAttributes = {
 					A922BAAF1E6E0F2600D9FAD0 = {
@@ -339,6 +357,7 @@
 				A922BB051E6E0F5C00D9FAD0 /* gravity_debug.c in Sources */,
 				A922BAFB1E6E0F5C00D9FAD0 /* gravity_semacheck1.c in Sources */,
 				A922BB001E6E0F5C00D9FAD0 /* gravity_core.c in Sources */,
+				A961617720E4AFDF00A39833 /* gravity_math.c in Sources */,
 				A922BB011E6E0F5C00D9FAD0 /* gravity_vm.c in Sources */,
 				A922BAF51E6E0F5C00D9FAD0 /* gravity_codegen.c in Sources */,
 				A922BB031E6E0F5C00D9FAD0 /* gravity_memory.c in Sources */,
@@ -367,6 +386,7 @@
 				A9FE6A271E753077007FC86B /* gravity_debug.c in Sources */,
 				A9FE6A281E753077007FC86B /* gravity_semacheck1.c in Sources */,
 				A9FE6A291E753077007FC86B /* gravity_core.c in Sources */,
+				A961617820E4AFDF00A39833 /* gravity_math.c in Sources */,
 				A9FE6A2A1E753077007FC86B /* gravity_vm.c in Sources */,
 				A9FE6A2B1E753077007FC86B /* gravity_codegen.c in Sources */,
 				A9FE6A2C1E753077007FC86B /* gravity_memory.c in Sources */,
@@ -387,15 +407,23 @@
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -434,15 +462,23 @@
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;

+ 3 - 3
api/exec_c.c

@@ -24,9 +24,9 @@ static const char *source =	"	extern var Math;				\
 								}";
 
 // error callback
-static void report_error (error_type_t error_type, const char *message,
+static void report_error (gravity_vm *vm, error_type_t error_type, const char *message,
 						  error_desc_t error_desc, void *xdata) {
-#pragma unused(xdata)
+    #pragma unused(vm, xdata)
 	const char *type = "N/A";
 	switch (error_type) {
 		case GRAVITY_ERROR_NONE: type = "NONE"; break;
@@ -176,7 +176,7 @@ int main(int argc, const char * argv[]) {
 		double t = gravity_vm_time(vm);
 
 		char buffer[512];
-		gravity_value_dump(result, buffer, sizeof(buffer));
+		gravity_value_dump(vm, result, buffer, sizeof(buffer));
 		printf("RESULT: %s (in %.4f ms)\n\n", buffer, t);
 	}
 

+ 4 - 4
api/exec_gravity.c

@@ -14,9 +14,9 @@ static const char *source = "func add (a, b) {return a + b;}; \
                              func mul (a, b) {return a * b;};";
 
 // error callback
-static void report_error (error_type_t error_type, const char *message,
+static void report_error (gravity_vm *vm, error_type_t error_type, const char *message,
 						  error_desc_t error_desc, void *xdata) {
-	#pragma unused(xdata)
+	#pragma unused(vm, xdata)
 	const char *type = "N/A";
 	switch (error_type) {
 		case GRAVITY_ERROR_NONE: type = "NONE"; break;
@@ -68,7 +68,7 @@ int main(int argc, const char * argv[]) {
 	if (gravity_vm_runclosure(vm, VALUE_AS_CLOSURE(add), add, params, 2)) {
 		gravity_value_t result = gravity_vm_result(vm);
 		printf("add result ");
-		gravity_value_dump(result, NULL, 0);
+		gravity_value_dump(vm, result, NULL, 0);
 	}
 
 	// lookup mul closure
@@ -79,7 +79,7 @@ int main(int argc, const char * argv[]) {
 	if (gravity_vm_runclosure(vm, VALUE_AS_CLOSURE(mul), mul, params, 2)) {
 		gravity_value_t result = gravity_vm_result(vm);
 		printf("mul result ");
-		gravity_value_dump(result, NULL, 0);
+		gravity_value_dump(vm, result, NULL, 0);
 	}
 
 	// free vm and core classes

+ 1 - 1
docs/quickstart.md

@@ -41,7 +41,7 @@ To directly execute a gravity file (without first serializing it to json):
 ### Unit Tests
 You can run [unit tests](unittest.md) by providing a path to a folder containing all test files:
 ```bash
-	./unittest path_to_test_folder
+	./gravity -t path_to_test_folder
 ```
 This should produce output like:
 	<img src="assets/images/unittest.png" width="666px" height="466px">

+ 9 - 121
gravity.xcodeproj/project.pbxproj

@@ -8,7 +8,6 @@
 
 /* Begin PBXBuildFile section */
 		A9506D381E69AB66009A0045 /* gravity.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506CFC1E69AB1E009A0045 /* gravity.c */; };
-		A9506D391E69AB6A009A0045 /* unittest.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506CFD1E69AB1E009A0045 /* unittest.c */; };
 		A9506D3A1E69AB78009A0045 /* gravity_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D001E69AB1E009A0045 /* gravity_ast.c */; };
 		A9506D3B1E69AB78009A0045 /* gravity_codegen.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D021E69AB1E009A0045 /* gravity_codegen.c */; };
 		A9506D3C1E69AB78009A0045 /* gravity_compiler.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D041E69AB1E009A0045 /* gravity_compiler.c */; };
@@ -21,36 +20,15 @@
 		A9506D431E69AB78009A0045 /* gravity_symboltable.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D121E69AB1E009A0045 /* gravity_symboltable.c */; };
 		A9506D441E69AB78009A0045 /* gravity_token.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D141E69AB1E009A0045 /* gravity_token.c */; };
 		A9506D451E69AB78009A0045 /* gravity_visitor.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D161E69AB1E009A0045 /* gravity_visitor.c */; };
-		A9506D461E69AB78009A0045 /* gravity_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D001E69AB1E009A0045 /* gravity_ast.c */; };
-		A9506D471E69AB78009A0045 /* gravity_codegen.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D021E69AB1E009A0045 /* gravity_codegen.c */; };
-		A9506D481E69AB78009A0045 /* gravity_compiler.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D041E69AB1E009A0045 /* gravity_compiler.c */; };
-		A9506D491E69AB78009A0045 /* gravity_ircode.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D061E69AB1E009A0045 /* gravity_ircode.c */; };
-		A9506D4A1E69AB78009A0045 /* gravity_lexer.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D081E69AB1E009A0045 /* gravity_lexer.c */; };
-		A9506D4B1E69AB78009A0045 /* gravity_optimizer.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D0A1E69AB1E009A0045 /* gravity_optimizer.c */; };
-		A9506D4C1E69AB78009A0045 /* gravity_parser.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D0C1E69AB1E009A0045 /* gravity_parser.c */; };
-		A9506D4D1E69AB78009A0045 /* gravity_semacheck1.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D0E1E69AB1E009A0045 /* gravity_semacheck1.c */; };
-		A9506D4E1E69AB78009A0045 /* gravity_semacheck2.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D101E69AB1E009A0045 /* gravity_semacheck2.c */; };
-		A9506D4F1E69AB78009A0045 /* gravity_symboltable.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D121E69AB1E009A0045 /* gravity_symboltable.c */; };
-		A9506D501E69AB78009A0045 /* gravity_token.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D141E69AB1E009A0045 /* gravity_token.c */; };
-		A9506D511E69AB78009A0045 /* gravity_visitor.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D161E69AB1E009A0045 /* gravity_visitor.c */; };
 		A9506D521E69AB7E009A0045 /* gravity_core.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D191E69AB1E009A0045 /* gravity_core.c */; };
 		A9506D531E69AB7E009A0045 /* gravity_vm.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D1B1E69AB1E009A0045 /* gravity_vm.c */; };
-		A9506D541E69AB7E009A0045 /* gravity_core.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D191E69AB1E009A0045 /* gravity_core.c */; };
-		A9506D551E69AB7E009A0045 /* gravity_vm.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D1B1E69AB1E009A0045 /* gravity_vm.c */; };
 		A9506D561E69AB86009A0045 /* gravity_hash.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D211E69AB1E009A0045 /* gravity_hash.c */; };
 		A9506D571E69AB86009A0045 /* gravity_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D241E69AB1E009A0045 /* gravity_memory.c */; };
 		A9506D581E69AB86009A0045 /* gravity_value.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D271E69AB1E009A0045 /* gravity_value.c */; };
-		A9506D591E69AB86009A0045 /* gravity_hash.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D211E69AB1E009A0045 /* gravity_hash.c */; };
-		A9506D5A1E69AB86009A0045 /* gravity_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D241E69AB1E009A0045 /* gravity_memory.c */; };
-		A9506D5B1E69AB86009A0045 /* gravity_value.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D271E69AB1E009A0045 /* gravity_value.c */; };
 		A9506D5C1E69AB8D009A0045 /* gravity_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2A1E69AB1E009A0045 /* gravity_debug.c */; };
 		A9506D5D1E69AB8D009A0045 /* gravity_json.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2C1E69AB1E009A0045 /* gravity_json.c */; };
 		A9506D5E1E69AB8D009A0045 /* gravity_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2E1E69AB1E009A0045 /* gravity_utils.c */; };
-		A9506D5F1E69AB8E009A0045 /* gravity_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2A1E69AB1E009A0045 /* gravity_debug.c */; };
-		A9506D601E69AB8E009A0045 /* gravity_json.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2C1E69AB1E009A0045 /* gravity_json.c */; };
-		A9506D611E69AB8E009A0045 /* gravity_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2E1E69AB1E009A0045 /* gravity_utils.c */; };
 		A9BB72F01F470832002FD2D6 /* gravity_math.c in Sources */ = {isa = PBXBuildFile; fileRef = A9BB72EC1F470832002FD2D6 /* gravity_math.c */; };
-		A9BB72F21F470888002FD2D6 /* gravity_math.c in Sources */ = {isa = PBXBuildFile; fileRef = A9BB72EC1F470832002FD2D6 /* gravity_math.c */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -63,21 +41,11 @@
 			);
 			runOnlyForDeploymentPostprocessing = 1;
 		};
-		A9506D331E69AB33009A0045 /* CopyFiles */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = /usr/share/man/man1/;
-			dstSubfolderSpec = 0;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 1;
-		};
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
 		A9506CF01E69AAEB009A0045 /* gravity */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gravity; sourceTree = BUILT_PRODUCTS_DIR; };
 		A9506CFC1E69AB1E009A0045 /* gravity.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gravity.c; sourceTree = "<group>"; };
-		A9506CFD1E69AB1E009A0045 /* unittest.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unittest.c; sourceTree = "<group>"; };
 		A9506CFF1E69AB1E009A0045 /* debug_macros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debug_macros.h; sourceTree = "<group>"; };
 		A9506D001E69AB1E009A0045 /* gravity_ast.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gravity_ast.c; sourceTree = "<group>"; };
 		A9506D011E69AB1E009A0045 /* gravity_ast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gravity_ast.h; sourceTree = "<group>"; };
@@ -124,7 +92,6 @@
 		A9506D2D1E69AB1E009A0045 /* gravity_json.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gravity_json.h; sourceTree = "<group>"; };
 		A9506D2E1E69AB1E009A0045 /* gravity_utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gravity_utils.c; sourceTree = "<group>"; };
 		A9506D2F1E69AB1E009A0045 /* gravity_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gravity_utils.h; sourceTree = "<group>"; };
-		A9506D371E69AB33009A0045 /* unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unittest; sourceTree = BUILT_PRODUCTS_DIR; };
 		A9BB72EC1F470832002FD2D6 /* gravity_math.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_math.c; sourceTree = "<group>"; };
 		A9BB72ED1F470832002FD2D6 /* gravity_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_math.h; sourceTree = "<group>"; };
 		A9BB72EE1F470832002FD2D6 /* gravity_optionals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_optionals.h; sourceTree = "<group>"; };
@@ -138,16 +105,17 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		A9506D321E69AB33009A0045 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		A90CDBC220E4AF2400616D56 /* cli */ = {
+			isa = PBXGroup;
+			children = (
+				A9506CFC1E69AB1E009A0045 /* gravity.c */,
+			);
+			path = cli;
+			sourceTree = "<group>";
+		};
 		A9506CE71E69AAEB009A0045 = {
 			isa = PBXGroup;
 			children = (
@@ -160,7 +128,6 @@
 			isa = PBXGroup;
 			children = (
 				A9506CF01E69AAEB009A0045 /* gravity */,
-				A9506D371E69AB33009A0045 /* unittest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -168,7 +135,7 @@
 		A9506CFA1E69AB1E009A0045 /* gravity */ = {
 			isa = PBXGroup;
 			children = (
-				A9506CFB1E69AB1E009A0045 /* cli */,
+				A90CDBC220E4AF2400616D56 /* cli */,
 				A9506CFE1E69AB1E009A0045 /* compiler */,
 				A9506D181E69AB1E009A0045 /* runtime */,
 				A9506D1E1E69AB1E009A0045 /* shared */,
@@ -179,15 +146,6 @@
 			path = src;
 			sourceTree = "<group>";
 		};
-		A9506CFB1E69AB1E009A0045 /* cli */ = {
-			isa = PBXGroup;
-			children = (
-				A9506CFC1E69AB1E009A0045 /* gravity.c */,
-				A9506CFD1E69AB1E009A0045 /* unittest.c */,
-			);
-			path = cli;
-			sourceTree = "<group>";
-		};
 		A9506CFE1E69AB1E009A0045 /* compiler */ = {
 			isa = PBXGroup;
 			children = (
@@ -292,23 +250,6 @@
 			productReference = A9506CF01E69AAEB009A0045 /* gravity */;
 			productType = "com.apple.product-type.tool";
 		};
-		A9506D301E69AB33009A0045 /* unittest */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = A9506D341E69AB33009A0045 /* Build configuration list for PBXNativeTarget "unittest" */;
-			buildPhases = (
-				A9506D311E69AB33009A0045 /* Sources */,
-				A9506D321E69AB33009A0045 /* Frameworks */,
-				A9506D331E69AB33009A0045 /* CopyFiles */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = unittest;
-			productName = gravity;
-			productReference = A9506D371E69AB33009A0045 /* unittest */;
-			productType = "com.apple.product-type.tool";
-		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -337,7 +278,6 @@
 			projectRoot = "";
 			targets = (
 				A9506CEF1E69AAEB009A0045 /* gravity */,
-				A9506D301E69AB33009A0045 /* unittest */,
 			);
 		};
 /* End PBXProject section */
@@ -372,35 +312,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		A9506D311E69AB33009A0045 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				A9506D491E69AB78009A0045 /* gravity_ircode.c in Sources */,
-				A9506D4B1E69AB78009A0045 /* gravity_optimizer.c in Sources */,
-				A9506D501E69AB78009A0045 /* gravity_token.c in Sources */,
-				A9506D5A1E69AB86009A0045 /* gravity_memory.c in Sources */,
-				A9506D5B1E69AB86009A0045 /* gravity_value.c in Sources */,
-				A9506D471E69AB78009A0045 /* gravity_codegen.c in Sources */,
-				A9506D601E69AB8E009A0045 /* gravity_json.c in Sources */,
-				A9506D551E69AB7E009A0045 /* gravity_vm.c in Sources */,
-				A9506D4D1E69AB78009A0045 /* gravity_semacheck1.c in Sources */,
-				A9506D4C1E69AB78009A0045 /* gravity_parser.c in Sources */,
-				A9506D5F1E69AB8E009A0045 /* gravity_debug.c in Sources */,
-				A9506D391E69AB6A009A0045 /* unittest.c in Sources */,
-				A9506D591E69AB86009A0045 /* gravity_hash.c in Sources */,
-				A9506D4E1E69AB78009A0045 /* gravity_semacheck2.c in Sources */,
-				A9506D4A1E69AB78009A0045 /* gravity_lexer.c in Sources */,
-				A9506D4F1E69AB78009A0045 /* gravity_symboltable.c in Sources */,
-				A9BB72F21F470888002FD2D6 /* gravity_math.c in Sources */,
-				A9506D461E69AB78009A0045 /* gravity_ast.c in Sources */,
-				A9506D611E69AB8E009A0045 /* gravity_utils.c in Sources */,
-				A9506D511E69AB78009A0045 /* gravity_visitor.c in Sources */,
-				A9506D481E69AB78009A0045 /* gravity_compiler.c in Sources */,
-				A9506D541E69AB7E009A0045 /* gravity_core.c in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
@@ -549,20 +460,6 @@
 			};
 			name = Release;
 		};
-		A9506D351E69AB33009A0045 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Debug;
-		};
-		A9506D361E69AB33009A0045 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Release;
-		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -584,15 +481,6 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		A9506D341E69AB33009A0045 /* Build configuration list for PBXNativeTarget "unittest" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				A9506D351E69AB33009A0045 /* Debug */,
-				A9506D361E69AB33009A0045 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
 /* End XCConfigurationList section */
 	};
 	rootObject = A9506CE81E69AAEB009A0045 /* Project object */;

+ 0 - 97
gravity.xcodeproj/xcuserdata/marco.xcuserdatad/xcschemes/unittest.xcscheme

@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "0930"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "A9506D301E69AB33009A0045"
-               BuildableName = "unittest"
-               BlueprintName = "unittest"
-               ReferencedContainer = "container:gravity.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "A9506D301E69AB33009A0045"
-            BuildableName = "unittest"
-            BlueprintName = "unittest"
-            ReferencedContainer = "container:gravity.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "A9506D301E69AB33009A0045"
-            BuildableName = "unittest"
-            BlueprintName = "unittest"
-            ReferencedContainer = "container:gravity.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-      <CommandLineArguments>
-         <CommandLineArgument
-            argument = "/Users/marco/GitHub/Gravity/test/unittest"
-            isEnabled = "YES">
-         </CommandLineArgument>
-      </CommandLineArguments>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "A9506D301E69AB33009A0045"
-            BuildableName = "unittest"
-            BlueprintName = "unittest"
-            ReferencedContainer = "container:gravity.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 0 - 5
gravity.xcodeproj/xcuserdata/marco.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -9,11 +9,6 @@
 			<key>orderHint</key>
 			<integer>0</integer>
 		</dict>
-		<key>unittest.xcscheme</key>
-		<dict>
-			<key>orderHint</key>
-			<integer>1</integer>
-		</dict>
 	</dict>
 	<key>SuppressBuildableAutocreation</key>
 	<dict>

+ 225 - 3
src/cli/gravity.c

@@ -14,16 +14,33 @@
 
 #define DEFAULT_OUTPUT "gravity.g"
 
+typedef struct {
+    bool            processed;
+    bool            is_fuzzy;
+    
+    uint32_t        ncount;
+    uint32_t        nsuccess;
+    uint32_t        nfailure;
+    
+    error_type_t    expected_error;
+    gravity_value_t expected_value;
+    int32_t         expected_row;
+    int32_t         expected_col;
+} unittest_data;
+
 typedef enum  {
 	OP_COMPILE,			// just compile source code and exit
 	OP_RUN,				// just run an already compiled file
 	OP_COMPILE_RUN,		// compile source code and run it
     OP_INLINE_RUN,      // compile and execure source passed inline
-	OP_REPL				// run a read eval print loop
+	OP_REPL,			// run a read eval print loop
+    OP_UNITTEST         // unit test mode
 } op_type;
 
 static const char *input_file = NULL;
 static const char *output_file = DEFAULT_OUTPUT;
+static const char *unittest_folder = NULL;
+static const char *test_folder_path = NULL;
 static bool quiet_flag = false;
 
 static void report_error (gravity_vm *vm, error_type_t error_type, const char *message, error_desc_t error_desc, void *xdata) {
@@ -73,7 +90,161 @@ static const char *load_file (const char *file, size_t *size, uint32_t *fileid,
 	return file_read(file, size);
 }
 
-// MARK: -
+// MARK: - Unit Test -
+
+static void unittest_init (const char *target_file, unittest_data *data) {
+    #pragma unused(target_file)
+    ++data->ncount;
+    data->processed = false;
+}
+
+static void unittest_cleanup (const char *target_file, unittest_data *data) {
+    #pragma unused(target_file,data)
+}
+
+static void unittest_callback (gravity_vm *vm, error_type_t error_type, const char *description, const char *notes, gravity_value_t value, int32_t row, int32_t col, void *xdata) {
+    #pragma unused(vm)
+    unittest_data *data = (unittest_data *)xdata;
+    data->expected_error = error_type;
+    data->expected_value = value;
+    data->expected_row = row;
+    data->expected_col = col;
+    
+    if (notes) printf("\tNOTE: %s\n", notes);
+    printf("\t%s\n", description);
+}
+
+static void unittest_error (gravity_vm *vm, error_type_t error_type, const char *message, error_desc_t error_desc, void *xdata) {
+    #pragma unused(vm)
+    
+    unittest_data *data = (unittest_data *)xdata;
+    if (data->processed == true) return; // ignore 2nd error
+    data->processed = true;
+    
+    const char *type = "NONE";
+    if (error_type == GRAVITY_ERROR_SYNTAX) type = "SYNTAX";
+    else if (error_type == GRAVITY_ERROR_SEMANTIC) type = "SEMANTIC";
+    else if (error_type == GRAVITY_ERROR_RUNTIME) type = "RUNTIME";
+    else if (error_type == GRAVITY_WARNING) type = "WARNING";
+    
+    if (error_type == GRAVITY_ERROR_RUNTIME) printf("\tRUNTIME ERROR: ");
+    else printf("\t%s ERROR on %d (%d,%d): ", type, error_desc.fileid, error_desc.lineno, error_desc.colno);
+    printf("%s\n", message);
+    
+    bool same_error = (data->expected_error == error_type);
+    bool same_row = (data->expected_row != -1) ? (data->expected_row == error_desc.lineno) : true;
+    bool same_col = (data->expected_col != -1) ? (data->expected_col == error_desc.colno) : true;
+    
+    if (data->is_fuzzy) {
+        ++data->nsuccess;
+        printf("\tSUCCESS\n");
+        return;
+    }
+    
+    if (same_error && same_row && same_col) {
+        ++data->nsuccess;
+        printf("\tSUCCESS\n");
+    } else {
+        ++data->nfailure;
+        printf("\tFAILURE\n");
+    }
+}
+
+static const char *unittest_read (const char *path, size_t *size, uint32_t *fileid, void *xdata) {
+    #pragma unused(fileid,xdata)
+    if (file_exists(path)) return file_read(path, size);
+    
+    // this unittest is able to resolve path only next to main test folder (not in nested folders)
+    const char *newpath = file_buildpath(path, test_folder_path);
+    if (!newpath) return NULL;
+    
+    const char *buffer = file_read(newpath, size);
+    mem_free(newpath);
+    
+    return buffer;
+}
+
+static void unittest_scan (const char *folder_path, unittest_data *data) {
+    DIRREF dir = directory_init(folder_path);
+    if (!dir) return;
+    
+    const char *target_file;
+    while ((target_file = directory_read(dir))) {
+        
+        #ifdef WIN32
+        const char winbuffer[MAX_PATH];
+        WideCharToMultiByte (CP_UTF8, 0, (LPCWSTR)target_file, -1, winbuffer, sizeof(winbuffer), NULL, NULL );
+        target_file = (const char *)winbuffer;
+        #endif
+        
+        // if file is a folder then start recursion
+        const char *full_path = file_buildpath(target_file, folder_path);
+        if (is_directory(full_path)) {
+            // skip disabled folder
+            if (strcmp(target_file, "disabled") == 0) continue;
+            unittest_scan(full_path, data);
+            continue;
+        }
+        
+        // test only files with a .gravity extension
+        if (strstr(full_path, ".gravity") == NULL) continue;
+        data->is_fuzzy = (strstr(full_path, "/fuzzy/") != NULL);
+        
+        // load source code
+        size_t size = 0;
+        const char *source_code = file_read(full_path, &size);
+        assert(source_code);
+        
+        // start unit test
+        unittest_init(target_file, data);
+        
+        // compile and run source code
+        printf("\n%d\tTest file: %s\n", data->ncount, target_file);
+        printf("\tTest path: %s\n", full_path);
+        mem_free(full_path);
+        
+        // initialize compiler and delegates
+        gravity_delegate_t delegate = {
+            .xdata = (void *)data,
+            .error_callback = unittest_error,
+            .unittest_callback = unittest_callback,
+            .loadfile_callback = unittest_read
+        };
+        
+        gravity_compiler_t *compiler = gravity_compiler_create(&delegate);
+        gravity_closure_t *closure = gravity_compiler_run(compiler, source_code, size, 0, false);
+        gravity_vm *vm = gravity_vm_new(&delegate);
+        gravity_compiler_transfer(compiler, vm);
+        gravity_compiler_free(compiler);
+        
+        if (closure) {
+            if (gravity_vm_runmain(vm, closure)) {
+                data->processed = true;
+                gravity_value_t result = gravity_vm_result(vm);
+                if (data->is_fuzzy || gravity_value_equals(result, data->expected_value)) {
+                    ++data->nsuccess;
+                    printf("\tSUCCESS\n");
+                } else {
+                    ++data->nfailure;
+                    printf("\tFAILURE\n");
+                }
+                gravity_value_free(NULL, data->expected_value);
+            }
+        }
+        gravity_vm_free(vm);
+        
+        // case for empty files or simple declarations test
+        if (!data->processed) {
+            ++data->nsuccess;
+            printf("\tSUCCESS\n");
+        }
+        
+        // cleanup unitest
+        unittest_cleanup(target_file, data);
+    }
+}
+
+// MARK: - General -
 
 static void print_version (void) {
 	printf("Gravity version %s (%s)\n", GRAVITY_VERSION, GRAVITY_BUILD_DATE);
@@ -126,6 +297,10 @@ static op_type parse_args (int argc, const char* argv[]) {
 		else if (strcmp(argv[i], "-q") == 0) {
 			quiet_flag = true;
 		}
+        else if ((strcmp(argv[i], "-t") == 0) && (i+1 < argc)) {
+            unittest_folder = argv[++i];
+            type = OP_UNITTEST;
+        }
 		else {
 			input_file = argv[i];
 			type = OP_COMPILE_RUN;
@@ -135,7 +310,7 @@ static op_type parse_args (int argc, const char* argv[]) {
 	return type;
 }
 
-// MARK: -
+// MARK: - Special Modes
 
 static void gravity_repl (void) {
 	printf("REPL not yet implemented.\n");
@@ -164,6 +339,50 @@ static void gravity_repl (void) {
 	 */
 }
 
+static void gravity_unittest (void) {
+    unittest_data data = {
+        .ncount = 0,
+        .nsuccess = 0,
+        .nfailure = 0
+    };
+    
+    if (unittest_folder == NULL) {
+        printf("Usage: gravity -t /path/to/unitest/\n");
+        exit(-1);
+    }
+    
+    // print console header
+    printf("==============================================\n");
+    printf("Gravity Unit Test Mode\n");
+    printf("Gravity version %s\n", GRAVITY_VERSION);
+    printf("Build date: %s\n", GRAVITY_BUILD_DATE);
+    printf("==============================================\n");
+    
+    mem_init();
+    nanotime_t tstart = nanotime();
+    test_folder_path = unittest_folder;
+    unittest_scan(unittest_folder, &data);
+    nanotime_t tend = nanotime();
+    
+    double result = ((double)((data.nsuccess * 100)) / (double)data.ncount);
+    printf("\n\n");
+    printf("==============================================\n");
+    printf("Total Tests: %d\n", data.ncount);
+    printf("Total Successes: %d\n", data.nsuccess);
+    printf("Total Failures: %d\n", data.nfailure);
+    printf("Result: %.2f %%\n", result);
+    printf("Time: %.4f ms\n", millitime(tstart, tend));
+    printf("==============================================\n");
+    printf("\n");
+    
+    // If we have 1 or more failures, return an error.
+    if (data.nfailure != 0) {
+        exit(1);
+    }
+    
+    exit(0);
+}
+
 // MARK: -
 
 int main (int argc, const char* argv[]) {
@@ -172,6 +391,9 @@ int main (int argc, const char* argv[]) {
 
 	// special repl case
 	if (type == OP_REPL) gravity_repl();
+    
+    // special unit test mode
+    if (type == OP_UNITTEST) gravity_unittest();
 
 	// initialize memory debugger (if activated)
 	mem_init();

+ 0 - 224
src/cli/unittest.c

@@ -1,224 +0,0 @@
-//
-//  unittest.c
-//  gravity
-//
-//  Created by Marco Bambini on 23/03/16.
-//  Copyright © 2016 CreoLabs. All rights reserved.
-//
-
-#include "gravity_compiler.h"
-#include "gravity_utils.h"
-#include "gravity_core.h"
-#include "gravity_vm.h"
-
-typedef struct {
-	bool			processed;
-    bool            is_fuzzy;
-
-	uint32_t		ncount;
-	uint32_t		nsuccess;
-	uint32_t		nfailure;
-
-	error_type_t	expected_error;
-	gravity_value_t expected_value;
-	int32_t			expected_row;
-	int32_t			expected_col;
-} test_data;
-
-static const char *test_folder_path;
-
-static void unittest_init (const char *target_file, test_data *data) {
-	#pragma unused(target_file)
-	++data->ncount;
-	data->processed = false;
-}
-
-static void unittest_cleanup (const char *target_file, test_data *data) {
-	#pragma unused(target_file,data)
-}
-
-static void	unittest_callback (gravity_vm *vm, error_type_t error_type, const char *description, const char *notes, gravity_value_t value, int32_t row, int32_t col, void *xdata) {
-	test_data *data = (test_data *)xdata;
-	data->expected_error = error_type;
-	data->expected_value = value;
-	data->expected_row = row;
-	data->expected_col = col;
-
-	if (notes) printf("\tNOTE: %s\n", notes);
-	printf("\t%s\n", description);
-}
-
-// MARK: -
-
-static void callback_error (gravity_vm *vm, error_type_t error_type, const char *message, error_desc_t error_desc, void *xdata) {
-	test_data *data = (test_data *)xdata;
-
-	if (data->processed == true) return; // ignore 2nd error
-	data->processed = true;
-
-	const char *type = "NONE";
-	if (error_type == GRAVITY_ERROR_SYNTAX) type = "SYNTAX";
-	else if (error_type == GRAVITY_ERROR_SEMANTIC) type = "SEMANTIC";
-	else if (error_type == GRAVITY_ERROR_RUNTIME) type = "RUNTIME";
-	else if (error_type == GRAVITY_WARNING) type = "WARNING";
-
-	if (error_type == GRAVITY_ERROR_RUNTIME) printf("\tRUNTIME ERROR: ");
-	else printf("\t%s ERROR on %d (%d,%d): ", type, error_desc.fileid, error_desc.lineno, error_desc.colno);
-	printf("%s\n", message);
-
-	bool same_error = (data->expected_error == error_type);
-	bool same_row = (data->expected_row != -1) ? (data->expected_row == error_desc.lineno) : true;
-	bool same_col = (data->expected_col != -1) ? (data->expected_col == error_desc.colno) : true;
-
-    if (data->is_fuzzy) {
-        ++data->nsuccess;
-        printf("\tSUCCESS\n");
-        return;
-    }
-
-	if (same_error && same_row && same_col) {
-		++data->nsuccess;
-		printf("\tSUCCESS\n");
-	} else {
-		++data->nfailure;
-		printf("\tFAILURE\n");
-	}
-}
-
-static const char *callback_read (const char *path, size_t *size, uint32_t *fileid, void *xdata) {
-	#pragma unused(fileid,xdata)
-	if (file_exists(path)) return file_read(path, size);
-
-	// this unittest is able to resolve path only next to main test folder (not in nested folders)
-	const char *newpath = file_buildpath(path, test_folder_path);
-	if (!newpath) return NULL;
-
-	const char *buffer = file_read(newpath, size);
-	mem_free(newpath);
-
-	return buffer;
-}
-
-static void test_folder (const char *folder_path, test_data *data) {
-	DIRREF dir = directory_init(folder_path);
-	if (!dir) return;
-
-	const char *target_file;
-	while ((target_file = directory_read(dir))) {
-
-		#ifdef WIN32
-		const char winbuffer[MAX_PATH];
-		WideCharToMultiByte (CP_UTF8, 0, (LPCWSTR)target_file, -1, winbuffer, sizeof(winbuffer), NULL, NULL );
-		target_file = (const char *)winbuffer;
-		#endif
-
-		// if file is a folder then start recursion
-		const char *full_path = file_buildpath(target_file, folder_path);
-		if (is_directory(full_path)) {
-			// skip disabled folder
-			if (strcmp(target_file, "disabled") == 0) continue;
-
-			test_folder(full_path, data);
-			continue;
-		}
-
-		// test only files with a .gravity extension
-		if (strstr(full_path, ".gravity") == NULL) continue;
-        data->is_fuzzy = (strstr(full_path, "/fuzzy/") != NULL);
-
-		// load source code
-		size_t size = 0;
-		const char *source_code = file_read(full_path, &size);
-		assert(source_code);
-
-		// start unit test
-		unittest_init(target_file, data);
-
-		// compile and run source code
-		printf("\n%d\tTest file: %s\n", data->ncount, target_file);
-		printf("\tTest path: %s\n", full_path);
-		mem_free(full_path);
-
-		// initialize compiler and delegates
-		gravity_delegate_t delegate = {
-			.xdata = (void *)data,
-			.error_callback = callback_error,
-			.unittest_callback = unittest_callback,
-			.loadfile_callback = callback_read
-		};
-
-		gravity_compiler_t *compiler = gravity_compiler_create(&delegate);
-		gravity_closure_t *closure = gravity_compiler_run(compiler, source_code, size, 0, false);
-		gravity_vm *vm = gravity_vm_new(&delegate);
-		gravity_compiler_transfer(compiler, vm);
-		gravity_compiler_free(compiler);
-
-		if (closure) {
-			if (gravity_vm_runmain(vm, closure)) {
-				data->processed = true;
-				gravity_value_t result = gravity_vm_result(vm);
-				if (data->is_fuzzy || gravity_value_equals(result, data->expected_value)) {
-					++data->nsuccess;
-					printf("\tSUCCESS\n");
-				} else {
-					++data->nfailure;
-					printf("\tFAILURE\n");
-				}
-				gravity_value_free(NULL, data->expected_value);
-			}
-		}
-		gravity_vm_free(vm);
-
-		// case for empty files or simple declarations test
-		if (!data->processed) {
-			++data->nsuccess;
-			printf("\tSUCCESS\n");
-		}
-
-		// cleanup unitest
-		unittest_cleanup(target_file, data);
-	}
-}
-
-int main (int argc, const char* argv[]) {
-	test_data data = {
-		.ncount = 0,
-		.nsuccess = 0,
-		.nfailure = 0
-	};
-
-	if (argc != 2) {
-		printf("Usage: unittest /path/to/unitest/\n");
-		return 1;
-	}
-
-	// print console header
-	printf("==============================================\n");
-	printf("Gravity UnitTest\n");
-	printf("Gravity version %s\n", GRAVITY_VERSION);
-	printf("Build date: %s\n", GRAVITY_BUILD_DATE);
-	printf("==============================================\n");
-
-	mem_init();
-	nanotime_t tstart = nanotime();
-	test_folder_path = argv[1];
-	test_folder(argv[1], &data);
-	nanotime_t tend = nanotime();
-
-	double result = ((double)((data.nsuccess * 100)) / (double)data.ncount);
-	printf("\n\n");
-	printf("==============================================\n");
-	printf("Total Tests: %d\n", data.ncount);
-	printf("Total Successes: %d\n", data.nsuccess);
-	printf("Total Failures: %d\n", data.nfailure);
-	printf("Result: %.2f %%\n", result);
-	printf("Time: %.4f ms\n", millitime(tstart, tend));
-	printf("==============================================\n");
-	printf("\n");
-
-	// If we have 1 or more failures, return an error.
-	if (data.nfailure != 0) {
-		return 1;
-	}
-	return 0;
-}