added loop and break keyword to vmrt

This commit is contained in:
Sven Vogel 2022-12-06 22:48:52 +01:00
parent edcfbea81d
commit ba0d6837f3
8 changed files with 118 additions and 41 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target /target
*.erpn
*.vsasm

View File

@ -1,10 +1,16 @@
0xc8c7a99bb88e156b: 0xc8c7a99bb88e156b:
push int 0x2
push int 0x4
cmp eq int
jump-unless 0x6
push int 0x1
ret
push int 0x0 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 ret

View File

@ -26,12 +26,18 @@ fn main() {
-- structs are tables -- structs are tables
main() = int { main() = int {
x:rat = 0.0;
unless 2 == 4 { loop {
yield 1; x = x + 4
unless x < 16 {
break
}
} }
yield 0; yield x;
} }
"; ";

View File

@ -614,6 +614,7 @@ fn parse_term<'a>(
Token::Assign(_, _, _) => { Token::Assign(_, _, _) => {
op_stack.push(token); op_stack.push(token);
} }
Token::Label(_, _) => output.push_back(token),
Token::Keyword(key, _) => { Token::Keyword(key, _) => {
match key { match key {
Keyword::Unless => (), Keyword::Unless => (),

View File

@ -200,3 +200,8 @@ pub const ERR74: &DebugMsg = &DebugMsg {
code: 74, code: 74,
msg: "Missing operands", msg: "Missing operands",
}; };
pub const ERR75: &DebugMsg = &DebugMsg {
typ: crate::token::MessageType::Critical,
code: 75,
msg: "Unknown operation",
};

View File

@ -1,5 +1,5 @@
use colored::{ColoredString, Colorize}; use colored::{ColoredString, Colorize};
use std::collections::VecDeque; use std::{collections::VecDeque, fmt::format};
use crate::parser::data::Diagnostics; use crate::parser::data::Diagnostics;
@ -78,7 +78,8 @@ impl Operator {
"-" => Operator::Sub, "-" => Operator::Sub,
"*" => Operator::Mul, "*" => Operator::Mul,
"/" => Operator::Div, "/" => Operator::Div,
"=" => Operator::Assign, "=" => {
Operator::Assign },
_ => { _ => {
crate::message(MessageType::Critical, "Unknown operator"); 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)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Keyword { pub enum Keyword<'a> {
Unless, Unless,
While, While,
/// while(true) loop /// while(true) loop
@ -265,10 +267,17 @@ pub enum Keyword {
Return, Return,
Yield, Yield,
Please, Please,
Goto(&'a str),
} }
impl Keyword { const GOTO_REGEX_SRC: &'static str = r"goto\s+([a-zA-Z0-9]+)";
pub fn parse<'a>(text: &'a str) -> Keyword {
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 { return match text {
"unless" => Keyword::Unless, "unless" => Keyword::Unless,
"while" => Keyword::While, "while" => Keyword::While,
@ -279,6 +288,11 @@ impl Keyword {
"yield" => Keyword::Yield, "yield" => Keyword::Yield,
"please" => Keyword::Please, "please" => Keyword::Please,
_ => { _ => {
for cap in GOTO_REGEX.captures_iter(text) {
return Keyword::Goto(cap.get(1).unwrap().as_str());
}
crate::message( crate::message(
MessageType::Critical, MessageType::Critical,
format!("not a known keyword: {}", text), format!("not a known keyword: {}", text),
@ -496,15 +510,17 @@ pub enum Token<'a> {
Decl(&'a str, Prim, DebugInfo), Decl(&'a str, Prim, DebugInfo),
Bool(bool, DebugInfo), Bool(bool, DebugInfo),
/// Keywords like ```if```,```break```,```while``` /// Keywords like ```if```,```break```,```while```
Keyword(Keyword, DebugInfo), Keyword(Keyword<'a>, DebugInfo),
Type(Prim, DebugInfo), Type(Prim, DebugInfo),
/// Semicolon /// Semicolon
Terminator(DebugInfo) Terminator(DebugInfo),
Label(&'a str, DebugInfo)
} }
impl<'a> std::fmt::Display for Token<'a> { impl<'a> std::fmt::Display for Token<'a> {
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 {
Token::Label(name, _) => f.write_fmt(format_args!(" {}:", name)),
Token::Type(t, _) => f.write_fmt(format_args!("__Type {:?}", t)), Token::Type(t, _) => f.write_fmt(format_args!("__Type {:?}", t)),
Token::Word(w, _) => f.write_fmt(format_args!("__Word {:?}", w)), Token::Word(w, _) => f.write_fmt(format_args!("__Word {:?}", w)),
Token::Delemiter(d, _) => f.write_fmt(format_args!("__Delemiter {:?}", d)), Token::Delemiter(d, _) => f.write_fmt(format_args!("__Delemiter {:?}", d)),
@ -540,11 +556,12 @@ impl<'a> Into<DebugInfo> for Token<'a> {
Token::Bool(_, d) => d, Token::Bool(_, d) => d,
Token::Keyword(_, d) => d, Token::Keyword(_, d) => d,
Token::Terminator(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! { lazy_static::lazy_static! {
static ref TOKEN_REGEX: regex::Regex = regex::Regex::new(TOKEN_REGEX_SRC).unwrap(); 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<Ve
}; };
tokens.push_back(match i { tokens.push_back(match i {
2 => Token::Keyword(Keyword::parse(mat.as_str()), debug_info), 2 => Token::Label(mat.as_str(), debug_info),
3 => Token::Type(Prim::from(mat.as_str(), &debug_info, diagnostics)?, debug_info), 3 => Token::Keyword(Keyword::parse(mat.as_str()), debug_info),
4 => Token::Bool(parse_bool(mat.as_str()), debug_info), 4 => Token::Type(Prim::from(mat.as_str(), &debug_info, diagnostics)?, debug_info),
5 => { 5 => Token::Bool(parse_bool(mat.as_str()), debug_info),
6 => {
let var_type = if let Some(mat) = enumerator.next().unwrap().1 { let var_type = if let Some(mat) = enumerator.next().unwrap().1 {
Some(Prim::from(mat.as_str(), &debug_info, diagnostics)?) Some(Prim::from(mat.as_str(), &debug_info, diagnostics)?)
} else { } else {
@ -593,21 +611,21 @@ pub fn tokenize<'a>(source: &'a str, diagnostics: &mut Diagnostics) -> Result<Ve
}; };
Token::Assign(mat.as_str(), var_type, debug_info) Token::Assign(mat.as_str(), var_type, debug_info)
} }
7 => { 8 => {
let var_type = let var_type =
Prim::from(enumerator.next().unwrap().1.unwrap().as_str(), &debug_info, diagnostics)?; Prim::from(enumerator.next().unwrap().1.unwrap().as_str(), &debug_info, diagnostics)?;
Token::Decl(mat.as_str(), var_type, debug_info) Token::Decl(mat.as_str(), var_type, debug_info)
} }
9 => Token::Word(mat.as_str(), debug_info), 10 => Token::Word(mat.as_str(), debug_info),
10 => Token::Number(mat.as_str(), NumHint::from(mat.as_str()), debug_info), 11 => 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::Operator(Operator::parse(mat.as_str()), None, debug_info),
12 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap(), debug_info), 13 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap(), debug_info),
13 => { 14 => {
line_count += 1; line_count += 1;
line_start = mat.start(); line_start = mat.start();
Token::LineBreak(debug_info) 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())); 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, "false" | "no" => false,
"maybe" => rand::random(), "maybe" => rand::random(),
_ => { _ => {
crate::message(MessageType::Critical, format!("token: {}", text)); crate::message(MessageType::Critical, format!("token is not a boolean value: {}", text));
panic!(); panic!();
}, },
}; };

View File

@ -201,6 +201,7 @@ pub struct Program {
enum LabelType { enum LabelType {
Unless(usize), Unless(usize),
Loop(usize), Loop(usize),
Break(usize),
Pad Pad
} }
@ -208,6 +209,8 @@ enum LabelType {
struct Compiletime<'a> { struct Compiletime<'a> {
vartable: HashMap<&'a str, usize>, vartable: HashMap<&'a str, usize>,
labels: Vec<LabelType>, labels: Vec<LabelType>,
lopctl: Vec<LabelType>,
marker: HashMap<&'a str, usize>,
stacksize: usize, stacksize: usize,
} }
@ -238,13 +241,20 @@ fn parse_term<'a>(
ct.stacksize += 1; ct.stacksize += 1;
} }
Token::Assign(name, _, _) => { Token::Assign(name, _, _) => {
ct.vartable.insert(name.clone(), ct.stacksize - 1); if ct.vartable.get(name).is_none() {
code.push(Instr::Store(ct.stacksize - 1)); 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, _) => { Token::Var(name, _) => {
code.push(Instr::Load(*ct.vartable.get(name).unwrap())); code.push(Instr::Load(*ct.vartable.get(name).unwrap()));
ct.stacksize += 1; ct.stacksize += 1;
} },
Token::Label(name, _) => {
ct.marker.insert(name, ct.stacksize + 1);
},
Token::Operator(op, hint, _) => { Token::Operator(op, hint, _) => {
code.push(match op { code.push(match op {
crate::token::Operator::Or => Instr::Operation(Operation::Bool(BoolOp::Or)), 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 => { crate::token::Keyword::Yield | crate::token::Keyword::Return => {
code.push(Instr::Return) code.push(Instr::Return)
}, },
crate::token::Keyword::Loop => {
ct.labels.push(LabelType::Loop(code.len()));
},
crate::token::Keyword::Unless => { crate::token::Keyword::Unless => {
ct.labels.push(LabelType::Unless(code.len())); ct.labels.push(LabelType::Unless(code.len()));
code.push(Instr::JumpUnless(0)); 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) => { LabelType::Unless(line) => {
prog[line] = Instr::JumpUnless(prog.len()); 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<Option<Da
}, },
Instr::JumpUnless(addr) => { Instr::JumpUnless(addr) => {
match stack.pop() { match stack.pop() {
Some(Data::Bool(false)) => x = *addr - 1, Some(Data::Bool(true)) => x = *addr - 1,
Some(Data::Bool(true)) => (), Some(Data::Bool(false)) => (),
_ => { _ => {
crate::message(crate::token::MessageType::Critical, "no condition for unless on stack"); crate::message(crate::token::MessageType::Critical, "no condition for unless on stack");
panic!(); panic!();

View File

@ -1,10 +1,16 @@
main: main:
Load Int 2
Load Int 4
Eq Int
Unless
Load Int 1
Yield
Load Int 0 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 Yield