diff --git a/Cargo.toml b/Cargo.toml index 5ee8a6d..1e18722 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,6 @@ edition = "2021" [dependencies] regex = "*" lazy_static = "1.4.0" -rand = "0.8.5" +rand = "*" colored = "*" diff --git a/prog.vsasm b/prog.vsasm new file mode 100644 index 0000000..c385967 --- /dev/null +++ b/prog.vsasm @@ -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 diff --git a/src/conf/mod.rs b/src/conf/mod.rs index 13ff46c..eb02ea8 100644 --- a/src/conf/mod.rs +++ b/src/conf/mod.rs @@ -1,31 +1,40 @@ use crate::parser::data::Diagnostics; +#[derive(Default)] pub struct Settings { - gen_erpn: bool + gen_erpn: bool, + gen_vsasm: bool, } impl Settings { - fn new() -> Self { - Settings { - gen_erpn: false - } - } - pub fn gen_erpn(&self) -> bool { self.gen_erpn } + + pub fn gen_vsasm(&self) -> bool { + self.gen_vsasm + } } pub fn parse_args(diagnostics: &mut Diagnostics) -> Settings { let args = std::env::args().collect::>(); - let mut settings = Settings::new(); + let mut settings = Settings::default(); for arg in args.iter() { match arg.as_str() { "--no-info" => diagnostics.set_loglvl(crate::parser::data::LogLvl::Warn), "--no-warn" => diagnostics.set_loglvl(crate::parser::data::LogLvl::Err), "--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" + )), _ => () } } diff --git a/src/inter/mod.rs b/src/inter/mod.rs index 021a9f5..822b83d 100644 --- a/src/inter/mod.rs +++ b/src/inter/mod.rs @@ -35,5 +35,6 @@ pub fn convert_to_erpn<'a>(funcs: &mut Vec>, declrs: &Vec>) { // write down function body write_expr(&mut file, &mut indent, func.expr.as_ref().unwrap()); + writeln!(&mut file).unwrap(); } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index eb2e3f7..d836ba2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,20 +18,20 @@ pub fn message(typ: MessageType, msg: S) where S: Into, { - println!("{}: {}", typ.to_colored(), msg.into().bold().bright_white()); + println!("{}: {}\n", typ.to_colored(), msg.into().bold().bright_white()); } fn main() { let source = r" - -foo(x:int) = int { - x / 2 -} +-- structs are tables main() = int { - a = foo(4) - - a + + unless 2 == 4 { + yield 1; + } + + yield 0; } "; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index fcdef03..db7fae7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -324,13 +324,14 @@ fn discover_exprs<'a>( } expr = VecDeque::new(); blocks.push(Block::new()); + continue; } '}' => { // pop topmost block of the stack, storing it in the next lower block if let Some(block) = blocks.pop() { if let Some(dst) = blocks.last_mut() { - dst.push_back(Expr::Block(block)); + dst.push_back(Expr::Block(block)); } else { diagnostics.set_err(&top, crate::msg::ERR41, ""); return Err(()); @@ -346,7 +347,7 @@ fn discover_exprs<'a>( } continue; - } + }, _ => (), }, _ => (), @@ -356,7 +357,7 @@ fn discover_exprs<'a>( } if !expr.is_empty() { - if let Some(block) = blocks.last_mut() { + if let Some(block) = blocks.last_mut() { block.push_back(Expr::Term(expr)); } else { diagnostics.set_err(expr.back().unwrap(), crate::msg::ERR40, ""); @@ -613,7 +614,13 @@ fn parse_term<'a>( Token::Assign(_, _, _) => { 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 { '(' => op_stack.push(token), @@ -689,6 +696,7 @@ fn parse_term<'a>( } _ => (), } + } while let Some(mut token) = op_stack.pop() { diff --git a/src/token/mod.rs b/src/token/mod.rs index 6bccd46..b0a9e4f 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -161,11 +161,10 @@ impl Operator { diagnostics: &mut crate::parser::data::Diagnostics, ) -> Result<(Option, Option),()> { for combination in types.iter() { - if let (result, hint) = - self.present_types(operands, combination.0, combination.1, dbginf, diagnostics)? - { - return Ok((result, hint)); - } + let (result, hint) = + self.present_types(operands, combination.0, combination.1, dbginf, diagnostics)?; + + return Ok((result, hint)); } Ok((None, None)) } @@ -500,7 +499,7 @@ pub enum Token<'a> { Keyword(Keyword, DebugInfo), Type(Prim, DebugInfo), /// Semicolon - Terminator(DebugInfo), + Terminator(DebugInfo) } impl<'a> std::fmt::Display for Token<'a> { diff --git a/src/vmrt/mod.rs b/src/vmrt/mod.rs index 91f5ae7..6cc680f 100644 --- a/src/vmrt/mod.rs +++ b/src/vmrt/mod.rs @@ -12,7 +12,17 @@ enum Data { Int(i64), Rat(f64), 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 { @@ -95,7 +105,7 @@ enum Instr { /// push the value stored at offset from the local stack onto the local stack Load(usize), /// store the value stored at the stack[0](offset) stack[1](value) onto the stack - Store, + Store(usize), Call(u64), @@ -103,9 +113,67 @@ enum Instr { Operation(Operation), + Jump(usize), + JumpUnless(usize), + 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 /// +----------------------------------+ /// | Parameter (0) | @@ -130,9 +198,16 @@ pub struct Program { procs: HashMap, } +enum LabelType { + Unless(usize), + Loop(usize), + Pad +} + #[derive(Default)] struct Compiletime<'a> { vartable: HashMap<&'a str, usize>, + labels: Vec, stacksize: usize, } @@ -143,8 +218,9 @@ fn parse_term<'a>( ct: &mut Compiletime<'a>, code: &mut Vec, ) -> Result<(), ()> { + for token in term.iter() { - let instr = match token { + match token { Token::Number(value, hint, _) => { code.push(Instr::Push(match hint { NumHint::Int => Data::Int(value.parse::().unwrap()), @@ -158,12 +234,12 @@ fn parse_term<'a>( } Token::Arg(name, _) => { let off = declr[x].get_arg_ord(name); - code.push(Instr::Load(off)); ct.stacksize += 1; } Token::Assign(name, _, _) => { ct.vartable.insert(name.clone(), ct.stacksize - 1); + code.push(Instr::Store(ct.stacksize - 1)); } Token::Var(name, _) => { code.push(Instr::Load(*ct.vartable.get(name).unwrap())); @@ -254,10 +330,13 @@ fn parse_term<'a>( Token::Keyword(keyword, _) => match keyword { crate::token::Keyword::Yield | crate::token::Keyword::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>, prog: &mut Vec, ) -> Result<(), ()> { + + ct.labels.push(LabelType::Pad); + for expr in block.iter() { 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(()) } @@ -320,7 +414,9 @@ pub fn compile<'a>(funcs: &Vec>, declrs: &Vec>, settings: &cr .insert(declrs[x].uuid(), create_proc(&declrs[x], code)); } - + if settings.gen_vsasm() { + output::dump_program(&prog); + } Ok(prog) } @@ -345,6 +441,14 @@ impl Runtimestack { pub fn peek(&mut self, index: usize) -> Data { 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, ()> { @@ -354,9 +458,10 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result { stack.pop(); } @@ -368,11 +473,28 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result { + let data = stack.top().clone(); + stack.put(*offset, data); + }, Instr::Call(addr) => { if let Some(value) = call_fn(prog, prog.procs.get(addr).unwrap(), &stack.stack)? { 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) => { let op0:Data = if let Some(data) = stack.pop() { data @@ -461,6 +583,7 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result (), } + x += 1; } Ok(stack.pop()) @@ -480,7 +603,7 @@ pub fn execute(prog: &Program) -> Result { return Err(()); } else { crate::message( - crate::token::MessageType::Critical, + crate::token::MessageType::Error, "Program has no main() = int function", ); return Err(()); diff --git a/src/vmrt/output.rs b/src/vmrt/output.rs index d74631b..4f49ccf 100644 --- a/src/vmrt/output.rs +++ b/src/vmrt/output.rs @@ -2,7 +2,7 @@ use std::io::Write; fn print_to_file(file: &mut std::fs::File, prog: &crate::vmrt::Program) -> std::io::Result<()> { 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() { writeln!(file, "{}", instr)?; diff --git a/test.erpn b/test.erpn index cc24e30..794496f 100644 --- a/test.erpn +++ b/test.erpn @@ -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: + Load Int 2 Load Int 4 - Store Int a - Call pi - Store Rat b - Load Bool true - Store Bool c - Load Int 3 - 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 + Eq Int + Unless + Load Int 1 + Yield + Load Int 0 + Yield +