feat: some boilerplate for typechecking
This commit is contained in:
@@ -4,7 +4,7 @@ use std::{
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use aiken_lang::error::ParseError;
|
||||
use aiken_lang::{error::ParseError, tipo};
|
||||
use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -16,10 +16,17 @@ pub enum Error {
|
||||
first: PathBuf,
|
||||
second: PathBuf,
|
||||
},
|
||||
|
||||
#[error("file operation failed")]
|
||||
FileIo { error: io::Error, path: PathBuf },
|
||||
|
||||
#[error("cyclical module imports")]
|
||||
ImportCycle { modules: Vec<String> },
|
||||
|
||||
/// Useful for returning many [`Error::Parse`] at once
|
||||
#[error("a list of errors")]
|
||||
List(Vec<Self>),
|
||||
|
||||
#[error("failed to parse")]
|
||||
Parse {
|
||||
path: PathBuf,
|
||||
@@ -29,9 +36,13 @@ pub enum Error {
|
||||
#[source]
|
||||
error: Box<ParseError>,
|
||||
},
|
||||
/// Useful for returning many [`Error::Parse`] at once
|
||||
#[error("a list of errors")]
|
||||
List(Vec<Self>),
|
||||
|
||||
#[error("type checking failed")]
|
||||
Type {
|
||||
path: PathBuf,
|
||||
src: String,
|
||||
error: tipo::error::Error,
|
||||
},
|
||||
}
|
||||
|
||||
impl Debug for Error {
|
||||
@@ -56,11 +67,12 @@ impl Debug for Error {
|
||||
impl miette::Diagnostic for Error {
|
||||
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||
match self {
|
||||
Error::DuplicateModule { .. } => Some(Box::new("aiken::project::duplicate_module")),
|
||||
Error::DuplicateModule { .. } => Some(Box::new("aiken::module::duplicate")),
|
||||
Error::FileIo { .. } => None,
|
||||
Error::ImportCycle { .. } => Some(Box::new("aiken::project::cyclical_import")),
|
||||
Error::Parse { .. } => Some(Box::new("aiken::parser")),
|
||||
Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")),
|
||||
Error::List(_) => None,
|
||||
Error::Parse { .. } => Some(Box::new("aiken::parser")),
|
||||
Error::Type { .. } => Some(Box::new("aiken::typecheck")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +88,9 @@ impl miette::Diagnostic for Error {
|
||||
"try moving the shared code to a separate module that the others can depend on\n- {}",
|
||||
modules.join("\n- ")
|
||||
))),
|
||||
Error::Parse { error, .. } => error.kind.help(),
|
||||
Error::List(_) => None,
|
||||
Error::Parse { error, .. } => error.kind.help(),
|
||||
Error::Type { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +99,9 @@ impl miette::Diagnostic for Error {
|
||||
Error::DuplicateModule { .. } => None,
|
||||
Error::FileIo { .. } => None,
|
||||
Error::ImportCycle { .. } => None,
|
||||
Error::Parse { error, .. } => error.labels(),
|
||||
Error::List(_) => None,
|
||||
Error::Parse { error, .. } => error.labels(),
|
||||
Error::Type { error, .. } => error.labels(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +110,9 @@ impl miette::Diagnostic for Error {
|
||||
Error::DuplicateModule { .. } => None,
|
||||
Error::FileIo { .. } => None,
|
||||
Error::ImportCycle { .. } => None,
|
||||
Error::Parse { src, .. } => Some(src),
|
||||
Error::List(_) => None,
|
||||
Error::Parse { src, .. } => Some(src),
|
||||
Error::Type { src, .. } => Some(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,6 @@ pub mod config;
|
||||
pub mod error;
|
||||
pub mod module;
|
||||
pub mod project;
|
||||
|
||||
pub use aiken_lang;
|
||||
pub use uplc;
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ops::Deref,
|
||||
ops::{Deref, DerefMut},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use aiken_lang::ast::{ModuleKind, UntypedModule};
|
||||
use aiken_lang::ast::{ModuleKind, TypedModule, UntypedModule};
|
||||
use petgraph::{algo, graph::NodeIndex, Direction, Graph};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct ParsedModule {
|
||||
pub path: PathBuf,
|
||||
pub name: String,
|
||||
@@ -120,6 +119,12 @@ impl Deref for ParsedModules {
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ParsedModules {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn find_cycle(
|
||||
origin: NodeIndex,
|
||||
parent: NodeIndex,
|
||||
@@ -149,3 +154,13 @@ fn find_cycle(
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CheckedModule {
|
||||
pub name: String,
|
||||
pub code: String,
|
||||
pub input_path: PathBuf,
|
||||
pub kind: ModuleKind,
|
||||
pub ast: TypedModule,
|
||||
// pub extra: ModuleExtra,
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use aiken_lang::ast::ModuleKind;
|
||||
use aiken_lang::{ast::ModuleKind, builtins, tipo};
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
error::Error,
|
||||
module::{ParsedModule, ParsedModules},
|
||||
module::{CheckedModule, ParsedModule, ParsedModules},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -20,20 +20,43 @@ pub struct Source {
|
||||
pub kind: ModuleKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Warning {
|
||||
Type {
|
||||
path: PathBuf,
|
||||
src: String,
|
||||
warning: tipo::error::Warning,
|
||||
},
|
||||
}
|
||||
|
||||
impl Warning {
|
||||
pub fn from_type_warning(warning: tipo::error::Warning, path: PathBuf, src: String) -> Warning {
|
||||
Warning::Type { path, warning, src }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Project {
|
||||
config: Config,
|
||||
defined_modules: HashMap<String, PathBuf>,
|
||||
module_types: HashMap<String, tipo::Module>,
|
||||
root: PathBuf,
|
||||
sources: Vec<Source>,
|
||||
defined_modules: HashMap<String, PathBuf>,
|
||||
warnings: Vec<Warning>,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn new(config: Config, root: PathBuf) -> Project {
|
||||
let mut module_types = HashMap::new();
|
||||
|
||||
module_types.insert("aiken".to_string(), builtins::prelude());
|
||||
|
||||
Project {
|
||||
config,
|
||||
defined_modules: HashMap::new(),
|
||||
module_types,
|
||||
root,
|
||||
sources: vec![],
|
||||
defined_modules: HashMap::new(),
|
||||
warnings: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +67,20 @@ impl Project {
|
||||
|
||||
let processing_sequence = parsed_modules.sequence()?;
|
||||
|
||||
let checked_modules = self.type_check(parsed_modules, processing_sequence)?;
|
||||
|
||||
println!("{:?}", checked_modules);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_source_files(&mut self) -> Result<(), Error> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -104,14 +141,62 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_source_files(&mut self) -> Result<(), Error> {
|
||||
let lib = self.root.join("lib");
|
||||
let scripts = self.root.join("scripts");
|
||||
fn type_check(
|
||||
&mut self,
|
||||
mut parsed_modules: ParsedModules,
|
||||
processing_sequence: Vec<String>,
|
||||
) -> Result<Vec<CheckedModule>, Error> {
|
||||
let mut modules = Vec::with_capacity(parsed_modules.len() + 1);
|
||||
|
||||
self.aiken_files(&scripts, ModuleKind::Script)?;
|
||||
self.aiken_files(&lib, ModuleKind::Lib)?;
|
||||
for name in processing_sequence {
|
||||
if let Some(ParsedModule {
|
||||
name,
|
||||
path,
|
||||
code,
|
||||
kind,
|
||||
package,
|
||||
ast,
|
||||
}) = parsed_modules.remove(&name)
|
||||
{
|
||||
let mut type_warnings = Vec::new();
|
||||
|
||||
Ok(())
|
||||
let ast = tipo::infer::module(
|
||||
ast,
|
||||
kind,
|
||||
&self.config.name,
|
||||
&self.module_types,
|
||||
&mut type_warnings,
|
||||
)
|
||||
.map_err(|error| Error::Type {
|
||||
path: path.clone(),
|
||||
src: code.clone(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
// Register any warnings emitted as type warnings
|
||||
let type_warnings = type_warnings
|
||||
.into_iter()
|
||||
.map(|w| Warning::from_type_warning(w, path.clone(), code.clone()));
|
||||
|
||||
self.warnings.extend(type_warnings);
|
||||
|
||||
// Register the types from this module so they can be imported into
|
||||
// other modules.
|
||||
self.module_types
|
||||
.insert(name.clone(), ast.type_info.clone());
|
||||
|
||||
modules.push(CheckedModule {
|
||||
kind,
|
||||
// extra,
|
||||
name,
|
||||
code,
|
||||
ast,
|
||||
input_path: path,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(modules)
|
||||
}
|
||||
|
||||
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {
|
||||
|
||||
Reference in New Issue
Block a user