Load main in the CLI and use it for gen

This commit is contained in:
Richard Feldman 2020-04-12 15:25:07 -04:00
parent 9ca754b8fd
commit 1517581ced
6 changed files with 84 additions and 424 deletions

View file

@ -1,7 +1,6 @@
extern crate roc_gen;
extern crate roc_reporting;
use crate::helpers::{infer_expr, uniq_expr_with};
use bumpalo::Bump;
use inkwell::context::Context;
use inkwell::module::Linkage;
@ -15,6 +14,7 @@ use roc_gen::llvm::build::{
};
use roc_gen::llvm::convert::basic_type_from_layout;
use roc_load::file::{load, LoadedModule, LoadingProblem};
use roc_module::symbol::Symbol;
use roc_mono::expr::{Expr, Procs};
use roc_mono::layout::Layout;
use std::time::SystemTime;
@ -29,8 +29,6 @@ use std::process::Command;
use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor};
use tokio::runtime::Builder;
pub mod helpers;
fn main() -> io::Result<()> {
let argv = std::env::args().collect::<Vec<String>>();
@ -67,6 +65,7 @@ fn main() -> io::Result<()> {
async fn load_file(src_dir: PathBuf, filename: PathBuf) -> Result<(), LoadingProblem> {
let compilation_start = SystemTime::now();
let arena = Bump::new();
// Step 1: compile the app and generate the .o file
let subs_by_module = MutMap::default();
@ -80,7 +79,7 @@ async fn load_file(src_dir: PathBuf, filename: PathBuf) -> Result<(), LoadingPro
let dest_filename = filename.with_extension("o");
gen(loaded, filename, Triple::host(), &dest_filename);
gen(&arena, loaded, filename, Triple::host(), &dest_filename);
let compilation_end = compilation_start.elapsed().unwrap();
@ -103,14 +102,16 @@ async fn load_file(src_dir: PathBuf, filename: PathBuf) -> Result<(), LoadingPro
.expect("`ar` failed to run");
// Step 3: have rustc compile the host and link in the .a file
let binary_path = cwd.join("app");
Command::new("rustc")
.args(&["-L", ".", "host.rs", "-o", "app"])
.args(&["-L", ".", "--crate-type", "bin", "host.rs", "-o", binary_path.as_path().to_str().unwrap()])
.current_dir(cwd)
.spawn()
.expect("rustc failed to run");
// Step 4: Run the compiled app
Command::new(cwd.join("app")).spawn().unwrap_or_else(|err| {
Command::new(binary_path).spawn().unwrap_or_else(|err| {
panic!(
"{} failed to run: {:?}",
cwd.join("app").to_str().unwrap(),
@ -121,27 +122,19 @@ async fn load_file(src_dir: PathBuf, filename: PathBuf) -> Result<(), LoadingPro
Ok(())
}
fn gen(loaded: LoadedModule, filename: PathBuf, target: Triple, dest_filename: &Path) {
fn gen(arena: &Bump, loaded: LoadedModule, filename: PathBuf, target: Triple, dest_filename: &Path) {
use roc_reporting::report::{can_problem, RocDocAllocator, DEFAULT_PALETTE};
use roc_reporting::type_error::type_problem;
// Build the expr
let arena = Bump::new();
let src = loaded.src;
let (loc_expr, _output, can_problems, subs, var, constraint, home, interns) =
uniq_expr_with(&arena, &src, &ImMap::default());
let mut type_problems = Vec::new();
let (content, mut subs) = infer_expr(subs, &mut type_problems, &constraint, var);
let home = loaded.module_id;
let src_lines: Vec<&str> = src.split('\n').collect();
let palette = DEFAULT_PALETTE;
// Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
let alloc = RocDocAllocator::new(&src_lines, home, &loaded.interns);
for problem in can_problems.into_iter() {
for problem in loaded.can_problems.into_iter() {
let report = can_problem(&alloc, filename.clone(), problem);
let mut buf = String::new();
@ -150,7 +143,7 @@ fn gen(loaded: LoadedModule, filename: PathBuf, target: Triple, dest_filename: &
println!("\n{}\n", buf);
}
for problem in type_problems.into_iter() {
for problem in loaded.type_problems.into_iter() {
let report = type_problem(&alloc, filename.clone(), problem);
let mut buf = String::new();
@ -159,6 +152,63 @@ fn gen(loaded: LoadedModule, filename: PathBuf, target: Triple, dest_filename: &
println!("\n{}\n", buf);
}
// Look up the types and expressions of the `provided` values
// TODO instead of hardcoding this to `main`, use the `provided` list and gen all of them.
let ident_ids = loaded.interns.all_ident_ids.get(&home).unwrap();
let main_ident_id = *ident_ids.get_id(&"main".into()).unwrap_or_else(|| {
todo!("TODO gracefully handle the case where `main` wasn't declared in the app")
});
let main_symbol = Symbol::new(home, main_ident_id);
let mut main_var = None;
let mut main_expr = None;
for (symbol, var) in loaded.exposed_vars_by_symbol {
if symbol == main_symbol {
main_var = Some(var);
break;
}
}
// We use a loop label here so we can break all the way out of a nested
// loop inside DeclareRec if we find the expr there.
//
// https://doc.rust-lang.org/1.30.0/book/first-edition/loops.html#loop-labels
'find_expr: for decl in loaded.declarations {
use roc_can::def::Declaration::*;
match decl {
Declare(def) => {
if def.pattern_vars.contains_key(&main_symbol) {
main_expr = Some(def.loc_expr);
break 'find_expr;
}
}
DeclareRec(defs) => {
for def in defs {
if def.pattern_vars.contains_key(&main_symbol) {
main_expr = Some(def.loc_expr);
break 'find_expr;
}
}
}
InvalidCycle( _, _) => {}
}
}
let loc_expr = main_expr.unwrap_or_else(|| panic!
("TODO gracefully handle the case where `main` was declared but not exposed")
);
let mut subs = loaded.solved.into_inner();
let content = match main_var {
Some(var) => subs.get_without_compacting(var).content,
None => todo!("TODO gracefully handle the case where `main` was declared but not exposed"),
};
// Generate the binary
let context = Context::create();
@ -181,14 +231,14 @@ fn gen(loaded: LoadedModule, filename: PathBuf, target: Triple, dest_filename: &
let main_fn_type =
basic_type_from_layout(&arena, &context, &layout, ptr_bytes).fn_type(&[], false);
let main_fn_name = "$Test.main";
let main_fn_name = "$main";
// Compile and add all the Procs before adding main
let mut env = roc_gen::llvm::build::Env {
arena: &arena,
builder: &builder,
context: &context,
interns,
interns: loaded.interns,
module: arena.alloc(module),
ptr_bytes,
};