feat: while loop

replace/7746dba3cc6b3860afe1faf69e86ed84ee46988d
Natapat Samutpong 2022-02-05 11:56:59 +07:00
parent f355183b8d
commit 5f263caf1a
10 changed files with 129 additions and 23 deletions

View File

@ -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

View File

@ -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())?); }

View File

@ -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()))

View File

@ -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<Type, Error>;
fn not(self) -> Result<Type, Error> {
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"),
}
}

View File

@ -18,17 +18,26 @@ pub fn parse_instr(src: &str) -> Vec<Instr> {
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::<usize>().unwrap() }); },
"PJMPF" => todo!(),
"JMP" => { result.push(Instr::Jump { to: tokens[1].parse::<isize>().unwrap() }); },
"JMPF" => { result.push(Instr::JumpIfFalse { to: tokens[1].parse::<isize>().unwrap() }); },
"EQ" => { result.push(Instr::Equal); },
"RET" => { result.push(Instr::Return); },
_ => {
if tokens[0].starts_with(".") {

View File

@ -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!()},
}
}

View File

@ -1,4 +1,4 @@
(fun print_true (print "True"))
(fun print_false (print "False"))
(fun main
(if true print_true print_false))
(if true (print_true) (print_false)))

View File

@ -1,4 +1,4 @@
(fun main (do
(let in (read))
(def in (read))
(print "Your input was: ")
(print in)))

View File

@ -0,0 +1,5 @@
; https://esolangs.org/wiki/Truth-machine
(fun main (do
(def x (read))
(while (equal x 1) (print x))
(print "Done!")))

View File

@ -1,7 +1,7 @@
(fun return_true true)
(fun main (do
(let name "John")
(def name "John")
(if (return_true)
(print name)
(print "no"))))