From 59d7b54473e54292d059977b26bdffccf1e965eb Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 9 Oct 2022 17:33:14 -0400 Subject: [PATCH] feat: start integrating miette --- Cargo.lock | 155 +++++++++++++++++++++++++++++++- crates/cli/Cargo.toml | 2 + crates/cli/src/error.rs | 16 +++- crates/cli/src/lib.rs | 3 + crates/cli/src/main.rs | 91 ++++++++++--------- crates/cli/src/project.rs | 95 ++++++++++++++++---- crates/lang/src/ast.rs | 12 ++- crates/lang/src/parser.rs | 2 +- crates/lang/src/tests/parser.rs | 2 +- 9 files changed, 308 insertions(+), 70 deletions(-) create mode 100644 crates/cli/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 14cc4d7a..7157511a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.3.8" @@ -40,6 +55,7 @@ dependencies = [ "clap", "hex", "ignore", + "miette", "pallas-addresses", "pallas-codec", "pallas-crypto", @@ -48,6 +64,7 @@ dependencies = [ "regex", "serde", "serde_json", + "thiserror", "toml", "uplc", "walkdir", @@ -93,6 +110,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base58" version = "0.2.0" @@ -141,6 +173,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" @@ -289,6 +327,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "globset" version = "0.4.9" @@ -385,6 +429,12 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "itoa" version = "1.0.3" @@ -434,8 +484,16 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d" dependencies = [ + "atty", + "backtrace", "miette-derive", "once_cell", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", "thiserror", "unicode-width", ] @@ -472,6 +530,15 @@ dependencies = [ "syn", ] +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -481,6 +548,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.15.0" @@ -502,6 +578,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "pallas-addresses" version = "0.14.0-alpha.4" @@ -805,6 +887,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -875,12 +963,46 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "supports-color" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9" +dependencies = [ + "atty", + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" +dependencies = [ + "atty", +] + +[[package]] +name = "supports-unicode" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2" +dependencies = [ + "atty", +] + [[package]] name = "syn" version = "1.0.100" @@ -915,26 +1037,41 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] name = "thiserror" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -980,6 +1117,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +[[package]] +name = "unicode-linebreak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" +dependencies = [ + "hashbrown", + "regex", +] + [[package]] name = "unicode-segmentation" version = "1.10.0" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9d84dddc..99228b51 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -25,3 +25,5 @@ toml = "0.5.9" walkdir = "2.3.2" ignore = "0.4.18" regex = "1.6.0" +miette = { version = "5.3.0", features = ["fancy"] } +thiserror = "1.0.37" diff --git a/crates/cli/src/error.rs b/crates/cli/src/error.rs index a899c806..0bc9b165 100644 --- a/crates/cli/src/error.rs +++ b/crates/cli/src/error.rs @@ -1,4 +1,18 @@ +use std::{io, path::PathBuf}; + +use aiken_lang::error::ParseError; + #[allow(dead_code)] +#[derive(Debug, thiserror::Error, miette::Diagnostic)] pub enum Error { - Io {}, + #[error("file operation failed")] + FileIo { path: PathBuf, error: io::Error }, + #[error("failed to parse Aiken source code")] + Parse { + path: PathBuf, + src: String, + error: Box, + }, + #[error("list of errors")] + List(Vec), } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs new file mode 100644 index 00000000..37fc93f1 --- /dev/null +++ b/crates/cli/src/lib.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod error; +pub mod project; diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 84c04760..a60b4e56 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,12 +1,11 @@ use std::{env, fmt::Write as _, fs}; -use config::Config; +use miette::IntoDiagnostic; use pallas_primitives::{ babbage::{TransactionInput, TransactionOutput}, Fragment, }; use pallas_traverse::{Era, MultiEraTx}; -use project::Project; use uplc::{ ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, machine::cost_model::ExBudget, @@ -17,14 +16,13 @@ use uplc::{ }, }; +use aiken::{config::Config, project::Project}; + mod args; -mod config; -mod error; -mod project; use args::{Args, TxCommand, UplcCommand}; -fn main() -> anyhow::Result<()> { +fn main() -> miette::Result<()> { let args = Args::default(); match args { @@ -42,10 +40,10 @@ fn main() -> anyhow::Result<()> { let project_path = if let Some(d) = directory { d } else { - env::current_dir()? + env::current_dir().into_diagnostic()? }; - let config = Config::load(project_path.clone())?; + let config = Config::load(project_path.clone()).into_diagnostic()?; let mut project = Project::new(config, project_path); @@ -61,9 +59,9 @@ fn main() -> anyhow::Result<()> { Args::New { name } => { if !name.exists() { - fs::create_dir_all(name.join("lib"))?; - fs::create_dir_all(name.join("policies"))?; - fs::create_dir_all(name.join("scripts"))?; + fs::create_dir_all(name.join("lib")).into_diagnostic()?; + fs::create_dir_all(name.join("policies")).into_diagnostic()?; + fs::create_dir_all(name.join("scripts")).into_diagnostic()?; } } @@ -79,24 +77,25 @@ fn main() -> anyhow::Result<()> { } => { let (tx_bytes, inputs_bytes, outputs_bytes) = if cbor { ( - fs::read(input)?, - fs::read(raw_inputs)?, - fs::read(raw_outputs)?, + fs::read(input).into_diagnostic()?, + fs::read(raw_inputs).into_diagnostic()?, + fs::read(raw_outputs).into_diagnostic()?, ) } else { - let cbor_hex = fs::read_to_string(input)?; - let inputs_hex = fs::read_to_string(raw_inputs)?; - let outputs_hex = fs::read_to_string(raw_outputs)?; + let cbor_hex = fs::read_to_string(input).into_diagnostic()?; + let inputs_hex = fs::read_to_string(raw_inputs).into_diagnostic()?; + let outputs_hex = fs::read_to_string(raw_outputs).into_diagnostic()?; ( - hex::decode(cbor_hex.trim())?, - hex::decode(inputs_hex.trim())?, - hex::decode(outputs_hex.trim())?, + hex::decode(cbor_hex.trim()).into_diagnostic()?, + hex::decode(inputs_hex.trim()).into_diagnostic()?, + hex::decode(outputs_hex.trim()).into_diagnostic()?, ) }; let tx = MultiEraTx::decode(Era::Babbage, &tx_bytes) - .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))?; + .or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes)) + .into_diagnostic()?; let inputs = Vec::::decode_fragment(&inputs_bytes).unwrap(); let outputs = Vec::::decode_fragment(&outputs_bytes).unwrap(); @@ -157,14 +156,14 @@ fn main() -> anyhow::Result<()> { out, cbor_hex, } => { - let code = std::fs::read_to_string(&input)?; + let code = std::fs::read_to_string(&input).into_diagnostic()?; - let program = parser::program(&code)?; + let program = parser::program(&code).into_diagnostic()?; - let program = Program::::try_from(program)?; + let program = Program::::try_from(program).into_diagnostic()?; if cbor_hex { - let bytes = program.to_flat()?; + let bytes = program.to_flat().into_diagnostic()?; if print { let mut output = String::new(); @@ -187,10 +186,10 @@ fn main() -> anyhow::Result<()> { format!("{}.flat", input.file_stem().unwrap().to_str().unwrap()) }; - fs::write(&out_name, &bytes)?; + fs::write(&out_name, &bytes).into_diagnostic()?; } } else { - let cbor = program.to_hex()?; + let cbor = program.to_hex().into_diagnostic()?; if print { println!("{}", &cbor); @@ -201,22 +200,22 @@ fn main() -> anyhow::Result<()> { format!("{}.cbor", input.file_stem().unwrap().to_str().unwrap()) }; - fs::write(&out_name, &cbor)?; + fs::write(&out_name, &cbor).into_diagnostic()?; } } } UplcCommand::Fmt { input, print } => { - let code = std::fs::read_to_string(&input)?; + let code = std::fs::read_to_string(&input).into_diagnostic()?; - let program = parser::program(&code)?; + let program = parser::program(&code).into_diagnostic()?; let pretty = program.to_pretty(); if print { println!("{}", pretty); } else { - fs::write(&input, pretty)?; + fs::write(&input, pretty).into_diagnostic()?; } } UplcCommand::Unflat { @@ -226,19 +225,20 @@ fn main() -> anyhow::Result<()> { cbor_hex, } => { let program = if cbor_hex { - let cbor = std::fs::read_to_string(&input)?; + let cbor = std::fs::read_to_string(&input).into_diagnostic()?; let mut cbor_buffer = Vec::new(); let mut flat_buffer = Vec::new(); - Program::::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer)? + Program::::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer) + .into_diagnostic()? } else { - let bytes = std::fs::read(&input)?; + let bytes = std::fs::read(&input).into_diagnostic()?; - Program::::from_flat(&bytes)? + Program::::from_flat(&bytes).into_diagnostic()? }; - let program: Program = program.try_into()?; + let program: Program = program.try_into().into_diagnostic()?; let pretty = program.to_pretty(); @@ -251,27 +251,30 @@ fn main() -> anyhow::Result<()> { format!("{}.uplc", input.file_stem().unwrap().to_str().unwrap()) }; - fs::write(&out_name, pretty)?; + fs::write(&out_name, pretty).into_diagnostic()?; } } UplcCommand::Eval { script, flat, args } => { let mut program = if flat { - let bytes = std::fs::read(&script)?; + let bytes = std::fs::read(&script).into_diagnostic()?; - let prog = Program::::from_flat(&bytes)?; + let prog = Program::::from_flat(&bytes).into_diagnostic()?; prog.into() } else { - let code = std::fs::read_to_string(&script)?; + let code = std::fs::read_to_string(&script).into_diagnostic()?; - let prog = parser::program(&code)?; + let prog = parser::program(&code).into_diagnostic()?; - Program::::try_from(prog)? + Program::::try_from(prog).into_diagnostic()? }; for arg in args { - let term: Term = parser::term(&arg)?.try_into()?; + let term: Term = parser::term(&arg) + .into_diagnostic()? + .try_into() + .into_diagnostic()?; program = program.apply_term(&term); } @@ -280,7 +283,7 @@ fn main() -> anyhow::Result<()> { match term { Ok(term) => { - let term: Term = term.try_into()?; + let term: Term = term.try_into().into_diagnostic()?; println!("\nResult\n------\n\n{}\n", term.to_pretty()); } diff --git a/crates/cli/src/project.rs b/crates/cli/src/project.rs index e85918ce..bb792c3b 100644 --- a/crates/cli/src/project.rs +++ b/crates/cli/src/project.rs @@ -1,11 +1,12 @@ use std::{ - fs, io, + collections::HashMap, + fs, path::{Path, PathBuf}, }; -use aiken_lang::ast::ModuleKind; +use aiken_lang::ast::{ModuleKind, UntypedModule}; -use crate::config::Config; +use crate::{config::Config, error::Error}; #[derive(Debug)] pub struct Source { @@ -15,6 +16,17 @@ pub struct Source { pub kind: ModuleKind, } +#[derive(Debug)] +struct ParsedModule { + path: PathBuf, + name: String, + code: String, + kind: ModuleKind, + package: String, + ast: UntypedModule, + // extra: ModuleExtra, +} + pub struct Project { config: Config, root: PathBuf, @@ -30,26 +42,61 @@ impl Project { } } - pub fn build(&mut self) -> io::Result<()> { + pub fn build(&mut self) -> Result<(), Error> { self.read_source_files()?; - for source in &self.sources { - println!("{:#?}", source); + self.parse_sources()?; - match aiken_lang::parser::script(&source.code) { - Ok(_) => (), + Ok(()) + } + + fn parse_sources(&mut self) -> Result, Error> { + let mut errors = Vec::new(); + let mut parsed_modules = HashMap::with_capacity(self.sources.len()); + + for Source { + path, + name, + code, + kind, + } in self.sources.drain(0..) + { + match aiken_lang::parser::script(&code) { + Ok(mut ast) => { + // Store the name + ast.name = name.clone(); + + let module = ParsedModule { + kind, + ast, + code, + name, + path, + package: "".to_string(), + }; + + let _ = parsed_modules.insert(module.name.clone(), module); + } Err(errs) => { - for err in errs { - eprintln!("{:#?}", err); + for error in errs { + errors.push(Error::Parse { + path: path.clone(), + src: code.clone(), + error: Box::new(error), + }) } } } } - Ok(()) + if errors.is_empty() { + Ok(parsed_modules) + } else { + Err(Error::List(errors)) + } } - fn read_source_files(&mut self) -> io::Result<()> { + fn read_source_files(&mut self) -> Result<(), Error> { let lib = self.root.join("lib"); let scripts = self.root.join("scripts"); @@ -59,7 +106,7 @@ impl Project { Ok(()) } - fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> io::Result<()> { + fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> { let paths = walkdir::WalkDir::new(dir) .follow_links(true) .into_iter() @@ -75,9 +122,12 @@ impl Project { Ok(()) } - fn add_module(&mut self, path: PathBuf, dir: &Path, kind: ModuleKind) -> io::Result<()> { - let name = self.module_name(dir, &path); - let code = fs::read_to_string(&path)?; + fn add_module(&mut self, path: PathBuf, dir: &Path, kind: ModuleKind) -> Result<(), Error> { + let name = self.module_name(dir, &path, kind); + let code = fs::read_to_string(&path).map_err(|error| Error::FileIo { + path: path.clone(), + error, + })?; self.sources.push(Source { name, @@ -89,7 +139,12 @@ impl Project { Ok(()) } - fn module_name(&self, package_path: &Path, full_module_path: &Path) -> String { + fn module_name( + &self, + package_path: &Path, + full_module_path: &Path, + kind: ModuleKind, + ) -> String { // ../../lib/module.ak // module.ak @@ -111,7 +166,11 @@ impl Project { let name = name.replace('\\', "/"); // project_name/module - format!("{}/{}", self.config.name, name) + if kind.is_lib() { + format!("{}/{}", self.config.name, name) + } else { + name + } } } diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 07b03898..b9408d8a 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -18,9 +18,19 @@ pub enum ModuleKind { Script, } +impl ModuleKind { + pub fn is_script(&self) -> bool { + matches!(self, ModuleKind::Script) + } + + pub fn is_lib(&self) -> bool { + matches!(self, ModuleKind::Lib) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Module { - pub name: Vec, + pub name: String, pub docs: Vec, pub type_info: Info, pub definitions: Vec, diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 7be388ee..276deb72 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -37,7 +37,7 @@ pub fn module_parser( kind, definitions, docs: vec![], - name: vec![], + name: "".to_string(), type_info: (), }) } diff --git a/crates/lang/src/tests/parser.rs b/crates/lang/src/tests/parser.rs index 2ea5e300..9c67ba9b 100644 --- a/crates/lang/src/tests/parser.rs +++ b/crates/lang/src/tests/parser.rs @@ -126,7 +126,7 @@ fn module() { ast::UntypedModule { docs: vec![], kind: ast::ModuleKind::Script, - name: vec![], + name: "".to_string(), type_info: (), definitions: vec![ ast::UntypedDefinition::Use {