diff --git a/.gitignore b/.gitignore index de47c87..0588520 100644 --- a/.gitignore +++ b/.gitignore @@ -325,3 +325,7 @@ binding/GravityObjC/GravityObjC.xcodeproj/project.xcworkspace/xcshareddata/IDEWo gravity.xcodeproj/xcuserdata/marco.xcuserdatad/xcschemes/gravity.xcscheme *.xcscheme *.xcscheme + +## Zig +**/zig-cache +**/zig-out \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index ef1fb47..38d5538 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ language: c os: - - osx - - linux + - windows + - osx + - linux compiler: - - gcc - - clang + - zig script: - - make - - test/unittest/run_all.sh + - zig build diff --git a/README.md b/README.md index 781c6d2..0ce8e64 100644 --- a/README.md +++ b/README.md @@ -41,57 +41,74 @@ class Vector { func main() { // initialize a new vector object var v1 = Vector(1,2,3); - + // initialize a new vector object var v2 = Vector(4,5,6); - + // call + function in the vector object var v3 = v1 + v2; - + // returns string "[1,2,3] + [4,5,6] = [5,7,9]" return "\(v1) + \(v2) = \(v3)"; } - ``` +``` ## Features -* multipass compiler -* dynamic typing -* classes and inheritance -* higher-order functions and classes -* lexical scoping -* coroutines (via fibers) -* nested classes -* closures -* garbage collection -* operator overriding -* powerful embedding API -* built-in unit tests -* built-in JSON serializer/deserializer -* **optional semicolons** + +- multipass compiler +- dynamic typing +- classes and inheritance +- higher-order functions and classes +- lexical scoping +- coroutines (via fibers) +- nested classes +- closures +- garbage collection +- operator overriding +- powerful embedding API +- built-in unit tests +- built-in JSON serializer/deserializer +- **optional semicolons** ## Special thanks + Gravity was supported by a couple of open-source projects. The inspiration for closures comes from the elegant Lua programming language; specifically from the document Closures in Lua. For fibers, upvalues handling and some parts of the garbage collector, my gratitude goes to Bob Nystrom and his excellent Wren programming language. A very special thanks should also go to my friend **Andrea Donetti** who helped me debugging and testing various aspects of the language. ## Documentation + The Getting Started page is a guide for downloading and compiling the language. There is also a more extensive language documentation. Official [wiki](https://github.com/marcobambini/gravity/wiki) is used to collect related projects and tools. ## Where Gravity is used -* Gravity is the core language built into Creo (https://creolabs.com) -* Gravity is the scripting language for the Untold game engine (https://youtu.be/OGrWq8jpK14?t=58) + +- Gravity is the core language built into Creo (https://creolabs.com) +- Gravity is the scripting language for the Untold game engine (https://youtu.be/OGrWq8jpK14?t=58) ## Community + Seems like a good idea to make a group chat for people to discuss Gravity.
[![Join the chat at https://gitter.im/gravity-lang/](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gravity-lang/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Contributing + Contributions to Gravity are welcomed and encouraged!
More information is available in the official [CONTRIBUTING](CONTRIBUTING.md) file. -* Open an issue: - * if you need help - * if you find a bug - * if you have a feature request - * to ask a general question -* Submit a pull request: - * if you want to contribute + +- Open an issue: + - if you need help + - if you find a bug + - if you have a feature request + - to ask a general question +- Submit a pull request: + - if you want to contribute ## License + Gravity is available under the permissive MIT license. + +## Let's get ziggy + +I made a thin wrapper for gravity in zig, you can fetch it like any other zig packages, +you can pass an optional parameter to the build options: "shared" + +- If set to true, the "gravity" artifact will be dynamically linked +- If set to false, it will be statically linked +- If not set, both of them will be created: "gravity" and "gravity_s", "gravity" is dynamic, "gravity_s" is static diff --git a/bootstrap.zig b/bootstrap.zig new file mode 100644 index 0000000..20c1251 --- /dev/null +++ b/bootstrap.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +extern fn c_main(argc: c_int, argv: [*c]const [*c]const u8) c_int; + +const DEBUG = builtin.mode == .Debug; +const BACK_ALLOCATOR = std.heap.c_allocator; + +pub fn main() u8 { + var gpa = std.heap.GeneralPurposeAllocator(.{}){ .backing_allocator = BACK_ALLOCATOR }; + defer if (DEBUG) { + _ = gpa.deinit(); + }; + const allocator = if (DEBUG) gpa.allocator() else BACK_ALLOCATOR; + + var args_list = std.ArrayList([*c]const u8).init(allocator); + defer { + for (args_list.items) |arg| { + allocator.free(std.mem.span(arg)); + } + args_list.deinit(); + } + + var args_it = std.process.argsWithAllocator(allocator) catch return 1; + defer args_it.deinit(); + + while (args_it.next()) |arg| { + args_list.append(@ptrCast(allocator.dupeZ(u8, arg) catch return 1)) catch return 1; + } + + args_list.ensureTotalCapacity(args_list.items.len + 1) catch return 1; + args_list.append(null) catch unreachable; + + const argv: [*c]const [*c]const u8 = @ptrCast(args_list.allocatedSlice()); + return @intCast(c_main(@intCast(args_list.items.len), argv) & 0xFF); +} diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..6372105 --- /dev/null +++ b/build.zig @@ -0,0 +1,131 @@ +const std = @import("std"); + +const GRAVITY_SRC_PATH: []const u8 = "src/"; + +const COMPILER_DIR: []const u8 = GRAVITY_SRC_PATH ++ "compiler/"; +const RUNTIME_DIR: []const u8 = GRAVITY_SRC_PATH ++ "runtime/"; +const SHARED_DIR: []const u8 = GRAVITY_SRC_PATH ++ "shared/"; +const UTILS_DIR: []const u8 = GRAVITY_SRC_PATH ++ "utils/"; +const OPT_DIR: []const u8 = GRAVITY_SRC_PATH ++ "optionals/"; +const SRC: []const []const u8 = &.{ + // COMPILER_DIR/*.c + COMPILER_DIR ++ "gravity_ast.c", + COMPILER_DIR ++ "gravity_codegen.c", + COMPILER_DIR ++ "gravity_compiler.c", + COMPILER_DIR ++ "gravity_ircode.c", + COMPILER_DIR ++ "gravity_lexer.c", + COMPILER_DIR ++ "gravity_optimizer.c", + COMPILER_DIR ++ "gravity_optimizer.c", + COMPILER_DIR ++ "gravity_parser.c", + COMPILER_DIR ++ "gravity_semacheck1.c", + COMPILER_DIR ++ "gravity_semacheck2.c", + COMPILER_DIR ++ "gravity_symboltable.c", + COMPILER_DIR ++ "gravity_token.c", + COMPILER_DIR ++ "gravity_visitor.c", + // RUNTIME_DIR/*.c + RUNTIME_DIR ++ "gravity_core.c", + RUNTIME_DIR ++ "gravity_vm.c", + // SHARED_DIR/*.c + SHARED_DIR ++ "gravity_hash.c", + SHARED_DIR ++ "gravity_memory.c", + SHARED_DIR ++ "gravity_value.c", + // UTILS_DIR/*.c + UTILS_DIR ++ "gravity_debug.c", + UTILS_DIR ++ "gravity_json.c", + UTILS_DIR ++ "gravity_utils.c", + // OPT_DIR/*.c + OPT_DIR ++ "gravity_opt_env.c", + OPT_DIR ++ "gravity_opt_file.c", + OPT_DIR ++ "gravity_opt_json.c", + OPT_DIR ++ "gravity_opt_math.c", +}; +const INCLUDE: []const []const u8 = &.{ + "-I", COMPILER_DIR, + "-I", RUNTIME_DIR, + "-I", SHARED_DIR, + "-I", UTILS_DIR, + "-I", OPT_DIR, +}; +const CFLAGS: []const []const u8 = @as([]const []const u8, &.{ + "-std=gnu99", + "-fgnu89-inline", + "-fPIC", + "-fno-sanitize=undefined", +}); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const lib_s = b.addStaticLibrary(.{ + .name = "gravity_s", + .target = target, + .optimize = optimize, + .link_libc = true, + }); + inline for (INCLUDE) |I| { + if (comptime std.mem.eql(u8, I, "-I")) + continue; + lib_s.addIncludePath(b.path(I)); + lib_s.installHeadersDirectory(b.path(I), ".", .{}); + } + lib_s.linkSystemLibrary("m"); + switch (target.result.os.tag) { + .windows => lib_s.linkSystemLibrary("Shlwapi"), + .openbsd, .freebsd, .netbsd, .dragonfly => {}, + else => if (!target.result.isDarwin()) lib_s.linkSystemLibrary("rt"), + } + lib_s.addCSourceFiles(.{ + .root = b.path("."), + .files = SRC, + .flags = INCLUDE ++ CFLAGS ++ @as([]const []const u8, &.{"-DBUILD_GRAVITY_API"}), + }); + + const lib = b.addSharedLibrary(.{ + .name = "gravity", + .target = target, + .optimize = optimize, + .link_libc = true, + }); + inline for (INCLUDE) |I| { + if (comptime std.mem.eql(u8, I, "-I")) + continue; + lib.addIncludePath(b.path(I)); + lib.installHeadersDirectory(b.path(I), ".", .{}); + } + lib.linkSystemLibrary("m"); + switch (target.result.os.tag) { + .windows => lib.linkSystemLibrary("Shlwapi"), + .openbsd, .freebsd, .netbsd, .dragonfly => {}, + else => if (!target.result.isDarwin()) lib.linkSystemLibrary("rt"), + } + lib.addCSourceFiles(.{ + .root = b.path("."), + .files = SRC, + .flags = INCLUDE ++ CFLAGS ++ @as([]const []const u8, &.{"-DBUILD_GRAVITY_API"}), + }); + + b.installArtifact(lib); + b.installArtifact(lib_s); + + const exe = b.addExecutable(.{ + .name = "gravity", + .root_source_file = b.path("bootstrap.zig"), + .target = target, + .optimize = optimize, + }); + exe.linkLibrary(lib_s); + exe.addCSourceFiles(.{ + .root = b.path("."), + .files = &.{"src/cli/gravity.c"}, + .flags = CFLAGS ++ @as([]const []const u8, &.{"-DZIG_BOOTSTRAP"}), + }); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + run_cmd.addArgs(b.args orelse &.{}); + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..bdee155 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "gravity", + .version = "0.8.5-build+1eb6d5ab98b9df4909513612a2bb9238a8304422", + + .dependencies = .{}, + + .paths = .{ + "src/", + "build.zig", + "build.zig.zon", + }, +} diff --git a/src/cli/gravity.c b/src/cli/gravity.c index 0f122fc..70ffb4e 100644 --- a/src/cli/gravity.c +++ b/src/cli/gravity.c @@ -7,35 +7,36 @@ // #include "gravity_compiler.h" +#include "gravity_core.h" +#include "gravity_opt_env.h" #include "gravity_optionals.h" #include "gravity_utils.h" -#include "gravity_core.h" #include "gravity_vm.h" -#include "gravity_opt_env.h" + #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; + 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 execute source passed inline - OP_REPL, // run a read eval print loop - OP_UNITTEST // unit test mode +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 execute source passed inline + OP_REPL, // run a read eval print loop + OP_UNITTEST // unit test mode } op_type; static const char *input_file = NULL; @@ -44,469 +45,519 @@ 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) { - (void) vm, (void) xdata; - const char *type = "N/A"; - switch (error_type) { - case GRAVITY_ERROR_NONE: type = "NONE"; break; - case GRAVITY_ERROR_SYNTAX: type = "SYNTAX"; break; - case GRAVITY_ERROR_SEMANTIC: type = "SEMANTIC"; break; - case GRAVITY_ERROR_RUNTIME: type = "RUNTIME"; break; - case GRAVITY_WARNING: type = "WARNING"; break; - case GRAVITY_ERROR_IO: type = "I/O"; break; - } - - if (error_type == GRAVITY_ERROR_RUNTIME) printf("RUNTIME ERROR: "); - else printf("%s ERROR on %d (%d,%d): ", type, error_desc.fileid, error_desc.lineno, error_desc.colno); - printf("%s\n", message); +static void report_error(gravity_vm *vm, error_type_t error_type, + const char *message, error_desc_t error_desc, + void *xdata) { + (void)vm, (void)xdata; + const char *type = "N/A"; + switch (error_type) { + case GRAVITY_ERROR_NONE: + type = "NONE"; + break; + case GRAVITY_ERROR_SYNTAX: + type = "SYNTAX"; + break; + case GRAVITY_ERROR_SEMANTIC: + type = "SEMANTIC"; + break; + case GRAVITY_ERROR_RUNTIME: + type = "RUNTIME"; + break; + case GRAVITY_WARNING: + type = "WARNING"; + break; + case GRAVITY_ERROR_IO: + type = "I/O"; + break; + } + + if (error_type == GRAVITY_ERROR_RUNTIME) + printf("RUNTIME ERROR: "); + else + printf("%s ERROR on %d (%d,%d): ", type, error_desc.fileid, + error_desc.lineno, error_desc.colno); + printf("%s\n", message); } -static const char *load_file (const char *file, size_t *size, uint32_t *fileid, void *xdata, bool *is_static) { - (void) fileid, (void) xdata; - - // this callback is called each time an import statement is parsed - // file arg represents what user wrote after the import keyword, for example: - // import "file2" - // import "file2.gravity" - // import "../file2" - // import "/full_path_to_file2" - - // it is callback's responsibility to resolve file path based on current working directory - // or based on user defined search paths - // and returns: - // size of file in *size - // fileid (if any) in *fileid - // content of file as return value of the function - - // fileid will then be used each time an error is reported by the compiler - // so it is responsibility of this function to map somewhere the association - // between fileid and real file/path name - - // fileid is not used in this example - // xdata not used here but it the xdata field set in the delegate - // please note than in this simple example the imported file must be - // in the same folder as the main input file - - if (is_static) *is_static = false; - if (!file_exists(file)) return NULL; - return file_read(file, size); +static const char *load_file(const char *file, size_t *size, uint32_t *fileid, + void *xdata, bool *is_static) { + (void)fileid, (void)xdata; + + // this callback is called each time an import statement is parsed + // file arg represents what user wrote after the import keyword, for example: + // import "file2" + // import "file2.gravity" + // import "../file2" + // import "/full_path_to_file2" + + // it is callback's responsibility to resolve file path based on current + // working directory or based on user defined search paths and returns: size + // of file in *size fileid (if any) in *fileid content of file as return value + // of the function + + // fileid will then be used each time an error is reported by the compiler + // so it is responsibility of this function to map somewhere the association + // between fileid and real file/path name + + // fileid is not used in this example + // xdata not used here but it the xdata field set in the delegate + // please note than in this simple example the imported file must be + // in the same folder as the main input file + + if (is_static) + *is_static = false; + if (!file_exists(file)) + return NULL; + return file_read(file, size); } // MARK: - Unit Test - -static void unittest_init (const char *target_file, unittest_data *data) { - (void) target_file; - ++data->ncount; - data->processed = false; +static void unittest_init(const char *target_file, unittest_data *data) { + (void)target_file; + ++data->ncount; + data->processed = false; } -static void unittest_cleanup (const char *target_file, unittest_data *data) { - (void) target_file, (void) data; +static void unittest_cleanup(const char *target_file, unittest_data *data) { + (void)target_file, (void)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) { - (void) 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_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) { + (void)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) { - (void) 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 void unittest_error(gravity_vm *vm, error_type_t error_type, + const char *message, error_desc_t error_desc, + void *xdata) { + (void)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, bool *is_static) { - (void) fileid, (void) xdata; - if (is_static) *is_static = false; - - 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 const char *unittest_read(const char *path, size_t *size, + uint32_t *fileid, void *xdata, + bool *is_static) { + (void)fileid, (void)xdata; + if (is_static) + *is_static = false; + + 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; - #ifdef WIN32 - char outbuffer[MAX_PATH]; - #else - char *outbuffer = NULL; - #endif - const char *target_file; - while ((target_file = directory_read(dir, outbuffer))) { - - // 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, 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"); +static void unittest_scan(const char *folder_path, unittest_data *data) { + DIRREF dir = directory_init(folder_path); + if (!dir) + return; +#ifdef WIN32 + char outbuffer[MAX_PATH]; +#else + char *outbuffer = NULL; +#endif + const char *target_file; + while ((target_file = directory_read(dir, outbuffer))) { + + // 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, 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"); } - - // cleanup unitest - unittest_cleanup(target_file, data); + 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); +static void print_version(void) { + printf("Gravity version %s (%s)\n", GRAVITY_VERSION, GRAVITY_BUILD_DATE); } -static void print_help (void) { - printf("Usage: gravity [options] [arguments...]\n"); - printf("\n"); - printf("To start the REPL (not yet supported):\n"); - printf(" gravity\n"); - printf("\n"); - printf("To compile and execute file:\n"); - printf(" gravity example.gravity\n"); - printf("\n"); - printf("Available options are:\n"); - printf(" --version show version information and exit\n"); - printf(" --help show command line usage and exit\n"); - printf(" -c input_file compile input_file\n"); - printf(" -o output_file specify output file name (default to gravity.json)\n"); - printf(" -x input_file execute input_file (JSON format expected)\n"); - printf(" -i source_code compile and execute source_code string\n"); - printf(" -q don't print result and execution time\n"); - printf(" -t folder run unit tests from folder\n"); +static void print_help(void) { + printf("Usage: gravity [options] [arguments...]\n"); + printf("\n"); + printf("To start the REPL (not yet supported):\n"); + printf(" gravity\n"); + printf("\n"); + printf("To compile and execute file:\n"); + printf(" gravity example.gravity\n"); + printf("\n"); + printf("Available options are:\n"); + printf(" --version show version information and exit\n"); + printf(" --help show command line usage and exit\n"); + printf(" -c input_file compile input_file\n"); + printf(" -o output_file specify output file name (default to " + "gravity.json)\n"); + printf(" -x input_file execute input_file (JSON format expected)\n"); + printf(" -i source_code compile and execute source_code string\n"); + printf(" -q don't print result and execution time\n"); + printf(" -t folder run unit tests from folder\n"); } -static op_type parse_args (int argc, const char* argv[]) { - if (argc == 1) return OP_REPL; +static op_type parse_args(int argc, const char *argv[]) { + if (argc == 1) + return OP_REPL; - if (argc == 2 && strcmp(argv[1], "--version") == 0) { - print_version(); - exit(0); - } - - if (argc == 2 && strcmp(argv[1], "--help") == 0) { - print_help(); - exit(0); - } + if (argc == 2 && strcmp(argv[1], "--version") == 0) { + print_version(); + exit(0); + } - op_type type = OP_RUN; - for (int i=1; i ", &length)) != NULL) { - // to be implemented - // gravity_compiler_eval(compiler, vm, line, length); - free(line); - } + /* + // setup compiler/VM delegate + gravity_delegate_t delegate = { + .error_callback = report_error, + }; - gravity_compiler_free(compiler); - gravity_vm_free(vm); - */ + gravity_compiler_t *compiler = gravity_compiler_create(&delegate); + gravity_vm *vm = gravity_vm_new(&delegate); + char *line = NULL; + int length = 0; + + printf("Welcome to Gravity v%s\n", GRAVITY_VERSION); + while((line = readline("> ", &length)) != NULL) { + // to be implemented + // gravity_compiler_eval(compiler, vm, line, length); + free(line); + } + + gravity_compiler_free(compiler); + gravity_vm_free(vm); + */ } -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); +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[]) { - // parse arguments and return operation type - op_type type = parse_args(argc, argv); +int +#ifdef ZIG_BOOTSTRAP +c_main +#else +main +#endif +(int argc, const char* argv[]) { + // parse arguments and return operation type + op_type type = parse_args(argc, argv); - // special repl case - if (type == OP_REPL) gravity_repl(); - - // special unit test mode - if (type == OP_UNITTEST) gravity_unittest(); + // special repl case + if (type == OP_REPL) + gravity_repl(); - // initialize memory debugger (if activated) - mem_init(); + // special unit test mode + if (type == OP_UNITTEST) + gravity_unittest(); - // closure to execute/serialize - gravity_closure_t *closure = NULL; + // initialize memory debugger (if activated) + mem_init(); - // optional compiler - gravity_compiler_t *compiler = NULL; + // closure to execute/serialize + gravity_closure_t *closure = NULL; - // setup compiler/VM delegate - gravity_delegate_t delegate = { - .error_callback = report_error, - .loadfile_callback = load_file - }; + // optional compiler + gravity_compiler_t *compiler = NULL; - // create VM - gravity_vm *vm = gravity_vm_new(&delegate); + // setup compiler/VM delegate + gravity_delegate_t delegate = {.error_callback = report_error, + .loadfile_callback = load_file}; - // pass argc and argv to the ENV class - gravity_env_register_args(vm, argc, argv); + // create VM + gravity_vm *vm = gravity_vm_new(&delegate); - // check if input file is source code that needs to be compiled - if ((type == OP_COMPILE) || (type == OP_COMPILE_RUN) || (type == OP_INLINE_RUN)) { + // pass argc and argv to the ENV class + gravity_env_register_args(vm, argc, argv); - // load source code - size_t size = 0; - const char *source_code = NULL; + // check if input file is source code that needs to be compiled + if ((type == OP_COMPILE) || (type == OP_COMPILE_RUN) || + (type == OP_INLINE_RUN)) { - if (type == OP_INLINE_RUN) { - source_code = input_file; - size = strlen(input_file); - } else { - source_code = file_read(input_file, &size); - } - - // sanity check - if (!source_code || !size) { - printf("Error loading %s %s\n", (type == OP_INLINE_RUN) ? "source" : "file", input_file); - goto cleanup; - } + // load source code + size_t size = 0; + const char *source_code = NULL; - // create closure to execute inline code - if (type == OP_INLINE_RUN) { - char *buffer = mem_alloc(NULL, size+1024); - assert(buffer); - size = snprintf(buffer, size+1024, "func main() {%s};", input_file); - source_code = buffer; - } + if (type == OP_INLINE_RUN) { + source_code = input_file; + size = strlen(input_file); + } else { + source_code = file_read(input_file, &size); + } - // create compiler - compiler = gravity_compiler_create(&delegate); + // sanity check + if (!source_code || !size) { + printf("Error loading %s %s\n", + (type == OP_INLINE_RUN) ? "source" : "file", input_file); + goto cleanup; + } - // compile source code into a closure - closure = gravity_compiler_run(compiler, source_code, size, 0, false, true); - if (!closure) goto cleanup; + // create closure to execute inline code + if (type == OP_INLINE_RUN) { + char *buffer = mem_alloc(NULL, size + 1024); + assert(buffer); + size = snprintf(buffer, size + 1024, "func main() {%s};", input_file); + source_code = buffer; + } - // check if closure needs to be serialized - if (type == OP_COMPILE) { - bool result = gravity_compiler_serialize_infile(compiler, closure, output_file); - if (!result) printf("Error serializing file %s\n", output_file); - goto cleanup; - } + // create compiler + compiler = gravity_compiler_create(&delegate); + + // compile source code into a closure + closure = gravity_compiler_run(compiler, source_code, size, 0, false, true); + if (!closure) + goto cleanup; + + // check if closure needs to be serialized + if (type == OP_COMPILE) { + bool result = + gravity_compiler_serialize_infile(compiler, closure, output_file); + if (!result) + printf("Error serializing file %s\n", output_file); + goto cleanup; + } - // op is OP_COMPILE_RUN so transfer memory from compiler to VM - gravity_compiler_transfer(compiler, vm); + // op is OP_COMPILE_RUN so transfer memory from compiler to VM + gravity_compiler_transfer(compiler, vm); - } else if (type == OP_RUN) { - // unserialize file - closure = gravity_vm_loadfile(vm, input_file); - if (!closure) { - printf("Error while loading compile file %s\n", input_file); - goto cleanup; - } + } else if (type == OP_RUN) { + // unserialize file + closure = gravity_vm_loadfile(vm, input_file); + if (!closure) { + printf("Error while loading compile file %s\n", input_file); + goto cleanup; } + } - // sanity check - assert(closure); + // sanity check + assert(closure); - if (gravity_vm_runmain(vm, closure)) { - gravity_value_t result = gravity_vm_result(vm); - double t = gravity_vm_time(vm); + if (gravity_vm_runmain(vm, closure)) { + gravity_value_t result = gravity_vm_result(vm); + double t = gravity_vm_time(vm); - char buffer[512]; - gravity_value_dump(vm, result, buffer, sizeof(buffer)); - if (!quiet_flag) { - printf("RESULT: %s (in %.4f ms)\n\n", buffer, t); - } + char buffer[512]; + gravity_value_dump(vm, result, buffer, sizeof(buffer)); + if (!quiet_flag) { + printf("RESULT: %s (in %.4f ms)\n\n", buffer, t); } + } cleanup: - if (compiler) gravity_compiler_free(compiler); - if (vm) gravity_vm_free(vm); - gravity_core_free(); - - #if GRAVITY_MEMORY_DEBUG - size_t current_memory = mem_leaks(); - if (current_memory != 0) { - printf("--> VM leaks: %zu bytes\n", current_memory); - mem_stat(); - } else { - printf("\tNo VM leaks found!\n"); - } - #endif - - return 0; + if (compiler) + gravity_compiler_free(compiler); + if (vm) + gravity_vm_free(vm); + gravity_core_free(); + +#if GRAVITY_MEMORY_DEBUG + size_t current_memory = mem_leaks(); + if (current_memory != 0) { + printf("--> VM leaks: %zu bytes\n", current_memory); + mem_stat(); + } else { + printf("\tNo VM leaks found!\n"); + } +#endif + + return 0; } diff --git a/src/compiler/gravity_ircode.c b/src/compiler/gravity_ircode.c index d0366b4..b53f93e 100644 --- a/src/compiler/gravity_ircode.c +++ b/src/compiler/gravity_ircode.c @@ -52,7 +52,7 @@ ircode_t *ircode_create (uint16_t nlocals) { marray_init(code->context); // init state array (register 0 is reserved) - bzero(code->state, MAX_REGISTERS * sizeof(bool)); + memset(code->state, 0, MAX_REGISTERS * sizeof(bool)); code->state[0] = true; for (uint32_t i=0; istate[i] = true; diff --git a/src/compiler/gravity_lexer.c b/src/compiler/gravity_lexer.c index 9d6bf86..0c9ea08 100644 --- a/src/compiler/gravity_lexer.c +++ b/src/compiler/gravity_lexer.c @@ -533,7 +533,7 @@ gravity_lexer_t *gravity_lexer_create (const char *source, size_t len, uint32_t gravity_lexer_t *lexer = mem_alloc(NULL, sizeof(gravity_lexer_t)); if (!lexer) return NULL; - bzero(lexer, sizeof(gravity_lexer_t)); + memset(lexer, 0, sizeof(gravity_lexer_t)); lexer->is_static = is_static; lexer->lineno = 1; lexer->buffer = source; diff --git a/src/runtime/gravity_core.h b/src/runtime/gravity_core.h index 956f361..d6d4ce9 100644 --- a/src/runtime/gravity_core.h +++ b/src/runtime/gravity_core.h @@ -30,8 +30,8 @@ gravity_value_t convert_value2int (gravity_vm *vm, gravity_value_t v); gravity_value_t convert_value2string (gravity_vm *vm, gravity_value_t v); // internal functions -gravity_closure_t *computed_property_create (gravity_vm *vm, gravity_function_t *getter_func, gravity_function_t *setter_func); -void computed_property_free (gravity_class_t *c, const char *name, bool remove_flag); +GRAVITY_API gravity_closure_t *computed_property_create (gravity_vm *vm, gravity_function_t *getter_func, gravity_function_t *setter_func); +GRAVITY_API void computed_property_free (gravity_class_t *c, const char *name, bool remove_flag); #ifdef __cplusplus } diff --git a/src/shared/gravity_config.h b/src/shared/gravity_config.h index b539ff6..f1d951d 100644 --- a/src/shared/gravity_config.h +++ b/src/shared/gravity_config.h @@ -19,29 +19,29 @@ #endif #if (!defined(HAVE_SNPRINTF) || !defined(snprintf)) -#define snprintf _snprintf +#define snprintf _snprintf #endif -typedef SSIZE_T ssize_t; -typedef int mode_t; +typedef SSIZE_T ssize_t; +typedef int mode_t; -#define open _open -#define close _close -#define read _read -#define write _write -#define __func__ __FUNCTION__ -#define PATH_SEPARATOR '\\' +#define open _open +#define close _close +#define read _read +#define write _write +#define __func__ __FUNCTION__ +#define PATH_SEPARATOR '\\' #else #include -#define PATH_SEPARATOR '/' +#define PATH_SEPARATOR '/' #endif // check if the compiler supports designated initializers when using c++ #ifndef GRAVITY_USE_HIDDEN_INITIALIZERS - #if __cplusplus && (__cplusplus < 202002L) - // C++ di requires C++20 - #define GRAVITY_USE_HIDDEN_INITIALIZERS 1 - #endif // __cplusplus +#if __cplusplus && (__cplusplus < 202002L) +// C++ di requires C++20 +#define GRAVITY_USE_HIDDEN_INITIALIZERS 1 +#endif // __cplusplus #endif // GRAVITY_USE_HIDDEN_INITIALIZERS #endif // __GRAVITY_CONFIG__