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