Files
rasp/src/main.rs

141 lines
4.3 KiB
Rust
Raw Normal View History

#![allow(dead_code)]
mod common;
mod vm;
use structopt::StructOpt;
use snafu::Snafu;
use std::{fs, io::{stdin, stdout, Write, Read}, process, path::PathBuf};
const DEFAULT_MAX_MEM: usize = 64 * 1024 * 1024;
#[derive(Snafu, Debug, Clone, PartialEq)]
enum ArgParseError<'a> {
#[snafu(display("invalid specified size: {} (sizes may end in G, M, or K)", text))]
InvalidSize { text: &'a str }
}
fn from_bytes_size<'a>(mut text: &'a str) -> std::result::Result<usize, ArgParseError<'a>> {
if text.is_empty() {
return Err(ArgParseError::InvalidSize { text })
}
let multiplier = match text.chars().last().unwrap() {
'k' | 'K' => 1024,
'm' | 'M' => 1024 * 1024,
'g' | 'G' => 1024 * 1024 * 1024,
_ => 1,
};
// trim the last character
if multiplier != 1 {
text = &text[0 .. text.len() - 1];
}
let value = text.parse::<usize>().map_err(|_| ArgParseError::InvalidSize { text })?;
Ok(value * multiplier)
}
#[test]
fn test_bytes_size() {
assert_eq!(from_bytes_size("64G"), Ok(64 * 1024 * 1024 * 1024));
assert_eq!(from_bytes_size("64M"), Ok(64 * 1024 * 1024));
assert_eq!(from_bytes_size("64K"), Ok(64 * 1024));
assert_eq!(from_bytes_size("64g"), Ok(64 * 1024 * 1024 * 1024));
assert_eq!(from_bytes_size("64m"), Ok(64 * 1024 * 1024));
assert_eq!(from_bytes_size("64k"), Ok(64 * 1024));
assert_eq!(from_bytes_size("64"), Ok(64));
assert!(from_bytes_size("64B").is_err());
assert!(from_bytes_size("64MB").is_err());
assert!(from_bytes_size("64GB").is_err());
assert!(from_bytes_size("64KB").is_err());
assert!(from_bytes_size("64b").is_err());
assert!(from_bytes_size("64mb").is_err());
assert!(from_bytes_size("64gb").is_err());
assert!(from_bytes_size("64kb").is_err());
}
#[derive(StructOpt, Debug)]
struct Options {
// maybe some other options:
// * debug
/// The input file to work with.
///
/// By default, the file will be executed. If -c is passed, it will not run and only compile.
/// Supplying - for the path will read from STDIN.
#[structopt(name = "FILE", parse(from_os_str))]
input: PathBuf,
/// The maximum amount of virtual memory allowed.
#[structopt(long, parse(try_from_str = from_bytes_size))]
max_mem: Option<usize>,
/// Whether to compile the input file to an object.
#[structopt(short, long)]
compile: bool,
/// The output file for compiled files.
///
/// If not supplied, a name will be inferred from the input file. Supplying - for the path will
/// output to STDOUT.
#[structopt(short = "o", long)]
compile_out: Option<PathBuf>
}
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
fn main() -> Result<()> {
use vm::{
state::State,
obj::{
assemble::{Asm, Assemble},
syn::{lexer, parser},
}
};
// get the object either from reading it, or from parsing an assembly file
let opt = Options::from_args();
let text = if Some("-") == opt.input.to_str() {
let mut text = String::new();
let stdin = stdin();
stdin.lock().read_to_string(&mut text)?;
text
} else {
fs::read_to_string(&opt.input)?
};
let lexerdef = lexer::lexerdef();
let lexer = lexerdef.lexer(&text);
let (res, errors) = parser::parse(&lexer);
// print errors
for err in errors.iter() {
println!("{}", err.pp(&lexer, &parser::token_epp));
}
if !errors.is_empty() {
process::exit(1);
}
let res = res.unwrap();
let mut asm = Asm::default();
let object = res.assemble(&mut asm)?;
if opt.compile {
let outfile = opt.compile_out.clone().unwrap_or_else(|| {
let mut outfile = opt.input.clone();
assert!(outfile.set_extension("obj"));
outfile
});
let bytes = object.to_bytes();
if Some("-") == outfile.to_str() {
let stdout = stdout();
stdout.lock().write(&bytes)?;
} else {
// write compiled file here
fs::write(outfile, &bytes)?;
}
Ok(())
} else {
let mut state = State::new();
state.load_object(object, opt.max_mem.unwrap_or(DEFAULT_MAX_MEM))?;
let status = state.exec()?;
process::exit((status & 0xffff_ffff) as i32);
}
}