From c0b0f32b4ec4db6961e1fc1f5ccd263598aa6df6 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Sun, 1 Mar 2020 03:10:11 +0100 Subject: [PATCH] Add a compiler CLI The compiler CLI lets you output assembly, the pretty-printed code, or a compiled binary. It shells out to clang for assembling. You have to have clang installed, and you have to have the runtime library in the correct place. I need to figure out a way to embed the runtime library or something... --- Cargo.lock | 299 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/main.rs | 91 +++++++++++++-- src/trans/x64.rs | 16 ++- 4 files changed, 395 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1037821..1c11f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,25 +1,124 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + [[package]] name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +dependencies = [ + "ppv-lite86", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "ess" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c70ece3a2affa9eda5d916d778fe80fe2b7357aeb2d7b38f35286cfa983571d0" +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +dependencies = [ + "libc", +] + [[package]] name = "ivy" version = "0.1.0" dependencies = [ "ess", "pretty", + "structopt", + "tempfile", ] +[[package]] +name = "libc" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + [[package]] name = "pretty" version = "0.9.0" @@ -30,8 +129,208 @@ dependencies = [ "typed-arena", ] +[[package]] +name = "proc-macro-error" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +dependencies = [ + "c2-chacha", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b3a3e93f5ad553c38b3301c8a0a0cec829a36783f6a0c467fc4bf553a5f5bf" +dependencies = [ + "clap", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea692d40005b3ceba90a9fe7a78fa8d4b82b0ce627eebbffc329aab850f3410e" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "typed-arena" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index d96e26a..cd4816b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ edition = "2018" [dependencies] ess = "0.4.2" pretty = "0.9" +structopt = "=0.3.5" +tempfile = "3.1" diff --git a/src/main.rs b/src/main.rs index 1e6374a..3a847cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,51 @@ -use std::io::{self, Read}; -use ivy::{parse, BUILTINS, ast::Ast, trans::{self, translate}}; +use ivy::{ + ast::Ast, + parse, + trans::{self, translate}, + BUILTINS, +}; +use std::{ + ffi::OsStr, + fs, + io::{self, Read, Write}, + path::{Path, PathBuf}, + process::Command, +}; +use structopt::{clap, StructOpt}; +use tempfile; + +/// The Ivy compiler. +/// +/// If no mode is specified, this will fully compile the program. +#[derive(Debug, StructOpt)] +#[structopt(setting = clap::AppSettings::ArgRequiredElseHelp)] +struct Opt { + /// Output assembly. + #[structopt(short = "S", long = "assembly", conflicts_with = "pretty")] + assembly: bool, + + /// Pretty-print the source code. + #[structopt(short = "p", long = "pretty")] + pretty: bool, + + /// Input path. Use "-" for stdin. + input: PathBuf, + + /// Output path. Defaults to stdout or a.out, depending on the mode. + #[structopt(short = "o", long = "output")] + output: Option, +} fn main() -> io::Result<()> { - let stdin = std::io::stdin(); - let mut text = String::new(); - stdin.lock().read_to_string(&mut text)?; + let opt = Opt::from_args(); + let text = match &opt.input { + path if path == Path::new("-") => { + let mut text = String::new(); + std::io::stdin().read_to_string(&mut text)?; + text + } + path => fs::read_to_string(path)?, + }; let sexp = match parse(&text) { Ok(item) => item, Err(err) => { @@ -19,6 +60,16 @@ fn main() -> io::Result<()> { std::process::exit(1); } }; + if opt.pretty { + match opt.output { + Some(path) => { + let mut file = fs::File::create(path)?; + writeln!(file, "{}", ast.to_doc().pretty(80))?; + } + None => println!("{}", ast.to_doc().pretty(80)), + } + return Ok(()); + } let code = match translate(&ast) { Ok(code) => code, Err(err) => { @@ -27,7 +78,33 @@ fn main() -> io::Result<()> { } }; builtins.retain(|_, v| code.globals.contains(&v.global_number().unwrap())); - let stdout = std::io::stdout(); - trans::x64::write_compile(&builtins, &mut stdout.lock(), &code)?; + if opt.assembly { + match opt.output { + Some(path) => { + let mut file = fs::File::create(path)?; + trans::x64::write_compile(&builtins, &mut file, &code)?; + } + None => { + let stdout = std::io::stdout(); + trans::x64::write_compile(&builtins, &mut stdout.lock(), &code)?; + } + } + return Ok(()); + } + let mut file = tempfile::Builder::new().suffix(".s").tempfile()?; + trans::x64::write_compile(&builtins, &mut file, &code)?; + Command::new("clang") + .args(&[ + "-masm=intel", + "-nostartfiles", + "-Lrt/target/release", + "-lrt", + "-Wl,-e,_start", + ]) + .arg(file.path()) + .arg("-o") + .arg(opt.output.unwrap_or_else(|| "a.out".into())) + .spawn()? + .wait()?; Ok(()) } diff --git a/src/trans/x64.rs b/src/trans/x64.rs index f2dd634..fbc7398 100644 --- a/src/trans/x64.rs +++ b/src/trans/x64.rs @@ -1,9 +1,9 @@ -use crate::trans::{Code, FnName, Func, Program, SsaName, Var}; use crate::ast; +use crate::trans::{Code, FnName, Func, Program, SsaName, Var}; use std::{ borrow::Cow, - fmt, collections::HashMap, + fmt, io::{self, Write}, }; @@ -340,10 +340,14 @@ impl fmt::Display for Inst { fn builtin(name: &str) -> (u16, &'static str, &'static [Inst]) { match name { - "debug" => (1, "debug", &[ - Inst::Load(Reg::Rdi, Addr::Off(Reg::Rdi, 0x18)), - Inst::Jmp(Label::Extern("_ivy_debug")), - ]), + "debug" => ( + 1, + "debug", + &[ + Inst::Load(Reg::Rdi, Addr::Off(Reg::Rdi, 0x18)), + Inst::Jmp(Label::Extern("_ivy_debug")), + ], + ), name => panic!("Unsupported builtin '{}' for x64", name), } } -- 2.43.2