From 5f263caf1ad21213609d36cee1bdca48eac459fd Mon Sep 17 00:00:00 2001 From: Natapat Samutpong Date: Sat, 5 Feb 2022 11:56:59 +0700 Subject: [PATCH] feat: while loop --- README.md | 5 +++- blspc/src/compiler/compile.rs | 55 +++++++++++++++++++++++++++++------ blspc/src/compiler/parser.rs | 4 +-- blspc/src/vm/instr.rs | 34 ++++++++++++++++++---- blspc/src/vm/parser.rs | 13 +++++++-- blspc/src/vm/vm.rs | 30 ++++++++++++++++++- example/if.blsp | 2 +- example/input.blsp | 2 +- example/truth_machine.blsp | 5 ++++ example/var.blsp | 2 +- 10 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 example/truth_machine.blsp diff --git a/README.md b/README.md index e3b7c0a..6193134 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ DONE: - Parsing, Compiling, Running(VM) - Intrinsic: - Function definition: `fun` - - Variable definition: `let` + - Variable definition: `def` - Do blocks: `do` - User input: `read` - Printing: `print` @@ -33,6 +33,9 @@ DONE: - `-` , `sub` - `*` , `mul` - `/` , `div` + - Comparison: + - `=` , `equal` + - `!` , `not` TODO: - Prove turing complete diff --git a/blspc/src/compiler/compile.rs b/blspc/src/compiler/compile.rs index 418c25e..6fd5d37 100644 --- a/blspc/src/compiler/compile.rs +++ b/blspc/src/compiler/compile.rs @@ -61,23 +61,42 @@ impl Compiler { let mut then = self.compile(cdr[1].clone())?; let mut else_ = self.compile(cdr[2].clone())?; - result.push(Instr::JumpIfFalse { to: len(&then) + 1}); // +1 for the jump instr + result.push(Instr::JumpIfFalse { to: len(&then) as isize + 1 }); // +1 for the jump instr result.append(&mut then); - result.push(Instr::Jump { to: len(&else_) }); + result.push(Instr::Jump { to: len(&else_) as isize }); result.append(&mut else_); }, - "let" => { + "def" => { let var_name = match &cdr[0] { Symbol(ref name) => name.clone(), _ => return Err(format!("Expected variable name, got {}", cdr[0])), }; - let body = &cdr[1]; - let var_pointer = self.next_register(); - self.variables.push((var_name, var_pointer)); + if let Some(v) = self.variables.iter().find(|v| v.0 == var_name) { + let r = v.1; + self.variables.retain(|v| v.0 != var_name); + self.variables.push((var_name, r)); + result.append(&mut self.compile(cdr[1].clone())?); + result.push(Instr::Store { address: r }); + } else { + let var_pointer = self.next_register(); + self.variables.push((var_name, var_pointer)); + result.append(&mut self.compile(cdr[1].clone())?); + result.push(Instr::Store { address: var_pointer }); + } + }, + "while" => { + let mut cond = self.compile(cdr[0].clone())?; + let mut body = self.compile(cdr[1].clone())?; - result.append(&mut self.compile(body.clone())?); - result.push(Instr::Store { address: var_pointer }); + let jump_length = (len(&body) as isize) + (len(&cond) as isize); + + result.append(&mut cond.clone()); + result.push(Instr::JumpIfFalse { to: jump_length + 1 }); + result.append(&mut body); + result.append(&mut cond); + result.push(Instr::Not); + result.push(Instr::JumpIfFalse { to: -jump_length }); }, _ => { result.append(&mut self.compile_intrinsic(call, &cdr)?); @@ -149,6 +168,21 @@ impl Compiler { result.push(Instr::Swap); result.push(Instr::Div); }, + "equal" | "=" => { + let mut lhs = self.compile_atom(&args[0])?; + result.append(&mut lhs); + + let mut rhs = self.compile_atom(&args[1])?; + result.append(&mut rhs); + + result.push(Instr::Equal); + }, + "not" | "!" => { + let mut lhs = self.compile_atom(&args[0])?; + result.append(&mut lhs); + + result.push(Instr::Not); + }, _ => { result.push(Instr::Comment { text: format!("{} function", intrinsic) }); result.push(Instr::JumpLabel { to: format!("function_{}", intrinsic), }); @@ -175,7 +209,10 @@ impl Compiler { result.push(Instr::Push { value: Type::Boolean(*b) }); }, Symbol(s) => { - let var_pointer = self.variables.iter().find(|&(ref name, _)| name == s).unwrap().1; + let var_pointer = match self.variables.iter().find(|&(ref name, _)| name == s) { + Some((_, pointer)) => *pointer, + None => return Err(format!("Undefined variable {}", s)), + }; result.push(Instr::Load { address: var_pointer }); }, _ => { result.append(&mut self.compile(atom.clone())?); } diff --git a/blspc/src/compiler/parser.rs b/blspc/src/compiler/parser.rs index 74a9dd8..05db943 100644 --- a/blspc/src/compiler/parser.rs +++ b/blspc/src/compiler/parser.rs @@ -89,8 +89,8 @@ impl Parser { let token = self.next().unwrap(); match token.as_str() { "null" => Ok(Nil), - "true" => Ok(Boolean(true)), - "false" => Ok(Boolean(false)), + "true" | "True" => Ok(Boolean(true)), + "false" | "False" => Ok(Boolean(false)), _ => { if Regex::new(r#"[+-]?([0-9]*[.])?[0-9]+"#).unwrap().is_match(&token) { Ok(Int(token.parse().unwrap())) diff --git a/blspc/src/vm/instr.rs b/blspc/src/vm/instr.rs index eed6af8..966e2dd 100644 --- a/blspc/src/vm/instr.rs +++ b/blspc/src/vm/instr.rs @@ -1,9 +1,9 @@ -use std::{fmt::Display, str::FromStr, ops::{Add, Sub, Mul, Div}}; +use std::{fmt::Display, str::FromStr, ops::{Add, Sub, Mul, Div, Not}}; use crate::vm::vm::Error::{self, InvalidAriphmeticOperation}; /// Literal types for the assembler. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum Type { Null, Int(i64), @@ -98,6 +98,17 @@ impl Div for Type { } } +impl Not for Type { + type Output = Result; + + fn not(self) -> Result { + match self { + Type::Boolean(b) => Ok(Type::Boolean(!b)), + _ => Err(InvalidAriphmeticOperation), + } + } +} + impl Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { @@ -168,13 +179,16 @@ pub enum Instr { Call, // Stack operations. Push { value: Type }, Pop { address: Register }, Swap, - // Stack arithmetic. + // Stack arithmetic operations. Add, Sub, Mul, Div, + Not, // Jumping. JumpLabel { to: String }, // Jump to (function) label. - Jump { to: usize }, // Jump with offset. - JumpIfFalse { to: usize }, + Jump { to: isize }, // Jump with offset. + JumpIfFalse { to: isize }, + // Comparison (with stack values). + Equal, Return, } @@ -186,19 +200,29 @@ impl Display for Instr { // ----------20--------- Parameter start Instr::Label { name } => write!(f, ".{}:", name), Instr::Comment { text } => write!(f, ";{}", text), + Instr::Load { address } => write!(f, " LOAD {}", address), Instr::Store { address } => write!(f, " STORE {}", address), + Instr::Call => write!(f, " CALL"), + Instr::Push { value } => write!(f, " PUSH {}", value), Instr::Pop { address } => write!(f, " POP {}", address), Instr::Swap => write!(f, " SWAP"), + Instr::Add => write!(f, " ADD"), Instr::Sub => write!(f, " SUB"), Instr::Mul => write!(f, " MUL"), Instr::Div => write!(f, " DIV"), + + Instr::Not => write!(f, " NOT"), + Instr::JumpLabel { to } => write!(f, " JMPL {}", to), Instr::Jump { to } => write!(f, " JMP {}", to), Instr::JumpIfFalse { to } => write!(f, " JMPF {}", to), + + Instr::Equal => write!(f, " EQ"), + Instr::Return => write!(f, " RET"), } } diff --git a/blspc/src/vm/parser.rs b/blspc/src/vm/parser.rs index e0c2506..0eba2db 100644 --- a/blspc/src/vm/parser.rs +++ b/blspc/src/vm/parser.rs @@ -18,17 +18,26 @@ pub fn parse_instr(src: &str) -> Vec { match tokens[0] { "LOAD" => { result.push(Instr::Load { address: register!(tokens[1].to_string()) }); }, "STORE" => { result.push(Instr::Store { address: register!(tokens[1].to_string()) }); }, + "CALL" => { result.push(Instr::Call); }, + "PUSH" => { result.push(Instr::Push { value: value!(tokens[1]) }); }, "POP" => { result.push(Instr::Pop { address: register!(tokens[1]) }); }, "SWAP" => { result.push(Instr::Swap); }, + "ADD" => { result.push(Instr::Add); }, "SUB" => { result.push(Instr::Sub); }, "MUL" => { result.push(Instr::Mul); }, "DIV" => { result.push(Instr::Div); }, + + "NOT" => { result.push(Instr::Not); }, + "JMPL" => { result.push(Instr::JumpLabel { to: tokens[1].to_string() }); }, - "JMP" => { result.push(Instr::Jump { to: tokens[1].parse::().unwrap() }); }, - "PJMPF" => todo!(), + "JMP" => { result.push(Instr::Jump { to: tokens[1].parse::().unwrap() }); }, + "JMPF" => { result.push(Instr::JumpIfFalse { to: tokens[1].parse::().unwrap() }); }, + + "EQ" => { result.push(Instr::Equal); }, + "RET" => { result.push(Instr::Return); }, _ => { if tokens[0].starts_with(".") { diff --git a/blspc/src/vm/vm.rs b/blspc/src/vm/vm.rs index 58027a6..8f6c3d9 100644 --- a/blspc/src/vm/vm.rs +++ b/blspc/src/vm/vm.rs @@ -60,6 +60,8 @@ impl VM { if !found { return Err(Error::NoMainFunction); } 'tco: loop { + // std::thread::sleep(std::time::Duration::from_millis(1000)); + self.instr_pointer += 1; if self.instr_pointer - 1 == instrs.len() as isize { result = Ok(()); @@ -78,12 +80,14 @@ impl VM { self.store(address, value)?; continue 'tco; }, + Call => { let index = &self.stack.pop().unwrap(); let args = &self.stack.pop().unwrap(); self.call(index, args, self.instr_pointer)?; continue 'tco; }, + Push { value } => { self.push(value.trim().clone())?; continue 'tco; @@ -100,6 +104,7 @@ impl VM { self.stack.push(bottom); continue 'tco; }, + Add => { let lhs = self.stack.pop().unwrap(); let rhs = self.stack.pop().unwrap(); @@ -124,6 +129,13 @@ impl VM { self.push((lhs / rhs)?)?; continue 'tco; }, + + Not => { + let value = self.stack.pop().unwrap(); + self.push((!value)?)?; + continue 'tco; + }, + JumpLabel { to } => { let pointer = self.get_function_pointer(to.to_string())?; self.jumped_from = self.instr_pointer; @@ -134,6 +146,22 @@ impl VM { self.instr_pointer += *to as isize + 1; continue 'tco; }, + JumpIfFalse { to } => { + let cond = self.stack.pop().unwrap(); + if cond == Type::Boolean(false) { + if *to < 0 { self.instr_pointer += *to as isize - 2; } + else { self.instr_pointer += *to as isize + 1; } + continue 'tco; + } + }, + + Equal => { + let lhs = self.stack.pop().unwrap(); + let rhs = self.stack.pop().unwrap(); + self.push(Type::Boolean(lhs == rhs))?; + continue 'tco; + }, + Return => { if self.jumped_from == 0 { return Ok(()); } self.instr_pointer = self.jumped_from; @@ -141,7 +169,7 @@ impl VM { continue 'tco; }, Label { .. } => {}, - _ => unimplemented!(), + _ => { dbg!(instr); unimplemented!()}, } } diff --git a/example/if.blsp b/example/if.blsp index 6068454..6a957a9 100644 --- a/example/if.blsp +++ b/example/if.blsp @@ -1,4 +1,4 @@ (fun print_true (print "True")) (fun print_false (print "False")) (fun main - (if true print_true print_false)) \ No newline at end of file + (if true (print_true) (print_false))) \ No newline at end of file diff --git a/example/input.blsp b/example/input.blsp index 22b8001..d197bc2 100644 --- a/example/input.blsp +++ b/example/input.blsp @@ -1,4 +1,4 @@ (fun main (do - (let in (read)) + (def in (read)) (print "Your input was: ") (print in))) diff --git a/example/truth_machine.blsp b/example/truth_machine.blsp new file mode 100644 index 0000000..5baddb2 --- /dev/null +++ b/example/truth_machine.blsp @@ -0,0 +1,5 @@ +; https://esolangs.org/wiki/Truth-machine +(fun main (do + (def x (read)) + (while (equal x 1) (print x)) + (print "Done!"))) \ No newline at end of file diff --git a/example/var.blsp b/example/var.blsp index 019468a..eb931f6 100644 --- a/example/var.blsp +++ b/example/var.blsp @@ -1,7 +1,7 @@ (fun return_true true) (fun main (do - (let name "John") + (def name "John") (if (return_true) (print name) (print "no")))) \ No newline at end of file