added operator typehinting

This commit is contained in:
Sven Vogel 2022-12-06 15:02:03 +01:00
parent ee1418c673
commit edcfbea81d
10 changed files with 197 additions and 77 deletions

View File

@ -8,6 +8,6 @@ edition = "2021"
[dependencies] [dependencies]
regex = "*" regex = "*"
lazy_static = "1.4.0" lazy_static = "1.4.0"
rand = "0.8.5" rand = "*"
colored = "*" colored = "*"

10
prog.vsasm Normal file
View File

@ -0,0 +1,10 @@
0xc8c7a99bb88e156b:
push int 0x2
push int 0x4
cmp eq int
jump-unless 0x6
push int 0x1
ret
push int 0x0
ret

View File

@ -1,31 +1,40 @@
use crate::parser::data::Diagnostics; use crate::parser::data::Diagnostics;
#[derive(Default)]
pub struct Settings { pub struct Settings {
gen_erpn: bool gen_erpn: bool,
gen_vsasm: bool,
} }
impl Settings { impl Settings {
fn new() -> Self {
Settings {
gen_erpn: false
}
}
pub fn gen_erpn(&self) -> bool { pub fn gen_erpn(&self) -> bool {
self.gen_erpn self.gen_erpn
} }
pub fn gen_vsasm(&self) -> bool {
self.gen_vsasm
}
} }
pub fn parse_args(diagnostics: &mut Diagnostics) -> Settings { pub fn parse_args(diagnostics: &mut Diagnostics) -> Settings {
let args = std::env::args().collect::<Vec<String>>(); let args = std::env::args().collect::<Vec<String>>();
let mut settings = Settings::new(); let mut settings = Settings::default();
for arg in args.iter() { for arg in args.iter() {
match arg.as_str() { match arg.as_str() {
"--no-info" => diagnostics.set_loglvl(crate::parser::data::LogLvl::Warn), "--no-info" => diagnostics.set_loglvl(crate::parser::data::LogLvl::Warn),
"--no-warn" => diagnostics.set_loglvl(crate::parser::data::LogLvl::Err), "--no-warn" => diagnostics.set_loglvl(crate::parser::data::LogLvl::Err),
"--erpn" => settings.gen_erpn = true, "--erpn" => settings.gen_erpn = true,
"--vsasm" => settings.gen_vsasm = true,
"-h" => println!(concat!(
"help:\n",
"--no-info: print only warning and errors\n",
"--no-warn: print only errors\n",
"--erpn: write a *.erpn (extended reverse polish notation) summary to disk\n",
"--vsasm: write a .vsasm (virtual simplified assembly language) summary to disk\n",
"-h: print this dialog"
)),
_ => () _ => ()
} }
} }

View File

@ -35,5 +35,6 @@ pub fn convert_to_erpn<'a>(funcs: &mut Vec<Func<'a>>, declrs: &Vec<Declr<'a>>) {
// write down function body // write down function body
write_expr(&mut file, &mut indent, func.expr.as_ref().unwrap()); write_expr(&mut file, &mut indent, func.expr.as_ref().unwrap());
writeln!(&mut file).unwrap();
} }
} }

View File

@ -18,20 +18,20 @@ pub fn message<S>(typ: MessageType, msg: S)
where where
S: Into<String>, S: Into<String>,
{ {
println!("{}: {}", typ.to_colored(), msg.into().bold().bright_white()); println!("{}: {}\n", typ.to_colored(), msg.into().bold().bright_white());
} }
fn main() { fn main() {
let source = r" let source = r"
-- structs are tables
foo(x:int) = int {
x / 2
}
main() = int { main() = int {
a = foo(4)
unless 2 == 4 {
a yield 1;
}
yield 0;
} }
"; ";

View File

@ -324,13 +324,14 @@ fn discover_exprs<'a>(
} }
expr = VecDeque::new(); expr = VecDeque::new();
blocks.push(Block::new()); blocks.push(Block::new());
continue; continue;
} }
'}' => { '}' => {
// pop topmost block of the stack, storing it in the next lower block // pop topmost block of the stack, storing it in the next lower block
if let Some(block) = blocks.pop() { if let Some(block) = blocks.pop() {
if let Some(dst) = blocks.last_mut() { if let Some(dst) = blocks.last_mut() {
dst.push_back(Expr::Block(block)); dst.push_back(Expr::Block(block));
} else { } else {
diagnostics.set_err(&top, crate::msg::ERR41, ""); diagnostics.set_err(&top, crate::msg::ERR41, "");
return Err(()); return Err(());
@ -346,7 +347,7 @@ fn discover_exprs<'a>(
} }
continue; continue;
} },
_ => (), _ => (),
}, },
_ => (), _ => (),
@ -356,7 +357,7 @@ fn discover_exprs<'a>(
} }
if !expr.is_empty() { if !expr.is_empty() {
if let Some(block) = blocks.last_mut() { if let Some(block) = blocks.last_mut() {
block.push_back(Expr::Term(expr)); block.push_back(Expr::Term(expr));
} else { } else {
diagnostics.set_err(expr.back().unwrap(), crate::msg::ERR40, ""); diagnostics.set_err(expr.back().unwrap(), crate::msg::ERR40, "");
@ -613,7 +614,13 @@ fn parse_term<'a>(
Token::Assign(_, _, _) => { Token::Assign(_, _, _) => {
op_stack.push(token); op_stack.push(token);
} }
Token::Keyword(_, _) => op_stack.push(token), Token::Keyword(key, _) => {
match key {
Keyword::Unless => (),
_ => ()
}
op_stack.push(token)
},
Token::Delemiter(char, _) => match char { Token::Delemiter(char, _) => match char {
'(' => op_stack.push(token), '(' => op_stack.push(token),
@ -689,6 +696,7 @@ fn parse_term<'a>(
} }
_ => (), _ => (),
} }
} }
while let Some(mut token) = op_stack.pop() { while let Some(mut token) = op_stack.pop() {

View File

@ -161,11 +161,10 @@ impl Operator {
diagnostics: &mut crate::parser::data::Diagnostics, diagnostics: &mut crate::parser::data::Diagnostics,
) -> Result<(Option<Prim>, Option<Prim>),()> { ) -> Result<(Option<Prim>, Option<Prim>),()> {
for combination in types.iter() { for combination in types.iter() {
if let (result, hint) = let (result, hint) =
self.present_types(operands, combination.0, combination.1, dbginf, diagnostics)? self.present_types(operands, combination.0, combination.1, dbginf, diagnostics)?;
{
return Ok((result, hint)); return Ok((result, hint));
}
} }
Ok((None, None)) Ok((None, None))
} }
@ -500,7 +499,7 @@ pub enum Token<'a> {
Keyword(Keyword, DebugInfo), Keyword(Keyword, DebugInfo),
Type(Prim, DebugInfo), Type(Prim, DebugInfo),
/// Semicolon /// Semicolon
Terminator(DebugInfo), Terminator(DebugInfo)
} }
impl<'a> std::fmt::Display for Token<'a> { impl<'a> std::fmt::Display for Token<'a> {

View File

@ -12,7 +12,17 @@ enum Data {
Int(i64), Int(i64),
Rat(f64), Rat(f64),
Bool(bool), Bool(bool),
Off(u64), }
impl std::fmt::Display for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Data::Int(v) => f.write_fmt(format_args!("int {:#x}", v))?,
Data::Rat(v) => f.write_fmt(format_args!("float {}", v))?,
Data::Bool(v) => f.write_fmt(format_args!("bool {}", v))?
}
Ok(())
}
} }
impl Data { impl Data {
@ -95,7 +105,7 @@ enum Instr {
/// push the value stored at offset from the local stack onto the local stack /// push the value stored at offset from the local stack onto the local stack
Load(usize), Load(usize),
/// store the value stored at the stack[0](offset) stack[1](value) onto the stack /// store the value stored at the stack[0](offset) stack[1](value) onto the stack
Store, Store(usize),
Call(u64), Call(u64),
@ -103,9 +113,67 @@ enum Instr {
Operation(Operation), Operation(Operation),
Jump(usize),
JumpUnless(usize),
Exit, Exit,
} }
impl std::fmt::Display for Instr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Instr::Pop => f.write_str("\tpop")?,
Instr::Exit => f.write_str("\texit")?,
Instr::Return => f.write_str("\tret")?,
Instr::Load(offset) => f.write_fmt(format_args!("\tload {:#x}", offset))?,
Instr::Push(value) => f.write_fmt(format_args!("\tpush {}", value))?,
Instr::Call(uuid) => f.write_fmt(format_args!("\tcall {:#x}", uuid))?,
Instr::Jump(offset) => f.write_fmt(format_args!("\tjump {:#x}", offset))?,
Instr::JumpUnless(offset) => f.write_fmt(format_args!("\tjump-unless {:#x}", offset))?,
Instr::Operation(op) => {
match op {
Operation::Int(op) => match op {
IntOp::Add => f.write_str("\tadd int")?,
IntOp::Sub => f.write_str("\tsub int")?,
IntOp::Mul => f.write_str("\tmul int")?,
IntOp::Div => f.write_str("\tdiv int")?,
IntOp::CmpEq => f.write_str("\tcmp eq int")?,
IntOp::CmpNEq => f.write_str("\tcmp not-eq int")?,
IntOp::CmpLt => f.write_str("\tcmp lt eq int")?,
IntOp::CmpGt => f.write_str("\tcmp gt int")?,
IntOp::CmpLtEq => f.write_str("\tcmp lt-eq int")?,
IntOp::CmpGtEq => f.write_str("\tcmp gt-eq int")?,
},
Operation::Rat(op) => match op {
RatOp::Add => f.write_str("\tadd float")?,
RatOp::Sub => f.write_str("\tsub float")?,
RatOp::Mul => f.write_str("\tmul float")?,
RatOp::Div => f.write_str("\tdiv float")?,
RatOp::CmpEq => f.write_str("\tcmp eq float")?,
RatOp::CmpNEq => f.write_str("\tcmp not-eq float")?,
RatOp::CmpLt => f.write_str("\tcmp lt eq float")?,
RatOp::CmpGt => f.write_str("\tcmp gt float")?,
RatOp::CmpLtEq => f.write_str("\tcmp lt-eq float")?,
RatOp::CmpGtEq => f.write_str("\tcmp gt-eq float")?,
},
Operation::Bool(op) => match op {
BoolOp::And => f.write_str("\tand")?,
BoolOp::Or => f.write_str("\tor")?,
BoolOp::Xor => f.write_str("\txor")?,
BoolOp::CmpNEq => f.write_str("\tcmp not-eq bool")?,
BoolOp::CmpEq => f.write_str("\tcmp eq bool")?,
}
}
},
Instr::Store(offset) => f.write_fmt(format_args!("\tstore {:#x}", offset))?,
}
Ok(())
}
}
/// function stack layout /// function stack layout
/// +----------------------------------+ /// +----------------------------------+
/// | Parameter (0) | /// | Parameter (0) |
@ -130,9 +198,16 @@ pub struct Program {
procs: HashMap<u64, Proc>, procs: HashMap<u64, Proc>,
} }
enum LabelType {
Unless(usize),
Loop(usize),
Pad
}
#[derive(Default)] #[derive(Default)]
struct Compiletime<'a> { struct Compiletime<'a> {
vartable: HashMap<&'a str, usize>, vartable: HashMap<&'a str, usize>,
labels: Vec<LabelType>,
stacksize: usize, stacksize: usize,
} }
@ -143,8 +218,9 @@ fn parse_term<'a>(
ct: &mut Compiletime<'a>, ct: &mut Compiletime<'a>,
code: &mut Vec<Instr>, code: &mut Vec<Instr>,
) -> Result<(), ()> { ) -> Result<(), ()> {
for token in term.iter() { for token in term.iter() {
let instr = match token { match token {
Token::Number(value, hint, _) => { Token::Number(value, hint, _) => {
code.push(Instr::Push(match hint { code.push(Instr::Push(match hint {
NumHint::Int => Data::Int(value.parse::<i64>().unwrap()), NumHint::Int => Data::Int(value.parse::<i64>().unwrap()),
@ -158,12 +234,12 @@ fn parse_term<'a>(
} }
Token::Arg(name, _) => { Token::Arg(name, _) => {
let off = declr[x].get_arg_ord(name); let off = declr[x].get_arg_ord(name);
code.push(Instr::Load(off)); code.push(Instr::Load(off));
ct.stacksize += 1; ct.stacksize += 1;
} }
Token::Assign(name, _, _) => { Token::Assign(name, _, _) => {
ct.vartable.insert(name.clone(), ct.stacksize - 1); ct.vartable.insert(name.clone(), ct.stacksize - 1);
code.push(Instr::Store(ct.stacksize - 1));
} }
Token::Var(name, _) => { Token::Var(name, _) => {
code.push(Instr::Load(*ct.vartable.get(name).unwrap())); code.push(Instr::Load(*ct.vartable.get(name).unwrap()));
@ -254,10 +330,13 @@ fn parse_term<'a>(
Token::Keyword(keyword, _) => match keyword { Token::Keyword(keyword, _) => match keyword {
crate::token::Keyword::Yield | crate::token::Keyword::Return => { crate::token::Keyword::Yield | crate::token::Keyword::Return => {
code.push(Instr::Return) code.push(Instr::Return)
},
crate::token::Keyword::Unless => {
ct.labels.push(LabelType::Unless(code.len()));
code.push(Instr::JumpUnless(0));
} }
_ => (), _ => (),
}, },
_ => (), _ => (),
}; };
} }
@ -271,9 +350,24 @@ fn parse_block<'a>(
ct: &mut Compiletime<'a>, ct: &mut Compiletime<'a>,
prog: &mut Vec<Instr>, prog: &mut Vec<Instr>,
) -> Result<(), ()> { ) -> Result<(), ()> {
ct.labels.push(LabelType::Pad);
for expr in block.iter() { for expr in block.iter() {
compile_expr(expr, x, declr, ct, prog)?; compile_expr(expr, x, declr, ct, prog)?;
} }
// resolve JumpUnless Labels
ct.labels.pop();
if let Some(label) = ct.labels.pop() {
match label {
LabelType::Unless(line) => {
prog[line] = Instr::JumpUnless(prog.len());
},
_ => ()
}
}
Ok(()) Ok(())
} }
@ -320,7 +414,9 @@ pub fn compile<'a>(funcs: &Vec<Func<'a>>, declrs: &Vec<Declr<'a>>, settings: &cr
.insert(declrs[x].uuid(), create_proc(&declrs[x], code)); .insert(declrs[x].uuid(), create_proc(&declrs[x], code));
} }
if settings.gen_vsasm() {
output::dump_program(&prog);
}
Ok(prog) Ok(prog)
} }
@ -345,6 +441,14 @@ impl Runtimestack {
pub fn peek(&mut self, index: usize) -> Data { pub fn peek(&mut self, index: usize) -> Data {
self.stack[index] self.stack[index]
} }
pub fn top(&self) -> &Data {
&self.stack[self.stack.len() - 1]
}
pub fn put(&mut self, offset: usize, value: Data) {
self.stack[offset] = value;
}
} }
fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result<Option<Data>, ()> { fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result<Option<Data>, ()> {
@ -354,9 +458,10 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result<Option<Da
for i in 0..proc.args { for i in 0..proc.args {
stack.push(superstack[superstack.len() - i - 1].clone()); stack.push(superstack[superstack.len() - i - 1].clone());
} }
for instr in proc.code.iter() { let mut x = 0;
match instr { while x < proc.code.len() {
match &proc.code[x] {
Instr::Pop => { Instr::Pop => {
stack.pop(); stack.pop();
} }
@ -368,11 +473,28 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result<Option<Da
let v = stack.peek(*offset); let v = stack.peek(*offset);
stack.push(v); stack.push(v);
}, },
Instr::Store(offset) => {
let data = stack.top().clone();
stack.put(*offset, data);
},
Instr::Call(addr) => { Instr::Call(addr) => {
if let Some(value) = call_fn(prog, prog.procs.get(addr).unwrap(), &stack.stack)? { if let Some(value) = call_fn(prog, prog.procs.get(addr).unwrap(), &stack.stack)? {
stack.push(value); stack.push(value);
} }
}, },
Instr::Jump(addr) => {
x = *addr - 1;
},
Instr::JumpUnless(addr) => {
match stack.pop() {
Some(Data::Bool(false)) => x = *addr - 1,
Some(Data::Bool(true)) => (),
_ => {
crate::message(crate::token::MessageType::Critical, "no condition for unless on stack");
panic!();
}
}
},
Instr::Operation(op) => { Instr::Operation(op) => {
let op0:Data = if let Some(data) = stack.pop() { let op0:Data = if let Some(data) = stack.pop() {
data data
@ -461,6 +583,7 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result<Option<Da
} }
_ => (), _ => (),
} }
x += 1;
} }
Ok(stack.pop()) Ok(stack.pop())
@ -480,7 +603,7 @@ pub fn execute(prog: &Program) -> Result<i64, ()> {
return Err(()); return Err(());
} else { } else {
crate::message( crate::message(
crate::token::MessageType::Critical, crate::token::MessageType::Error,
"Program has no main() = int function", "Program has no main() = int function",
); );
return Err(()); return Err(());

View File

@ -2,7 +2,7 @@ use std::io::Write;
fn print_to_file(file: &mut std::fs::File, prog: &crate::vmrt::Program) -> std::io::Result<()> { fn print_to_file(file: &mut std::fs::File, prog: &crate::vmrt::Program) -> std::io::Result<()> {
for proc in prog.procs.iter() { for proc in prog.procs.iter() {
writeln!(file, "\n0x{:#x}:", proc.0)?; writeln!(file, "\n{:#x}:", proc.0)?;
for instr in proc.1.code.iter() { for instr in proc.1.code.iter() {
writeln!(file, "{}", instr)?; writeln!(file, "{}", instr)?;

View File

@ -1,40 +1,10 @@
pi:
Load Rat 5.1415926535
foo:
Load Int 5
Load Int 6
Add Int
Store Int x
Load Arg x
Load Int 6
Lt Int
Unless
Load Bool true
Yield
Please
Load Arg x
Load Int 6
Lt Int
Unless
Load Bool true
Yield
Load Bool true
Yield
main: main:
Load Int 2
Load Int 4 Load Int 4
Store Int a Eq Int
Call pi Unless
Store Rat b Load Int 1
Load Bool true Yield
Store Bool c Load Int 0
Load Int 3 Yield
Load Rat 4.0
Call foo
Store Bool r
Load Int 3
Load Rat 5.0
Call foo
Store Bool h
Load Int 4
Store Int b
Load Int 9