|
|
|
|
@@ -7,7 +7,7 @@ use libmsp430::{
|
|
|
|
|
parser::{error::Error as PError, Parser},
|
|
|
|
|
};
|
|
|
|
|
use msp430_asm::{
|
|
|
|
|
cursor::{color, Color::*},
|
|
|
|
|
cursor::{fg, Color::*},
|
|
|
|
|
split_twice::SplitTwice,
|
|
|
|
|
};
|
|
|
|
|
use std::{
|
|
|
|
|
@@ -19,103 +19,16 @@ 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 {
|
|
|
|
|
} else if stdin().is_terminal() {
|
|
|
|
|
// if stdin is a terminal, enter parse-checked REPL mode.
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
fn asm(buf: &str) -> Result<(), Box<dyn Error>> {
|
|
|
|
|
match Parser::new(buf).parse::<Statements>()?.to_canonical().assemble() {
|
|
|
|
|
@@ -129,3 +42,80 @@ fn asm(buf: &str) -> Result<(), Box<dyn Error>> {
|
|
|
|
|
}
|
|
|
|
|
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())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|