From 358dfb832a4489f48a464b9db712733d5b55663c Mon Sep 17 00:00:00 2001 From: John Date: Tue, 25 Jun 2024 21:28:55 -0500 Subject: [PATCH] boy-debug: Add extended identifier ("string") syntax + string expressions. Add load-file statements --- boy-debug/src/lib.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/boy-debug/src/lib.rs b/boy-debug/src/lib.rs index 0eb1ecd..6478f2c 100644 --- a/boy-debug/src/lib.rs +++ b/boy-debug/src/lib.rs @@ -255,15 +255,21 @@ pub mod cli { } .emit(Token::Op(Range)) } + + fn string(&mut self) -> Option { + let mut value = String::new(); + while let Some(c) = self.text.next_if(|&c| c != '"') { + value.push(c) + } + self.take().emit(Token::Ident(value)) + } } impl> Iterator for Lexer { type Item = Token; fn next(&mut self) -> Option { match self.sync().peek()? { - // '{' => self.take().emit(Op(CurlyOpen)), '[' => self.take().emit(Op(BrackOpen)), '(' => self.take().emit(Op(ParenOpen)), - // '}' => self.take().emit(Op(CurlyClose)), ']' => self.take().emit(Op(BrackClose)), ')' => self.take().emit(Op(ParenClose)), @@ -280,6 +286,7 @@ pub mod cli { '>' => self.take().chain('>', Op(Shr)), '<' => self.take().chain('<', Op(Shl)), '.' => self.take().range(), // special handling allows for one or two dots + '"' => self.take().string(), // string-packed identifiers '?' => self.take().emit(Token::Ident("regs".into())), '$' | '#' => self.take().digits::<16>(), @@ -358,7 +365,8 @@ pub mod cli { Expr::parse(lexer, 0)?, Expr::parse(lexer.then(Token::Op(Op::Comma))?, 0)?, ], - Verb::Get + Verb::Load + | Verb::Get | Verb::Run | Verb::Break | Verb::Unbreak @@ -392,6 +400,7 @@ pub mod cli { "ub" | "unbreak" => Verb::Unbreak, "di" | "dis" => Verb::Disasm, "eval" => Verb::Eval, + "load" => Verb::Load, "regs" => Verb::DumpRegs, "cart" => Verb::DumpCart, "trace" => Verb::Trace, @@ -408,6 +417,7 @@ pub mod cli { Token::Reg(r) => Expr::Reg(r), Token::Num(n) => Expr::Int(n as _), Token::Bool(b) => Expr::Bool(b), + Token::Ident(s) => Expr::String(s), Token::Op(Op::ParenOpen) => { let head = Expr::parse(p, 0)?; if p.then(Token::Op(Op::ParenClose)).is_none() { @@ -581,6 +591,7 @@ pub mod ast { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Verb { + Load, Set, Get, Run, @@ -598,6 +609,7 @@ pub mod ast { impl Display for Verb { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Verb::Load => "load", Verb::Set => "set", Verb::Get => "get", Verb::Run => "run", @@ -620,6 +632,7 @@ pub mod ast { Int(isize), Bool(bool), Reg(Reg), + String(String), Prefix(Op, Box), Binary(Op, Box, Box), Postfix(Op, Box), @@ -632,7 +645,7 @@ pub mod ast { /// Traverses the expression depth-first, calling function f() on every subexpression pub fn visit(&mut self, f: &impl Fn(&mut Expr)) { match self { - Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) => {} + Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) | Expr::String(_)=> {} Expr::Array(v) => v.iter_mut().for_each(|e| e.visit(f)), Expr::Prefix(_, e) | Expr::Postfix(_, e) => e.visit(f), Expr::Binary(_, a, b) | Expr::Range(a, b) | Expr::Index(a, b) => { @@ -682,7 +695,7 @@ pub mod ast { /// Attempts to solve a top-level expression using basic arithmetic rules pub fn eval_one(&mut self) -> Option { match self { - Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) => None, + Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) | Expr::String(_) => None, Expr::Array(_) | Expr::Range(_, _) => None, Expr::Prefix(op, e) => e.eval_prefix(*op), Expr::Binary(op, lhs, rhs) => lhs.eval_binary(*op, rhs), @@ -743,6 +756,7 @@ pub mod ast { Expr::Int(i) => write!(f, "{i:02x}"), Expr::Bool(b) => b.fmt(f), Expr::Reg(r) => r.fmt(f), + Expr::String(s) => write!(f, "\"{s}\""), Expr::Prefix(op, e) => write!(f, "{op}{e}"), Expr::Binary(op, a, b) => write!(f, "{a} {op} {b}"), Expr::Postfix(op, e) => write!(f, "{e}{op}"), @@ -786,6 +800,7 @@ pub mod ast { Expr::Int(_) | Expr::Bool(_) | Expr::Reg(_) + | Expr::String(_) | Expr::Prefix(_, _) | Expr::Binary(_, _, _) | Expr::Postfix(_, _) @@ -893,6 +908,12 @@ pub mod gameboy { // Intrepret basic statements let Stmt { verb, dst, src } = stmt.solve(&Expr::eval_one); match (verb, dst, src) { + (Verb::Load, Expr::String(file), ..) => { + if let Err(e) = self.bus.read_file(file) { + eprintln!("{e}"); + return Response::Failure + } + } (Verb::Set, Expr::Int(addr), Expr::Int(data)) => { self.bus.write(*addr as _, *data as _); }