v0.3.0 #1

Merged
j merged 12 commits from v0.3.0 into main 2024-02-01 20:11:02 +00:00
3 changed files with 103 additions and 95 deletions
Showing only changes of commit 6b5663ae4e - Show all commits

View File

@ -10,5 +10,4 @@ publish.workspace = true
[dependencies] [dependencies]
libmsp430 = { path = ".." } libmsp430 = { path = ".." }
anes = { version = "0.2.0" }
argp = { version = "0.3.0" } argp = { version = "0.3.0" }

View File

@ -62,12 +62,31 @@ pub mod split_twice {
pub mod cursor { pub mod cursor {
use std::fmt::{Arguments, Display}; use std::fmt::{Arguments, Display};
pub macro csi($($t:tt)*) {format_args!("\x1b[{}", format_args!($($t)*))} /// Moves to the {line}th previous line
pub macro previous($line:literal) {
csi!("{}F", $line)
}
pub macro color($fg:expr, $($t:tt)*) { /// Injects a Command Sequence Introducer
pub macro csi($($t:tt)*) {
format_args!("\x1b[{}", format_args!($($t)*))
}
/// Formats the args with a foreground [Color]
pub macro fg($fg:expr, $($t:tt)*) {
Colorized::new(Some($fg), None, format_args!($($t)*)) Colorized::new(Some($fg), None, format_args!($($t)*))
} }
/// Formats the args with a background [Color]
pub macro bg($bg:expr, $(t:tt)*) {
Colorized::new(None, Some($bg), format_args!($($t)*))
}
/// Formats the args with both a foreground and background [Color]
pub macro color($fg:expr, $bg:expr, $($t:tt)*) {
Colorized::new(Some($fg), Some($bg), format_args!($($t)*))
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Color { pub enum Color {
#[default] #[default]

View File

@ -7,7 +7,7 @@ use libmsp430::{
parser::{error::Error as PError, Parser}, parser::{error::Error as PError, Parser},
}; };
use msp430_asm::{ use msp430_asm::{
cursor::{color, Color::*}, cursor::{fg, Color::*},
split_twice::SplitTwice, split_twice::SplitTwice,
}; };
use std::{ use std::{
@ -19,103 +19,16 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut buf = String::new(); let mut buf = String::new();
if let Some(file) = parse_args_or_exit::<args::Args>(argp::DEFAULT).file { if let Some(file) = parse_args_or_exit::<args::Args>(argp::DEFAULT).file {
buf = std::fs::read_to_string(file)?; buf = std::fs::read_to_string(file)?;
} else if !stdin().is_terminal() { } 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. // if stdin is a terminal, enter parse-checked REPL mode.
repl::repl(&mut buf)?; repl::repl(&mut buf)?;
} else {
// if stdin is not a terminal, don't parsecheck each line.
stdin().lock().read_to_string(&mut buf)?;
} }
asm(&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::MoveCursorToPreviousLine;
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 printfl ($($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!(
"{}",
color!(DarkGray, "{} v{}", env!("CARGO_BIN_NAME"), env!("CARGO_PKG_VERSION"))
);
printfl!("{}", 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::new(&line).parse::<Statements>() {
Err(error) => errpp(&line, linenr, &error),
Ok(_) => {
okpp(&line, linenr);
*buf += &line;
linenr += 1;
}
}
line.clear();
printfl!("{}", linenr!(linenr));
}
println!("{}", color!(Gray, "[EOF]"));
Ok(())
}
fn okpp(line: &str, linenr: i32) {
println!(
"{}{}{}",
move_cursor!(1, 5),
color!(Green, "{:4}", linenr!(linenr)),
line.trim_end(),
);
}
/// Pretty-prints a line error
fn errpp(line: &str, linenr: i32, err: &PError) {
let loc = err.loc;
if stderr().is_terminal() {
let line = line.trim_end();
eprint!("{}{}", MoveCursorToPreviousLine(1), color!(Red, "{}", linenr!(linenr)));
let (start, mid, end) = line.split_twice(loc.start, loc.end);
eprintln!("{start}{}{end} {}", color!(Red, "{}", mid), color!(DarkGray, "; {}", err));
} else {
eprintln!("{} ({err})", line.trim())
}
}
}
// Parses and assembles a buffer, then prints it in hex to stdout // Parses and assembles a buffer, then prints it in hex to stdout
fn asm(buf: &str) -> Result<(), Box<dyn Error>> { fn asm(buf: &str) -> Result<(), Box<dyn Error>> {
match Parser::new(buf).parse::<Statements>()?.to_canonical().assemble() { match Parser::new(buf).parse::<Statements>()?.to_canonical().assemble() {
@ -129,3 +42,80 @@ fn asm(buf: &str) -> Result<(), Box<dyn Error>> {
} }
Ok(()) Ok(())
} }
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 {
//! The REPL reads a line, parses it, evaluates the line, and prints, in a loop
use super::*;
use msp430_asm::cursor::*;
use std::io::{stderr, Write};
/// Prints the line number
macro linenr($n: expr) {
format_args!("{:4}: ", $n)
}
/// [println], but without the newline
macro printfl ($($x: expr),+) {
{print!($($x),+); let _ = ::std::io::stdout().flush();}
}
/// Runs the read-evaluate-print loop
pub fn repl(buf: &mut String) -> Result<(), Box<dyn Error>> {
let mut line = String::new();
let mut linenr = 1;
println!("{}", fg!(DarkGray, "{} v{}", env!("CARGO_BIN_NAME"), env!("CARGO_PKG_VERSION")));
printfl!("{}", 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::new(&line).parse::<Statements>() {
Err(error) => format_err(&line, linenr, &error),
Ok(_) => {
format_ok(&line, linenr);
*buf += &line;
linenr += 1;
}
}
line.clear();
printfl!("{}", linenr!(linenr));
}
println!("{}", fg!(Gray, "[EOF]"));
Ok(())
}
/// Rewrites the line in OK format, with a green linenr
fn format_ok(line: &str, linenr: i32) {
println!("{}{}{}", previous!(1), fg!(Green, "{:4}", linenr!(linenr)), line.trim_end(),);
}
/// Pretty-prints a line error
fn format_err(line: &str, linenr: i32, err: &PError) {
let loc = err.loc;
if stderr().is_terminal() {
let line = line.trim_end();
eprint!("{}{}", previous!(1), fg!(Red, "{}", linenr!(linenr)));
let (start, mid, end) = line.split_twice(loc.start, loc.end);
eprintln!("{start}{}{end} {}", fg!(Red, "{}", mid), fg!(DarkGray, "; {}", err));
} else {
eprintln!("{} ({err})", line.trim())
}
}
}