2020-02-03 17:56:32 -05:00
|
|
|
#![allow(dead_code)]
|
|
|
|
|
|
2020-01-25 19:17:39 -05:00
|
|
|
mod common;
|
|
|
|
|
mod vm;
|
|
|
|
|
|
2020-02-25 14:19:12 -05:00
|
|
|
use structopt::StructOpt;
|
2020-02-25 14:55:14 -05:00
|
|
|
use snafu::Snafu;
|
2020-02-26 14:24:30 -05:00
|
|
|
use std::{fs, io::{stdin, stdout, Write, Read}, path::{Path, PathBuf}, process};
|
2020-02-25 14:55:14 -05:00
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
2020-02-25 14:19:12 -05:00
|
|
|
|
|
|
|
|
#[derive(StructOpt, Debug)]
|
|
|
|
|
struct Options {
|
|
|
|
|
// maybe some other options:
|
|
|
|
|
// * debug
|
2020-02-25 18:12:28 -05:00
|
|
|
|
|
|
|
|
/// 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.
|
2020-02-25 14:19:12 -05:00
|
|
|
#[structopt(name = "FILE", parse(from_os_str))]
|
|
|
|
|
input: PathBuf,
|
2020-02-25 14:55:14 -05:00
|
|
|
|
2020-02-25 18:12:28 -05:00
|
|
|
/// The maximum amount of virtual memory allowed.
|
|
|
|
|
#[structopt(long, parse(try_from_str = from_bytes_size))]
|
2020-02-25 14:55:14 -05:00
|
|
|
max_mem: Option<usize>,
|
2020-02-25 18:12:28 -05:00
|
|
|
|
2020-02-26 14:24:30 -05:00
|
|
|
/// The output file for generated files (preprocessed, compiled, etc).
|
2020-02-25 18:12:28 -05:00
|
|
|
///
|
|
|
|
|
/// If not supplied, a name will be inferred from the input file. Supplying - for the path will
|
|
|
|
|
/// output to STDOUT.
|
|
|
|
|
#[structopt(short = "o", long)]
|
2020-02-26 14:24:30 -05:00
|
|
|
out: Option<PathBuf>,
|
|
|
|
|
|
|
|
|
|
/// Only run the preprocessor.
|
|
|
|
|
#[structopt(short = "E", long)]
|
|
|
|
|
preprocess_only: bool,
|
|
|
|
|
|
|
|
|
|
/// Only compile the input file to an object.
|
|
|
|
|
#[structopt(short = "c", long)]
|
|
|
|
|
compile_only: bool,
|
|
|
|
|
|
2020-02-25 14:19:12 -05:00
|
|
|
}
|
2020-01-27 19:07:57 -05:00
|
|
|
|
|
|
|
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
|
|
|
|
|
2020-02-26 14:24:30 -05:00
|
|
|
fn get_reader(path: impl AsRef<Path>) -> Result<Box<dyn Read>> {
|
|
|
|
|
if let Some("-") = path.as_ref().to_str() {
|
|
|
|
|
Ok(Box::new(stdin()))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(Box::new(fs::File::open(path.as_ref())?))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_writer(path: impl AsRef<Path>) -> Result<Box<dyn Write>> {
|
|
|
|
|
if let Some("-") = path.as_ref().to_str() {
|
|
|
|
|
Ok(Box::new(stdout()))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(Box::new(fs::File::create(path.as_ref())?))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 19:07:57 -05:00
|
|
|
fn main() -> Result<()> {
|
2020-02-18 17:44:41 -05:00
|
|
|
use vm::{
|
|
|
|
|
state::State,
|
2020-02-27 15:52:17 -05:00
|
|
|
obj::assemble::AsmSession,
|
2020-02-18 17:44:41 -05:00
|
|
|
};
|
2020-02-25 18:12:28 -05:00
|
|
|
|
2020-02-25 14:19:12 -05:00
|
|
|
let opt = Options::from_args();
|
2020-02-27 15:52:17 -05:00
|
|
|
let mut asm_session = AsmSession::default();
|
|
|
|
|
asm_session.include(&opt.input)?;
|
|
|
|
|
let object = asm_session.assemble()?;
|
2020-02-26 14:24:30 -05:00
|
|
|
|
|
|
|
|
if opt.compile_only {
|
|
|
|
|
let outfile = opt.out.clone().unwrap_or_else(|| {
|
2020-02-25 18:12:28 -05:00
|
|
|
let mut outfile = opt.input.clone();
|
|
|
|
|
assert!(outfile.set_extension("obj"));
|
|
|
|
|
outfile
|
|
|
|
|
});
|
|
|
|
|
let bytes = object.to_bytes();
|
2020-02-26 14:24:30 -05:00
|
|
|
let mut writer = get_writer(&outfile)?;
|
|
|
|
|
writer.write(&bytes)?;
|
2020-02-25 18:12:28 -05:00
|
|
|
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);
|
|
|
|
|
}
|
2020-02-10 16:31:08 -05:00
|
|
|
}
|