feat: some boilerplate for typechecking
This commit is contained in:
parent
ed2ef4fa9b
commit
4df3de0a03
|
@ -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> {
|
||||
|
|
|
@ -481,6 +481,7 @@ pub struct IfBranch<Expr> {
|
|||
pub location: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypedRecordUpdateArg {
|
||||
pub label: String,
|
||||
pub location: Span,
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
pub enum Origin {
|
||||
Src,
|
||||
Test,
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -10,6 +10,7 @@ use crate::{
|
|||
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypedExpr {
|
||||
Int {
|
||||
location: Span,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub mod ast;
|
||||
pub mod build;
|
||||
pub mod builtins;
|
||||
pub mod error;
|
||||
pub mod expr;
|
||||
pub mod lexer;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
ast::{Constant, FieldMap, Span, TypedConstant},
|
||||
build::Origin,
|
||||
};
|
||||
use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant};
|
||||
|
||||
pub mod error;
|
||||
pub mod infer;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Type {
|
||||
|
@ -105,9 +105,10 @@ pub enum ValueConstructorVariant {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
pub name: Vec<String>,
|
||||
pub origin: Origin,
|
||||
pub kind: ModuleKind,
|
||||
pub package: String,
|
||||
pub types: HashMap<String, TypeConstructor>,
|
||||
pub types_constructors: HashMap<String, Vec<String>>,
|
||||
|
@ -115,6 +116,7 @@ pub struct Module {
|
|||
pub accessors: HashMap<String, AccessorsMap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeConstructor {
|
||||
pub public: bool,
|
||||
pub origin: Span,
|
||||
|
@ -123,12 +125,14 @@ pub struct TypeConstructor {
|
|||
pub typ: Arc<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AccessorsMap {
|
||||
pub public: bool,
|
||||
pub tipo: Arc<Type>,
|
||||
pub accessors: HashMap<String, RecordAccessor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RecordAccessor {
|
||||
// TODO: smaller int. Doesn't need to be this big
|
||||
pub index: u64,
|
||||
|
@ -136,6 +140,7 @@ pub struct RecordAccessor {
|
|||
pub tipo: Arc<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PatternConstructor {
|
||||
Record {
|
||||
name: String,
|
||||
|
@ -143,6 +148,7 @@ pub enum PatternConstructor {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ModuleValueConstructor {
|
||||
Record {
|
||||
name: String,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
|
@ -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!()
|
||||
}
|
|
@ -5,7 +5,7 @@ pub type Datum {
|
|||
}
|
||||
|
||||
pub type Redeemer {
|
||||
Buy,
|
||||
Buy
|
||||
Sell
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue