Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Set up Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.85"
toolchain: "1.87"
components: rustfmt, clippy
- name: Build
run: cargo build --verbose
Expand Down
61 changes: 17 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,56 +89,25 @@ The WASM backend will:
* 🔢 Increment / decrement (`i++`, `i--`)
* 🔧 Functions with parameters, return values, and recursion
* 🧩 Nested function calls (`add(add(1,2), 3)`)
* 📦 Arrays (literals, index read/write, pass to functions)
* 🖨️ Print statements (`print x;`)
* 🔁 REPL (interactive shell)
* 📂 File execution (`rusty run file.rts`)
* ✅ CI pipeline (GitHub Actions)

---

## 🧪 Example

```ts
let x = (2 + 3) * 4;
print "x = " + x;

function max(a, b) {
if (a > b) {
return a;
} else {
return b;
}
}

print "max is " + max(x, 15);

function factorial(n) {
if (n < 2) { return 1; }
return n * factorial(n - 1);
}
print "5! = " + factorial(5);

for (let i = 0; i < 5; i++) {
if (i == 2) { continue; }
if (i == 4) { break; }
print i;
}

let name = "Rusty";
if (name && x > 10) {
print name + " works!";
}
```
## 🧪 Examples

Output:
Check out the [`example/`](example/) folder for sample RTS programs, including:

```
x = 20
max is 20
5! = 120
0
1
3
Rusty works!
- [`test.rts`](example/test.rts) — array basics (literals, indexing, mutation)
- [`merge_sort.rts`](example/merge_sort.rts) — merge sort implementation using arrays, functions, and recursion

Run any example with:

```bash
cargo run -- run example/merge_sort.rts
```

---
Expand Down Expand Up @@ -210,15 +179,19 @@ cargo run -- repl
* [x] Functions (declaration, params, return)
* [x] Nested / recursive function calls
* [x] Frame-based call stack
* [x] Arrays (literals, indexing, mutation)
* [x] Array pass/return from functions
* [ ] Type annotations
* [ ] Arrays and objects
* [ ] Objects / maps
* [ ] Closures
* [ ] `<=`, `>=` operators

---

### Runtime Evolution
### Runtime & Tooling

* [x] Bytecode VM
* [x] CI pipeline (build, test, clippy, fmt)
* [ ] WASM backend (in progress)
* [ ] Bytecode optimizations
* [ ] Register-based VM (optional)
Expand Down
20 changes: 10 additions & 10 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ pub enum Statement {
},
While {
condition: Expression,
body: Vec<Statement>
body: Vec<Statement>,
},
For {
init: Box<Statement>,
condition: Expression,
update: Box<Statement>,
body: Vec<Statement>
body: Vec<Statement>,
},
Assignment {
name: String,
Expand All @@ -32,16 +32,16 @@ pub enum Statement {
Function {
name: String,
params: Vec<String>,
body: Vec<Statement>
body: Vec<Statement>,
},
Return {
value: Expression
value: Expression,
},
AssignmentIndex {
array: String,
index: Expression,
value: Expression,
}
},
}

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -55,7 +55,7 @@ pub enum BinaryOperation {
Equal,
NotEqual,
And,
Or
Or,
}

#[derive(Debug, Clone)]
Expand All @@ -70,22 +70,22 @@ pub enum Expression {
},
Unary {
op: BinaryOperation,
expr: Box<Expression>
expr: Box<Expression>,
},
Call {
name: String,
args: Vec<Expression>
args: Vec<Expression>,
},
ArrayLiteral(Vec<Expression>),
Index {
array: Box<Expression>,
index: Box<Expression>,
}
},
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
Number(i32),
String(String),
Array(Vec<Value>),
}
}
103 changes: 66 additions & 37 deletions src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::ast::{BinaryOperation, Expression, Statement, Value};
#[derive(Debug)]
pub struct FunctionBytecode {
pub params: Vec<String>,
pub instructions: Vec<Instruction>
pub instructions: Vec<Instruction>,
}

pub struct LoopContext {
Expand All @@ -17,7 +17,7 @@ pub struct LoopContext {
#[derive(Debug)]
pub struct Program {
pub main: Vec<Instruction>,
pub functions: HashMap<String, FunctionBytecode>
pub functions: HashMap<String, FunctionBytecode>,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -45,13 +45,15 @@ pub enum Instruction {
CallFunction(String, usize),
CreateArray(usize),
LoadIndex,
StoreIndex
StoreIndex,
}

fn compile_expr(instructions: &mut Vec<Instruction>, expr: &Expression) {
match expr {
Expression::Number(x) => instructions.push(Instruction::LoadConst(Value::Number(*x))),
Expression::String(s) => instructions.push(Instruction::LoadConst(Value::String(s.clone()))),
Expression::String(s) => {
instructions.push(Instruction::LoadConst(Value::String(s.clone())))
}
Expression::Identifier(val) => instructions.push(Instruction::LoadVar(val.to_string())),
Expression::Binary { left, op, right } => {
if *op != BinaryOperation::And && *op != BinaryOperation::Or {
Expand All @@ -75,37 +77,38 @@ fn compile_expr(instructions: &mut Vec<Instruction>, expr: &Expression) {
let jump_instructions_index = instructions.len() - 1;
instructions.push(Instruction::PopTop);
compile_expr(instructions, right);
instructions[jump_instructions_index] = Instruction::JumpIfFalse(instructions.len());
instructions[jump_instructions_index] =
Instruction::JumpIfFalse(instructions.len());
}
BinaryOperation::Or => {
instructions.push(Instruction::DuplicateTop);
instructions.push(Instruction::JumpIfTrue(0));
let jump_instructions_index = instructions.len() - 1;
instructions.push(Instruction::PopTop);
compile_expr(instructions, right);
instructions[jump_instructions_index] = Instruction::JumpIfTrue(instructions.len());
instructions[jump_instructions_index] =
Instruction::JumpIfTrue(instructions.len());
}
}
}
Expression::Unary { op, expr } => {
compile_expr(instructions, expr);
match op {
BinaryOperation::Subtraction => instructions.push(Instruction::Negate),
_ => {}
if op == &BinaryOperation::Subtraction {
instructions.push(Instruction::Negate);
}
},
}
Expression::Call { name, args } => {
for arg in args {
compile_expr(instructions, arg);
}
instructions.push(Instruction::CallFunction(name.clone(), args.len()));
},
}
Expression::ArrayLiteral(elements) => {
for element in elements {
compile_expr(instructions, element);
}
instructions.push(Instruction::CreateArray(elements.len()));
},
}
Expression::Index { array, index } => {
compile_expr(instructions, array);
compile_expr(instructions, index);
Expand All @@ -124,12 +127,16 @@ pub fn compile_statements(
Statement::VarDecl { name, value } => {
compile_expr(instructions, &value);
instructions.push(Instruction::DeclareVar(name));
},
}
Statement::Print { value } => {
compile_expr(instructions, &value);
instructions.push(Instruction::Print);
}
Statement::If { condition, body, else_body } => {
Statement::If {
condition,
body,
else_body,
} => {
compile_expr(instructions, &condition);

let jump_if_false_index = instructions.len();
Expand All @@ -140,11 +147,13 @@ pub fn compile_statements(
if let Some(else_body) = else_body {
let jump_to_end_index = instructions.len();
instructions.push(Instruction::Jump(0));
instructions[jump_if_false_index] = Instruction::JumpIfFalse(instructions.len());
instructions[jump_if_false_index] =
Instruction::JumpIfFalse(instructions.len());
compile_statements(else_body, instructions, loop_ctx.as_deref_mut());
instructions[jump_to_end_index] = Instruction::Jump(instructions.len());
} else {
instructions[jump_if_false_index] = Instruction::JumpIfFalse(instructions.len());
instructions[jump_if_false_index] =
Instruction::JumpIfFalse(instructions.len());
}
}
Statement::While { condition, body } => {
Expand All @@ -171,8 +180,13 @@ pub fn compile_statements(
for idx in ctx.break_placeholders {
instructions[idx] = Instruction::Jump(instructions.len());
}
},
Statement::For { init, condition, update, body } => {
}
Statement::For {
init,
condition,
update,
body,
} => {
compile_statements(vec![*init], instructions, loop_ctx.as_deref_mut());

let loop_start_index = instructions.len();
Expand Down Expand Up @@ -201,44 +215,50 @@ pub fn compile_statements(
for idx in ctx.break_placeholders {
instructions[idx] = Instruction::Jump(instructions.len());
}
},
}
Statement::Assignment { name, value } => {
compile_expr(instructions, &value);
instructions.push(Instruction::AssignVar(name));
},
}
Statement::Break => {
if let Some(ctx) = loop_ctx.as_deref_mut() {
instructions.push(Instruction::Jump(0));
ctx.break_placeholders.push(instructions.len() - 1);
} else {
panic!("'break' used outside of a loop");
}
},
}
Statement::Continue => {
if let Some(ctx) = loop_ctx.as_deref_mut() {
instructions.push(Instruction::Jump(0));
ctx.continue_placeholders.push(instructions.len() - 1);
} else {
panic!("'continue' used outside of a loop");
}
},
Statement::Function { name:_, params:_, body:_ } => {},
}
Statement::Function {
name: _,
params: _,
body: _,
} => {}
Statement::Return { value } => {
compile_expr(instructions, &value);
instructions.push(Instruction::Return);
},
Statement::Expression(expr) => {
match expr {
Expression::Call { name, args } => {
for arg in &args {
compile_expr(instructions, arg);
}
instructions.push(Instruction::CallFunction(name.clone(), args.len()));
},
_ => compile_expr(instructions, &expr)
}
Statement::Expression(expr) => match expr {
Expression::Call { name, args } => {
for arg in &args {
compile_expr(instructions, arg);
}
instructions.push(Instruction::CallFunction(name.clone(), args.len()));
}
_ => compile_expr(instructions, &expr),
},
Statement::AssignmentIndex { array, index, value } => {
Statement::AssignmentIndex {
array,
index,
value,
} => {
instructions.push(Instruction::LoadVar(array.clone()));

// index is expression
Expand All @@ -262,11 +282,20 @@ pub fn compile_program(statements: Vec<Statement>) -> Program {
if let Statement::Function { name, params, body } = statement {
let mut func_instructions = Vec::new();
compile_statements(body, &mut func_instructions, None);
functions.insert(name, FunctionBytecode { params, instructions: func_instructions });
functions.insert(
name,
FunctionBytecode {
params,
instructions: func_instructions,
},
);
} else {
compile_statements(vec![statement], &mut main_instructions, None);
}
}

Program { main: main_instructions, functions }
}
Program {
main: main_instructions,
functions,
}
}
Loading
Loading