diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index b214207..b389406 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -99,6 +99,21 @@ impl Compiler { } instrs } + Expr::If(c, t, f) => { + let mut instrs = self.compile_expr(c.0); + let t = self.compile_expr(t.0); + if let Some(f) = f { + let f = self.compile_expr(f.0); + instrs.push(Instr::JumpIfFalse(t.len() + 1)); + instrs.extend(t); + instrs.push(Instr::Jump(f.len())); + instrs.extend(f); + } else { + instrs.push(Instr::JumpIfFalse(t.len())); + instrs.extend(t); + } + instrs + } Expr::Do(es) => { let mut instrs = vec![]; for e in es { diff --git a/examples/sim.sial b/examples/sim.sial index 25b5d21..f74d729 100644 --- a/examples/sim.sial +++ b/examples/sim.sial @@ -1,14 +1,4 @@ -fun foo = \x -> x - fun main = do - foo(1) - [1, 2, 3] - true - print("Hello, World") - let seven = 7 in - print(5 - seven) - - let a = 34 in - let b = 35 in - print(a + b) -end \ No newline at end of file + let foo = if true then 10 else 5 in + print(foo) +end diff --git a/parser/src/lib.rs b/parser/src/lib.rs index f539ed9..54126c6 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -44,6 +44,9 @@ pub enum Token { Fun, Let, In, + If, + Then, + Else, Do, End, } @@ -102,6 +105,9 @@ impl std::fmt::Display for Token { Token::Fun => write!(f, "fun"), Token::Let => write!(f, "let"), Token::In => write!(f, "in"), + Token::If => write!(f, "if"), + Token::Then => write!(f, "then"), + Token::Else => write!(f, "else"), Token::Do => write!(f, "do"), Token::End => write!(f, "end"), } @@ -155,6 +161,9 @@ pub fn lexer() -> impl Parser, Error = Simple> { "fun" => Token::Fun, "let" => Token::Let, "in" => Token::In, + "if" => Token::If, + "then" => Token::Then, + "else" => Token::Else, "do" => Token::Do, "end" => Token::End, _ => Token::Sym(s), @@ -223,6 +232,11 @@ pub enum Expr { Lambda(Vec, Box>), Call(Box>, Vec>), Let(Vec<(String, Spanned)>, Option>>), + If( + Box>, + Box>, + Option>>, + ), Do(Vec>), } @@ -327,6 +341,15 @@ pub fn expr_parser() -> impl P> { .map(|binds| Expr::Let(binds, None)) .labelled("let"); + let if_ = just(Token::If) + .ignore_then(expr.clone()) + .then_ignore(just(Token::Then)) + .then(expr.clone()) + .then(just(Token::Else).ignore_then(expr.clone()).or_not()) + .map(|((cond, then), else_)| { + Expr::If(Box::new(cond), Box::new(then), else_.map(Box::new)) + }); + let block = just(Token::Do) .ignore_then(expr.clone().repeated()) .then_ignore(just(Token::End)) @@ -339,6 +362,7 @@ pub fn expr_parser() -> impl P> { .or(lam) .or(let_in) .or(let_def) + .or(if_) .or(block) .map_with_span(|e, s| (e, s)) .boxed() diff --git a/vm/src/exec.rs b/vm/src/exec.rs index e644027..54b3c74 100644 --- a/vm/src/exec.rs +++ b/vm/src/exec.rs @@ -31,7 +31,7 @@ impl Executor { } pub fn run(&mut self) -> Result<(), Error> { - for _ in 0..self.instrs.len() { + while self.ip < self.instrs.len() { self.step()?; self.ip += 1; } @@ -249,6 +249,19 @@ impl Executor { self.set(name, v)?; } + Instr::Jump(n) => { + self.ip += n; + } + Instr::JumpIfFalse(n) => { + if let Value::Bool(b) = self.pop()? { + if !b { + self.ip += n; + } + } else { + return Err(Error::make("can't apply `if` to non-boolean", self.ip)); + } + } + Instr::Print => { let v = self.pop()?; println!("{}", v); diff --git a/vm/src/model.rs b/vm/src/model.rs index da223aa..1de5f4f 100644 --- a/vm/src/model.rs +++ b/vm/src/model.rs @@ -159,6 +159,9 @@ pub enum Instr { Get(String), // ┐ 1 + string.len() + 1 bytes Set(String), // ┘ + Jump(usize), // ┐ 9 bytes: 1 byte for the enum, 8 bytes for the usize (64-bit) + JumpIfFalse(usize), // ┘ + Print, // 1 byte } @@ -178,7 +181,7 @@ impl Instr { Instr::Pop | Instr::Dup => 1, Instr::ListMake(_) | Instr::ListGet(_) | Instr::ListSet(_) => { - std::mem::size_of::() + 1 + 1 + std::mem::size_of::() } Instr::ListLen | Instr::ListJoin => 1, @@ -193,6 +196,8 @@ impl Instr { Instr::Get(s) | Instr::Set(s) => 1 + s.len() + 1, + Instr::Jump(_) | Instr::JumpIfFalse(_) => 1 + std::mem::size_of::(), + Instr::Print => 1, } } @@ -285,6 +290,15 @@ impl Instr { bytes.push(0x00); } + Instr::Jump(n) => { + bytes.push(index!()); + bytes.extend(n.to_le_bytes()); + } + Instr::JumpIfFalse(n) => { + bytes.push(index!()); + bytes.extend(n.to_le_bytes()); + } + Instr::Print => bytes.push(index!()), } bytes