main.rs: Basic TUI experience: line numbers, better errors
This commit is contained in:
parent
417ef03e41
commit
d73c5b2e5d
13
Cargo.toml
13
Cargo.toml
@ -2,11 +2,22 @@
|
|||||||
name = "msp430-asm"
|
name = "msp430-asm"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["John Breaux"]
|
rust-version = "1.70"
|
||||||
|
authors = ["John Breaux <j@soft.fish>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "msp430-asm"
|
||||||
|
path = "examples/msp430-asm/main.rs"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex = "1.9.3"
|
regex = "1.9.3"
|
||||||
|
# TODO: Remove dependency on regex
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
anes = { version = "0.1.6" }
|
||||||
|
argp = { version = "0.3.0" }
|
||||||
|
136
examples/msp430-asm/main.rs
Normal file
136
examples/msp430-asm/main.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
//! Simple frontend for the assembler
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
use argp::parse_args_or_exit;
|
||||||
|
use msp430_asm::preamble::*;
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
io::{stdin, IsTerminal, Read},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Some(file) = parse_args_or_exit::<args::Args>(argp::DEFAULT).file {
|
||||||
|
buf = std::fs::read_to_string(file)?;
|
||||||
|
} else if !stdin().is_terminal() {
|
||||||
|
// if stdin is not a terminal, don't parsecheck each line.
|
||||||
|
stdin().lock().read_to_string(&mut buf)?;
|
||||||
|
} else {
|
||||||
|
// if stdin is a terminal, enter parse-checked REPL mode.
|
||||||
|
repl::repl(&mut buf)?;
|
||||||
|
}
|
||||||
|
asm(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod args {
|
||||||
|
use argp::FromArgs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// Assembles MSP430 assembly into 16-bit little-endian machine code. \
|
||||||
|
/// If used interactively, syntax is checked on a per-line basis.
|
||||||
|
#[derive(Debug, FromArgs)]
|
||||||
|
pub struct Args {
|
||||||
|
/// File to load. If not provided, takes input from stdin.
|
||||||
|
#[argp(option, short = 'f')]
|
||||||
|
pub file: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod repl {
|
||||||
|
use super::*;
|
||||||
|
use anes::{Color, MoveCursorToPreviousLine, ResetAttributes, SetForegroundColor};
|
||||||
|
use msp430_asm::{
|
||||||
|
assembler::error::AssemblyError, error::Error as MspError, lexer::error::LexError, parser::error::ParseError,
|
||||||
|
};
|
||||||
|
use std::io::{stderr, Write};
|
||||||
|
|
||||||
|
macro color ($color: expr, $fmt: literal, $($str: expr),*) {
|
||||||
|
format_args!(concat!("{}", $fmt, "{}"), ::anes::SetForegroundColor($color),$($str,)* ::anes::ResetAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro linenr($n: expr) {
|
||||||
|
format_args!("{:4}: ", $n)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro printflush ($($x: expr),+) {
|
||||||
|
{print!($($x),+); let _ = ::std::io::stdout().flush();}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro move_cursor($x:expr, $y:expr) {
|
||||||
|
format_args!("{}{}", ::anes::MoveCursorToPreviousLine($x), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repl(buf: &mut String) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut line = String::new();
|
||||||
|
let mut linenr = 1;
|
||||||
|
println!(
|
||||||
|
"{}{} v{}{}",
|
||||||
|
SetForegroundColor(Color::DarkGray),
|
||||||
|
env!("CARGO_BIN_NAME"),
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
ResetAttributes
|
||||||
|
);
|
||||||
|
printflush!("{}", linenr!(linenr));
|
||||||
|
while let Ok(len) = stdin().read_line(&mut line) {
|
||||||
|
match len {
|
||||||
|
0 => break, // No newline (reached EOF)
|
||||||
|
1 => continue, // Line is empty
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
// Try to parse this line in isolation (this restricts preprocessing)
|
||||||
|
match Parser::default().parse(&line) {
|
||||||
|
Err(error) => errpp(&line, linenr, &error.into()),
|
||||||
|
Ok(_) => {
|
||||||
|
okpp(&line, linenr);
|
||||||
|
*buf += &line;
|
||||||
|
linenr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.clear();
|
||||||
|
printflush!("{}", linenr!(linenr));
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn okpp(line: &str, linenr: i32) {
|
||||||
|
println!("{}{}{}", move_cursor!(1, 5), color!(Color::Green, "{:4}", linenr!(linenr)), line.trim_end(),);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty-prints a line error
|
||||||
|
fn errpp(line: &str, linenr: i32, err: &msp430_asm::error::Error) {
|
||||||
|
if stderr().is_terminal() {
|
||||||
|
let line = line.trim_end();
|
||||||
|
eprint!("{}{}", MoveCursorToPreviousLine(1), color!(Color::Red, "{}", linenr!(linenr)));
|
||||||
|
match err {
|
||||||
|
// TODO: use a recursive enum to store all valid states
|
||||||
|
MspError::LexError(LexError::Contextual(c, e))
|
||||||
|
| MspError::ParseError(ParseError::LexError(LexError::Contextual(c, e)))
|
||||||
|
| MspError::AssemblyError(AssemblyError::ParseError(ParseError::LexError(LexError::Contextual(
|
||||||
|
c,
|
||||||
|
e,
|
||||||
|
)))) => {
|
||||||
|
let (start, end) = line.split_at(c.position() - 1);
|
||||||
|
eprintln!("{start}{} ({e})", color!(Color::Red, "{}", end));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
eprintln!("{} ({err})", color!(Color::Red, "{}", line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("{} ({err})", line.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses and assembles a buffer, then prints it in hex to stdout
|
||||||
|
fn asm(buf: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
match Assembler::assemble(&Parser::default().parse(&buf)?) {
|
||||||
|
Err(error) => println!("{error}"),
|
||||||
|
Ok(out) => {
|
||||||
|
for word in out {
|
||||||
|
print!("{:04x} ", word.swap_bytes())
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
//! Simple frontend for the assembler
|
|
||||||
|
|
||||||
use msp430_asm::preamble::*;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut repl = true;
|
|
||||||
for arg in std::env::args() {
|
|
||||||
match arg.as_str() {
|
|
||||||
"-" | "-f" | "--file" => repl = false,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = String::new();
|
|
||||||
if repl {
|
|
||||||
let mut line = String::new();
|
|
||||||
while let Ok(len) = std::io::stdin().read_line(&mut line) {
|
|
||||||
match len {
|
|
||||||
0 => break, // No newline (reached EOF)
|
|
||||||
1 => continue, // Line is empty
|
|
||||||
_ => {
|
|
||||||
// Try to parse this line in isolation (this restricts preprocessing)
|
|
||||||
match Parser::default().parse(&line) {
|
|
||||||
Ok(_) => {
|
|
||||||
buf += &line;
|
|
||||||
}
|
|
||||||
Err(error) => println!("{error}"),
|
|
||||||
}
|
|
||||||
line.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match Assembler::assemble(&Parser::default().parse(&buf)?) {
|
|
||||||
Err(error) => println!("{error}"),
|
|
||||||
Ok(out) => {
|
|
||||||
for word in out {
|
|
||||||
print!("{:04x} ", word.swap_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
|
||||||
} else {
|
|
||||||
std::io::stdin().lock().read_to_string(&mut buf)?;
|
|
||||||
let tree = Parser::default().parse(&buf);
|
|
||||||
match &tree {
|
|
||||||
Ok(tree) => {
|
|
||||||
//msp430_asm::linker::Printer::default().visit_root(tree);
|
|
||||||
for insn in msp430_asm::assembler::Assembler::assemble(tree)? {
|
|
||||||
print!("{:04x} ", insn.swap_bytes())
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
Err(error) => eprintln!("{error}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user