diff --git a/.gitignore b/.gitignore index ea8c4bf..5f526f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +*.erpn +*.vsasm diff --git a/prog.vsasm b/prog.vsasm index c385967..f420973 100644 --- a/prog.vsasm +++ b/prog.vsasm @@ -1,10 +1,16 @@ 0xc8c7a99bb88e156b: - push int 0x2 - push int 0x4 - cmp eq int - jump-unless 0x6 - push int 0x1 - ret push int 0x0 + store 0x0 + load 0x0 + push int 0x4 + add int + store 0x0 + load 0x0 + push int 0x10 + cmp lt eq int + jump-unless 0xb + jump 0xc + jump 0x2 + load 0x0 ret diff --git a/src/main.rs b/src/main.rs index d836ba2..910005c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,12 +26,18 @@ fn main() { -- structs are tables main() = int { + + x:rat = 0.0; - unless 2 == 4 { - yield 1; + loop { + x = x + 4 + + unless x < 16 { + break + } } - yield 0; + yield x; } "; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index db7fae7..489b96c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -614,6 +614,7 @@ fn parse_term<'a>( Token::Assign(_, _, _) => { op_stack.push(token); } + Token::Label(_, _) => output.push_back(token), Token::Keyword(key, _) => { match key { Keyword::Unless => (), diff --git a/src/parser/msg.rs b/src/parser/msg.rs index 104e0a5..a8b5e2b 100644 --- a/src/parser/msg.rs +++ b/src/parser/msg.rs @@ -200,3 +200,8 @@ pub const ERR74: &DebugMsg = &DebugMsg { code: 74, msg: "Missing operands", }; +pub const ERR75: &DebugMsg = &DebugMsg { + typ: crate::token::MessageType::Critical, + code: 75, + msg: "Unknown operation", +}; diff --git a/src/token/mod.rs b/src/token/mod.rs index b0a9e4f..22575a8 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -1,5 +1,5 @@ use colored::{ColoredString, Colorize}; -use std::collections::VecDeque; +use std::{collections::VecDeque, fmt::format}; use crate::parser::data::Diagnostics; @@ -78,7 +78,8 @@ impl Operator { "-" => Operator::Sub, "*" => Operator::Mul, "/" => Operator::Div, - "=" => Operator::Assign, + "=" => { + Operator::Assign }, _ => { crate::message(MessageType::Critical, "Unknown operator"); @@ -248,14 +249,15 @@ impl Operator { } } _ => { - panic!("Unknown operator"); + diagnostics.set_err(info, crate::msg::ERR75, format!("token {:?}", self)); + return Err(()); } } } } #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum Keyword { +pub enum Keyword<'a> { Unless, While, /// while(true) loop @@ -265,10 +267,17 @@ pub enum Keyword { Return, Yield, Please, + Goto(&'a str), } -impl Keyword { - pub fn parse<'a>(text: &'a str) -> Keyword { +const GOTO_REGEX_SRC: &'static str = r"goto\s+([a-zA-Z0-9]+)"; + +lazy_static::lazy_static! { + static ref GOTO_REGEX: regex::Regex = regex::Regex::new(GOTO_REGEX_SRC).unwrap(); +} + +impl<'a> Keyword<'a> { + pub fn parse(text: &'a str) -> Keyword<'a> { return match text { "unless" => Keyword::Unless, "while" => Keyword::While, @@ -279,6 +288,11 @@ impl Keyword { "yield" => Keyword::Yield, "please" => Keyword::Please, _ => { + + for cap in GOTO_REGEX.captures_iter(text) { + return Keyword::Goto(cap.get(1).unwrap().as_str()); + } + crate::message( MessageType::Critical, format!("not a known keyword: {}", text), @@ -496,15 +510,17 @@ pub enum Token<'a> { Decl(&'a str, Prim, DebugInfo), Bool(bool, DebugInfo), /// Keywords like ```if```,```break```,```while``` - Keyword(Keyword, DebugInfo), + Keyword(Keyword<'a>, DebugInfo), Type(Prim, DebugInfo), /// Semicolon - Terminator(DebugInfo) + Terminator(DebugInfo), + Label(&'a str, DebugInfo) } impl<'a> std::fmt::Display for Token<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Token::Label(name, _) => f.write_fmt(format_args!(" {}:", name)), Token::Type(t, _) => f.write_fmt(format_args!("__Type {:?}", t)), Token::Word(w, _) => f.write_fmt(format_args!("__Word {:?}", w)), Token::Delemiter(d, _) => f.write_fmt(format_args!("__Delemiter {:?}", d)), @@ -540,11 +556,12 @@ impl<'a> Into for Token<'a> { Token::Bool(_, d) => d, Token::Keyword(_, d) => d, Token::Terminator(d) => d, + Token::Label(_, d) => d, } } } -const TOKEN_REGEX_SRC: &'static str = r"(#.*|--.*)|(unless|while|loop|break|cont|ret|yield|please)|(int|rat|bool)|(true|false|ye|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>=])|([(){}])|(\n)|(;)"; +const TOKEN_REGEX_SRC: &'static str = r"(#.*|--.*)|'([a-zA-Z0-9]+)|(goto\s+[a-zA-Z0-9]+|unless|while|loop|break|cont|ret|yield|please)|(int|rat|bool)|(true|false|ye|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=[^=]|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>=])|([(){}])|(\n)|(;)"; lazy_static::lazy_static! { static ref TOKEN_REGEX: regex::Regex = regex::Regex::new(TOKEN_REGEX_SRC).unwrap(); @@ -582,10 +599,11 @@ pub fn tokenize<'a>(source: &'a str, diagnostics: &mut Diagnostics) -> Result Token::Keyword(Keyword::parse(mat.as_str()), debug_info), - 3 => Token::Type(Prim::from(mat.as_str(), &debug_info, diagnostics)?, debug_info), - 4 => Token::Bool(parse_bool(mat.as_str()), debug_info), - 5 => { + 2 => Token::Label(mat.as_str(), debug_info), + 3 => Token::Keyword(Keyword::parse(mat.as_str()), debug_info), + 4 => Token::Type(Prim::from(mat.as_str(), &debug_info, diagnostics)?, debug_info), + 5 => Token::Bool(parse_bool(mat.as_str()), debug_info), + 6 => { let var_type = if let Some(mat) = enumerator.next().unwrap().1 { Some(Prim::from(mat.as_str(), &debug_info, diagnostics)?) } else { @@ -593,21 +611,21 @@ pub fn tokenize<'a>(source: &'a str, diagnostics: &mut Diagnostics) -> Result { + 8 => { let var_type = Prim::from(enumerator.next().unwrap().1.unwrap().as_str(), &debug_info, diagnostics)?; Token::Decl(mat.as_str(), var_type, debug_info) } - 9 => Token::Word(mat.as_str(), debug_info), - 10 => Token::Number(mat.as_str(), NumHint::from(mat.as_str()), debug_info), - 11 => Token::Operator(Operator::parse(mat.as_str()), None, debug_info), - 12 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap(), debug_info), - 13 => { + 10 => Token::Word(mat.as_str(), debug_info), + 11 => Token::Number(mat.as_str(), NumHint::from(mat.as_str()), debug_info), + 12 => Token::Operator(Operator::parse(mat.as_str()), None, debug_info), + 13 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap(), debug_info), + 14 => { line_count += 1; line_start = mat.start(); Token::LineBreak(debug_info) } - 14 => Token::Terminator(debug_info), + 15 => Token::Terminator(debug_info), _ => { diagnostics.set_err(&debug_info, crate::msg::ERR71, format!("token: {}", mat.as_str())); @@ -628,7 +646,7 @@ fn parse_bool(text: &str) -> bool { "false" | "no" => false, "maybe" => rand::random(), _ => { - crate::message(MessageType::Critical, format!("token: {}", text)); + crate::message(MessageType::Critical, format!("token is not a boolean value: {}", text)); panic!(); }, }; diff --git a/src/vmrt/mod.rs b/src/vmrt/mod.rs index 6cc680f..07591d7 100644 --- a/src/vmrt/mod.rs +++ b/src/vmrt/mod.rs @@ -201,6 +201,7 @@ pub struct Program { enum LabelType { Unless(usize), Loop(usize), + Break(usize), Pad } @@ -208,6 +209,8 @@ enum LabelType { struct Compiletime<'a> { vartable: HashMap<&'a str, usize>, labels: Vec, + lopctl: Vec, + marker: HashMap<&'a str, usize>, stacksize: usize, } @@ -238,13 +241,20 @@ fn parse_term<'a>( ct.stacksize += 1; } Token::Assign(name, _, _) => { - ct.vartable.insert(name.clone(), ct.stacksize - 1); - code.push(Instr::Store(ct.stacksize - 1)); + if ct.vartable.get(name).is_none() { + ct.vartable.insert(name.clone(), ct.stacksize - 1); + code.push(Instr::Store(ct.stacksize - 1)); + } else { + code.push(Instr::Store(*ct.vartable.get(name).unwrap())); + } } Token::Var(name, _) => { code.push(Instr::Load(*ct.vartable.get(name).unwrap())); ct.stacksize += 1; - } + }, + Token::Label(name, _) => { + ct.marker.insert(name, ct.stacksize + 1); + }, Token::Operator(op, hint, _) => { code.push(match op { crate::token::Operator::Or => Instr::Operation(Operation::Bool(BoolOp::Or)), @@ -331,9 +341,20 @@ fn parse_term<'a>( crate::token::Keyword::Yield | crate::token::Keyword::Return => { code.push(Instr::Return) }, + crate::token::Keyword::Loop => { + ct.labels.push(LabelType::Loop(code.len())); + }, crate::token::Keyword::Unless => { ct.labels.push(LabelType::Unless(code.len())); code.push(Instr::JumpUnless(0)); + }, + crate::token::Keyword::Goto(label) => { + let index = ct.marker.get(label).unwrap(); + code.push(Instr::Jump(*index)); + }, + crate::token::Keyword::Break => { + ct.lopctl.push(LabelType::Break(code.len())); + code.push(Instr::Jump(0)); } _ => (), }, @@ -364,6 +385,18 @@ fn parse_block<'a>( LabelType::Unless(line) => { prog[line] = Instr::JumpUnless(prog.len()); }, + LabelType::Loop(line) => { + prog.push(Instr::Jump(line)); + + if let Some(label) = ct.lopctl.pop() { + match label { + LabelType::Break(line) => { + prog[line] = Instr::Jump(prog.len()); + }, + _ => () + } + } + }, _ => () } } @@ -487,8 +520,8 @@ fn call_fn(prog: &Program, proc: &Proc, superstack: &[Data]) -> Result { match stack.pop() { - Some(Data::Bool(false)) => x = *addr - 1, - Some(Data::Bool(true)) => (), + Some(Data::Bool(true)) => x = *addr - 1, + Some(Data::Bool(false)) => (), _ => { crate::message(crate::token::MessageType::Critical, "no condition for unless on stack"); panic!(); diff --git a/test.erpn b/test.erpn index 794496f..3b4c359 100644 --- a/test.erpn +++ b/test.erpn @@ -1,10 +1,16 @@ main: - Load Int 2 - Load Int 4 - Eq Int - Unless - Load Int 1 - Yield Load Int 0 + Store Int x + Loop + Load Var x + Load Int 4 + Add Int + Store Int x + Load Var x + Load Int 16 + Lt Int + Unless + Break + Load Var x Yield