mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
refactor: a large amount of refactor
This commit is contained in:
parent
8382f0b5af
commit
83e0121dee
|
@ -24,3 +24,4 @@ Hello, World!
|
|||
TODO:
|
||||
- Quote, Quasiquote, etc.
|
||||
- Optimizing
|
||||
- Remove unnecessary copying in the entire codebase
|
||||
|
|
|
@ -4,24 +4,38 @@ use structopt::StructOpt;
|
|||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "blspc")]
|
||||
pub struct Args {
|
||||
/// Verbose mode (-v, -vv, -vvv, etc.). Max is 2 currently.
|
||||
#[structopt(short, long, parse(from_occurrences))]
|
||||
pub verbose: u8,
|
||||
pub struct Opts {
|
||||
#[structopt(subcommand)]
|
||||
pub commands: Option<Args>,
|
||||
}
|
||||
|
||||
/// Compliation mode (-c).
|
||||
#[structopt(short, long)]
|
||||
pub compile: bool,
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "args")]
|
||||
pub enum Args {
|
||||
#[structopt(name = "compile")]
|
||||
Compile (CompileOpts),
|
||||
#[structopt(name = "run")]
|
||||
Run (RunOpts),
|
||||
}
|
||||
|
||||
/// Run mode (-r).
|
||||
#[structopt(short, long)]
|
||||
pub run: bool,
|
||||
|
||||
/// Files to process.
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "compile", about = "Compile Options")]
|
||||
pub struct CompileOpts {
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
pub file: PathBuf,
|
||||
|
||||
/// Output file.
|
||||
#[structopt(short, long, parse(from_os_str))]
|
||||
#[structopt(name = "OUTPUT", parse(from_os_str))]
|
||||
pub output: Option<PathBuf>,
|
||||
#[structopt(short, long)]
|
||||
pub debug: bool,
|
||||
#[structopt(short = "b", long)]
|
||||
pub with_comment: bool,
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "run", about = "Run Options")]
|
||||
pub struct RunOpts {
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
pub file: PathBuf,
|
||||
#[structopt(short, long)]
|
||||
pub debug: bool,
|
||||
}
|
|
@ -2,7 +2,6 @@ use crate::{vm::instr::*, compiler::parser::Sexpr::{self, *}};
|
|||
pub struct Compiler {
|
||||
pub instructions: Vec<Instr>,
|
||||
pub register_pointer: usize,
|
||||
pub label_pointer: usize,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
|
@ -10,7 +9,6 @@ impl Compiler {
|
|||
Compiler {
|
||||
instructions: Vec::new(),
|
||||
register_pointer: 1,
|
||||
label_pointer: 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,184 +22,89 @@ impl Compiler {
|
|||
Register { value: self.register_pointer - 1 }
|
||||
}
|
||||
|
||||
fn next_label(&mut self) -> usize {
|
||||
let l = self.label_pointer;
|
||||
self.label_pointer += 1;
|
||||
l
|
||||
}
|
||||
|
||||
fn current_label(&self) -> usize {
|
||||
self.label_pointer - 1
|
||||
}
|
||||
|
||||
pub fn compile(&mut self, ast: Sexpr, depth: usize) -> Result<Vec<Instr>, String> {
|
||||
pub fn compile(&mut self, src: Sexpr) -> Result<Vec<Instr>, String> {
|
||||
let mut result = Vec::new();
|
||||
let comp = src.clone(); // Used for commenting
|
||||
|
||||
match ast {
|
||||
Cons(car, cdr) => {
|
||||
match *car {
|
||||
Symbol(ref s) => {
|
||||
match s.as_str() {
|
||||
"do" => {
|
||||
for c in cdr {
|
||||
result.append(&mut self.compile(c, depth + 1)?);
|
||||
}
|
||||
},
|
||||
"if" => {
|
||||
// TODO: Remove .clone()
|
||||
let mut cond = self.compile(cdr[0].clone(), depth + 1)?;
|
||||
result.append(&mut cond);
|
||||
|
||||
result.push(Instr::PopJumpIfFalse {
|
||||
to: 999, // To be replaced later
|
||||
label: self.next_label(),
|
||||
});
|
||||
|
||||
let mut then = self.compile(cdr[1].clone(), depth + 1)?;
|
||||
let jump_label = self.next_label();
|
||||
|
||||
let mut else_ = self.compile(cdr[2].clone(), depth + 1)?;
|
||||
let else_label = self.current_label() - else_.len() + 1;
|
||||
|
||||
let idx = result.len() - 1;
|
||||
match result[idx] {
|
||||
Instr::PopJumpIfFalse { to: _, label: l } => {
|
||||
result[idx] = Instr::PopJumpIfFalse { to: else_label, label: l, };
|
||||
'tco: loop {
|
||||
match src {
|
||||
Cons(car, cdr) => {
|
||||
match *car {
|
||||
Symbol(ref call) => {
|
||||
match call.as_str() {
|
||||
"do" => {
|
||||
for c in cdr {
|
||||
result.append(&mut self.compile(c)?);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
"fun" => {
|
||||
result.push(Instr::Comment { text: format!("--- {}", comp) });
|
||||
let function_name = match &cdr[0] {
|
||||
Symbol(ref name) => format!("function_{}", name.clone()),
|
||||
_ => return Err(format!("Expected function name, got {}", cdr[0])),
|
||||
};
|
||||
let body = &cdr[1];
|
||||
|
||||
result.push(Instr::Label{ name: function_name });
|
||||
result.append(&mut self.compile(body.clone())?);
|
||||
result.push(Instr::Return);
|
||||
},
|
||||
"print" => {
|
||||
result.append(&mut self.compile(cdr[0].clone())?);
|
||||
let to = self.next_register();
|
||||
let call_register = self.next_register();
|
||||
|
||||
result.push(Instr::Pop { address: to });
|
||||
result.push(Instr::Store {
|
||||
address: call_register,
|
||||
value: Type::Int(1),
|
||||
});
|
||||
result.push(Instr::Call {
|
||||
address: call_register,
|
||||
args: to,
|
||||
});
|
||||
}
|
||||
|
||||
result.append(&mut then);
|
||||
result.push(Instr::Jump {
|
||||
to: self.current_label() + 1,
|
||||
label: jump_label,
|
||||
});
|
||||
result.append(&mut else_);
|
||||
}
|
||||
_ => {
|
||||
result.append(&mut self.compile_intrinsic(s, &cdr, depth + 1)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(format!("Expected symbol, got {:?}", car)),
|
||||
}
|
||||
}
|
||||
_ => { result.append(&mut self.compile_atom(&ast, depth + 1)?); },
|
||||
}
|
||||
_ => { dbg!(call); unimplemented!() },
|
||||
} // End `match call`
|
||||
}, // End `Symbol(call)`
|
||||
_ => { dbg!(car); unimplemented!() },
|
||||
} // End `match car`
|
||||
}, // End `Cons(car, cdr)`
|
||||
_ => { result.append(&mut self.compile_atom(src)?); },
|
||||
} // End `match src`
|
||||
|
||||
break 'tco;
|
||||
} // End `loop`
|
||||
|
||||
if depth == 0 {
|
||||
result.push(Instr::Store {
|
||||
address: self.next_register(),
|
||||
value: Type::Int(0),
|
||||
label: self.next_label(),
|
||||
});
|
||||
result.push(Instr::Return {
|
||||
value: self.current_register(),
|
||||
label: self.next_label(),
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn compile_atom(&mut self, atom: &Sexpr, depth: usize) -> Result<Vec<Instr>, String> {
|
||||
fn compile_atom(&mut self, atom: Sexpr) -> Result<Vec<Instr>, String> {
|
||||
let mut result = Vec::new();
|
||||
let comp = atom.clone(); // Used for commenting
|
||||
|
||||
match atom {
|
||||
Int(i) => {
|
||||
result.push(Instr::Push {
|
||||
value: Type::Int(*i),
|
||||
label: self.next_label(),
|
||||
});
|
||||
result.push(Instr::Comment { text: format!("----- {}", comp) });
|
||||
result.push(Instr::Push { value: Type::Int(i) });
|
||||
},
|
||||
Float(f) => {
|
||||
result.push(Instr::Push {
|
||||
value: Type::Float(*f),
|
||||
label: self.next_label(),
|
||||
});
|
||||
},
|
||||
Boolean(b) => {
|
||||
result.push(Instr::Push {
|
||||
value: Type::Boolean(*b),
|
||||
label: self.next_label(),
|
||||
});
|
||||
result.push(Instr::Comment { text: format!("----- {}", comp) });
|
||||
result.push(Instr::Push { value: Type::Float(f) });
|
||||
},
|
||||
Str(s) => {
|
||||
result.push(Instr::Push {
|
||||
value: Type::String(s.clone()),
|
||||
label: self.next_label(),
|
||||
});
|
||||
result.push(Instr::Comment { text: format!("----- {}", comp) });
|
||||
result.push(Instr::Push { value: Type::String(s) });
|
||||
},
|
||||
_ => {
|
||||
result.append(&mut self.compile(atom.clone(), depth + 1)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn compile_intrinsic(&mut self, intrinsic: &String, args: &[Sexpr], depth: usize) -> Result<Vec<Instr>, String> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
match intrinsic.as_str() {
|
||||
"print" => {
|
||||
let mut arg = self.compile_atom(&args[0], depth + 1)?;
|
||||
result.append(&mut arg);
|
||||
let arg_pointer = self.current_register();
|
||||
result.push(Instr::Pop {
|
||||
address: arg_pointer,
|
||||
label: self.next_label(),
|
||||
});
|
||||
|
||||
let call_register = self.next_register();
|
||||
result.push(Instr::Store {
|
||||
address: call_register,
|
||||
value: Type::Int(1),
|
||||
label: self.next_label(),
|
||||
});
|
||||
|
||||
result.push(Instr::Call {
|
||||
address: call_register,
|
||||
args: arg_pointer,
|
||||
label: self.next_label(),
|
||||
});
|
||||
Boolean(b) => {
|
||||
result.push(Instr::Comment { text: format!("----- {}", comp) });
|
||||
result.push(Instr::Push { value: Type::Boolean(b) });
|
||||
},
|
||||
"add" | "+" => {
|
||||
let mut lhs = self.compile_atom(&args[0], depth + 1)?;
|
||||
result.append(&mut lhs);
|
||||
|
||||
let mut rhs = self.compile_atom(&args[1], depth + 1)?;
|
||||
result.append(&mut rhs);
|
||||
|
||||
result.push(Instr::Add { label: self.next_label() });
|
||||
Symbol(s) => {
|
||||
result.push(Instr::Comment { text: format!("----- {} variable", comp) });
|
||||
result.push(Instr::Jump { to: format!("function_{}", s), });
|
||||
},
|
||||
"sub" | "-" => {
|
||||
let mut lhs = self.compile_atom(&args[0], depth + 1)?;
|
||||
result.append(&mut lhs);
|
||||
|
||||
let mut rhs = self.compile_atom(&args[1], depth + 1)?;
|
||||
result.append(&mut rhs);
|
||||
|
||||
result.push(Instr::Sub { label: self.next_label() });
|
||||
},
|
||||
"mul" | "*" => {
|
||||
let mut lhs = self.compile_atom(&args[0], depth + 1)?;
|
||||
result.append(&mut lhs);
|
||||
|
||||
let mut rhs = self.compile_atom(&args[1], depth + 1)?;
|
||||
result.append(&mut rhs);
|
||||
|
||||
result.push(Instr::Mul { label: self.next_label() });
|
||||
},
|
||||
"div" | "/" => {
|
||||
let mut lhs = self.compile_atom(&args[0], depth + 1)?;
|
||||
result.append(&mut lhs);
|
||||
|
||||
let mut rhs = self.compile_atom(&args[1], depth + 1)?;
|
||||
result.append(&mut rhs);
|
||||
|
||||
result.push(Instr::Div { label: self.next_label() });
|
||||
},
|
||||
_ => return Err(format!("Unknown intrinsic: {}", intrinsic)),
|
||||
_ => { dbg!(atom); unimplemented!() },
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
|
|
|
@ -14,7 +14,7 @@ impl std::fmt::Display for Sexpr {
|
|||
match self {
|
||||
Int(i) => write!(f, "{}", i),
|
||||
Float(fl) => write!(f, "{}", fl),
|
||||
Str(s) => write!(f, "{}", s),
|
||||
Str(s) => write!(f, "\"{}\"", s),
|
||||
Boolean(b) => write!(f, "{}", b),
|
||||
Symbol(s) => write!(f, "{}", s),
|
||||
Cons(car, cdr) => {
|
||||
|
@ -59,7 +59,6 @@ impl Parser {
|
|||
Some(s) => match s.as_str() {
|
||||
")" => Err(format!("Unexpected ')' at position {}", self.position)),
|
||||
// TODO: Handle quote and that stuff.
|
||||
"'" => { unimplemented!() },
|
||||
"(" => self.parse_sequence(")"),
|
||||
_ => self.parse_atom(),
|
||||
}
|
||||
|
@ -85,24 +84,6 @@ impl Parser {
|
|||
self.next();
|
||||
Ok(Sexpr::Cons(Box::new(car), cdr))
|
||||
}
|
||||
|
||||
fn parse_quote_sequence(&mut self, end: &str) -> ParseResult {
|
||||
let car = Symbol("list".to_string());
|
||||
|
||||
self.next();
|
||||
let mut cdr = Vec::new();
|
||||
loop {
|
||||
let token = match self.peek() {
|
||||
Some(token) => token,
|
||||
None => return Err(format!("Unexpected end of input, expected '{}'", end)),
|
||||
};
|
||||
if token == end { break; }
|
||||
cdr.push(self.parse()?)
|
||||
}
|
||||
|
||||
self.next();
|
||||
Ok(Sexpr::Cons(Box::new(car), cdr))
|
||||
}
|
||||
|
||||
fn parse_atom(&mut self) -> ParseResult {
|
||||
let token = self.next().unwrap();
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{fs::{read_to_string, File}, path::{Path, PathBuf}, io::{Write, Seek},
|
|||
use structopt::StructOpt;
|
||||
|
||||
mod args;
|
||||
use args::Args;
|
||||
use args::Opts;
|
||||
|
||||
mod util;
|
||||
use util::cover_paren;
|
||||
|
@ -16,43 +16,22 @@ use vm::{vm::VM, parser::parse_instr};
|
|||
|
||||
fn main() {
|
||||
let start = Instant::now();
|
||||
let args = Args::from_args();
|
||||
|
||||
let debug = match args.verbose {
|
||||
0 => false,
|
||||
1 => true,
|
||||
2 => true,
|
||||
_ => true,
|
||||
};
|
||||
let args = Opts::from_args();
|
||||
|
||||
match (args.compile, args.run) {
|
||||
(true, true) => {
|
||||
eprintln!("TODO: Compile and run at the same time.");
|
||||
std::process::exit(1);
|
||||
},
|
||||
// Compile
|
||||
(true, false) => {
|
||||
let src = read_to_string(&args.file).unwrap();
|
||||
compile_src(src, args.output, args.file, debug, start);
|
||||
},
|
||||
// Run
|
||||
(false, true) => {
|
||||
let src = read_to_string(&args.file).unwrap();
|
||||
run_src(src, debug);
|
||||
},
|
||||
(false, false) => {
|
||||
if args.file.extension() == Some("blsp".as_ref()) {
|
||||
if let Some(commands) = args.commands {
|
||||
match commands {
|
||||
args::Args::Compile(args) => {
|
||||
let src = read_to_string(&args.file).unwrap();
|
||||
let debug = args.debug;
|
||||
compile_src(src, args.output, args.file, debug, start);
|
||||
} else if args.file.extension() == Some("bsm".as_ref()) {
|
||||
},
|
||||
args::Args::Run(args) => {
|
||||
let src = read_to_string(&args.file).unwrap();
|
||||
let debug = args.debug;
|
||||
run_src(src, debug);
|
||||
} else {
|
||||
panic!("No mode specified");
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn compile_src(src: String, path: Option<PathBuf>, file: PathBuf, debug: bool, start: Instant) {
|
||||
|
@ -69,11 +48,12 @@ fn compile_src(src: String, path: Option<PathBuf>, file: PathBuf, debug: bool, s
|
|||
match result {
|
||||
Ok(ast) => {
|
||||
let mut compiler = Compiler::new();
|
||||
let code = compiler.compile(ast, 0);
|
||||
let code = compiler.compile(ast);
|
||||
match code {
|
||||
Ok(code) => {
|
||||
let mut file = File::create(format!("{}.bsm", file_name)).unwrap();
|
||||
for line in code {
|
||||
if line.to_string().starts_with(";") { continue; }
|
||||
write!(file, "{}\n", line).unwrap();
|
||||
}
|
||||
file.seek(std::io::SeekFrom::End(-1)).unwrap(); // Trim last newline
|
||||
|
|
|
@ -5,23 +5,22 @@ use crate::vm::vm::Error::{self, InvalidAriphmeticOperation};
|
|||
/// Literal types for the assembler.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Type {
|
||||
Null,
|
||||
Null, StackGuard,
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Boolean(bool),
|
||||
String(String),
|
||||
Array(Vec<Type>),
|
||||
Symbol(String),
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn as_bool(&self) -> bool {
|
||||
match self {
|
||||
Type::Null => false,
|
||||
Type::Boolean(b) => *b,
|
||||
Type::Int(i) => *i != 0,
|
||||
Type::Float(f) => *f != 0.0,
|
||||
Type::String(s) => !s.is_empty(),
|
||||
Type::Array(a) => !a.is_empty(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,12 +33,8 @@ impl Type {
|
|||
|
||||
pub fn trim(&self) -> Type {
|
||||
match self {
|
||||
Type::Null => Type::Null,
|
||||
Type::Int(i) => Type::Int(*i),
|
||||
Type::Float(f) => Type::Float(*f),
|
||||
Type::Boolean(b) => Type::Boolean(*b),
|
||||
Type::String(s) => Type::String(s[1..s.len() - 1].to_string()),
|
||||
Type::Array(a) => Type::Array(a.iter().map(|t| t.trim()).collect()),
|
||||
_ => self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,15 +48,7 @@ impl Type {
|
|||
false => "false".to_string(),
|
||||
},
|
||||
Type::String(s) => s.clone(),
|
||||
Type::Array(a) => {
|
||||
let mut s = "[".to_string();
|
||||
for (i, t) in a.iter().enumerate() {
|
||||
s.push_str(&t.fmt());
|
||||
if i < a.len() - 1 { s.push_str(", "); }
|
||||
}
|
||||
s.push_str("]");
|
||||
s
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,19 +113,12 @@ impl Div for Type {
|
|||
impl Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Type::Null => write!(f, ":NULL"),
|
||||
Type::Int(i) => write!(f, ":{}", i),
|
||||
Type::Float(fl) => write!(f, ":{}", fl),
|
||||
Type::Boolean(b) => write!(f, ":{}", b),
|
||||
Type::String(s) => write!(f, "$\"{}\"", s),
|
||||
Type::Array(a) => {
|
||||
write!(f, "[[ ")?;
|
||||
for (i, t) in a.iter().enumerate() {
|
||||
write!(f, "{}", t)?;
|
||||
if i < a.len() - 1 { write!(f, ", ")?; }
|
||||
}
|
||||
write!(f, " ]]")
|
||||
}
|
||||
Type::Symbol(s) => write!(f, "function_{}", s),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,39 +177,41 @@ impl FromStr for Register {
|
|||
/// Instructions for the assembler.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Instr {
|
||||
// Load a literal value onto the stack.
|
||||
// Load { address: Register, label: usize },
|
||||
Label { name: String }, Comment { text: String },
|
||||
// Store a literal value into a register.
|
||||
Store { address: Register, value: Type, label: usize },
|
||||
Store { address: Register, value: Type },
|
||||
// Call intrinsic function.
|
||||
Call { address: Register, args: Register, label: usize },
|
||||
Call { address: Register, args: Register },
|
||||
// Stack operations.
|
||||
Push { value: Type, label: usize }, Pop { address: Register, label: usize },
|
||||
Push { value: Type }, Pop { address: Register },
|
||||
// Stack arithmetic.
|
||||
Add { label: usize }, Sub { label: usize },
|
||||
Mul { label: usize }, Div { label: usize },
|
||||
Add, Sub,
|
||||
Mul, Div,
|
||||
// Jumping
|
||||
Jump { to: usize, label: usize },
|
||||
PopJumpIfFalse { to: usize, label: usize },
|
||||
Jump { to: String },
|
||||
PopJumpIfFalse { to: usize },
|
||||
|
||||
Return { value: Register, label: usize },
|
||||
Return,
|
||||
}
|
||||
|
||||
impl Display for Instr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
// Instr::Load { address, label } => write!(f, "{}: LOAD {}", label, address),
|
||||
Instr::Store { address, value , label} => write!(f, "{} STORE {} {}", label, address, value),
|
||||
Instr::Call { address, args, label } => write!(f, "{} CALL {} {}", label, address, args),
|
||||
Instr::Push { value, label } => write!(f, "{} PUSH {}", label, value),
|
||||
Instr::Pop { address, label } => write!(f, "{} POP {}", label, address),
|
||||
Instr::Add { label } => write!(f, "{} ADD", label),
|
||||
Instr::Sub { label } => write!(f, "{} SUB", label),
|
||||
Instr::Mul { label } => write!(f, "{} MUL", label),
|
||||
Instr::Div { label } => write!(f, "{} DIV", label),
|
||||
Instr::Jump { to, label } => write!(f, "{} JMP {}", label, to),
|
||||
Instr::PopJumpIfFalse { to, label } => write!(f, "{} POP_JUMP_IF_FALSE {}", label, to),
|
||||
Instr::Return { value, label } => write!(f, "{} RETURN {}", label, value),
|
||||
// --4-- Padding
|
||||
// ----------20--------- Parameter start
|
||||
Instr::Label { name } => write!(f, ".{}:", name),
|
||||
Instr::Comment { text } => write!(f, ";{}", text),
|
||||
Instr::Store { address, value } => write!(f, " STORE {} {}", address, value),
|
||||
Instr::Call { address, args } => write!(f, " CALL {} {}", address, args),
|
||||
Instr::Push { value } => write!(f, " PUSH {}", value),
|
||||
Instr::Pop { address } => write!(f, " POP {}", address),
|
||||
Instr::Add => write!(f, " ADD"),
|
||||
Instr::Sub => write!(f, " SUB"),
|
||||
Instr::Mul => write!(f, " MUL"),
|
||||
Instr::Div => write!(f, " DIV"),
|
||||
Instr::Jump { to } => write!(f, " JMP {}", to),
|
||||
Instr::PopJumpIfFalse { to } => write!(f, " PJMPF {}", to),
|
||||
Instr::Return => write!(f, " RET"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ const REGEX: &str = r###"[^\s\$";]+|\$"[^"]*"|;.*"###;
|
|||
|
||||
macro_rules! value { ($s:expr) => { $s.parse::<Type>().unwrap() }; }
|
||||
macro_rules! register { ($s:expr) => { $s.parse::<Register>().unwrap() }; }
|
||||
macro_rules! label { ($s:expr) => { $s.parse::<usize>().unwrap() }; }
|
||||
|
||||
pub fn parse_instr(src: &str) -> Vec<Instr> {
|
||||
let regex = Regex::new(REGEX).unwrap();
|
||||
|
@ -16,44 +15,30 @@ pub fn parse_instr(src: &str) -> Vec<Instr> {
|
|||
let tokens = regex.find_iter(line).map(|m| m.as_str()).collect::<Vec<_>>();
|
||||
if tokens[0].starts_with(";") { continue; }
|
||||
|
||||
let label = label!(tokens[0]);
|
||||
|
||||
match tokens[1] {
|
||||
match tokens[0] {
|
||||
"STORE" => { result.push(Instr::Store {
|
||||
address: register!(tokens[2]),
|
||||
value: value!(tokens[3]),
|
||||
label,
|
||||
address: register!(tokens[1]),
|
||||
value: value!(tokens[2]),
|
||||
}); },
|
||||
"CALL" => { result.push(Instr::Call {
|
||||
address: register!(tokens[2]),
|
||||
args: register!(tokens[3]),
|
||||
label,
|
||||
address: register!(tokens[1]),
|
||||
args: register!(tokens[2]),
|
||||
}); },
|
||||
"PUSH" => { result.push(Instr::Push {
|
||||
value: value!(tokens[2]),
|
||||
label,
|
||||
}); },
|
||||
"POP" => { result.push(Instr::Pop {
|
||||
address: register!(tokens[2]),
|
||||
label,
|
||||
}); },
|
||||
"ADD" => { result.push(Instr::Add { label }); },
|
||||
"SUB" => { result.push(Instr::Sub { label }); },
|
||||
"MUL" => { result.push(Instr::Mul { label }); },
|
||||
"DIV" => { result.push(Instr::Div { label }); },
|
||||
"JMP" => { result.push(Instr::Jump {
|
||||
to: label!(tokens[2]),
|
||||
label,
|
||||
}); },
|
||||
"POP_JUMP_IF_FALSE" => { result.push(Instr::PopJumpIfFalse {
|
||||
to: label!(tokens[2]),
|
||||
label,
|
||||
}); },
|
||||
"RETURN" => { result.push(Instr::Return {
|
||||
value: register!(tokens[2]),
|
||||
label,
|
||||
}); },
|
||||
_ => panic!("Unknown instruction: {}", tokens[1]),
|
||||
"PUSH" => { result.push(Instr::Push { value: value!(tokens[1]) }); },
|
||||
"POP" => { result.push(Instr::Pop { address: register!(tokens[1]) }); },
|
||||
"ADD" => { result.push(Instr::Add); },
|
||||
"SUB" => { result.push(Instr::Sub); },
|
||||
"MUL" => { result.push(Instr::Mul); },
|
||||
"DIV" => { result.push(Instr::Div); },
|
||||
"JMP" => { result.push(Instr::Jump { to: tokens[1].to_string() }); },
|
||||
"PJMPF" => todo!(),
|
||||
"RET" => { result.push(Instr::Return); },
|
||||
_ => {
|
||||
if tokens[0].starts_with(".") {
|
||||
let name = &tokens[0][1..tokens[0].len() - 1];
|
||||
result.push(Instr::Label { name: name.to_string() });
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@ use std::fmt::Display;
|
|||
use crate::vm::instr::{Instr::{self, *}, Type, Register};
|
||||
|
||||
pub enum Error {
|
||||
NoMainFunction,
|
||||
StackOverflow,
|
||||
UnknownFunction(String),
|
||||
UnknownFunctionCall(isize, isize),
|
||||
InvalidAriphmeticOperation,
|
||||
}
|
||||
|
@ -11,7 +13,9 @@ pub enum Error {
|
|||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::NoMainFunction => write!(f, "Main function not found"),
|
||||
Error::StackOverflow => write!(f, "Stack overflow"),
|
||||
Error::UnknownFunction(name) => write!(f, "Unknown function: {}", name),
|
||||
Error::UnknownFunctionCall(l, e) => write!(f, "Unknown function call at {}: {}", l, e),
|
||||
Error::InvalidAriphmeticOperation => write!(f, "Invalid ariphmetic operation"),
|
||||
}
|
||||
|
@ -20,9 +24,11 @@ impl Display for Error {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct VM {
|
||||
pub instr_pointer: isize,
|
||||
pub registers: Vec<Type>,
|
||||
pub stack: Vec<Type>,
|
||||
instr_pointer: isize,
|
||||
jumped_from: isize,
|
||||
registers: Vec<Type>,
|
||||
stack: Vec<Type>,
|
||||
function_pointer: Vec<(String, isize)>, // (name, index)
|
||||
}
|
||||
|
||||
pub type VMReturn = Result<(), Error>;
|
||||
|
@ -31,18 +37,34 @@ impl VM {
|
|||
pub fn new() -> Self {
|
||||
VM {
|
||||
instr_pointer: 0,
|
||||
jumped_from: 0,
|
||||
registers: vec![Type::Null; 1024],
|
||||
stack: Vec::new(),
|
||||
function_pointer: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, instrs: Vec<Instr>, debug: bool) -> VMReturn {
|
||||
let mut result: VMReturn;
|
||||
|
||||
for (idx, instr) in instrs.iter().enumerate() {
|
||||
match instr {
|
||||
Label { name } => {
|
||||
if name == "function_main" { self.instr_pointer = idx as isize; }
|
||||
self.function_pointer.push((name.clone(), idx as isize));
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
'tco: loop {
|
||||
self.instr_pointer += 1;
|
||||
if self.instr_pointer - 1 == instrs.len() as isize { return Ok(()); }
|
||||
|
||||
let instr = &instrs[self.instr_pointer as usize - 1];
|
||||
if debug { print_debug(&self, instr); }
|
||||
if self.instr_pointer - 1 == instrs.len() as isize {
|
||||
result = Ok(());
|
||||
break 'tco;
|
||||
}
|
||||
|
||||
let instr = &instrs[(self.instr_pointer - 1) as usize];
|
||||
match instr {
|
||||
Store { address, value, .. } => {
|
||||
self.store(&address, &value)?;
|
||||
|
@ -54,51 +76,57 @@ impl VM {
|
|||
call(address, args, self.instr_pointer)?;
|
||||
continue 'tco;
|
||||
},
|
||||
Push { value, .. } => {
|
||||
Push { value } => {
|
||||
self.push(value.trim().clone())?;
|
||||
continue 'tco;
|
||||
},
|
||||
Pop { address, .. } => {
|
||||
Pop { address } => {
|
||||
let value = self.stack.pop();
|
||||
self.store(&address, &value.unwrap())?;
|
||||
continue 'tco;
|
||||
},
|
||||
Add { .. } => {
|
||||
Add => {
|
||||
let lhs = self.stack.pop().unwrap();
|
||||
let rhs = self.stack.pop().unwrap();
|
||||
self.push((lhs + rhs)?)?;
|
||||
continue 'tco;
|
||||
},
|
||||
Sub { .. } => {
|
||||
Sub => {
|
||||
let lhs = self.stack.pop().unwrap();
|
||||
let rhs = self.stack.pop().unwrap();
|
||||
self.push((lhs - rhs)?)?;
|
||||
continue 'tco;
|
||||
},
|
||||
Mul { .. } => {
|
||||
Mul => {
|
||||
let lhs = self.stack.pop().unwrap();
|
||||
let rhs = self.stack.pop().unwrap();
|
||||
self.push((lhs * rhs)?)?;
|
||||
continue 'tco;
|
||||
},
|
||||
Div { .. } => {
|
||||
Div => {
|
||||
let lhs = self.stack.pop().unwrap();
|
||||
let rhs = self.stack.pop().unwrap();
|
||||
self.push((lhs / rhs)?)?;
|
||||
continue 'tco;
|
||||
},
|
||||
Jump { to, .. } => {
|
||||
self.instr_pointer = *to as isize - 1;
|
||||
Jump { to } => {
|
||||
let pointer = self.get_function_pointer(to.to_string())?;
|
||||
self.jumped_from = self.instr_pointer;
|
||||
self.instr_pointer = pointer;
|
||||
continue 'tco;
|
||||
},
|
||||
PopJumpIfFalse { to, .. } => {
|
||||
let value = self.stack.pop().unwrap();
|
||||
if !value.as_bool() { self.instr_pointer = *to as isize - 1; }
|
||||
Return => {
|
||||
if self.jumped_from == 0 { return Ok(()); }
|
||||
self.instr_pointer = self.jumped_from;
|
||||
self.jumped_from = 0;
|
||||
continue 'tco;
|
||||
},
|
||||
Return { .. } => return Ok(()),
|
||||
};
|
||||
Label { .. } => {},
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn push(&mut self, value: Type) -> Result<(), Error> {
|
||||
|
@ -112,6 +140,15 @@ impl VM {
|
|||
// TODO: Remove .clone()
|
||||
Ok(self.registers[address.value()] = value.clone())
|
||||
}
|
||||
|
||||
fn get_function_pointer(&mut self, name: String) -> Result<isize, Error> {
|
||||
for (idx, (n, _)) in self.function_pointer.iter().enumerate() {
|
||||
if n == &name {
|
||||
return Ok(idx as isize);
|
||||
}
|
||||
}
|
||||
Err(Error::UnknownFunction(name))
|
||||
}
|
||||
}
|
||||
|
||||
fn print_debug(vm: &VM, curr_instr: &Instr) {
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
(print "Hello, world!")
|
||||
(fun str "Hello, world!")
|
||||
|
||||
(fun main
|
||||
(print str))
|
|
@ -1,6 +0,0 @@
|
|||
(fun factorial (x)
|
||||
(if (<= x 1)
|
||||
1
|
||||
(* x (factorial (- x 1)))))
|
||||
(do
|
||||
(print (factorial 7)))
|
12
test.sh
Executable file
12
test.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
in=$1 # Get first's file name
|
||||
name=${in%.*} # Remove extension
|
||||
|
||||
make debug
|
||||
blspc compile $name.blsp
|
||||
echo -e "------------------------------------------- SOURCE"
|
||||
cat ./$name.blsp
|
||||
echo -e "\n----------------------------------------- COMPILED"
|
||||
cat ./$name.bsm
|
||||
echo -e "------------------------------------------- OUTPUT"
|
||||
blspc run $name.bsm
|
||||
echo -e "--------------------------------------------------"
|
Loading…
Reference in a new issue