boy-debug: Add extended identifier ("string") syntax + string expressions. Add load-file statements

This commit is contained in:
John 2024-06-25 21:28:55 -05:00
parent 054f9f49cb
commit 358dfb832a

View File

@ -255,15 +255,21 @@ pub mod cli {
}
.emit(Token::Op(Range))
}
fn string(&mut self) -> Option<Token> {
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<I: Iterator<Item = char>> Iterator for Lexer<I> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
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<Expr>),
Binary(Op, Box<Expr>, Box<Expr>),
Postfix(Op, Box<Expr>),
@ -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<Expr> {
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 _);
}