feat: start project building

This commit is contained in:
rvcas
2022-10-05 18:57:45 -04:00
committed by Lucas
parent 756e7c7680
commit ff26db2245
11 changed files with 305 additions and 20 deletions

View File

@@ -21,3 +21,7 @@ serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.85"
uplc = { path = '../uplc', version = "0.0.18" }
aiken-lang = { path = "../lang", version = "0.0.19" }
toml = "0.5.9"
walkdir = "2.3.2"
ignore = "0.4.18"
regex = "1.6.0"

View File

@@ -9,10 +9,11 @@ use clap::{Parser, Subcommand};
pub enum Args {
/// Build an aiken project
Build,
/// Check a file or project
/// Typecheck a project project
Check {
/// Specific aiken file to check
input: Option<PathBuf>,
/// Path to project
#[clap(short, long)]
directory: Option<PathBuf>,
},
/// Start a development server
Dev,

21
crates/cli/src/config.rs Normal file
View File

@@ -0,0 +1,21 @@
use std::{fs, io, path::PathBuf};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Config {
pub name: String,
pub version: String,
#[serde(default)]
pub description: String,
}
impl Config {
pub fn load(dir: PathBuf) -> io::Result<Config> {
let raw_config = fs::read_to_string(dir.join("aiken.toml"))?;
let config = toml::from_str(&raw_config).unwrap();
Ok(config)
}
}

3
crates/cli/src/error.rs Normal file
View File

@@ -0,0 +1,3 @@
pub enum Error {
Io {},
}

View File

@@ -1,10 +1,12 @@
use std::{fmt::Write as _, fs};
use std::{env, fmt::Write as _, fs};
use config::Config;
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,
@@ -16,6 +18,9 @@ use uplc::{
};
mod args;
mod config;
mod error;
mod project;
use args::{Args, TxCommand, UplcCommand};
@@ -33,19 +38,18 @@ fn main() -> anyhow::Result<()> {
todo!()
}
Args::Check { input } => {
if let Some(input) = input {
let src = fs::read_to_string(&input)?;
Args::Check { directory } => {
let project_path = if let Some(d) = directory {
d
} else {
env::current_dir()?
};
match aiken_lang::parser::script(&src) {
Ok(_) => (),
Err(errs) => {
for err in errs {
eprintln!("{:#?}", err);
}
}
}
}
let config = Config::load(project_path.clone())?;
let mut project = Project::new(config, project_path);
project.build()?;
}
Args::Dev => {

134
crates/cli/src/project.rs Normal file
View File

@@ -0,0 +1,134 @@
use std::{
fs, io,
path::{Path, PathBuf},
};
use aiken_lang::ast::ModuleKind;
use crate::config::Config;
#[derive(Debug)]
pub struct Source {
pub path: PathBuf,
pub name: String,
pub code: String,
pub kind: ModuleKind,
}
pub struct Project {
config: Config,
root: PathBuf,
sources: Vec<Source>,
}
impl Project {
pub fn new(config: Config, root: PathBuf) -> Project {
Project {
config,
root,
sources: vec![],
}
}
pub fn build(&mut self) -> io::Result<()> {
self.read_source_files()?;
for source in &self.sources {
println!("{:#?}", source);
match aiken_lang::parser::script(&source.code) {
Ok(_) => (),
Err(errs) => {
for err in errs {
eprintln!("{:#?}", err);
}
}
}
}
Ok(())
}
fn read_source_files(&mut self) -> io::Result<()> {
let lib = self.root.join("lib");
let scripts = self.root.join("scripts");
self.aiken_files(&scripts, ModuleKind::Script)?;
self.aiken_files(&lib, ModuleKind::Lib)?;
Ok(())
}
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> io::Result<()> {
let paths = walkdir::WalkDir::new(dir)
.follow_links(true)
.into_iter()
.filter_map(Result::ok)
.filter(|e| e.file_type().is_file())
.map(|d| d.into_path())
.filter(move |d| is_aiken_path(d, dir));
for path in paths {
self.add_module(path, dir, kind)?;
}
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)?;
self.sources.push(Source {
name,
code,
kind,
path,
});
Ok(())
}
fn module_name(&self, package_path: &Path, full_module_path: &Path) -> String {
// ../../lib/module.ak
// module.ak
let mut module_path = full_module_path
.strip_prefix(package_path)
.expect("Stripping package prefix from module path")
.to_path_buf();
// module
let _ = module_path.set_extension("");
// Stringify
let name = module_path
.to_str()
.expect("Module name path to str")
.to_string();
// normalise windows paths
let name = name.replace('\\', "/");
// project_name/module
format!("{}/{}", self.config.name, name)
}
}
fn is_aiken_path(path: &Path, dir: impl AsRef<Path>) -> bool {
use regex::Regex;
let re = Regex::new(&format!(
"^({module}{slash})*{module}\\.ak$",
module = "[a-z][_a-z0-9]*",
slash = "(/|\\\\)",
))
.expect("is_aiken_path() RE regex");
re.is_match(
path.strip_prefix(dir)
.expect("is_gleam_path(): strip_prefix")
.to_str()
.expect("is_gleam_path(): to_str"),
)
}

View File

@@ -14,7 +14,6 @@ pub type UntypedModule = Module<(), UntypedDefinition>;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ModuleKind {
Contract,
Lib,
Script,
}

View File

@@ -11,7 +11,7 @@ pub enum Type {
/// custom type such as `Person`. The type can take other types as
/// arguments (aka "generics" or "parametric polymorphism").
///
/// If the type is defined in the Gleam prelude the `module` field will be
/// If the type is defined in the Aiken prelude the `module` field will be
/// empty, otherwise it will contain the name of the module that
/// defines the type.
///
@@ -44,7 +44,7 @@ pub enum Type {
pub enum TypeVar {
/// Unbound is an unbound variable. It is one specific type but we don't
/// know what yet in the inference process. It has a unique id which can be used to
/// identify if two unbound variable Rust values are the same Gleam type variable
/// identify if two unbound variable Rust values are the same Aiken type variable
/// instance or not.
///
Unbound { id: u64 },
@@ -57,7 +57,7 @@ pub enum TypeVar {
///
/// # Example
///
/// ```gleam
/// ```aiken
/// type Cat(a) {
/// Cat(name: a)
/// }