commit - e415d90b662b2024a6f38196ded11f3881cbf636
commit + 37c978c740aa2c23c2be7f6165ee14b888c150b5
blob - fb580a3dd5681dd53cdf648d3238b0d3ff185147
blob + 4b49dea9826b681b7046ab844cfe218f7c151aec
--- src/compiler/mod.rs
+++ src/compiler/mod.rs
use crate::vm::value::Value;
use opcode::{Chunk, OpCode};
+struct Local {
+ name: String,
+ mutable: bool,
+}
+
pub struct Compiler {
chunk: Chunk,
+ locals: Vec<Local>,
}
impl Compiler {
pub fn new() -> Self {
- Self { chunk: Chunk::new() }
+ Self { chunk: Chunk::new(), locals: Vec::new() }
}
+ fn resolve_local(&self, name: &str) -> Option<usize> {
+ self.locals.iter().rposition(|l| l.name == name)
+ }
+
pub fn compile(
mut self, program: &Program,
) -> Result<Chunk, OlangError> {
.ok_or_else(|| OlangError::new(
"no main() function found", Span::new(0, 0),
))?;
-
for stmt in &main_fn.body {
self.compile_stmt(stmt)?;
}
-
self.chunk.emit_op(OpCode::Return, 0);
Ok(self.chunk)
}
fn compile_stmt(&mut self, stmt: &Stmt) -> Result<(), OlangError> {
match stmt {
+ Stmt::Let { name, mutable, value, .. } => {
+ self.compile_expr(value)?;
+ self.locals.push(Local {
+ name: name.clone(), mutable: *mutable,
+ });
+ }
+ Stmt::Assign { name, value, span } => {
+ let slot = self.resolve_local(name).ok_or_else(|| {
+ OlangError::new(
+ format!("undefined variable '{name}'"), *span,
+ )
+ })?;
+ if !self.locals[slot].mutable {
+ return Err(OlangError::new(
+ format!("cannot assign to immutable variable '{name}'"),
+ *span,
+ ));
+ }
+ self.compile_expr(value)?;
+ self.chunk.emit_op(OpCode::SetLocal, 0);
+ self.chunk.emit_byte(slot as u8, 0);
+ }
Stmt::Print { args, .. } => {
for arg in args {
self.compile_expr(arg)?;
fn compile_expr(&mut self, expr: &Expr) -> Result<(), OlangError> {
match expr {
- Expr::IntLit(v, _) => {
- self.chunk.emit_constant(Value::Int(*v), 0);
+ Expr::IntLit(v, _) => self.chunk.emit_constant(Value::Int(*v), 0),
+ Expr::FloatLit(v, _) => self.chunk.emit_constant(Value::Float(*v), 0),
+ Expr::StrLit(s, _) => self.chunk.emit_constant(Value::Str(s.clone()), 0),
+ Expr::BoolLit(v, _) => self.chunk.emit_constant(Value::Bool(*v), 0),
+ Expr::Ident(name, span) => {
+ let slot = self.resolve_local(name).ok_or_else(|| {
+ OlangError::new(format!("undefined variable '{name}'"), *span)
+ })?;
+ self.chunk.emit_op(OpCode::GetLocal, 0);
+ self.chunk.emit_byte(slot as u8, 0);
}
- Expr::FloatLit(v, _) => {
- self.chunk.emit_constant(Value::Float(*v), 0);
- }
- Expr::StrLit(s, _) => {
- self.chunk.emit_constant(Value::Str(s.clone()), 0);
- }
- Expr::BoolLit(v, _) => {
- self.chunk.emit_constant(Value::Bool(*v), 0);
- }
Expr::Unary { op, expr, .. } => {
self.compile_expr(expr)?;
match op {
BinOp::Mul => OpCode::Mul,
BinOp::Div => OpCode::Div,
BinOp::Mod => OpCode::Mod,
- _ => {
- return Err(OlangError::new(
- format!("unsupported binary op: {:?}", op),
- Span::new(0, 0),
- ));
- }
+ _ => return Err(OlangError::new(
+ format!("unsupported binary op: {:?}", op),
+ Span::new(0, 0),
+ )),
};
self.chunk.emit_op(opcode, 0);
}
- _ => {
+ Expr::Call { name, args: _, span } => {
return Err(OlangError::new(
- format!("unsupported expression: {:?}", expr),
- Span::new(0, 0),
+ format!("function calls not yet supported: {name}"),
+ *span,
));
}
}
blob - 9c9cbdf349de944ccdf23437fd67f1d7b8e00860
blob + fca54dff3512cbf3bac7237083caa50df0523e85
--- src/compiler/opcode.rs
+++ src/compiler/opcode.rs
Div,
Mod,
Negate,
+ GetLocal,
+ SetLocal,
Print,
Return,
}
5 => Some(OpCode::Div),
6 => Some(OpCode::Mod),
7 => Some(OpCode::Negate),
- 8 => Some(OpCode::Print),
- 9 => Some(OpCode::Return),
+ 8 => Some(OpCode::GetLocal),
+ 9 => Some(OpCode::SetLocal),
+ 10 => Some(OpCode::Print),
+ 11 => Some(OpCode::Return),
_ => None,
}
}
impl Chunk {
pub fn new() -> Self {
- Self {
- code: Vec::new(),
- constants: Vec::new(),
- lines: Vec::new(),
- }
+ Self { code: Vec::new(), constants: Vec::new(), lines: Vec::new() }
}
pub fn emit_byte(&mut self, byte: u8, line: usize) {
println!("{:<12} {:4} ({})", "Constant", index, value);
offset + 2
}
+ OpCode::GetLocal | OpCode::SetLocal => {
+ let slot = self.code[offset + 1] as usize;
+ println!("{:<12} {:4}", format!("{:?}", op), slot);
+ offset + 2
+ }
OpCode::Print => {
let count = self.code[offset + 1] as usize;
println!("{:<12} {:4}", "Print", count);
blob - 6e43e1f2d1aa8021436c58f5f3813ae58beb6812
blob + 4309c4ed0f97962469ee0a7463c9f86ce883004c
--- src/vm/mod.rs
+++ src/vm/mod.rs
};
self.push(result);
}
+ OpCode::GetLocal => {
+ let slot = self.read_byte() as usize;
+ let value = self.stack[slot].clone();
+ self.push(value);
+ }
+ OpCode::SetLocal => {
+ let slot = self.read_byte() as usize;
+ let value = self.stack.last()
+ .expect("stack underflow").clone();
+ self.stack[slot] = value;
+ self.pop();
+ }
OpCode::Print => {
let count = self.read_byte() as usize;
let start = self.stack.len() - count;