This repository is a bytecode interpreter that was made for a custom programming language. This implementation is based on the C Lox interpreter from the book Crafting Interpreters.
This language features:
- Complete Control Flow 🔄: Robust control structures for flexible programming.
- User-Defined Functions 🛠️: Ability to create custom functions for code reusability.
- Dynamic Typing 🏷️: Runtime type evaluation for greater coding flexibility.
- Garbage Collection 🗑️: Automated memory management to prevent leaks.
- Cross-Platform Compatibility 💼: Runs seamlessly on various operating systems.
- Exception Handling 🚫: Robust error detection and handling mechanism.
This is how the language looks like:
#ifStatement.ec
say "** If **";
store a = 5;
store check = a == 20;
if (check) {
say "Running true...";
} else {
say "Running false...";
}
- Lexical Analysis (Tokenization) 🔍: This step converts the raw source code into a series of tokens. Each token represents an atomic element of the language, like a keyword, identifier, literal, or operator.
- Parsing 📚: In this phase, the tokens are analyzed to construct an intermediate representation. Unlike interpreters that rely solely on an AST, this interpreter transforms the parsed structures into a dynamically sized array, suitable for the next step.
- Bytecode Generation 🗜️: This crucial step involves translating the parsed representation of the source code into bytecode. Bytecode is a low-level set of instructions that is not human-readable like the source code but is much more efficient for a machine to interpret.
- Bytecode Interpretation 💻: The VM reads the bytecode instructions one by one and executes them. This involves:
- Instruction Dispatch 📡: Decoding the instruction to determine what operation to perform.
- Execution ✅: Performing the operation, which may involve arithmetic calculations, memory access, control flow changes, function calls, etc.
- Runtime Environment Management 🌐: The VM manages the runtime environment which includes:
- Stack 📚: Used for tracking function calls, local variables, and intermediate values.
- Heap 🗃️: For dynamic memory allocation, used by objects, arrays, and other complex data types.
- Global Variable Store 🌍: Where global variables are stored and accessed.
- Dynamic Typing 🔮: The type of variables is determined at runtime rather than at compile-time, allowing for more flexible and less rigid code, but requiring additional type checking during execution to ensure operations are valid for the given types.
- Garbage Collection 🗑️: The VM includes a garbage collector that periodically reclaims memory no longer in use, preventing memory leaks and helping manage the heap efficiently.
- Error Handling
⚠️ : The VM handles errors that occur during the execution of bytecode, such as illegal operations, memory access violations, or unhandled exceptions. It ensures that error messages are generated and that the program is terminated gracefully if necessary.
To build and run the EC Language interpreter, ensure your system has the following:
- C Compiler 🛠️: A modern C compiler like GCC or Clang.
- CMake 🏗️: CMake version 3.x or higher for the build process.
- Make 📈: Used for automating the build process on compatible systems.
With these tools installed, you can proceed to clone the repository, build the project, and explore the EC Language interpreter.
First build this project. Here we use CMake:
$ mkdir build && cd build # Create a CMake workspace
$ cmake .. && make
$ ./eclang # Start eclang
Running Examples 🚀
$ ./eclang ../examples/hello-world.eclang
Hello, world!
- Book: Crafting Interpreters
- Blog post: Crafting "Crafting Interpreters"
- Wiki: [Lox implementations](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations