mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
feat: while loop
This commit is contained in:
parent
f355183b8d
commit
5f263caf1a
|
@ -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
|
||||
|
|
|
@ -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())?); }
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(".") {
|
||||
|
|
|
@ -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!()},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)))
|
|
@ -1,4 +1,4 @@
|
|||
(fun main (do
|
||||
(let in (read))
|
||||
(def in (read))
|
||||
(print "Your input was: ")
|
||||
(print in)))
|
||||
|
|
5
example/truth_machine.blsp
Normal file
5
example/truth_machine.blsp
Normal 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!")))
|
|
@ -1,7 +1,7 @@
|
|||
(fun return_true true)
|
||||
|
||||
(fun main (do
|
||||
(let name "John")
|
||||
(def name "John")
|
||||
(if (return_true)
|
||||
(print name)
|
||||
(print "no"))))
|
Loading…
Reference in a new issue