### Quickstart ## Getting started ### Install To install Gravity, simply execute the commands given below. This should make two executables: **gravity**, the compiler itself and **unittest**, the test runner. ```bash git clone https://github.com/marcobambini/gravity.git cd gravity make ``` > If you want to access the gravity compiler globally just add it to your **PATH**. You can also use the **Xcode** project to create the gravity or unittest executables. ### Configure your editor Programming is way more enjoyable when you have the right tools. That's why we've equipped several code editors with Gravity support. Just click on your favourite editor and configure it accordingly: * [Visual Studio Code](https://github.com/Dohxis/vscode-gravity) * [Atom](https://github.com/Tribex/atom-language-gravity) * [vim](https://github.com/hallzy/gravity.vim) * [BBEdit](https://github.com/marcobambini/bbedit-gravity) ### Command line To view all possible flags you can run the command below: ```bash ./gravity --help ``` To compile a gravity file to a exec.json executable: ```bash ./gravity -c myfile.gravity -o exec.json ``` To execute a precompiled json executable file: ```bash ./gravity -x exec.json ``` To directly execute a gravity file (without first serializing it to json): ```bash ./gravity myfile.gravity ``` ### Unit Tests You can run [unit tests](unittest.md) by providing a path to a folder containing all test files: ```bash ./gravity -t path_to_test_folder ``` This should produce output like: ### Hello World A simple Hello World code in Gravity looks like: ```swift func main() { System.print("Hello World!") } ``` --- ### README

Gravity Programming Language

**Gravity** is a powerful, dynamically typed, lightweight, embeddable programming language written in C without any external dependencies (except for stdlib). It is a class-based concurrent scripting language with a modern [Swift](https://github.com/apple/swift) like syntax. **Gravity** supports procedural programming, object-oriented programming, functional programming and data-driven programming. Thanks to special built-in methods, it can also be used as a prototype-based programming language. **Gravity** has been developed from scratch for the [Creo](https://creolabs.com) project in order to offer an easy way to write portable code for the iOS and Android platforms. It is written in portable C code that can be compiled on any platform using a C99 compiler. The VM code is about 4K lines long, the multipass compiler code is about 7K lines and the shared code is about 3K lines long. The compiler and virtual machine combined, add less than 200KB to the executable on a 64 bit system. > Comments in the C code make it easy to read and understand. ## What Gravity code looks like ```swift class Vector { // instance variables var x = 0; var y = 0; var z = 0; // constructor func init (a = 0, b = 0, c = 0) { x = a; y = b; z = c; } // instance method (built-in operator overriding) func + (v) { if (v is Int) return Vector(x+v, y+v, z+v); else if (v is Vector) return Vector(x+v.x, y+v.y, z+v.z); return null; } // instance method (built-in String conversion overriding) func String() { // string interpolation support return "[\(x),\(y),\(z)]"; } } 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 (manifest typing coming soon) * 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 --- ### Coverpage

Gravity Programming Language

# Gravity 0.8.0 > An embeddable programming language. * Simple and lightweight * No external dependencies * Register based virtual machine [GitHub](https://github.com/marcobambini/gravity) [Get Started](README.md) --- ### Sidebar * INTRODUCTION * [Overview](README.md) * [Getting Started](quickstart.md) * LANGUAGE GUIDE * [Syntax](syntax.md) * [Operators](operators.md) * [Types](types.md) * [Object](object.md) * [Int](int.md) * [Float](float.md) * [String](string.md) * [Bool](bool.md) * [Null](null.md) * [List](list.md) * [Map](map.md) * [Enum](enum.md) * [Range](range.md) * [Function](func.md) * [Closure](closure.md) * [Class](class.md) * [Control Flow](controlflow.md) * [Loops](loop.md) * [Fibers](fiber.md) * OPTIONALS * [System class](system.md) * [Math class](math.md) * [File class](file.md) * [ENV class](env.md) * ADVANCED * [Embedding](embedding.md) * [Extending](extending.md) * [Introspection](introspection.md) * [Unit test](unittest.md) * [Contributing](contrib.md) --- ### Bool ### Bool The Bool data type can have only two values, they are the literals true and false. A Bool value expresses the validity of a condition (tells whether the condition is true or false). ```swift var a = true; var b = false; ``` --- ### Class ## Class Every value in Gravity is an object, and every object is an instance of a class. Classes define an object's behavior and state. Behavior is defined by methods which live in the class. Every object of the same class supports the same methods. State is defined in fields, whose values are stored in each instance.

Like [functions](func.md) a **Class is a first class object**, that means that it can be stored in local variables (even in [Lists](list.md) or [Maps](map.md)), passed as a function parameter or returned by a function. Gravity supports **nested classes** and **single inheritance**. ### Defining a class Like most programming languages the class keyword is used to declare a new class: ```swift class Italy { } ``` ### Instantiate a class A class in gravity can be instantiated by simply executing it (without the new keyword): ```swift var instance = Italy(); ``` ### Methods Functions declared inside a class are called methods and are used to add behaviors to objects that belong to a specific class: ```swift class Italy { func print() { System.print("Hello from Italy"); } } ``` ### Properties Variables declared inside a class are called properties and are used to add states to objects that belong to a specific class: ```swift class Italy { var population = 60656000; var area = 301340; // in km2 func density() { return population/area; } } func main() { var it = Italy(); return it.density(); // returns 201 } ``` ### Class methods and properties A class method (or property) is a method (or property) that operates on class objects rather than instances of the class. In Gravity you can specify a class method (or property) using the static keyword: ```swift class Italy { static var population = 60656000; static var area = 301340; // in km2 static func density() { return population/area; } } func main() { return Italy.density(); } ``` ### Class constructors and destructors Class constructors get called whenever an object of that class is instantiated. Constructors can optionally have parameters. To add a constructor to your class, create a method called init(). Class destructors get called when the object gets deleted/goes out of scope. To add a destructor to your class, create a method called deinit(). ```swift class MyClass { private var _value; // Constructor - called when the class is instantiated func init(value) { System.print("MyClass instantiated with value: " + value); _value = value; } // Destructor - Called when the object instance of this class is destroyed/goes out of scope. func deinit() { System.print("MyClass instance deinitialized"); } } func main() { // Instantiate MyClass var instance = MyClass("Hello, World!"); } ``` ### Getters and Setters: As a convenient way to execute some code when a property is read or written, Gravity fully support custom getters and setters: ```swift class foo { private var _a = 12; var a { set {_a = value * 100;} // value is default parameter name get {return _a/2;} }; var b { // in this case b is a write-only property set (newb) {_a = newb * 50;} // parameter name can be specified }; } func main() { var f = foo(); f.a = 14; // 14*100 = 1400 return f.a; // 1400/2 = 700 } ``` ### Adding methods at runtime: Sometimes you need to add methods at runtime to a particular instance, this is far more efficient than subclassing and in many cases it could be a decision than can be applied only at runtime. Gravity provides a convenient **bind** method specifically developed to manage this feature: ```swift class foo { func f1() {System.print("Hello from f1");} } func main() { var obj = foo(); obj.f1(); // Output: Hello from f1 // add a new f2 method to obj instance obj.bind("f2", {System.print("Hello from f2");}); obj.f2(); // Output: Hello from f2 // replace f1 method obj.bind("f1", {System.print("Hello from f1 new");}); obj.f1(); // Output: Hello from f1 new // with unbind you can remove an existing method obj.unbind("f2"); obj.f2(); // RUNTIME ERROR: Unable to find f2 } ``` ### Nested classes: There are many cases where nested classes can lead to more readable and maintainable code, for example as a way of logically grouping classes that are only used in one place: ```swift class Database { public var query; class RecordSet { public var sql; public func run() { if (!sql) return 0; System.print(sql); return sql.length(); } func init() { System.print("RecordSet init called"); } } func init() { System.print("Database init called"); query = RecordSet(); } } func main() { var db = Database(); db.query.sql = "Hello World from Gravity!"; return db.query.run(); } ``` ### Inheritance Single class inheritance is supported in Gravity. ```swift class Bird { func talk() { System.print("Cheep") } } class Owl : Bird { func talk() { System.print("Hoot") } } func main() { var bird = Bird() bird.talk() // Output: Cheep var owl = Owl() owl.talk() // Output: Hoot } ``` ### Access specifiers The public and private keywords can be used to restrict access to specific parts of code. --- ### Closure ## Closure Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures can capture and store references to any constants and variables from the context in which they are defined. Closures can be nested and can be anonymous (without a name): ```swift func f1(a) { return func(b) { return a + b; } } func main() { var addTen = f1(10); return addTen(20); // result is 30 } ``` ### Disassemble A closure can be disassembled in order to reveal its bytecode: ```swift func sum (a,b) { return a + b; } func main() { System.print(sum.disassemble()); } // Output: // 000000 ADD 3 1 2 // 000001 RET 3 ``` --- ### Contrib ## Contributing If you find any grammatical issue, please report it using Github Issues. Or, if some sentence or paragraph is difficult to understand, feel free to make a pull request. This manual is in active development and I'll regularly update and improve it. I am not a native English speaker so feel free to correct me if something is not properly written.

If you have any question related to the material or the development of the language, feel free to open a GitHub issue or to contact me. ### About me I am Marco Bambini and you can reach me at: * Twitter: [_marcobambini](https://twitter.com/_marcobambini) * Email: [marco@creolabs.com](mailto:marco@creolabs.com) --- ### Controlflow ## Control flow Gravity provides a variety of control flow statements. Control flow is used to determine which chunks of code are executed and how many times. Branching statements and expressions decide whether or not to execute some code and looping ones execute something more than once. ### If statement It is often useful to execute different pieces of code based on certain conditions. You might want to run an extra piece of code when an error occurs, or to display a message when a value becomes too high or too low. To do this, you make parts of your code conditional. In its simplest form, the if statement has a single if condition. It executes a set of statements only if that condition is true: ```swift var counter = 30; if (counter <= 10) { // do something here } ``` The if statement can provide an alternative set of statements, known as an else clause, for situations when the if condition is false. These statements are indicated by the else keyword: ```swift var counter = 30; if (counter <= 10) { // do something here } else { // do something else here } ``` More complex if statement: ```swift var counter = 30; if (counter <= 10) { // do something here } else if (counter <= 20) { // do something else here } else { // do something else here } ``` --- ### Embedding ## Embedding Gravity can be easily embedded into any C/C++ code. Suppose to have the following Gravity code: ```swift func sum (a, b) { return a + b } func mul (a, b) { return a * b } func main () { var a = 10 var b = 20 return sum(a, b) + mul(a, b) } ``` To keep the code as simple as possible I skipped any error check condition that would be required. The bare minimum C code to embed the above Gravity code would be: ``` /* Detailed source-code truncated for AI context efficiency. */ ``` To load and execute a `myfile` Gravity source code from disk the required changes would be minimum: ```c int main (void) { size_t size = 0; const char *source_code = file_read("myfile.gravity", &size); ... // compile Gravity source code into bytecode (embedded into a closure) // notice the change of the is_static bool parameter to false gravity_closure_t *closure = gravity_compiler_run(compiler, source_code, size, 0, false, true); ... } ``` To directly execute the `mul` Gravity function and pass some parameter from C to Gravity some minor changes need to be performed: ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ### Enum ## Enum Enums defines a common type for a group of related values. If you are familiar with C, you will know that C enumerations assign related names to a set of integer values. Enums in Gravity are much more flexible and enable you to assign any literal value (Int, Float, String, Bool): ```swift enum state { nothing, // default to 0 active, // default to 1 inactive, // default to 2 undetermined = 666, error // 667 } enum math { pi = 3.141592, e = 2.718281, goldratio = 1.618033 } enum company { ceo = "Gauss", cto = "Eurel", cfo = "Nostradamus" } enum mixed { one = "Hello World", two = 3.1415, three = 666, four = true } func main() { var a = state.active; // a = 1 var b = math.pi; // b = 3.1415 var c = company.ceo; // c = "Gauss" var d = mixed.four; // d = true } ``` > Enum is a static operator, which means that at compile time the real value of the enum item is automatically replaced by Gravity. --- ### Env ## ENV ENV is a class than enables to interact with environmental variables ### Class methods ```swift // read an env variable var value = ENV.get("VAR_NAME") // write an env value ENV.set("VAR_KEY", "VAR_VALUE") // list all env variables var list = ENV.keys() ``` ### Class Constants ```swift var max_arg = ENV.argc - 1 var my_arg = ENV.argv[max_arg] ``` --- ### Extending ## Extending Gravity Gravity can be extended at runtime using the C API (please read the [Embedding](https://marcobambini.github.io/gravity/#/embedding) section before proceeding). Three steps are required: 1. Create a new class 2. Add methods and properties to the class 3. Register this class inside the VM In this simple example we'll create a "Foo" class with a "sum" method in C and then we'll execute it from Gravity. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` For more examples see the file gravity_core.c in the src/runtime/ directory. Most of the Gravity classes are built using these same APIs. --- ### Fiber ## Fiber Fibers are user-space threads without a scheduler; a Fiber can yield and resume its execution from the place it has exited. A Fibers (or coroutine as called in other languages) are special functions that can be interrupted at any time by the user.

When a conventional function is invoked, execution begins at the start, and once a function exits, it is finished. By contrast, Fibers can exit by calling other Fibers, which may later return to the point where they were invoked in the original coroutine: ```swift func main() { var fiber = Fiber.create({ System.print("fiber 1"); Fiber.yield() System.print("fiber 2"); }); System.print("main 1"); fiber.call() System.print("main 2"); fiber.call() System.print("main 3"); } // Output: // main 1 // fiber 1 // main 2 // fiber 2 // main 3 ``` A Fiber is created with `create`: ```swift Fiber.create( { System.print("\(self) is the current fiber") }) ``` and executed till the next `yield` with `fiber.call()` ```swift var closure = { System.print("1") Fiber.yield() System.print("2") Fiber.yield() System.print("3") Fiber.yield() System.print("Done") } var fiber = Fiber.create(closure) fiber.call() // prints 1 fiber.call() // prints 2 fiber.call() // prints 3 fiber.call() // prints Done System.print(fiber.isDone()) // prints true ``` There are 2 types of yield: 1. `Fiber.yield()` it returns the controll to the function calling `call()` 2. `Fiber.yieldWaitTime(seconds)` it returns the controll to the function calling `call()` and also store the current time internally. The later enable a call check of the total time in seconds passed since last `call()`. If the time amount is not enough the call is void and the fiber is not entered. Example: To implement a function that do some stuff every second, like a timer, a way is to use `Fiber.yieldWaitTime(seconds)` ```swift var fiber = Fiber.create({ var keepGoing = true while (keepGoing) { keepGoing = doSomeStuff() Console.write("Waiting") Fiber.yieldWaitTime(1.0) Console.write("Elapsed time: \(self.elapsedTime())") } }) ... // Note: this strict loop is just for reference, not a real case. while (!fiber.isDone()) { fiber.call() } ``` --- ### File ## File File is a class to add I/O capabilities to Gravity. ### Class methods example ```swift func main() { var target_file = "FULL_PATH_TO_A_TEXT_FILE_HERE"; var target_folder = "FULL_PATH_TO_A_FOLDER_HERE"; // FILE TEST var size = File.size(target_file); var exists = File.exists(target_file); var is_dir = File.is_directory(target_file); var data = File.read(target_file); System.print("File: " + target_file); System.print("Size: " + size); System.print("Exists: " + exists); System.print("Is Directory: " + is_dir); System.print("Data: " + data); // FOLDER TEST func closure (file_name, full_path, is_directory) { if (is_directory) { System.print("+ \(file_name)"); } else { System.print(" \(file_name)"); } } var recursive = true; var n = File.directory_scan(target_folder, recursive, closure); // return the number of file processed return n; } ``` ### Read/write buffer example ```swift func main() { var target_file = "FULL_PATH_TO_A_TEXT_FILE_HERE"; // WRITE TEST // 2nd argument to open is the same as the mode argument to the fopen function // https://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html var f = File.open(target_file, "w+"); f.write("This is the first line\n"); f.write("This is the second line\n"); f.write("This is the third line\n"); f.close(); // READ TEST f = File.open(target_file, "r"); var data = f.read(40); f.close(); return data; } ``` ### Read line example ``` func main() { var target_file = "FULL_PATH_TO_A_TEXT_FILE_HERE"; var f = File.open(target_file, "w+"); f.write("This is the first line\n"); f.write("This is the second line\n"); f.write("This is the third line\n"); f.close(); f = File.open(target_file, "r"); while (!f.isEOF()) { var line = f.read("\n"); System.print(line); } f.close(); return 0; } ``` --- ### Float ### Float In most dynamically typed programming language both Integers and Float are internally represented by a C double value. In a modern 64bit system, this implementation leads to some issue because some integer values cannot be correctly represented by a double value (for more details please read [Storage of integer values in double](https://www.viva64.com/en/l/0018/)). In Gravity Int and Float are internally represented by two different types to mitigate rounding errors. An Float represents a 64 bit floating point number (can optionally be compiled as 32 bit floating point number): ```swift var a = 3.1415; // float var b = 1.25e2; // scientific notation var f = 30.5.radians // returns the result of converting 30.5 degrees to radians var f = 3.14.degrees. // returns the result of converting 3.14 radians to degrees ``` The Float class exposes also a min/max property used to know at runtime lower/upper bound values: ```swift var min = Float.min; // 2.22507e-308 in 64bit systems var max = Float.max; // 1.79769e+308 in 64bit systems ``` Other useful methods: ```swift var f = 3.1415; // float var f1 = f.ceil(); // result is 4 (ceil computes the smallest integer value not less than f) var f2 = f.round(); // result is 3 (round computes the nearest integer value to f) var f3 = f.floor(); // result is 3 (floor computes the largest integer value not greater than f) ``` --- ### Func ## Function Functions are first class objects like [Int](types.md) or [String](types.md) and can be stored in local variables (even in [Lists](list.md) or [Maps](map.md)), passed as function parameters or returned by a function. Functions can be implemented in Gravity or in a [native language](api.md) with calling conventions compatible with ANSI C.

Functions are called by value. This means that foo(1) calls the function which is the value of the variable foo. Calling a value that is not a function (or does not implement the exec method) will raise a runtime error. ```swift func main() { var a = 10; var b = 20; return a + b; } ``` ```swift func f1() { return 10; } func f2() { return f1; } func main() { // a is now function f2 var a = f2; // b is now the return value of f2 which is function f1 var b = a(); // return value is f1() which is 10 return b(); // above code is equivalent to return f2()(); } ``` ### Function parameters Functions aren’t very useful if you can’t pass values to them so you can provide a parameter list in the function declaration. Gravity performs no check on the number of parameters so you can call a function providing more or less parameters. ```swift func sum(a, b) { return a + b; } // execute the sum function // and returns 30 as result sum(10,20); ``` If a function is called with missing arguments (less than declared), the missing values are set to **undefined**. ```swift // sum modified to take in account missing arguments func sum(a, b) { // equivalent to if (a == undefined) a = 30; if (!a) a = 30; // equivalent to if (b == undefined) b = 50; if (!b) b = 50; return a + b; } // execute the sum function without any argument // a has a 30 default value and b has a 50 default value // return value is 80 sum(); ``` If a function is called with more arguments (more than declared), the additional arguments can be accessed using the **_args** array. ```swift // sum modified to accept a variable number of arguments func sum() { var tot = 0; for (var i in 0..<_args.count) { tot += _args[i]; } return tot; } // execute the sum function with a variable number // of arguments returns 550 as result sum(10,20,30,40,50,60,70,80,90,100); ``` ### Recursion Function recursion is fully supported in Gravity (current function can be accessed using the _func reserved keyword): ```swift func fibonacci (n) { if (n<2) return n; // could be written as return _func(n-2) + _func(n-1) return fibonacci(n-2) + fibonacci(n-1); } func main() { return fibonacci(20); } ``` ### Returning values A function without a return statement returns **null** by default. You can explicitly return a value using a return statement. --- ### Int ### Int In most dynamically typed programming language both Integers and Float are internally represented by a C double value. In a modern 64bit system, this implementation leads to some issue because some integer values cannot be correctly represented by a double value (for more details please read [Storage of integer values in double](https://www.viva64.com/en/l/0018/)). In Gravity Int and Float are internally represented by two different types to mitigate rounding errors. An Int represents a 64 bit signed number (can optionally be compiled as 32 bit signed number): ```swift var a = 123; // decimal var b = 0xFF; // hexadecimal var c = 0O7777; // octal var d = 0B0101; // binary var e = Int.random(1, 10) // returns a random int between 1 and 10 inclusive var f = 30.radians // returns the result of converting 30 degrees to radians var f = 3.degrees // returns the result of converting 3 radians to degrees ``` An Int can also be used as a convenient way to execute loops: ```swift 5.loop() { System.print("Hello World"); } // result // Hello World // Hello World // Hello World // Hello World // Hello World ``` The Int class exposes a min/max property used to know at runtime lower/upper bound values: ```swift var min = Int.min; // -9223372036854775808 in 64bit systems var max = Int.max; // 9223372036854775807 in 64bit systems ``` --- ### Introspection ### Introspection Type introspection is a core feature of Gravity. In Gravity, the Object class (ancestor of every class) provides methods for checking the instance's class. All Objects now responds to the following methods: ```swift Object.introspection(); Object.methods(); Object.properties(); ``` Each method support two optional parameters: * param1 a Bool value (default false), if set to true returns extended information * param2 a Bool value (default false), if set to true it scan super classes hierarchy Result can be a List or a Map (in case of param1 set to true). Examples: ```swift List.introspection(); // returns: [sort,reduce,loop,sorted,contains,filter,count,reverse,iterate,push,remove,pop,storeat,loadat,reversed,indexOf,next,map,join] List.introspection(true); // returns: [sorted:[isvar:false,name:sorted],next:[isvar:false,name:next],sort:[isvar:false,name:sort],filter:[isvar:false,name:filter],storeat:[isvar:false,name:storeat],map:[isvar:false,name:map],indexOf:[isvar:false,name:indexOf],reversed:[isvar:false,name:reversed],contains:[isvar:false,name:contains],loop:[isvar:false,name:loop],reduce:[isvar:false,name:reduce],count:[isvar:true,name:count,readonly:true],loadat:[isvar:false,name:loadat],iterate:[isvar:false,name:iterate],remove:[isvar:false,name:remove]... ``` --- ### List ## List Lists (or arrays) are simple sequence of objects, their size is dynamic and their index starts always at 0. They provide fast random access to their elements. You can create a list by placing a sequence of comma-separated expressions inside square brackets: ```swift var r = [1, 2, "Hello", 3.1415, true]; // list has a count property var n = r.count; // n is 5 ``` ### Accessing items You can access an element from a list by calling the subscript operator [] on it with the index of the element you want. Like most languages, indices start at 0: ```swift var names = ["Mark", "Andrew", "Paul", "Ross", "Frank", "Max"]; names[0]; // "Mark" names[2]; // "Paul" ``` Negative indices count backwards from the end: ```swift var names = ["Mark", "Andrew", "Paul", "Ross", "Frank", "Max"]; names[-1]; // "Max" names[-2]; // "Frank" ``` ### Iterating items The subscript operator works well for finding values when you know the key you’re looking for, but sometimes you want to see everything that’s in the list. Since the List class implements the iterator method, you can easily use it in a for loop: ```swift var people = ["Mark", "Andrew", "Paul", "Ross", "Frank", "Max"]; for (var name in people) { System.print("Current name is " + name); } ``` ### Adding items A List instance can be expanded by setting an index that is greater than the current size of the list: ```swift var list = [10,20,30,40,50]; list[30] = 22; // list contains now 31 elements (index 0...30) ``` ### List as a stack The List class implements the push/pop methods as a convenient way to treat a list as a stack: ```swift var list = [10,20,30,40,50]; list.push(100); // add 100 to the list var v1 = list.pop(); // pop 100 var v2 = list.pop(); // pop 50 ``` ### List Contains The List class implements the contains methods as a convenient way to check for the existence of a value in a list: ```swift var list = [1, 2, "Hello", 3.1415, true]; return list.contains(3.1415); // Returns: true ``` ### List Join The List class implements the join method as a convenient way to interpret a list as a string: ```swift var list = [1,2,3,4,5]; list.join(" + "); // Becomes: "1 + 2 + 3 + 4 + 5" ``` ### List Map The List class implements the map method as a convenient way to create a new list using the current values of a list in some defined way: ```swift var numbers = [1,2,3,4,5,6,7,8,9,10] var squared = numbers.map(func(num) { return num*num }) // squared is now equal to [1,4,9,16,25,36,49,64,81,100] ``` ### List Filter The List class implements the filter method as a convenient way to create a new list that contains the elements of the original list which passed a specified test: ```swift var numbers = [1,2,3,4,5,6,7,8,9,10] var even = numbers.filter(func(num) { return !(num % 2) }) // even is now equal to [2,4,6,8,10] ``` ### List Reduce The List class implements the reduce method as a convenient way to create a new list reduces a list to a single value based on a provided callback: ```swift var numbers = [1,2,3,4,5,6,7,8,9,10] var sum = numbers.reduce(0, func(num1, num2) { return num1+num2 }) // sum is now equal to 55 ``` ### List Sort The List class implements the sort method as a convenient way to sort its items. By default, the sort() method sorts the values as strings (or numbers) in alphabetical (for strings) and ascending order: ```swift var fruits = ["Banana", "Orange", "Apple", "Mango"]; fruits.sort(); // fruits is now [Apple,Banana,Mango,Orange] var numbers = [10, 3.14, 82, 1, 7]; numbers.sort(); // numbers is now [1,3.14,7,10,82] // if you need to customize the sort algorithm you can provide a closure func compare (a, b) { return (a < b); } var fruits = ["Banana", "Orange", "Apple", "Mango"]; fruits.sort(compare); // fruits is now [Orange,Mango,Banana,Apple] ``` --- ### Loop ## Loop ### While loop A while loop performs a set of statements until a condition becomes false. These kind of loops are best used when the number of iterations is not known before the first iteration begins. ```swift func main() { var i = 0; while (i < 50000) { i += 1; } return i; } ``` ### Repeat-while loop The other variation of the while loop, known as the repeat-while loop, performs a single pass through the loop block first, before considering the loop’s condition. It then continues to repeat the loop until the condition is false. ```swift func main() { var i = 0; repeat { i += 1; } while (i < 50000); return i; } ``` ### For loop You can access an element from a list by calling the subscript operator [] on it with the index of the element you want. As in most languages, indices start at zero: ```swift var count = 0; for (var i in 0...40) { count += i; } return count; ``` The for in loop can be used over any object that supports iteration, such as [Lists](list.md), Strings or [Maps](map.md). ### Loop method Performing a loop is very common operation in any programming language, so Gravity adds a very convenient way to run a loop by adding a special loop method to some classes (Int, Range, List, String and Map) that accepts a [closure](closure.md) as parameter: ```swift func main() { 4.loop({System.print("Hello World");}); } // Output: // Hello World // Hello World // Hello World // Hello World ``` If we need to access the current index of the loop we can just rewrite the closure: ```swift func main() { var target = 5; target.loop(func (value){System.print("Hello World " + value);}); } // Output: // Hello World 0 // Hello World 1 // Hello World 2 // Hello World 3 // Hello World 4 ``` Loop within a [Range](types.md): ```swift func main() { var target = 0...4; target.loop(func (value){System.print("Hello World " + value);}); } // also in reverse order func main() { var target = 4...0; target.loop(func (value){System.print("Hello World " + value);}); } ``` Loop within a [Lists](list.md): ```swift func main() { var target = [10,20,30,40,50,60,70,80,90]; target.loop(func (value){System.print("Hello World " + value);}); } ``` Loop within a String: ```swift func main() { var s = "abcdefghijklmnopqrstuvwxyz"; var vowels = "" s.loop(func (c) { if (c == "a" or c == "e" or c == "i" or c == "o" or c == "u") { vowels += c; } }) System.print(vowels) // aeiou } ``` Loop within a [Maps](map.md) where the key is passed as closure argument (please note that key order is not preserved): ```swift func main() { var target = ["key1":10,"key2":20,"key3":30,"key4":40]; target.loop(func (key){System.print(key);}); } // Output: // key1 // key2 // key4 // key3 ``` --- ### Map ## Map Maps are associative containers implemented as pairs each of which maps a key to a value. You can create a map by placing a series of comma-separated entries inside square brackets. Each entry is a key and a value separated by a colon: ```swift // create a new map with 4 entries var d = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; // map has a count property var n = d.count; // n is 4 // create an empty map var map = [:]; ``` ### Looking up values You can access an element from a list by calling the subscript operator [] on it with the key of the element you want: ```swift var names = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; names["Mark"]; // 1 names["Andrew"]; // 2 ``` ### Iterating items The subscript operator works well for finding values when you know the key you’re looking for, but sometimes you want to see everything that’s in the map. Since the Map class implements the iterator method (through the keys method), you can easily use it in a for loop: ```swift var people = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; for (var name in people.keys()) { System.print("Current name is " + name); } ``` ### Adding items An item can be added to a map by simply setting a key/value: ```swift var people = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; people["Kiara"] = 5; // people now contains the "Kiara" key with value 5 ``` ### Removing items The remove method has been added to the map class as a conveniente way to remove keys: ```swift var people = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; people.remove("Paul"); people.remove("Ross"); return people.count; // 2 is returned in this case ``` ### Retrieving keys The keys method has been added to the map class as a conveniente way to get access to all keys: ```swift var people = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; return people.keys; // ["Mark", "Andrew", "Paul", "Ross"] is returned in this case ``` ### Checking for a key To check if a key has been added to a map you can use the he hasKey method: ```swift var people = ["Mark":1, "Andrew":2, "Paul":3, "Ross":4]; return people.hasKey("Max"); // false is returned in this case because the "Max" key is not in the people map ``` --- ### Math ## Math The Math class is a class in Gravity that offers various methods for calculating more complex mathematics than the standard +,-,/, and *. ### Mathematical Constants ```swift Math.PI; // pi (~3.141593) Math.E; // e (~2.718282) Math.LN2; // natural log of 2 (ie. Math.log(2) = ~0.693147) Math.LN10; // natural log of 10 (ie. Math.log(10) = ~2.302585) Math.LOG2E; // log base 2 of e (~1.442695) Math.LOG10E; // log base 10 of e (~0.434294) Math.SQRT2; // sqrt of 2 (ie. Math.sqrt(2) = ~1.414214) Math.SQRT1_2; // sqrt of 0.5 (ie. Math.sqrt(0.5) = ~0.707107) ``` ### Absolute Values **Math.abs()** is a method that returns the absolute value of an integer of float. ```swift Math.abs(-10); // returns 10 Math.abs(10); // also returns 10 ``` ### Trig Functions The Math class also contains several Trigonometric Functions. All values that represent angles are in radians for these methods, and they all expect radians for inputs. You can use the ".radians" and ".degrees" properties of the Int and Float class to do conversions. ```swift Math.acos(-1); // returns pi Math.asin(0.5); // returns 0.523599 ( = pi/6) Math.atan(1); // returns 0.785398 ( = pi/4 ) Math.atan2(-1,-1); // returns -2.356194 ( = -3pi/4 ) Math.cos(Math.PI); // returns -1 Math.cos(180.radians); // returns -1 (same as above) Math.sin(Math.PI); // returns 0 Math.tan(Math.PI/4); // returns 1 ``` ### Ceiling, Floor ```swift Math.ceil(4.1) // returns 5 Math.floor(4.1) // returns 4 ``` ### Rounding Return number rounded to ndigits precision after the decimal point. If ndigits is omitted, it returns the nearest integer to its input. For Float values are rounded to the closest multiple of 10 to the power minus ndigits. ```swift Math.round(4.1) // returns 4 Math.round(4.5) // returns 5 Math.round(65.34634) // returns 65.0 Math.round(65.34634,1) // returns 65.3 Math.round(65.34634,2) // returns 65.35 ``` ### Exponents and Radicals ```swift // e to the power of x Math.exp(1) // returns 2.718282 (e) Math.exp(2) // returns 7.389056 (e^2) // x to the power of y Math.pow(2,3); // returns 8 Math.sqrt(9); // 3 Math.cbrt(8); // 2 Math.xrt(4,16); // 2 (4th root of 16) ``` ### Logarithms ```swift // log base e Math.log(Math.E) // returns 1 Math.log10(10) // returns 1 Math.logx(2,2) // returns 1 Math.logx(2,4) // returns 2 ``` ### Max and Min ```swift Math.max(-1,10,2); // Returns 10 Math.min(-1,10,2); // Returns -1 ``` ### Random Number ```swift Math.random() // Returns a random number between 0.0 and 1.0 Math.random(N) // Returns a random number between 0 and N (or between 0.0 and N.0 if N is Float) Math.random(N1,N2) // Returns a random number between N1 and N2 (they must be both Int or Float) ``` ### GCF and LCM ```swift Math.gcf(12,15,21); // 3 Math.lcm(6,15,2); // 30 ``` --- ### Null ### Null It indicates the absence of a value. If you call a method that doesn’t return anything and get its returned value, you get null back. The null data type is also used to initialize uninitialized variables with a default value. ```swift class Newton { var mass = 10; var acceleration; func force() { return mass * acceleration; } } func f2() { var sir = Newton(); // acceleration instance variable has no default value // so it is automatically set to null return sir.force(); } func f1() { var a; // a is uninitialized so it has a default null value return a; } ``` --- ### Object ### Object Object is the root class of every object inside Gravity. Through the Object class, objects inherit a basic interface to the runtime system and the ability to behave as Gravity objects. All the built-in Gravity type are built from the base Object class and when you declare a new Class in Gravity without a super class then it is set by default to Object. The new [Introspection](introspection.md) feature is built on top of the base Object class (so any other Class automatically inherits that feature). ## Built-in types Gravity has some built-in types that extend and overrides methods and classes from the base Object class: * [Int](int.md) * [Float](float.md) * [String](string.md) * [Bool](bool.md) * [Null](null.md) * [Class](class.md) * [Function](func.md) * [Fiber](fiber.md) * Instance * [List](list.md) * [Map](map.md) * [Range](range.md) * [Function](func.md) * [Closure](closure.md) * [Fiber](fiber.md) ## Special internal methods Some methods has a very special meaning, for example by implementing the **exec** method your class is able to be executable via the object() notation. By implementing the **loadat/storeat** method your object can be accessed via the subscript shortcut object[i]. The **load/store** method is internally used to implement the dot notation (object.property). The Class class overrides the exec method in order to implement Object instantiation and initialization: ```swift var foo = Foo() // means execute the exec method of the Foo class ``` All these special methods are implemented by the base Object class in order to provide most the basic functionalities that an user expects for a modern object oriented programming language. --- ### Operators ## Operators An operator is a special symbol or phrase that you use to check, change, or combine values. For example, the addition operator (+) adds two numbers, as in **var i = 1 + 2**, and the logical AND operator (&&) combines two Boolean values, as in **if (flag1 && flag2)**.

Gravity supports most standard C operators and improves several capabilities to eliminate common coding errors. The assignment operator (=) does not return a value, to prevent it from being mistakenly used when the equal to operator (==) is intended. Gravity also provides two [range](types.md) operators as a shortcut for expressing a range of values. ### Arithmetic Operators * Addition (+) * Subtraction (-) * Multiplication (*) * Division (/) * Remainder (%) ```swift var n1 = 1 + 2 // equals 3 var n2 = 5 - 3 // equals 2 var n3 = 2 * 3 // equals 6 var n4 = 10.0 / 2.5 // equals 4.0 var n5 = 9 % 4 // equals 1 ``` ### Assignment Operator The assignment operator = initialize or update a value: ```swift var a = 50; // a = 50 var b = a; // b = 50 var c = a * b; // c = 50 * 50 ``` Please note that contrary to many other programming languages, the assignment operator has no side effect, it means that it does not return any value. ### Comparison Operators The comparison operators return a Bool value to indicate whether or not the statement is true: * Equal (==) * Not equal (!=) * Less than (<) * Less than or equal (<=) * Greater than (>) * Greater than or equal (>=) * Identical (===) * Not identical (!==) * Type check (is) * Pattern match (~=) ```swift 1 == 1 // true because 1 is equal to 1 1 != 2 // true because 1 is not equal to 2 1 < 2 // true because 1 is less than 2 1 <= 1 // true because 1 is less than or equal to 1 1 > 2 // false because 1 is not greater than 2 1 >= 1 // true because 1 is greater than or equal to 1 1 === 1 // true because 1 is identical to 1 (same value and same class) 1 is Int // true because 1 is of class Int ``` Gravity performs some conversions at runtime, so 1 == "1" but not 1 === '1'. ### Logical Operators The comparison operators return a Bool value to indicate whether or not the statement is true: * Logical NOT (!) * Logical AND (&&) * Logical OR (||) ```swift !1 // false because 1 is true 1 && 0 // false because one of the two values is false 1 || 0 // true because one of the two values is true ``` In order to improve code readability the reserved keywords **not, and, or** has been introduces as an alisas to logical operators. ### Bitwise Operators * Bitwise shift left (<<) * Bitwise shift right (>>) * Bitwise AND (&) * Bitwise OR (|) * Bitwise XOR (^) * Bitwise NOT or one's complement (~) ```swift var n = 0B00110011; var n1 = n << 2 // equals 11001100 var n2 = n >> 2 // equals 00001100 var n3 = n & 0B00001111 // equals 00000011 var n4 = n | 0B00001111 // equals 00111111 var n5 = n ^ 0B00001111 // equals 00111100 var n6 = ~n; // equals 11001100 ``` ### Compound Assignment Operators As a shortcut, assignment and operators can be combined together: * Multiply and assign (*=) * Divide and assign (/=) * Remainder and assign (%=) * Add and assign (+=) * Subtract and assign (-=) * Left bit shift and assign (<<=) * Right bit shift and assign (>>=) * Bitwise AND and assign (&=) * Bitwise XOR and assign (^=) * Bitwise OR and assign (|=) --- ### Range ### Range A range is an object that represents a consecutive range of numbers. Syntax for this type has been directly inspired by Swift. ```swift // a represents a range with values 1,2,3 var a = 1...3; // b represents a range with values 1,2 var b = 1..<3; // Ranges have also a conveniente count property var n1 = a.count; // n1 is now 3 var n2 = b.count; // n2 is now 2 ``` A range is expecially useful in for loops: ```swift for (var i in 1...10) { // repeat for 10 times (with i from 1 to 10) } ``` --- ### String ### String Strings are an immutable sequence of characters. String literals can be surrounded with double or single quotes. Gravity supports UTF-8 strings and characters. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` Strings can contain inline expressions with backslash and parentheses. ```swift var amount = 7 var fruit = "apples" var n = "You have \(amount) \(fruit)!" // n is now "You have 7 apples!" ``` --- ### Syntax ## Syntax **Gravity** syntax is designed to be familiar to people coming from C-like languages like Javascript, Swift, C++, C# and many more. We started working on this new language a year before Apple announced Swift and we were happily surprised to discovered how similar both syntax appear. >In Gravity semicolon separator **;** is completely optional. What Gravity code looks like: ```swift class Rectangle { // instance variables var width; var height; // instance method func area() { return width*height; } // constructor func init(w, h) { width = w; height = h; } } func main() { // initialize a new Rectangle object var r = Rectangle(20, 10); // return value is 20*10 = 200 return r.area(); } ``` ### Comments Gravity supports both line comments: ```swift // This is a line comment ``` and block comments: ```swift /* This is a multi-line comment */ ``` While Gravity uses C-Style comments, Gravity still supports the common "#!" shebang to tell your shell what program to execute the file with. However, the shebang must be on the first line of the file in order to use it in this way: ```swift #!/path/to/gravity func main() { System.print("Execute as: path/to/file.gravity"); System.print("Instead of: gravity path/to/file.gravity"); } ``` ### Include To statically include a gravity file from within another gravity file you can use the #include statement. Please note that in the current version, the path to the included file is relative to the location that the gravity executable was run. ```swift // adder.gravity func add(x, y) { return x+y; } ``` ```swift // main.gravity #include "adder.gravity" func main() { System.print("5+4=" + add(5,4)); } ``` ### Import While #include is used to include file at compilation time, the import statement enables you to load modules at runtime. Import is under active development and it will be available pretty soon. ### Reserved Keywords Like many other programming languages Gravity has some reserved keywords that assume a very specific meaning in the context of the source code: ```swift if in or is for var and not func else true enum case null file lazy super false break while class const event _func _args struct repeat switch return public static extern import module default private continue internal undefined ``` ### Identifiers Identifiers represent a naming rule used to identify objects inside your source code. Gravity is a case-sensitive language. Identifiers start with a letter or underscore and may contain letters, digits, and underscores (function identifiers can be any of the [built-in operators](operators.md) in order to override a default behaviour): ```swift a _thisIsValid Hello_World foo123 BYE_BYE ``` ### Blocks and Scope Every named identifier introduced in some portion of the source code is introduced in a scope. The scope is the largest part of the source code in which that identifier is valid. The names declared by a declaration are introduced into a specific scope based on the context of the declaration. For instance, local variable declarations introduce the name into the block scope, whereas class member variable declarations introduce the name into class scope.

There are three scopes defined: **block scope**, **class scope** and **file scope**. Names declared in the block scope become visible immediately after its completed declarator. This means you cannot refer to a name within the block scope until after it has been fully declared. Names declared in the file and class scopes become visible immediately upon executing the starting statement of the script. This means you can refer to a name within the file or class scopes before it has been fully declared.

These are all valid scopes: ```swift // file scope can refer to a name // before it has been fully declared func f1() { return f2(); } func f2() { return 42; } // block scope can be nested and // can hide other local variables func f3() { var a = 10; if (a > 0) { var a = 20; } // 10 is returned here return a; } ``` --- ### System ## System System class is a class registered in every Gravity VM that offers some useful methods and properties. ### Print methods ```swift func main() { // print to stdout and add a newline character System.print("Hello World"); // print to stdout without any newline character appended System.put("Hello World"); } ``` ### Garbage collector methods Gravity automatically manages memory for you using a tri-colour marking garbage collector, using the System class the user has the ability to change some of its settings and even disable it when certain performance critical tasks need to be performed: ```swift func main() { // disable GC System.gcEnabled = false; // ratio used during automatic recomputation of the new gcthreshold value var ratio = System.gcRatio; // minimum GC threshold size var minthreshold = System.gcMinThreshold; // memory required to trigger a GC var threshold = System.gcThreshold; // enable GC System.gcEnabled = true; } ``` ### Time related methods There are times where it could be really useful to easily measure how much time is spent in a given task: ```swift func main() { var t1 = System.nanotime(); perform_my_task(); var t2 = System.nanotime(); // return elapsed time in ms return ((t2-t1) / 1000000.0); } ``` ### System Exit There are times where it could be useful to have main() return an error code back to your shell: ```swift func main() { foo(); // Do something useful System.exit(5); } ``` In your terminal, you can now reference the return code: ```bash # Returns: 5 $ echo $? ``` --- ### Types ## Values and Types Gravity is a dynamically typed language so variables do not have a type, although they refer to a value that does have a type. In Gravity everything is an object (with methods you can call and instance variables you can use). Basic built-in types are of class [Object](object.md), [Int](int.md), [Float](float.md), [String](string.md), [Bool](bool.md), [Null](null.md), [Class](class.md), [Function](func.md), [Fiber](fiber.md), Instance, [List](list.md), [Map](map.md) and [Range](range.md). ### Manifest Typing Gravity supports manifest typing, so you can specify which type is associated with an Object with a syntax like: ```swift var s:String = "Hello World"; var n:Int = 100; ``` It can also be used in func parameters: ```swift func sum (a:Int, b: Int) { return a + b; } ``` > Manifest typing is currently used in autocompletion feature, full static type checking will be introduced in a future release. --- ### Unittest ## Unit Test Unit testing is so important (expecially for a programming language) that Gravity has built-in unit testing capabilities. What a user needs to do is setup some delegate C methods in order to be able to correctly setup a unit-test. Gravity already has a unit-test executable so in order to add your code to it you need to create a .gravity file using the special **#unittest** preprocessor macro: ```swift #unittest { name: "A simple add operation."; error: NONE; result: 33; }; func main() { var a = 11; var b = 22; return a + b; } ``` > A unit test can also be written in order to be able to check for syntax, semantic and runtime errors. For syntax and semantic errors the user can also specify row and column of the expected generated error. --- ### ARCHITECTURE # Gravity Language Architecture This document provides a detailed technical description of the Gravity programming language implementation, covering the full compilation pipeline, runtime virtual machine, type system, garbage collector, embedding API, and supporting infrastructure. ## Table of Contents - [1. High-Level Overview](#1-high-level-overview) - [2. Compilation Pipeline](#2-compilation-pipeline) - [2.1 Lexer](#21-lexer) - [2.2 Parser](#22-parser) - [2.3 Abstract Syntax Tree (AST)](#23-abstract-syntax-tree-ast) - [2.4 Semantic Analysis — Pass 1](#24-semantic-analysis--pass-1) - [2.5 Semantic Analysis — Pass 2](#25-semantic-analysis--pass-2) - [2.6 Code Generation (AST → IR)](#26-code-generation-ast--ir) - [2.7 IR Representation](#27-ir-representation) - [2.8 Optimizer & Bytecode Emission](#28-optimizer--bytecode-emission) - [3. Runtime Virtual Machine](#3-runtime-virtual-machine) - [3.1 VM Structure](#31-vm-structure) - [3.2 Instruction Dispatch](#32-instruction-dispatch) - [3.3 Instruction Set](#33-instruction-set) - [3.4 Instruction Encoding](#34-instruction-encoding) - [3.5 Stack and Call Frames](#35-stack-and-call-frames) - [3.6 Fiber (Coroutine) Model](#36-fiber-coroutine-model) - [3.7 Execution Flow](#37-execution-flow) - [3.8 Fast-Path Optimizations](#38-fast-path-optimizations) - [4. Value System and Type Hierarchy](#4-value-system-and-type-hierarchy) - [4.1 Value Representation](#41-value-representation) - [4.2 Object Header and GC Metadata](#42-object-header-and-gc-metadata) - [4.3 Built-in Types](#43-built-in-types) - [4.4 Functions and Closures](#44-functions-and-closures) - [4.5 Upvalues](#45-upvalues) - [4.6 Classes and Instances](#46-classes-and-instances) - [5. Garbage Collector](#5-garbage-collector) - [6. Core Data Structures](#6-core-data-structures) - [6.1 Hash Table](#61-hash-table) - [6.2 Dynamic Array](#62-dynamic-array) - [6.3 Memory Management](#63-memory-management) - [7. Optional Modules](#7-optional-modules) - [7.1 Registration Pattern](#71-registration-pattern) - [7.2 Math Module](#72-math-module) - [7.3 File Module](#73-file-module) - [7.4 JSON Module](#74-json-module) - [7.5 ENV Module](#75-env-module) - [8. Embedding API](#8-embedding-api) - [8.1 Compiler API](#81-compiler-api) - [8.2 VM API](#82-vm-api) - [8.3 Delegate Pattern](#83-delegate-pattern) - [8.4 Bridging](#84-bridging) - [9. Utilities](#9-utilities) - [10. CLI](#10-cli) - [11. Build System](#11-build-system) - [12. Test Infrastructure](#12-test-infrastructure) --- ## 1. High-Level Overview Gravity is a dynamically typed, embeddable programming language written in portable C99 with zero external dependencies (only stdlib). It features Swift-like syntax and supports procedural, object-oriented, functional, and prototype-based programming paradigms. The implementation follows a classic multi-pass compiler architecture that produces register-based bytecode executed by a stack-based virtual machine with coroutine (fiber) support. ### Source Layout ``` src/ ├── cli/ CLI entry point (gravity.c) ├── compiler/ Lexer, parser, AST, semantic analysis, IR, optimizer, codegen ├── runtime/ Virtual machine (gravity_vm), built-in types (gravity_core) ├── shared/ Value representation, opcodes, hash table, dynamic array, memory/GC ├── optionals/ Optional modules: Math, File, JSON, ENV └── utils/ Debug disassembler, JSON serialization, file I/O, UTF-8 utilities ``` ### Full Pipeline ``` Source Code │ ▼ ┌──────────┐ │ Lexer │ Character stream → Token stream └────┬─────┘ ▼ ┌──────────┐ │ Parser │ Token stream → Abstract Syntax Tree └────┬─────┘ ▼ ┌──────────────┐ │ Semacheck 1 │ Gather non-local declarations into symbol tables └──────┬───────┘ ▼ ┌──────────────┐ │ Semacheck 2 │ Resolve identifiers, detect upvalues, validate scopes └──────┬───────┘ ▼ ┌──────────────┐ │ Codegen │ AST → IR instructions (virtual registers) └──────┬───────┘ ▼ ┌──────────────┐ │ Optimizer │ Constant folding, dead code elimination, label resolution └──────┬───────┘ ▼ ┌──────────────┐ │ Bytecode │ Packed 32-bit instruction words └──────┬───────┘ ▼ ┌──────────────┐ │ VM │ Register-based execution with computed goto dispatch └──────────────┘ ``` The compiler entry point (`gravity_compiler_run` in `gravity_compiler.c`) orchestrates this pipeline: it creates a mini VM for GC during compilation, runs the parser to produce an AST, applies both semantic passes, generates IR code, optimizes it into final bytecode, and returns a `gravity_closure_t` ready for execution. --- ## 2. Compilation Pipeline ### 2.1 Lexer **Files:** `src/compiler/gravity_lexer.c`, `src/compiler/gravity_lexer.h` The lexer is a zero-allocation streaming tokenizer that scans source code character-by-character, producing tokens without copying or modifying the input buffer. Token values are pointers into the original source string. #### Lexer State ```c struct gravity_lexer_t { const char *buffer; // source buffer (not owned) uint32_t offset; // current byte offset uint32_t position; // current character position (UTF-8 aware) uint32_t length; // buffer length in bytes uint32_t lineno; // 1-based line number uint32_t colno; // 0-based column number uint32_t fileid; // source file identifier gtoken_s token; // current token bool peeking; // in peek mode gravity_delegate_t *delegate; // error callback }; ``` #### Token Structure ```c struct gtoken_s { gtoken_t type; // token type (enum) uint32_t lineno; // line number uint32_t colno; // column at end of token uint32_t position; // byte offset of first character uint32_t bytes; // length in bytes uint32_t length; // length in UTF-8 characters uint32_t fileid; // source file ID gbuiltin_t builtin; // builtin identifier (__LINE__, __FILE__, etc.) const char *value; // pointer into source buffer (NOT null-terminated) }; ``` #### Token Categories (~80 total) | Category | Count | Examples | |----------|-------|---------| | General | 8 | `EOF`, `ERROR`, `COMMENT`, `STRING`, `NUMBER`, `IDENTIFIER`, `SPECIAL`, `MACRO` | | Keywords | 36 | `func`, `class`, `var`, `const`, `if`, `else`, `for`, `while`, `return`, `import`, `enum`, `switch`, `true`, `false`, `null`, `undefined`, `super`, `isa`, ... | | Operators | 36 | `+`, `-`, `*`, `/`, `%`, `&`, `\|`, `^`, `~`, `<<`, `>>`, `<`, `<=`, `==`, `!=`, `===`, `!==`, `~=`, `&&`, `\|\|`, `=`, `+=`, `..<`, `...`, ... | | Punctuators | 10 | `(`, `)`, `[`, `]`, `{`, `}`, `;`, `:`, `.`, `,` | #### Key Features - **UTF-8 support:** Tracks both byte offset and character position separately using `utf8_charbytes()`. Handles 1–4 byte sequences. - **Number literals:** Decimal, hexadecimal (`0x`), binary (`0b`), octal (`0o`), floating-point with scientific notation (`1.25e-2`). Uses state-machine with lookahead. - **String literals:** Single (`'`) and double (`"`) quoted strings with backslash escapes. Multi-line strings tracked across line boundaries. - **String interpolation:** Detected as `LITERAL_STRING_INTERPOLATED` for `"text \(expr)"` syntax. - **Nested block comments:** `/* ... /* ... */ ... */` with a nesting depth counter. - **Builtin identifiers:** `__LINE__`, `__FILE__`, `__COLUMN__`, `__CLASS__`, `__FUNC__` resolved during lexing. - **Line separators:** CR, LF, CR+LF, NEL (U+0085), LS (U+2028). --- ### 2.2 Parser **Files:** `src/compiler/gravity_parser.c`, `src/compiler/gravity_parser.h` The parser uses a **Pratt parser** (top-down operator precedence) to build an Abstract Syntax Tree. It supports a lexer stack for `#include` directives and maintains a declaration scope stack for context tracking. #### Parser State ```c struct gravity_parser_t { lexer_r *lexer; // stack of lexers (for includes) gnode_r *declarations; // declaration scope stack gnode_r *statements; // statement list being built gravity_delegate_t *delegate; // error callbacks uint32_t nerrors; // accumulated error count uint32_t unique_id; // unique identifier counter uint32_t depth; // statement nesting depth uint32_t expr_depth; // expression nesting depth }; ``` #### Precedence Levels ``` PREC_LOWEST = 0 PREC_ASSIGN = 90 = += -= *= /= %= <<= >>= &= |= ^= PREC_TERNARY = 100 ?: PREC_LOGICAL_OR = 110 || PREC_LOGICAL_AND = 120 && PREC_COMPARISON = 130 < <= > >= == != === !== ~= PREC_ISA = 132 is PREC_RANGE = 135 ..< ... PREC_TERM = 140 + - | ^ PREC_FACTOR = 150 * / % & PREC_SHIFT = 160 << >> PREC_UNARY = 170 + - ! ~ PREC_CALL = 200 . ( [ ``` Each grammar rule carries a prefix handler, infix handler, precedence level, and a right-associativity flag: ```c typedef struct { parse_func prefix; // prefix expression handler (or NULL) parse_func infix; // infix expression handler (or NULL) prec_level precedence; // binding power const char *name; // operator name for diagnostics bool right; // right-associative } grammar_rule; ``` #### Statement Types - Compound statements (blocks) - Variable/constant declarations (`var`, `const`, with optional type annotations and initialization) - Function declarations (with parameters, default values) - Class declarations (with inheritance, access modifiers, struct flag) - Enum declarations - Module declarations - Control flow: `if`/`else`, `switch`/`case`/`default`, `for`, `while`, `repeat` - Jump statements: `break`, `continue`, `return` - Expression statements - Empty statements #### Error Recovery - **One error per line:** Suppresses cascading errors from the same source line. - **Token synchronization:** `parse_skip_until()` advances to a recovery point (e.g., next statement boundary). - **Recursion limits:** `MAX_RECURSION_DEPTH = 1000` for statements, `MAX_EXPRESSION_DEPTH = 512` for expressions. --- ### 2.3 Abstract Syntax Tree (AST) **Files:** `src/compiler/gravity_ast.c`, `src/compiler/gravity_ast.h` The AST uses a **non-uniform node design** — each node type has its own struct, but all share a common base for dispatch. A visitor pattern (`gvisitor_t`) is used for all tree traversals. #### Node Types (21 total) **Statements (7):** | Node | Purpose | |------|---------| | `NODE_LIST_STAT` | Root/global statement list | | `NODE_COMPOUND_STAT` | Block with local scope and symbol table | | `NODE_LABEL_STAT` | Switch case/default label | | `NODE_FLOW_STAT` | `if`/`else`, `switch`, ternary | | `NODE_JUMP_STAT` | `break`, `continue`, `return` | | `NODE_LOOP_STAT` | `while`, `repeat`, `for` loops | | `NODE_EMPTY_STAT` | Empty statement | **Declarations (6):** | Node | Purpose | |------|---------| | `NODE_ENUM_DECL` | Enumeration definition | | `NODE_FUNCTION_DECL` | Function (with params, defaults, upvalue list) | | `NODE_VARIABLE_DECL` | Variable/constant declaration group | | `NODE_CLASS_DECL` | Class (with superclass, protocols, ivar counts) | | `NODE_MODULE_DECL` | Module definition | | `NODE_VARIABLE` | Individual variable within a declaration | **Expressions (8):** | Node | Purpose | |------|---------| | `NODE_BINARY_EXPR` | Binary operations | | `NODE_UNARY_EXPR` | Unary operations | | `NODE_FILE_EXPR` | `__FILE__` constant | | `NODE_LIST_EXPR` | Array/map literals | | `NODE_LITERAL_EXPR` | Numbers, strings, booleans | | `NODE_IDENTIFIER_EXPR` | Variable references | | `NODE_KEYWORD_EXPR` | `true`, `false`, `null`, `undefined`, `super` | | `NODE_POSTFIX_EXPR` | Calls, subscripts, property access (with subtypes) | #### Base Node ```c typedef struct { gnode_n tag; // node type discriminant uint32_t refcount; // reference counting for shared nodes uint32_t block_length; // byte length (for autocompletion) gtoken_s token; // source location bool is_assignment; // assignment target flag void *decl; // enclosing declaration } gnode_t; ``` #### Location Tracking After semantic analysis, each identifier is annotated with a resolved location: ```c typedef enum { LOCATION_LOCAL, // local variable LOCATION_GLOBAL, // global variable LOCATION_UPVALUE, // closure upvalue LOCATION_CLASS_IVAR_SAME, // instance variable (same class) LOCATION_CLASS_IVAR_OUTER // instance variable (outer class) } gnode_location_type; typedef struct { gnode_location_type type; uint16_t index; // symbol index uint16_t nup; // upvalue or outer index } gnode_location_t; ``` #### Visitor Pattern ```c typedef struct gvisitor { uint32_t nerr; void *data; // visitor-specific state void *delegate; // error callback delegate // 22 callbacks — one per node type, plus pre/post hooks void (*visit_pre)(visitor, node); void (*visit_post)(visitor, node); void (*visit_list_stmt)(visitor, node); void (*visit_compound_stmt)(visitor, node); void (*visit_function_decl)(visitor, node); // ... one for each AST node type } gvisitor_t; ``` The dispatch function `gvisit()` calls `visit_pre`, then the node-specific callback based on `node->tag`, then `visit_post`. --- ### 2.4 Semantic Analysis — Pass 1 **File:** `src/compiler/gravity_semacheck1.c` The first semantic pass gathers all **non-local declarations** into symbol tables, enabling forward references. It does not perform full name resolution or type checking. #### What It Does 1. Creates symbol tables for each scope (global, class, module, enum). 2. Inserts function, class, enum, module, and variable declarations. 3. Reports duplicate declaration errors. 4. Assigns instance variable indices for class members. 5. Applies name mangling for static class members (prefixed with `"$"`). #### Symbol Table ```c struct symboltable_t { ghash_r *stack; // stack of hash tables (nested scopes) uint16_t count1; // local variable counter uint16_t count2; // instance variable counter uint16_t count3; // static variable counter symtable_tag tag; // GLOBAL, FUNC, CLASS, MODULE, or ENUM }; ``` This pass enables forward references — a function can call another function declared later in the same scope: ```swift func foo() { return bar(); } func bar() { return 42; } ``` --- ### 2.5 Semantic Analysis — Pass 2 **File:** `src/compiler/gravity_semacheck2.c` The second semantic pass validates all identifiers within function bodies, resolves variable references, and detects closure upvalues. #### What It Does 1. Validates all identifier references (reports "undefined variable" errors). 2. Resolves each identifier to its declaration and sets the `location` field. 3. Detects upvalue usage and builds upvalue lists for closures. 4. Validates declaration nesting constraints. 5. Checks `break`/`continue` appear only inside loops. 6. Validates module declarations are at global scope. #### Identifier Lookup Order The lookup traverses the declaration stack from innermost to outermost: 1. Local scope (current compound statement) 2. Enclosing function scopes 3. Enclosing class scopes (including superclass hierarchy) 4. Module scope 5. Global scope #### Declaration Nesting Rules What can be declared inside each construct: ``` │ func var enum class module ------------------------------------------------- func │ YES YES NO YES YES var │ YES NO NO YES YES enum │ YES NO NO YES YES class │ YES NO NO YES YES module │ NO NO NO NO NO ------------------------------------------------- ``` --- ### 2.6 Code Generation (AST → IR) **File:** `src/compiler/gravity_codegen.c` The code generator walks the AST using the visitor pattern and emits IR instructions with virtual registers. It maintains a context stack of functions and classes being compiled. ```c struct codegen_t { gravity_object_r context; // stack of functions/classes gnode_class_r superfix; // superclass resolution stack uint32_t lasterror; // last error line gravity_vm *vm; // mini VM for GC during codegen }; ``` #### Key Responsibilities - **Operator mapping:** Converts token operators to opcodes (e.g., `TOK_OP_ADD` → `ADD`). - **Implicit self:** Inserts self parameter for instance methods. - **Super calls:** Emits `LOADS` instruction for superclass method lookup. - **Collection literals:** `LISTNEW`/`MAPNEW` + `SETLIST` instructions. - **Range literals:** `RANGENEW` with inclusive/exclusive flag. - **String interpolation:** Converts `"text \(expr)"` into string concatenation operations. - **Closures:** `CLOSURE` instruction references the function in the constant pool; `CLOSE` releases upvalues when scope exits. --- ### 2.7 IR Representation **Files:** `src/compiler/gravity_ircode.c`, `src/compiler/gravity_ircode.h` The IR is a flat sequence of instructions with virtual registers, acting as the bridge between the AST and final packed bytecode. #### IR Instruction ```c typedef struct { opcode_t op; // operation code optag_t tag; // metadata tag int32_t p1, p2, p3; // operand parameters union { double d; // embedded float constant (DOUBLE_TAG) int64_t n; // embedded int constant (INT_TAG) }; uint32_t lineno; // source line for debug info } inst_t; ``` #### Instruction Tags | Tag | Meaning | |-----|---------| | `NO_TAG` | Normal instruction | | `INT_TAG` | Carries an embedded integer literal | | `DOUBLE_TAG` | Carries an embedded float literal | | `LABEL_TAG` | Label marker (resolved to offset by optimizer) | | `SKIP_TAG` | Dead instruction (removed by optimizer) | | `RANGE_INCLUDE_TAG` | Inclusive range flag | | `RANGE_EXCLUDE_TAG` | Exclusive range flag | | `PRAGMA_MOVE_OPTIMIZATION` | Hint for move elimination | #### Register Allocation The IR uses a bitmask-based register allocator (256 registers max = 32 bytes of bitmask): - **Local registers** `[0 .. nlocals-1]`: Reserved for parameters and local variables. - **Temp registers** `[nlocals .. 255]`: Allocated/freed for expression evaluation. - Register 0 is always reserved. Key operations: - `ircode_register_push_temp()` — allocate the next free temp register. - `ircode_register_pop()` — free the most recently allocated temp register. - `ircode_register_first_temp_available()` — find first free temp slot. #### Label Management Three separate label stacks manage control flow: - `label_true` — target for true branch of conditionals. - `label_false` — target for false branch. - `label_check` — target for loop checks and safety guards. --- ### 2.8 Optimizer & Bytecode Emission **Files:** `src/compiler/gravity_optimizer.c`, `src/compiler/gravity_optimizer.h` The optimizer is the final compilation stage. It converts IR instructions into packed 32-bit bytecodes, resolves labels, and applies peephole optimizations. #### Optimizations Performed 1. **Constant folding:** Arithmetic on constant operands evaluated at compile time. ``` LOADI r1, 5 ; LOADI r2, 3 ; ADD r0, r1, r2 → LOADI r0, 8 ``` 2. **Dead code elimination:** Unreachable instructions after unconditional jumps/returns are marked `SKIP` and removed. 3. **Move elimination:** Redundant `MOVE` instructions are detected via `PRAGMA_MOVE_OPTIMIZATION` hints and removed when safe. 4. **Label resolution:** Symbolic labels are mapped to concrete instruction offsets. #### 32-Bit Instruction Encoding ``` Standard (3 operands): [ opcode:6 | A:8 | B:8 | C:10 ] LOADI (immediate): [ opcode:6 | A:8 | sign:1 | N:17 ] JUMP (offset): [ opcode:6 | N:26 ] ``` --- ## 3. Runtime Virtual Machine ### 3.1 VM Structure **Files:** `src/runtime/gravity_vm.c`, `src/runtime/gravity_vm.h` The VM is an opaque struct (`gravity_vm`) with the following key components: ```c struct gravity_vm { // Execution gravity_fiber_t *fiber; // current fiber (coroutine) gravity_hash_t *context; // global variable table gravity_delegate_t *delegate; // runtime delegate uint32_t pc; // program counter bool aborted; // runtime error flag // Recursion limits uint32_t maxccalls; // max nested C calls (default: 100) uint32_t nccalls; // current C call depth gravity_int_t maxrecursion;// max recursive depth (0 = unlimited) // Garbage collector int32_t gcenabled; // reference-counted enable flag gravity_object_t *gchead; // linked list of all GC objects gravity_object_r graylist; // mark phase gray list gravity_object_r gctemp; // temporary GC-protected objects gravity_int_t memallocated;// total allocated memory gravity_int_t gcthreshold; // GC trigger threshold (default: 5MB) gravity_int_t gcminthreshold; // minimum threshold (default: 1MB) gravity_float_t gcratio; // threshold growth ratio (default: 0.5) // Callbacks vm_transfer_cb transfer; // object allocation hook vm_cleanup_cb cleanup; // VM cleanup hook vm_filter_cb filter; // selective cleanup filter }; ``` An internal operator name cache (`cache[GRAVITY_VTABLE_SIZE]`) holds pre-computed strings for operator method names (`"+"`, `"-"`, `"*"`, etc.) to avoid repeated allocations during dispatch. --- ### 3.2 Instruction Dispatch **File:** `src/runtime/gravity_vmmacros.h` The VM uses **computed goto** for instruction dispatch (GCC/Clang), falling back to a `switch` statement on MSVC: ```c // Computed goto (GCC/Clang): #define DISPATCH() goto *dispatchTable[OPCODE_GET_OPCODE(*ip)] // Switch fallback (MSVC): #define INTERPRET_LOOP switch (OPCODE_GET_OPCODE(*ip)) #define CASE_CODE(x) case x: ``` Computed goto provides O(1) dispatch with no branch prediction overhead. Each opcode is a label address stored in a static table, and `DISPATCH()` performs an indirect jump. Key macros in the dispatch loop: | Macro | Purpose | |-------|---------| | `OPCODE_GET_OPCODE(inst)` | Extract 6-bit opcode | | `OPCODE_GET_ONE8bit_ONE18bit(inst, A, N)` | Decode register + immediate | | `OPCODE_GET_THREE8bit(inst, A, B, C)` | Decode three register operands | | `LOAD_FRAME()` | Synchronize local variables from fiber state | | `STORE_FRAME()` | Save local variables back to fiber state | | `PUSH_FRAME(closure, stackstart, dest, nargs)` | Create a new call frame | | `FN_COUNTREG(f, nargs)` | Compute register window size: `max(nparams, nargs) + nlocals + ntemps` | --- ### 3.3 Instruction Set The VM implements **56 opcodes** (6-bit opcode field supports up to 64): #### General (5) | Opcode | Description | |--------|-------------| | `RET0` | Return null | | `HALT` | Stop VM execution | | `NOP` | No operation | | `RET` | Return value from register | | `CALL` | Call function/closure | #### Load/Store (13) | Opcode | Semantics | |--------|-----------| | `LOAD` | `R(A) = R(B)[R(C)]` — property access | | `LOADAT` | `R(A) = R(B)[R(C)]` — subscript access | | `LOADS` | Super property access | | `LOADK` | `R(A) = K(Bx)` — load constant from pool | | `LOADG` | `R(A) = G[K(Bx)]` — load global | | `LOADI` | `R(A) = N` — load inline integer | | `LOADU` | `R(A) = U(B)` — load upvalue | | `MOVE` | `R(A) = R(B)` — register copy | | `STORE` | `R(B)[R(C)] = R(A)` — property write | | `STOREAT` | `R(B)[R(C)] = R(A)` — subscript write | | `STOREG` | `G[K(Bx)] = R(A)` — store global | | `STOREU` | `U(B) = R(A)` — store upvalue | #### Jump (2) | Opcode | Semantics | |--------|-----------| | `JUMP` | Unconditional jump (26-bit signed offset) | | `JUMPF` | Jump if false (18-bit signed offset) | #### Arithmetic & Logic (19) | Opcode | Operation | |--------|-----------| | `ADD`, `SUB`, `MUL`, `DIV`, `REM` | Arithmetic | | `AND`, `OR` | Logical and/or | | `LT`, `GT`, `LEQ`, `GEQ` | Ordered comparison | | `EQ`, `NEQ` | Equality | | `EQQ`, `NEQQ` | Strict equality (identity) | | `ISA` | Instance-of check | | `MATCH` | Pattern match (`~=`) | | `NEG`, `NOT` | Unary negation/logical not | #### Bitwise (6) | Opcode | Operation | |--------|-----------| | `LSHIFT`, `RSHIFT` | Bit shifts | | `BAND`, `BOR`, `BXOR` | Bitwise and/or/xor | | `BNOT` | Bitwise complement | #### Collections (4) | Opcode | Semantics | |--------|-----------| | `MAPNEW` | `R(A) = new Map(B)` | | `LISTNEW` | `R(A) = new List(B)` | | `RANGENEW` | `R(A) = new Range(B, C, flag)` | | `SETLIST` | Populate list/map from register range | #### Closures (2) | Opcode | Semantics | |--------|-----------| | `CLOSURE` | Create closure from function constant | | `CLOSE` | Close open upvalues at register level | #### Special (1) | Opcode | Semantics | |--------|-----------| | `CHECK` | Clone struct value (enforces value semantics) | #### Operator Vtable Each class defines operator methods via a vtable indexed by `GRAVITY_VTABLE_INDEX`: ```c typedef enum { GRAVITY_ADD_INDEX, // "+" GRAVITY_SUB_INDEX, // "-" GRAVITY_MUL_INDEX, // "*" GRAVITY_DIV_INDEX, // "/" // ... one for each overloadable operator GRAVITY_EXEC_INDEX // "()" — call } GRAVITY_VTABLE_INDEX; ``` --- ### 3.4 Instruction Encoding All instructions are 32 bits wide with varying field layouts: ``` Standard 3-operand: [ opcode:6 ][ A:8 ][ B:8 ][ C:10 ] Immediate (LOADI): [ opcode:6 ][ A:8 ][ sign:1 ][ N:17 ] Jump (JUMP): [ opcode:6 ][ N:26 ] ``` Operand extraction uses bit shifts and masks: ```c #define OPCODE_GET_OPCODE(v) ((v >> 26) & 0x3F) #define OPCODE_GET_THREE8bit(v, A, B, C) A = (v >> 18) & 0xFF; \ B = (v >> 10) & 0xFF; \ C = v & 0x3FF; #define OPCODE_GET_ONE8bit_ONE18bit(v, A, N) A = (v >> 18) & 0xFF; \ N = v & 0x3FFFF; ``` --- ### 3.5 Stack and Call Frames #### Call Frame ```c typedef struct { uint32_t *ip; // instruction pointer uint32_t dest; // destination register for return value uint16_t nargs; // actual argument count gravity_list_t *args; // implicit _args array (if needed) gravity_closure_t *closure; // closure being executed gravity_value_t *stackstart; // first stack slot of this frame bool outloop; // set when called from gravity_vm_runclosure } gravity_callframe_t; ``` #### Stack Layout Per Frame ``` stackstart[0] = self (implicit first parameter) stackstart[1..n] = explicit parameters stackstart[n+1..m] = local variables stackstart[m+1..p] = temporary values ``` #### Sliding Register Window When a `CALL` instruction executes, the register window for the callee starts at `r2+1` (where `r2` is the callable register). This sliding window design minimizes value copying between frames: ``` Caller: [ ... | self | arg1 | arg2 | ... ] ↑ rwin = r2 + 1 → callee's stackstart Callee: [ self | arg1 | arg2 | locals... | temps... ] ``` The stack grows on demand (power-of-2 reallocation). When the stack is reallocated, all frame pointers are adjusted to maintain consistency. The stack never shrinks. --- ### 3.6 Fiber (Coroutine) Model **Fibers** are Gravity's concurrency primitive. Each fiber has its own stack and call frame array, enabling cooperative multitasking. ```c typedef struct { gravity_class_t *isa; gravity_gc_t gc; // Stack gravity_value_t *stack; // value stack buffer gravity_value_t *stacktop; // current stack pointer uint32_t stackalloc; // allocated capacity // Call frames gravity_callframe_t *frames; // frame buffer uint32_t nframes; // frames in use uint32_t framesalloc; // allocated capacity // Closures gravity_upvalue_t *upvalues; // open upvalue linked list // Status gravity_fiber_status status; // NEVER_EXECUTED, RUNNING, ABORTED, TERMINATED, TRYING char *error; // error message bool trying; // inside try block gravity_fiber_t *caller; // parent fiber gravity_value_t result; // final result // Timing (for yield with timeout) nanotime_t lasttime; gravity_float_t timewait; gravity_float_t elapsedtime; } gravity_fiber_t; ``` Fiber status values: `FIBER_NEVER_EXECUTED`, `FIBER_RUNNING`, `FIBER_ABORTED_WITH_ERROR`, `FIBER_TERMINATED`, `FIBER_TRYING`. --- ### 3.7 Execution Flow #### `gravity_vm_exec` — Main Bytecode Loop ```c bool gravity_vm_exec(gravity_vm *vm) { DECLARE_DISPATCH_TABLE; // Load fiber, frame, function, stackstart, ip, bytecode ... while (1) { INTERPRET_LOOP { CASE_CODE(ADD): { // 1. Decode operands // 2. Check fast path (inline int/float arithmetic) // 3. Fallback: look up "+" method on r2's class // 4. Call method, store result DISPATCH(); } CASE_CODE(CALL): { // 1. Decode: r1=dest, r2=callable, r3=nargs // 2. Compute register window: rwin = r2 + 1 // 3. Resolve closure (directly or via "exec" method) // 4. Push frame, fill defaults for missing args // 5. Dispatch by type: // - NATIVE: PUSH_FRAME, continue loop // - INTERNAL: call C function directly // - BRIDGED: call delegate->bridge_execute DISPATCH(); } CASE_CODE(RET): { // 1. Pop frame // 2. Close open upvalues // 3. If outloop flag → return to gravity_vm_runclosure // 4. Else → continue with caller frame DISPATCH(); } // ... 53 more opcodes } } } ``` #### `gravity_vm_runclosure` — External Entry Point Called from the embedding API or internally to invoke a specific closure: 1. Validate VM is not aborted. 2. Set up stack window and parameters. 3. Dispatch by function type: - **Native:** Increment `nccalls`, call `gravity_vm_exec()`, decrement. - **Internal:** Call C function pointer directly. - **Bridged:** Call delegate `bridge_execute` callback. 4. Restore frame pointers and adjust stack top. --- ### 3.8 Fast-Path Optimizations - **Inline arithmetic:** When both operands are `Int` or `Float`, arithmetic is computed directly without method lookup. - **Jump fusion:** Compare instructions (e.g., `EQ`, `LT`) peek ahead for a following `JUMPF`. If found, the compare and jump are fused into a single operation. - **Register window:** The sliding register window avoids copying arguments between caller and callee. - **Computed goto:** O(1) instruction dispatch with no branch prediction overhead. - **Pre-allocated frames:** Call frames and stack space are pre-allocated and reused. --- ## 4. Value System and Type Hierarchy ### 4.1 Value Representation **Files:** `src/shared/gravity_value.h`, `src/shared/gravity_value.c` Gravity uses a **16-byte tagged union** for all values (not NaN-boxing): ```c typedef struct { gravity_class_t *isa; // 8 bytes: type tag (pointer to class) union { // 8 bytes: payload gravity_int_t n; // integer value gravity_float_t f; // float/double value gravity_object_t *p; // pointer to heap object }; } gravity_value_t; ``` The `isa` pointer serves double duty: it identifies the type and provides the method lookup table. Special sentinel values: - **Null:** `isa = NULL`, `n = 0` - **Undefined:** `isa = NULL`, `n = 1` **Unboxed types** (value stored directly in the union): `Bool`, `Int`, `Float`, `Null`, `Undefined`. **Boxed types** (pointer to heap-allocated object): `String`, `List`, `Map`, `Class`, `Instance`, `Closure`, `Function`, `Range`, `Fiber`, `Upvalue`. --- ### 4.2 Object Header and GC Metadata All heap-allocated objects share a common header: ```c typedef struct gravity_object_s { gravity_class_t *isa; // class pointer (method dispatch) gravity_gc_t gc; // GC metadata } gravity_object_t; typedef struct { bool isdark; // marked during GC bool visited; // prevents double-counting in size calc gravity_object_t *next; // intrusive linked list (GC object chain) gravity_gc_callback free; // destructor callback gravity_gc_callback size; // size reporting callback gravity_gc_callback blacken; // mark-children callback } gravity_gc_t; ``` Every heap object is linked into the VM's GC chain via `gc.next`. The three callbacks (`free`, `size`, `blacken`) implement type-specific GC behavior without virtual dispatch overhead. --- ### 4.3 Built-in Types The runtime registers these built-in classes (in `gravity_core.c`): | Class | Behavior | |-------|----------| | `gravity_class_int` | 64-bit integer, arithmetic operators, bitwise ops | | `gravity_class_float` | IEEE 754 double, arithmetic operators | | `gravity_class_bool` | Boolean, logical operators | | `gravity_class_null` | Null singleton | | `gravity_class_string` | Immutable UTF-8 string, concatenation, methods | | `gravity_class_object` | Base class (all types inherit from this) | | `gravity_class_function` | Function prototype | | `gravity_class_closure` | Closure (function + captured environment) | | `gravity_class_fiber` | Fiber (coroutine) | | `gravity_class_class` | Metaclass | | `gravity_class_instance` | User-defined class instance | | `gravity_class_list` | Dynamic array | | `gravity_class_map` | Hash map | | `gravity_class_range` | Integer range (inclusive or exclusive) | | `gravity_class_upvalue` | Captured variable reference | Each class binds operator methods and instance methods. For example, `gravity_class_int` binds `"+"`, `"-"`, `"*"`, etc. as well as methods like `loop()`, `random()`, and conversion operators. --- ### 4.4 Functions and Closures #### Function Prototype ```c typedef struct { gravity_class_t *isa; gravity_gc_t gc; const char *identifier; // function name uint16_t nparams; // formal parameters (including self) uint16_t nlocals; // local variables uint16_t ntemps; // temporary registers uint16_t nupvalues; // captured variables gravity_exec_type tag; // execution type union { // EXEC_TYPE_NATIVE (compiled Gravity code): struct { gravity_value_r cpool; // constant pool gravity_value_r pvalue; // default parameter values gravity_value_r pname; // parameter names uint32_t ninsts; // instruction count uint32_t *bytecode; // packed 32-bit instructions uint32_t *lineno; // line number mapping (debug) bool useargs; // needs implicit _args array }; // EXEC_TYPE_INTERNAL (C callback): gravity_c_internal internal; // bool (*)(vm, args, nargs, rindex) // EXEC_TYPE_SPECIAL (computed property): struct { uint16_t index; // property index void *special[2]; // [0]=getter, [1]=setter }; }; } gravity_function_t; ``` Execution types: - `EXEC_TYPE_NATIVE` — compiled Gravity bytecode. - `EXEC_TYPE_INTERNAL` — C function callback with signature `bool (*)(gravity_vm*, gravity_value_t*, uint16_t, uint32_t)`. - `EXEC_TYPE_BRIDGED` — external bridge, executed via delegate callback. - `EXEC_TYPE_SPECIAL` — getter/setter computed property. #### Closure ```c typedef struct { gravity_class_t *isa; gravity_gc_t gc; gravity_vm *vm; // owning VM gravity_function_t *f; // function prototype (shared) gravity_object_t *context; // captured self reference gravity_upvalue_t **upvalue; // captured upvalue array uint32_t refcount; // bridge reference counting } gravity_closure_t; ``` Multiple closures can share the same function prototype while having different captured environments. --- ### 4.5 Upvalues Upvalues implement Lua-style **open/closed** variable capture: ```c typedef struct upvalue_s { gravity_class_t *isa; gravity_gc_t gc; gravity_value_t *value; // points to stack slot (open) or self->closed (closed) gravity_value_t closed; // storage when variable leaves scope struct upvalue_s *next; // linked list (ordered by stack position) } gravity_upvalue_t; ``` - **Open upvalue:** `value` points to a live stack slot. The fiber maintains a linked list of open upvalues ordered by descending stack address. - **Closed upvalue:** When the enclosing function returns, the captured value is copied from the stack into `closed`, and `value` is repointed to `&self->closed`. The `CLOSE` instruction walks the open upvalue list and closes any upvalues at or above a given register level. --- ### 4.6 Classes and Instances #### Class ```c typedef struct { gravity_class_t *isa; // metaclass gravity_gc_t gc; gravity_class_t *objclass; // metaclass reference const char *identifier; // class name bool has_outer; // has outer class ivar bool is_struct; // value semantics (copy on assignment) bool is_inited; // metaclass initialized void *xdata; // bridge extension data gravity_class_t *superclass; // parent class const char *superlook; // extern superclass name (lazy binding) gravity_hash_t *htable; // method/property hash table uint32_t nivars; // instance variable count gravity_value_r inames; // ivar names (debug) gravity_value_t *ivars; // static (class) variables } gravity_class_t; ``` Method resolution traverses the superclass chain. Methods and computed properties are stored in the class hash table. #### Instance ```c typedef struct { gravity_class_t *isa; gravity_gc_t gc; gravity_class_t *objclass; // actual class void *xdata; // bridge extension data gravity_value_t *ivars; // instance variable array (indexed by position) } gravity_instance_t; ``` Instance variables are stored in a flat array indexed by position (set during semacheck1), providing O(1) access. --- ## 5. Garbage Collector **Location:** `src/runtime/gravity_vm.c` Gravity uses a **tri-color mark-and-sweep** garbage collector. ### Mark Phase 1. Mark temporary protected objects (in `vm->gctemp`). 2. Mark the current fiber as a root. 3. Mark all globals in the context hash table. 4. Process the gray list: for each gray object, call its `blacken` callback to mark all referenced objects. 5. Repeat until the gray list is empty. ### Sweep Phase 1. Walk the `vm->gchead` linked list. 2. For each object **not** marked (`!isdark`): call its `free` callback and remove it from the chain. 3. For each marked object: clear the `isdark` flag for the next cycle. ### GC Triggers - **Automatic:** When `memallocated >= gcthreshold` during `gravity_gc_transfer` (object allocation). - **Manual:** `gravity_gc_start(vm)`. - **Stress test:** Every allocation (when compiled with `GRAVITY_GC_STRESSTEST`). ### Dynamic Threshold Adjustment After each collection: ``` new_threshold = memallocated + (memallocated * gcratio / 100) if (new_threshold < minthreshold) new_threshold = minthreshold if (new_threshold < original) new_threshold = original ``` Default values: `gcthreshold = 5MB`, `gcminthreshold = 1MB`, `gcratio = 0.5 (50%)`. ### GC-Safe Coding Pattern The enable flag is reference-counted, allowing nested disable/enable calls: ```c gravity_gc_setenabled(vm, false); // disable GC (increments counter) // ... allocate objects safely ... gravity_gc_setenabled(vm, true); // re-enable (decrements counter) ``` Temporary objects can be protected from collection: ```c gravity_gc_temppush(vm, object); // protect // ... use object ... gravity_gc_temppop(vm); // unprotect ``` --- ## 6. Core Data Structures ### 6.1 Hash Table **Files:** `src/shared/gravity_hash.c`, `src/shared/gravity_hash.h` A chained hash table used for symbol tables, class method lookup, global variables, and the `Map` type. ```c typedef struct hash_node_s { uint32_t hash; // cached hash value gravity_value_t key; gravity_value_t value; struct hash_node_s *next; // collision chain } hash_node_t; struct gravity_hash_t { uint32_t size; // bucket count uint32_t count; // entry count hash_node_t **nodes; // bucket array gravity_hash_compute_fn compute_fn; // hash function gravity_hash_isequal_fn isequal_fn; // equality function gravity_hash_iterate_fn free_fn; // entry cleanup callback void *data; // callback context }; ``` | Property | Value | |----------|-------| | Hash function | Murmur3-32 (seed 5381) | | Collision resolution | Chaining (linked list per bucket) | | Load factor | 0.75 | | Growth strategy | Double bucket count on resize | | Initial size | 32 buckets | | Max entries | 2^30 | Hash function variants: `gravity_hash_compute_buffer()` for strings, `gravity_hash_compute_int()` for integers, `gravity_hash_compute_float()` for floats. --- ### 6.2 Dynamic Array **File:** `src/shared/gravity_array.h` A macro-based generic dynamic array: ```c #define marray_t(type) struct { size_t n, m; type *p; } // count capacity data ``` | Macro | Purpose | |-------|---------| | `marray_init(v)` | Initialize to zero | | `marray_push(T, v, x)` | Append (doubles capacity if needed) | | `marray_pop(v)` | Remove and return last element | | `marray_get(v, i)` | Access element by index | | `marray_size(v)` | Current element count | | `marray_max(v)` | Current capacity | | `marray_resize(T, v, n)` | Extend capacity to at least `n` | | `marray_destroy(v)` | Free backing memory | Growth strategy: double capacity on each reallocation. --- ### 6.3 Memory Management **Files:** `src/shared/gravity_memory.h`, `src/shared/gravity_memory.c` Production mode provides thin wrappers around `malloc`/`realloc`/`free` with max block size enforcement (`MAX_MEMORY_BLOCK = 150MB`). Debug mode (`GRAVITY_MEMORY_DEBUG`) adds: - Tracking of every allocation with call stack. - Detection of double-free and use-after-free. - Leak reporting on shutdown. All allocations go through `mem_alloc()`, which integrates with the VM's `memallocated` counter for GC threshold tracking. --- ## 7. Optional Modules ### 7.1 Registration Pattern **File:** `src/optionals/gravity_optionals.h` Each optional module follows the same pattern: 1. Compile-time guard (`#ifndef GRAVITY_INCLUDE_MATH` / `#define GRAVITY_INCLUDE_MATH`). 2. Macro wrappers that become no-ops when disabled. 3. Singleton class with reference counting. 4. Static methods bound to the metaclass. 5. Registration: `gravity_vm_setvalue(vm, name, class)`. ```c // Typical module lifecycle: static gravity_class_t *gravity_class_math = NULL; static uint32_t refcount = 0; void gravity_math_register(gravity_vm *vm) { if (!gravity_class_math) create_optional_class(); ++refcount; gravity_vm_setvalue(vm, "Math", VALUE_FROM_OBJECT(gravity_class_math)); } void gravity_math_free(void) { if (--refcount) return; // wait for all VMs to unregister // destroy class ... } ``` Computed properties (read-only constants) use a getter-only closure: ```c gravity_closure_t *closure = computed_property_create(NULL, NEW_FUNCTION(getter), NULL); gravity_class_bind(meta, "PI", VALUE_FROM_OBJECT(closure)); ``` --- ### 7.2 Math Module **File:** `src/optionals/gravity_opt_math.c` — Class name: `"Math"` **Methods (23):** | Category | Functions | |----------|-----------| | Trigonometric | `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2` | | Rounding | `ceil`, `floor`, `round` (with optional precision) | | Logarithmic | `log`, `log10`, `logx` (custom base) | | Algebraic | `abs`, `sqrt`, `cbrt`, `xrt` (nth root), `pow`, `exp` | | Combinatorial | `gcf`, `lcm` | | Interpolation | `lerp` | | Comparison | `min`, `max` (variadic) | | Random | `random()`, `random(max)`, `random(min, max)` | **Constants (8):** `PI`, `E`, `LN2`, `LN10`, `LOG2E`, `LOG10E`, `SQRT2`, `SQRT1_2`. Random number generator: LFSR258 (64-bit) or LFSR113 (32-bit), seeded with `nanotime()` on first call. --- ### 7.3 File Module **File:** `src/optionals/gravity_opt_file.c` — Class name: `"File"` Uses a custom `gravity_file_t` struct wrapping a `FILE*` pointer, with GC integration for automatic cleanup. **Class (static) methods:** `size`, `exists`, `delete`, `read`, `write`, `buildpath`, `is_directory`, `directory_create`, `directory_scan`. **Instance methods:** `open` (factory), `read`, `write`, `seek`, `eof`, `error`, `flush`, `close`. The `directory_scan` method accepts a closure callback invoked for each entry with `(filename, fullpath, isdir)`. --- ### 7.4 JSON Module **File:** `src/optionals/gravity_opt_json.c` — Class name: `"JSON"` Two static methods: - `stringify(value)` — Serialize any Gravity value to a JSON string. Handles nested structures, escapes special characters, uses heap allocation for strings >4KB. - `parse(jsonString)` — Deserialize a JSON string into nested Gravity lists and maps. Returns `null` for invalid JSON. --- ### 7.5 ENV Module **File:** `src/optionals/gravity_opt_env.c` — Class name: `"ENV"` **Methods:** `get(key)`, `set(key, value)`, `keys()`. **Properties:** `argc` (read-only), `argv` (read-only list). Supports map-access syntax: `ENV["PATH"]` via overloaded load/store-at handlers. Cross-platform: uses `_putenv_s` on Windows, `setenv` on Unix. --- ## 8. Embedding API ### 8.1 Compiler API **File:** `src/compiler/gravity_compiler.h` ```c gravity_compiler_t *gravity_compiler_create(gravity_delegate_t *delegate); gravity_closure_t *gravity_compiler_run(compiler, source, len, fileid, is_static, add_debug); gnode_t *gravity_compiler_ast(compiler); void gravity_compiler_transfer(compiler, vm); // move objects to VM's GC void gravity_compiler_free(compiler); ``` Serialization for ahead-of-time compilation: ```c json_t *gravity_compiler_serialize(compiler, closure); bool gravity_compiler_serialize_infile(compiler, closure, path); ``` --- ### 8.2 VM API **File:** `src/runtime/gravity_vm.h` ```c // Lifecycle gravity_vm *gravity_vm_new(gravity_delegate_t *delegate); gravity_vm *gravity_vm_newmini(void); // lightweight (no optionals) void gravity_vm_free(vm); void gravity_vm_reset(vm); // Execution bool gravity_vm_runmain(vm, closure); bool gravity_vm_runclosure(vm, closure, sender, params, nparams); gravity_value_t gravity_vm_result(vm); // Globals void gravity_vm_setvalue(vm, key, value); gravity_value_t gravity_vm_getvalue(vm, key, keylen); gravity_value_t gravity_vm_lookup(vm, key); // Memory & GC void gravity_vm_transfer(vm, object); void gravity_gc_start(vm); void gravity_gc_setenabled(vm, enabled); void gravity_gc_setvalues(vm, threshold, minthreshold, ratio); // Bytecode loading gravity_closure_t *gravity_vm_loadfile(vm, path); gravity_closure_t *gravity_vm_loadbuffer(vm, buffer, len); // Optional modules void gravity_opt_register(vm); void gravity_opt_free(void); ``` --- ### 8.3 Delegate Pattern **File:** `src/shared/gravity_delegate.h` The delegate is a struct of function pointers used for all communication between the compiler/VM and the host application: ```c typedef struct { // Error handling gravity_error_callback error_callback; // syntax, semantic, runtime errors // Compiler hooks gravity_loadfile_callback loadfile_callback; // resolve import paths gravity_filename_callback filename_callback; // map fileid → filename gravity_precode_callback precode_callback; // inject code at parse time gravity_parser_callback parser_callback; // syntax highlighting hook gravity_type_callback type_callback; // bind type annotations // Logging gravity_log_callback log_callback; gravity_log_clear log_clear; // Bridge (C interop) gravity_bridge_initinstance bridge_initinstance; gravity_bridge_execute bridge_execute; gravity_bridge_blacken bridge_blacken; gravity_bridge_equals bridge_equals; gravity_bridge_clone bridge_clone; gravity_bridge_size bridge_size; gravity_bridge_free bridge_free; gravity_bridge_getvalue bridge_getvalue; gravity_bridge_setvalue bridge_setvalue; // Testing gravity_unittest_callback unittest_callback; } gravity_delegate_t; ``` Error types: `GRAVITY_ERROR_SYNTAX`, `GRAVITY_ERROR_SEMANTIC`, `GRAVITY_ERROR_RUNTIME`, `GRAVITY_ERROR_IO`, `GRAVITY_WARNING`. --- ### 8.4 Bridging Gravity supports binding external (C, Objective-C, Swift) objects through the bridge delegate callbacks: - `EXEC_TYPE_BRIDGED` functions are dispatched via `delegate->bridge_execute`. - Instance creation goes through `delegate->bridge_initinstance`. - Property access uses `bridge_getvalue` / `bridge_setvalue`. - Objects store host-side data in the `xdata` pointer present on classes, instances, and functions. #### Typical Embedding Usage ```c // 1. Create compiler gravity_delegate_t delegate = {.error_callback = report_error}; gravity_compiler_t *compiler = gravity_compiler_create(&delegate); // 2. Compile gravity_closure_t *closure = gravity_compiler_run( compiler, source, strlen(source), 0, true, true); // 3. Create VM and transfer ownership gravity_vm *vm = gravity_vm_new(&delegate); gravity_compiler_transfer(compiler, vm); gravity_compiler_free(compiler); // 4. Execute if (gravity_vm_runmain(vm, closure)) { gravity_value_t result = gravity_vm_result(vm); // ... use result ... } // 5. Cleanup gravity_vm_free(vm); gravity_core_free(); ``` --- ## 9. Utilities ### Debug / Disassembler **Files:** `src/utils/gravity_debug.c`, `src/utils/gravity_debug.h` - `opcode_name(opcode_t)` — maps opcode enum to mnemonic string. - `opcode_constname(int)` — maps constant pool indices to names (`SUPER`, `NULL`, `UNDEFINED`, `TRUE`, `FALSE`, etc.). - `gravity_disassemble()` — full bytecode disassembler; outputs human-readable assembly with line numbers and decoded operands. ### JSON Serialization **Files:** `src/utils/gravity_json.c`, `src/utils/gravity_json.h` Two components: - **Serializer:** `json_t` object with hierarchical `json_add_*()`, `json_begin/end_array()`, `json_begin/end_object()` functions. Used by the compiler to serialize bytecode to JSON. - **Parser:** Third-party JSON parser (`json_parse()`) that produces a `json_value` tree. Used by `gravity_vm_loadfile` to deserialize compiled bytecode. ### File I/O and Platform Utilities **Files:** `src/utils/gravity_utils.c`, `src/utils/gravity_utils.h` - High-resolution timer: `nanotime()` (platform-specific: `mach_absolute_time` on macOS, `clock_gettime` on Linux, `QueryPerformanceCounter` on Windows). - File operations: `file_read`, `file_write`, `file_exists`, `file_delete`, `file_size`, `file_buildpath`. - Directory operations: `directory_create`, `directory_init`, `directory_read`, `is_directory`. - String utilities: `string_dup`, `string_replace`, `string_reverse`. - UTF-8: `utf8_charbytes`, `utf8_encode`, `utf8_len`, `utf8_nbytes`, `utf8_reverse`. - Number parsing: `number_from_bin`, `number_from_hex`, `number_from_oct`. --- ## 10. CLI **File:** `src/cli/gravity.c` ### Operation Modes | Flag | Mode | Description | |------|------|-------------| | *(filename)* | `OP_COMPILE_RUN` | Compile and execute in one pass | | `-c file` | `OP_COMPILE` | Compile to bytecode file (default: `gravity.json`) | | `-x file` | `OP_RUN` | Execute precompiled JSON bytecode | | `-i 'code'` | `OP_INLINE_RUN` | Compile and execute inline string (wrapped in `func main() { ... }`) | | `-t folder` | `OP_UNITTEST` | Run unit tests recursively | | `-o file` | — | Specify output filename | | `-q` | — | Quiet mode (suppress result and timing) | The CLI sets up a `gravity_delegate_t` with `error_callback` and `loadfile_callback` (for `import` resolution), then drives the compiler and VM through the standard embedding API. --- ## 11. Build System **File:** `Makefile` ### Targets | Target | Output | |--------|--------| | `make` | `gravity` CLI executable | | `make mode=debug` | Debug build (`-g -O0 -DDEBUG`) | | `make lib` | Shared library (`libgravity.dylib` / `.so` / `.dll`) | | `make example` | C embedding API example | | `make clean` | Remove all build artifacts | ### Compiler Flags ``` -std=gnu99 -fgnu89-inline -fPIC -DBUILD_GRAVITY_API -O2 (release) -g -O0 -DDEBUG (debug) ``` ### Platform Detection - **macOS:** `libgravity.dylib` - **Linux/BSD:** `libgravity.so`, links `-lm` - **Windows:** `gravity.dll`, links `Shlwapi` ### Dependencies - C99-compatible compiler - Standard C library (including `math.h`) - Platform headers (`dirent.h`, `sys/time.h`, or Windows equivalents) - No external library dependencies --- ## 12. Test Infrastructure ### Test Format Unit tests are individual `.gravity` files in `test/unittest/`. Each test declares expected results in a metadata block: ```swift #unittest { name: "Test description"; result: expected_value; }; func main() { // test logic return actual_value; } ``` The test runner compiles and executes each file, then compares the return value of `main()` against the declared `result`. ### Test Metadata Fields | Field | Purpose | |-------|---------| | `name` | Human-readable test description | | `result` | Expected return value (compared with `==`) | | `expected_error` | Expected error type (for negative tests) | | `expected_row` | Expected error line number | | `expected_col` | Expected error column number | ### Running Tests ```bash ./gravity -t test/unittest/ # run all tests ./test/unittest/run_all.sh # run with timeouts (used by CI) ./gravity test/unittest/test_file.gravity # run a single test ``` ### Test Organization Tests are organized by category in subdirectories: compiler phases, language features, built-in types, optional modules, edge cases, and bug regressions. The runner recursively scans the target directory, skips any `/disabled/` subdirectories, and applies fuzzy comparison for tests under `/fuzzy/`. CI runs: `make && test/unittest/run_all.sh` --- ### CHANGELOG # Changelog All notable changes to Gravity are documented in this file. ## [Unreleased] ### Fixed - **Wrong format specifier in a codegen error message** — `report_error(..., "Invalid argument expression at index %d.", j+1)` passed a `size_t` to a `%d` conversion, which reads only 32 bits of a 64-bit argument: undefined behaviour in a variadic call. The three `report_error` helpers now carry `__attribute__((format(printf, ...)))` on gcc and clang, so the compiler type-checks every call site and this class of mistake fails the build instead of needing an external analyser to spot it. - **Heap buffer overflow in `list_storeat` when growing the list fails** — storing past the end of a list reallocates the backing array, but `marray_resize` leaves both the pointer and the capacity untouched when the `realloc` fails, so the existing `if (!list->array.p)` guard never fired: the old, smaller buffer is still there and still non-NULL. The count was then set to the requested index and the fill loop wrote well past the end of the allocation. The check now tests the capacity actually obtained, and the out-of-memory case is reported as `Not enough memory to resize List.` as intended. Reachable from a script: `x[4444444444444444444] = 0` asks for a single multi-gigabyte allocation. ### Removed - The CodeQL workflow. It ran on `github/codeql-action@v1`, deprecated since January 2023 and no longer updated, so it kept reporting green without being a current analysis. Its one open finding is fixed above, and the compiler now checks that class directly. The `build-and-test` workflow, including its address + undefined sanitizer job, is unaffected. ### Changed - The sanitizer CI job caps a single allocation at 1 GB (`max_allocation_size_mb`) so the pathological allocations in `test/fuzzy` fail cleanly instead of pushing the runner into the OOM killer, and pins `abort_on_error` so a sanitizer finding arrives as a signal on every platform rather than as the bare exit code 1 the runtime defaults to on Linux. The fuzzing step also scans the output for sanitizer reports, which the exit code alone does not reliably convey. --- ## [0.9.8] - 2026-08-05 Security and memory-safety release. Every issue below was found by external reporters fuzzing the compiler and the bytecode loader, and each fix ships with a regression test. ### Fixed - **NULL dereference in `gravity_vm_loadbuffer`** — a serialized function object without an `identifier` field, such as `{"x":{"type":"function"}}`, reached `strlen(NULL)` and crashed the process. The loader now validates the structure of every JSON executable before using it: the root and each entry must be objects, the identifier must be present exactly once and be a string, and unknown object types are rejected. Malformed input is reported as a load error instead of crashing (issue #444). - **Signed 64-bit integer overflow in `json_parse_ex`** — the integer and exponent accumulators multiplied by 10 per digit with no range check, so any literal longer than 19 significant digits overflowed. Signed overflow is undefined behaviour: the parser stored a wrapped value, and builds compiled with `-fsanitize=undefined` trapped with SIGILL. Both accumulators are now range-checked and over-long literals are rejected (issue #447). - **Pointer-arithmetic overflow in the JSON scan loop** — `for (state.ptr = json; ; ++state.ptr)` incremented unconditionally, so input that ended while the scanner was still inside a string or comment advanced the pointer past one-past-the-end, which is undefined behaviour. The loop now stops at the end of the buffer regardless of scanner state (issue #448). - **Heap out-of-bounds read in `parse_number_expression`** — the `0x`/`0b`/`0o` prefix check read `value[1]` without confirming the token was at least two bytes, so a source file whose last token was a bare `0` read one byte past the buffer. The prefix is only inspected when the token is long enough (issue #446). - **Compiler crash (SIGFPE) folding a floating-point remainder** — `optimize_const_instruction` folded `%` by truncating both operands to `int64_t`, so any divisor with `0 < |divisor| < 1` became an integer division by zero and killed the compiler on `1 % 0.5`. Float remainder is now folded with `remainder()`, matching `operator_float_rem`, and mixed Int/Float remainders are left to the runtime because REM dispatches on the class of the left operand. This also fixes a silent wrong answer: `5.5 % 2.0` folded to `1` where the VM evaluates `-0.5` (issue #443). - **Undefined behaviour in Int arithmetic** — Gravity Ints wrap on overflow, but the wrap was performed on signed operands in the VM fast path, in the `operator_int_*` methods and in the constant folder, which is undefined in C and traps under `-fsanitize=undefined`. All three paths now go through new `GRAVITY_INT_ADD/SUB/MUL/NEG/DIV/REM` helpers that compute on the unsigned counterpart. The helpers also handle `GRAVITY_INT_MIN op -1`, which on x86 faults in `idiv` rather than merely wrapping (issue #443). - **Optional classes never released** — `gravity_core_free` decremented the refcount of the optional classes without the matching balance, so `Math`, `File`, `JSON` and `ENV` were leaked by every embedder that created and destroyed a VM (issue #442). - **Core reference leaked by every `gravity_compiler_run`** — the compiler took a reference to the core classes on each run and never released it, so the count never returned to zero and the core was never torn down (issue #442). - **Double free of the inline source buffer** — `gravity -i` passed its heap-allocated wrapper source to `gravity_compiler_run` with `is_static` false, which hands the buffer to the lexer; the lexer freed it in `parser_run` and the CLI freed the same pointer again on the way out, aborting every inline run under a hardened allocator. ### Added - `test/loadbuffer/` — a suite of malformed JSON executables that must each be rejected as a load error without crashing, plus `json_bounds.c` (`make jsontest`), 60 bounds checks that drive the JSON scanner directly. Run with `test/loadbuffer/run_all.sh`. - A GitHub Actions workflow building with gcc and clang on Linux and macOS, and a second job that builds with `-fsanitize=address,undefined` and runs the unit tests, the fuzzing corpus and the loader tests through it. ### Changed - Version bumped to **0.9.8** (`GRAVITY_VERSION`, `GRAVITY_VERSION_NUMBER`). - The usage text now prints the real default output file name, `gravity.g`; `README.md` and `CLAUDE.md` documented a stale `gravity.json`. --- ## [0.9.7] - 2026-04-14 ### Fixed - **Float precision loss in JSON bytecode serialization** — float constants were written with `%f` (6 decimal places), silently rounding small values like `-0.000000004` to zero and causing `RUNTIME ERROR: Unknown LOADK index` on the `-c`/`-x` (compile + execute bytecode) path. Switched to `%.17g` for full IEEE 754 double round-trip precision (issue #420). - **Float constant deduplication in cpool** — `gravity_function_cpool_add` used the epsilon-based `gravity_value_equals` (EPSILON = 1e-6) to detect duplicate constants, incorrectly merging distinct small floats into a single pool entry. The cpool now uses exact bit-level comparison for float values (issue #420). - **`gravity_optionals.h` unconditionally defined all optional-module guards** — the `#ifndef GRAVITY_INCLUDE_*` blocks always defined every guard, making it impossible to exclude modules at compile time. The guards are now left undefined by default; embedders define only the modules they need. The Gravity CLI and runtime define all four (issue #426). - **Makefile dependency errors** — four issues: `gravity` and `example` were incorrectly listed as `.PHONY` targets (causing unconditional rebuilds); `lib` depended on the `gravity` executable instead of just `$(OBJ)`; `gravity.c` and `example.c` were compiled only during the link step so `-MMD` never generated `.d` header-dependency files for them; `make clean` did not remove `libgravity.dylib` on macOS (issue #413). - **`run_all.sh` portability** — the test runner used GNU `timeout` which is not available on macOS. The script now auto-detects `timeout`, `gtimeout` (Homebrew coreutils), or falls back to a pure-bash kill-watcher. ### Changed - Version bumped to **0.9.7** (`GRAVITY_VERSION`, `GRAVITY_VERSION_NUMBER`). --- ## [0.9.6] - 2026-04-14 ### Fixed - **Stack overflow now produces a clean runtime error** instead of a hard crash (SIGSEGV). Infinite recursion and pathological call depths are caught by a configurable stack size limit before the process runs out of memory. - **Class `$init` chain infinite recursion** — parent-class `$init` helpers (`$init2`, `$init3`, …) previously used a dynamic name lookup against `self`, which resolved to the wrong (overriding) function when called from a subclass, producing infinite recursion. The compiler now emits a direct static closure reference (`LOADK`) so dispatch is always to the correct ancestor function. - **Fiber stack growth in `gravity_fiber_reassign`** — a large register-window allocation in `$moduleinit` could cause the initial stack pointer to overshoot `DEFAULT_MINSTACK_SIZE` (256 slots), leaving `stacktop` pointing into unallocated memory (issue #437). - **`gravity_opt_free` double-free** — optional module cleanup now checks the reference count before freeing (PR #436). ### Added - `GRAVITY_VM_MAXSTACK` — runtime-configurable maximum fiber stack size (default 1 048 576 slots / 16 MB). Readable and writable via `gravity_vm_get` / `gravity_vm_set` with key `"maxStack"`. - Re-enabled two previously disabled tests (`heap.gravity`, `loop1.gravity`) — both now pass with the new OOM error reporting. ### Changed - Version bumped to **0.9.6** (`GRAVITY_VERSION`, `GRAVITY_VERSION_NUMBER`). --- ## [0.9.5] - 2024 ### Fixed - Numerous memory leaks and use-after-free errors throughout the compiler and runtime. - Memory safety improvements across GC, value handling, and object lifecycle. - Clang build compatibility (PR #435). ### Changed - Documentation updates: ARCHITECTURE.md rewritten; README refreshed. --- ## [0.9.0] - 2023 ### Fixed - Several memory leaks plugged across the compiler pipeline. - Missing Makefile dependencies (PR #431). - `File.read()` now returns `null` when zero characters are read. - Replaced unsafe `printf` calls with `snprintf`. - Fixed issue #394. ### Added - New unit test for leak-related regression coverage. --- ## [0.8.5] - 2022 ### Fixed - Setter issue that affected several unit tests. - File read size mismatch due to line-ending differences on Windows (PR #365). - Compilation failure introduced by `O_BINARY` on non-Windows platforms. ### Added - Unit tests integrated into CI (PR #378). - BSD shared-object build support; removed `WITH_GETLINE` (PR #375). --- ## [0.8.4] - 2022 ### Fixed - Issue #379. - Hash table header organisation (PR #369). --- ## [0.8.3] - 2021 ### Fixed - Regression introduced by lazy-loading of superclasses in 0.8.2. --- ## [0.8.2] - 2021 ### Added - Lazy loading of extern superclasses at runtime. - Preliminary support for instance `deinit` (destructor). - `System.input()` (PR #342). - `ENV.argc` / `ENV.argv` properties (PR #343). - ObjC binding example. - C++ binding example. - Ternary expression and `switch` statement codegen (PR #336). - Optional `File` class (cross-platform). - `gravity_instance_lookup_real_property` helper. - `gravity_config.h` for platform-specific configuration (PR #301). - Improved CMake: supports CLI, shared lib, and static lib targets (PR #299). ### Fixed - Inner class constructor returning wrong instance. - Computed property (setter) bug. - Sign-conversion and char-type warnings flagged by sanitizers. - `stat` return-value check. - `uint32_t`-to-`char` conversion in `utf8_encode`. - `size_t` comparison against negative value in `file_read`. - Various Windows / MSVC compatibility fixes. - Implicit `long`-to-`double` conversion warning under Clang. - Emscripten include-guard fix. ### Changed - Improved superclass type checking in the semantic analyser. - Improved error handling and detection (0.8.0). - `File.eof` renamed from `isEOF`. --- ## [0.7.9] - 2020 ### Fixed - Improved error handling in the VM and runtime. ### Added - `vm` back-reference stored on `gravity_closure_t`. - Computed-goto support for Clang on Windows. - `DISPATCH_INNER` macro for `do/while(0)` loops without computed goto. - `xdata` parameter on `delegate->optional_classes` (PR #307). --- ## [0.7.8] - 2020 ### Added - Preliminary `Struct` support. - `bind` method fix; unit test added. ### Fixed - Possible GC issue (unit test added). --- ## [0.7.7] - 2020 ### Fixed - `super` keyword resolution issue; unit test added. --- ## [0.7.6] - 2020 ### Changed - Optional classes renamed for consistency. ### Fixed - Setter unwanted side effect. --- ## [0.7.5] - 2020 ### Improved - `float`/`double` to `String` conversion accuracy. - Various core methods. --- ## [0.7.4] - 2020 ### Fixed - `String.length` is now UTF-8 aware; `String.bytes` added (unit test added). - Function returning address of local variable on Windows (`directory_read`). - Division-by-zero warning suppression in GCC. - Const output-buffer issue on Windows (`WideCharToMultiByte`). ### Added - More BSD targets in `make` and CMake. --- ## [0.7.0] - 2019 ### Added - `String.split` and string iteration are now Unicode-aware; unit test added. - Support for local `enum` declarations; unit test added. ### Fixed - Local class declarations. - Superclass resolution edge cases. - `continue` keyword inside `for` loops. - `self` parameter in complex postfix expressions. - Comparison between different object types no longer raises a spurious runtime error. --- ## [0.6.x] - 2018–2019 Series of incremental releases adding language features (closures, ranges, maps, lists, optional modules) and fixing compiler and runtime issues. See git history for per-commit details. --- ## [0.5.x] - 2017–2018 Initial public release series establishing the core language, VM, and compiler pipeline. --- ### CONTRIBUTING #### Contributing to Gravity Everyone is welcome to contribute to Gravity. Contributing doesn’t just mean submitting pull requests, there are many different ways for you to get involved, including reporting bugs, add new functionalities or just participating in the project. #### Where to make changes Core libraries and documentation is probably the areas that need major contribution. Any change to the core VM or other parts of the compiler are welcomed. #### Code style and syntax rules Looking at the Gravity source code I think you can easily follow the same coding style and syntax rule. Private functions usually don't begin with a gravity_ prefix and are marked as static. Feel free to expand this section with a more formal description of the syntax rules. #### Testing rules If you fix a bug or if you add a new functionality then a unit-test is required. A unit-test is a single source code file that is able to run under the unittest executable file. You are free to test your functionalities/fixes into a single unit-test file or split the test in more files. #### Add Docs If you add new functionality, or edit the way that current functionality works, add or edit the docs to reflect this so that there is documentation of the new changes for new users. #### Contributors File Don't forget to add your name and your email address to the official **CONTRIBUTORS** file! --- ### README

Gravity Programming Language

**Gravity** is a powerful, dynamically typed, lightweight, embeddable programming language written in C without any external dependencies (except for stdlib). It is a class-based concurrent scripting language with modern Swift-like syntax. **Gravity** supports procedural programming, object-oriented programming, functional programming, and data-driven programming. Thanks to special built-in methods, it can also be used as a prototype-based programming language. **Gravity** has been developed from scratch for the Creo project in order to offer an easy way to write portable code for the iOS and Android platforms. It is written in portable C code that can be compiled on any platform using a C99 compiler. The VM code is about 6.5K lines long, the multipass compiler code is about 10K lines and the shared code is about 4.7K lines long. The compiler and virtual machine combined add less than 200KB to the executable on a 64-bit system. ## What Gravity code looks like ```swift class Vector { // instance variables var x = 0; var y = 0; var z = 0; // constructor func init (a = 0, b = 0, c = 0) { x = a; y = b; z = c; } // instance method (built-in operator overriding) func + (v) { if (v is Int) return Vector(x+v, y+v, z+v); else if (v is Vector) return Vector(x+v.x, y+v.y, z+v.z); return null; } // instance method (built-in String conversion overriding) func String() { // string interpolation support return "[\(x),\(y),\(z)]"; } } 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 with optimizer * dynamic typing * classes and inheritance * higher-order functions and classes * lexical scoping * coroutines (via fibers) * nested classes * closures * garbage collection (mark-and-sweep) * operator overriding * string interpolation * enums, modules, and structs (value types) * switch/case and ranges * optional modules (Math, File, JSON, ENV) * powerful embedding API with bridging support * built-in unit tests * built-in JSON serializer/deserializer * **optional semicolons** ## Building **Make (Linux / macOS / BSD)** ```bash make # Build the gravity CLI executable make mode=debug # Debug build with symbols make lib # Build shared library (libgravity.dylib/so/dll) make staticlib # Build static library (libgravity.a) make example # Build the C embedding API example make clean # Clean all build artifacts ``` **CMake (cross-platform, including Windows)** ```bash cmake -B build cmake --build build # Optionally disable the CLI and build the library only: cmake -B build -DBUILD_CLI=OFF cmake --build build ``` Requires a C99 compiler. No external dependencies. ## Usage ```bash ./gravity file.gravity # Compile and execute a source file ./gravity -c file.gravity # Compile to bytecode (outputs gravity.g) ./gravity -o out.json -c file.gravity # Compile to a specific output file ./gravity -x gravity.g # Execute precompiled bytecode ./gravity -i 'return 2 + 3' # Execute inline code ./gravity -t test/unittest # Run unit tests ``` ## Testing ```bash ./gravity -t test/unittest # Run all unit tests via the VM ./test/unittest/run_all.sh # Run all unit tests via shell script (with per-test timeouts) ./gravity test/unittest/somefile.gravity # Run a single test file ``` The `test/` directory also contains `fuzzy/` (randomised fuzzing inputs) and `infiniteloop/` (tests that must terminate with a runtime error rather than hang). ## Project Structure ``` src/ ├── cli/ Command-line interface ├── compiler/ Lexer, parser, AST, semantic analysis, IR, optimizer, codegen ├── runtime/ Stack-based VM, built-in types and core methods ├── shared/ Value representation, opcodes, hash table, array, memory/GC ├── optionals/ Optional modules: Math, File, JSON, ENV └── utils/ Debug disassembler, JSON serialization, file I/O, UTF-8 ``` For a comprehensive technical deep-dive into the implementation, see [ARCHITECTURE.md](ARCHITECTURE.md). ## Embedding API Gravity is designed to be embedded inside a host application. The complete API lives in `src/runtime/gravity_vm.h` and `src/compiler/gravity_compiler.h`. A minimal example: ```c #include "gravity_compiler.h" #include "gravity_core.h" #include "gravity_vm.h" static void report_error(gravity_vm *vm, error_type_t type, const char *description, error_desc_t desc, void *xdata) { printf("%s\n", description); } int main(void) { const char *source = "func main() { return 6 * 7; }"; gravity_delegate_t delegate = {.error_callback = report_error}; // compile gravity_compiler_t *compiler = gravity_compiler_create(&delegate); gravity_closure_t *closure = gravity_compiler_run(compiler, source, strlen(source), 0, true, true); // create VM and transfer compiler-owned objects into it gravity_vm *vm = gravity_vm_new(&delegate); gravity_compiler_transfer(compiler, vm); gravity_compiler_free(compiler); // execute and read result if (gravity_vm_runmain(vm, closure)) { gravity_value_t result = gravity_vm_result(vm); gravity_value_dump(vm, result, NULL, 0); // prints: 42 } gravity_vm_free(vm); gravity_core_free(); return 0; } ``` See [`examples/example.c`](examples/example.c) and the [embedding documentation](https://gravity-lang.org) for the full bridging API. ## 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. For implementation internals, see the [Architecture Document](ARCHITECTURE.md). ## 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) ## Changelog See [CHANGELOG.md](CHANGELOG.md) for a summary of changes across versions. ## Community [](https://github.com/marcobambini/gravity/discussions) Questions, ideas, and general discussion are welcome in [GitHub Discussions](https://github.com/marcobambini/gravity/discussions). ## 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 ## License Gravity is available under the permissive MIT license. ---