feat: some boilerplate for typechecking

This commit is contained in:
rvcas 2022-10-13 18:34:25 -04:00 committed by Lucas
parent ed2ef4fa9b
commit 4df3de0a03
13 changed files with 264 additions and 34 deletions

View File

@ -4,7 +4,7 @@ use std::{
path::PathBuf, path::PathBuf,
}; };
use aiken_lang::error::ParseError; use aiken_lang::{error::ParseError, tipo};
use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode}; use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
#[allow(dead_code)] #[allow(dead_code)]
@ -16,10 +16,17 @@ pub enum Error {
first: PathBuf, first: PathBuf,
second: PathBuf, second: PathBuf,
}, },
#[error("file operation failed")] #[error("file operation failed")]
FileIo { error: io::Error, path: PathBuf }, FileIo { error: io::Error, path: PathBuf },
#[error("cyclical module imports")] #[error("cyclical module imports")]
ImportCycle { modules: Vec<String> }, ImportCycle { modules: Vec<String> },
/// Useful for returning many [`Error::Parse`] at once
#[error("a list of errors")]
List(Vec<Self>),
#[error("failed to parse")] #[error("failed to parse")]
Parse { Parse {
path: PathBuf, path: PathBuf,
@ -29,9 +36,13 @@ pub enum Error {
#[source] #[source]
error: Box<ParseError>, error: Box<ParseError>,
}, },
/// Useful for returning many [`Error::Parse`] at once
#[error("a list of errors")] #[error("type checking failed")]
List(Vec<Self>), Type {
path: PathBuf,
src: String,
error: tipo::error::Error,
},
} }
impl Debug for Error { impl Debug for Error {
@ -56,11 +67,12 @@ impl Debug for Error {
impl miette::Diagnostic for Error { impl miette::Diagnostic for Error {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self { match self {
Error::DuplicateModule { .. } => Some(Box::new("aiken::project::duplicate_module")), Error::DuplicateModule { .. } => Some(Box::new("aiken::module::duplicate")),
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::ImportCycle { .. } => Some(Box::new("aiken::project::cyclical_import")), Error::ImportCycle { .. } => Some(Box::new("aiken::module::cyclical")),
Error::Parse { .. } => Some(Box::new("aiken::parser")),
Error::List(_) => None, 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- {}", "try moving the shared code to a separate module that the others can depend on\n- {}",
modules.join("\n- ") modules.join("\n- ")
))), ))),
Error::Parse { error, .. } => error.kind.help(),
Error::List(_) => None, 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::DuplicateModule { .. } => None,
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::Parse { error, .. } => error.labels(),
Error::List(_) => None, 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::DuplicateModule { .. } => None,
Error::FileIo { .. } => None, Error::FileIo { .. } => None,
Error::ImportCycle { .. } => None, Error::ImportCycle { .. } => None,
Error::Parse { src, .. } => Some(src),
Error::List(_) => None, Error::List(_) => None,
Error::Parse { src, .. } => Some(src),
Error::Type { src, .. } => Some(src),
} }
} }
} }

View File

@ -2,3 +2,6 @@ pub mod config;
pub mod error; pub mod error;
pub mod module; pub mod module;
pub mod project; pub mod project;
pub use aiken_lang;
pub use uplc;

View File

@ -1,16 +1,15 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
ops::Deref, ops::{Deref, DerefMut},
path::PathBuf, path::PathBuf,
}; };
use aiken_lang::ast::{ModuleKind, UntypedModule}; use aiken_lang::ast::{ModuleKind, TypedModule, UntypedModule};
use petgraph::{algo, graph::NodeIndex, Direction, Graph}; use petgraph::{algo, graph::NodeIndex, Direction, Graph};
use crate::error::Error; use crate::error::Error;
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)]
pub struct ParsedModule { pub struct ParsedModule {
pub path: PathBuf, pub path: PathBuf,
pub name: String, 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( fn find_cycle(
origin: NodeIndex, origin: NodeIndex,
parent: NodeIndex, parent: NodeIndex,
@ -149,3 +154,13 @@ fn find_cycle(
false 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,
}

View File

@ -4,12 +4,12 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use aiken_lang::ast::ModuleKind; use aiken_lang::{ast::ModuleKind, builtins, tipo};
use crate::{ use crate::{
config::Config, config::Config,
error::Error, error::Error,
module::{ParsedModule, ParsedModules}, module::{CheckedModule, ParsedModule, ParsedModules},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -20,20 +20,43 @@ pub struct Source {
pub kind: ModuleKind, 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 { pub struct Project {
config: Config, config: Config,
defined_modules: HashMap<String, PathBuf>,
module_types: HashMap<String, tipo::Module>,
root: PathBuf, root: PathBuf,
sources: Vec<Source>, sources: Vec<Source>,
defined_modules: HashMap<String, PathBuf>, warnings: Vec<Warning>,
} }
impl Project { impl Project {
pub fn new(config: Config, root: PathBuf) -> Project { pub fn new(config: Config, root: PathBuf) -> Project {
let mut module_types = HashMap::new();
module_types.insert("aiken".to_string(), builtins::prelude());
Project { Project {
config, config,
defined_modules: HashMap::new(),
module_types,
root, root,
sources: vec![], sources: vec![],
defined_modules: HashMap::new(), warnings: vec![],
} }
} }
@ -44,6 +67,20 @@ impl Project {
let processing_sequence = parsed_modules.sequence()?; 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(()) Ok(())
} }
@ -104,14 +141,62 @@ impl Project {
} }
} }
fn read_source_files(&mut self) -> Result<(), Error> { fn type_check(
let lib = self.root.join("lib"); &mut self,
let scripts = self.root.join("scripts"); 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)?; for name in processing_sequence {
self.aiken_files(&lib, ModuleKind::Lib)?; 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> { fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {

View File

@ -481,6 +481,7 @@ pub struct IfBranch<Expr> {
pub location: Span, pub location: Span,
} }
#[derive(Debug)]
pub struct TypedRecordUpdateArg { pub struct TypedRecordUpdateArg {
pub label: String, pub label: String,
pub location: Span, pub location: Span,

View File

@ -1,4 +0,0 @@
pub enum Origin {
Src,
Test,
}

View File

@ -0,0 +1,17 @@
use std::collections::HashMap;
use crate::{ast::ModuleKind, tipo};
pub fn prelude() -> tipo::Module {
let mut prelude = tipo::Module {
name: vec!["gleam".to_string()],
package: "".to_string(),
kind: ModuleKind::Lib,
types: HashMap::new(),
types_constructors: HashMap::new(),
values: HashMap::new(),
accessors: HashMap::new(),
};
prelude
}

View File

@ -10,6 +10,7 @@ use crate::{
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor}, tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
}; };
#[derive(Debug)]
pub enum TypedExpr { pub enum TypedExpr {
Int { Int {
location: Span, location: Span,

View File

@ -1,5 +1,5 @@
pub mod ast; pub mod ast;
pub mod build; pub mod builtins;
pub mod error; pub mod error;
pub mod expr; pub mod expr;
pub mod lexer; pub mod lexer;

View File

@ -1,9 +1,9 @@
use std::{cell::RefCell, collections::HashMap, sync::Arc}; use std::{cell::RefCell, collections::HashMap, sync::Arc};
use crate::{ use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant};
ast::{Constant, FieldMap, Span, TypedConstant},
build::Origin, pub mod error;
}; pub mod infer;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Type { pub enum Type {
@ -105,9 +105,10 @@ pub enum ValueConstructorVariant {
}, },
} }
#[derive(Debug, Clone)]
pub struct Module { pub struct Module {
pub name: Vec<String>, pub name: Vec<String>,
pub origin: Origin, pub kind: ModuleKind,
pub package: String, pub package: String,
pub types: HashMap<String, TypeConstructor>, pub types: HashMap<String, TypeConstructor>,
pub types_constructors: HashMap<String, Vec<String>>, pub types_constructors: HashMap<String, Vec<String>>,
@ -115,6 +116,7 @@ pub struct Module {
pub accessors: HashMap<String, AccessorsMap>, pub accessors: HashMap<String, AccessorsMap>,
} }
#[derive(Debug, Clone)]
pub struct TypeConstructor { pub struct TypeConstructor {
pub public: bool, pub public: bool,
pub origin: Span, pub origin: Span,
@ -123,12 +125,14 @@ pub struct TypeConstructor {
pub typ: Arc<Type>, pub typ: Arc<Type>,
} }
#[derive(Debug, Clone)]
pub struct AccessorsMap { pub struct AccessorsMap {
pub public: bool, pub public: bool,
pub tipo: Arc<Type>, pub tipo: Arc<Type>,
pub accessors: HashMap<String, RecordAccessor>, pub accessors: HashMap<String, RecordAccessor>,
} }
#[derive(Debug, Clone)]
pub struct RecordAccessor { pub struct RecordAccessor {
// TODO: smaller int. Doesn't need to be this big // TODO: smaller int. Doesn't need to be this big
pub index: u64, pub index: u64,
@ -136,6 +140,7 @@ pub struct RecordAccessor {
pub tipo: Arc<Type>, pub tipo: Arc<Type>,
} }
#[derive(Debug)]
pub enum PatternConstructor { pub enum PatternConstructor {
Record { Record {
name: String, name: String,
@ -143,6 +148,7 @@ pub enum PatternConstructor {
}, },
} }
#[derive(Debug)]
pub enum ModuleValueConstructor { pub enum ModuleValueConstructor {
Record { Record {
name: String, name: String,

View File

@ -0,0 +1,72 @@
use std::sync::Arc;
use miette::Diagnostic;
use crate::ast::{Span, TodoKind};
use super::Type;
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum Error {}
#[derive(Debug, PartialEq, Clone)]
pub enum Warning {
Todo {
kind: TodoKind,
location: Span,
typ: Arc<Type>,
},
ImplicitlyDiscardedResult {
location: Span,
},
UnusedLiteral {
location: Span,
},
NoFieldsRecordUpdate {
location: Span,
},
AllFieldsRecordUpdate {
location: Span,
},
UnusedType {
location: Span,
imported: bool,
name: String,
},
UnusedConstructor {
location: Span,
imported: bool,
name: String,
},
UnusedImportedValue {
location: Span,
name: String,
},
UnusedImportedModule {
location: Span,
name: String,
},
UnusedPrivateModuleConstant {
location: Span,
name: String,
},
UnusedPrivateFunction {
location: Span,
name: String,
},
UnusedVariable {
location: Span,
name: String,
},
}

View File

@ -0,0 +1,19 @@
use std::collections::HashMap;
use crate::ast::{ModuleKind, TypedModule, UntypedModule};
use super::{
error::{Error, Warning},
Module,
};
pub fn module(
// ids: &UniqueIdGenerator,
mut module: UntypedModule,
kind: ModuleKind,
package: &str,
modules: &HashMap<String, Module>,
warnings: &mut Vec<Warning>,
) -> Result<TypedModule, Error> {
todo!()
}

View File

@ -5,7 +5,7 @@ pub type Datum {
} }
pub type Redeemer { pub type Redeemer {
Buy, Buy
Sell Sell
} }