Creating a programming language sounds fun and challenging, so I decided to try it out. :D
(This repo is just a study, so don't expect it to be a complete programming language)
- Little to no AI involvement, I want to do most of the work myself to learn as much as possible.
- It's interpreted (tree-walking interpreter) and compiled (bytecode + VM): both modes coexist.
- It's named Marselo because my friend suggested it and I liked it. (It's just a name, no special meaning)
- You may find random PT-BR comments/code, because it's my native language and sometimes I write comments in Portuguese to express my thoughts more naturally. (But I'll try to keep it mostly in English for better readability)
- Files use the
.mrsextension. - REPL (Read-Eval-Print Loop) for interactive coding and testing snippets.
- Bytecode serialization for saving compiled code to disk and loading it later, improving startup time, with
--compile - Tracer for debugging:
--traceflag to print executed instructions and their operands during runtime, helping to understand the execution flow and identify issues. (Tiny and basic UI at/tools/inspection/index.html) --
Also, simple tooling and w/e:
- Inside
/editors, you may find a basic syntax highlighting extension for VSCode that I made for learning as well (never done that before). - Formatter (kinda basic, like prettier but for Marselo).
--watchflag for auto-reloading on file changes.
- Tail call optimization (TCO) -> improves performance of recursive functions and prevents stack overflow
- Better error handling -> more informative error messages with line numbers and context
- Try/catch -> exception handling for better error management
- Standard library -> useful built-in functions and modules for common tasks
When will you implement these features? Idk, maybe never? I'm just doing this for fun and learning, so no promises :D
Source Code (.mrs)
↓
Lexer (Tokenizer) → characters → tokens
↓
Parser (Pratt Parsing) → tokens → AST
↓
┌─────────────────────────────────────┐
│ Interpreter → AST → execution (tree-walking)
│ Compiler → AST → bytecode
│ └── VM → bytecode → execution
└─────────────────────────────────────┘
Marselo can be used as an embedded scripting library:
import Marselo from './marselo';
const mrs = new Marselo({ mode: 'vm' }); // or 'interpret'
// Expose host functions to Marselo scripts
mrs.register('double', (n: number) => n * 2);
// Execute code: state persists between calls
mrs.run(`var x = 10;`);
mrs.run(`print(double(x));`); // 20
// Evaluate an expression and get the result back
const result = mrs.eval(`x * 3`); // 30- Numbers (
42,3.14) - Strings (
"hello") - Booleans (
true,false) - Null (
null) - Arrays (
[1, 2, 3]) - Objects / Maps (
{ key: value })
- Arithmetic:
+,-,*,/,% - Comparison:
==,!=,<,>,<=,>= - Logical:
&&,||,! - Nullish coalescing:
?? - Unary:
-x,!x
var x = 10;
var name = "Marselo";
fn add(a, b) {
return a + b;
}
// Functions as values (closures!)
var greet = fn(name) {
return "Hello, " + name;
};
var createCounter = fn() {
var total = 0;
return fn() {
total = total + 1;
return total;
};
};
var counter = createCounter();
print(counter()); // 1
print(counter()); // 2
if (x > 10) {
print("big");
} else {
print("small");
}
while (x > 0) {
x = x - 1;
}
for (var i = 0; i < 10; i = i + 1) {
print(i);
}
var nums = [1, 2, 3];
print(nums[0]); // 1
nums[0] = 99;
print(nums[0]); // 99
var matrix = [[1, 2], [3, 4]];
print(matrix[0][1]); // 2
var person = { name: "Marselo", age: 42 };
print(person["name"]); // Marselo
person["age"] = 43;
import "lib.mrs";
// everything declared in lib.mrs is available here
// imports are hoisted, so order doesn't matter
| Function | Description |
|---|---|
print(...) |
Print values to stdout |
len(arr) |
Return the length of an array or string |
push(arr, val) |
Append a value to an array, return new length |
pop(arr) |
Remove and return the last element of an array |
range(start, end) |
Generate an array of numbers from start to end (exclusive) |
map(arr, fn) |
Apply a function to each element, return new array |
split(str, sep) |
Split a string into an array |
join(arr, sep) |
Join an array into a string |
upper(str) |
Uppercase a string |
lower(str) |
Lowercase a string |
trim(str) |
Trim whitespace from a string |
substring(str, start, end) |
Extract a substring |
input(prompt) |
Read a line from stdin |
num(str) |
Parse a string as a number |
var x = 10;
print(x);
I'm familiar with it :D
Go back to the first section and read it again :D