feat: register import, types, and values in environment
This commit is contained in:
parent
d0287d418b
commit
81c87ab4da
|
@ -45,6 +45,15 @@ pub enum Error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn total(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Error::List(errors) => errors.len(),
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Error {
|
impl Debug for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let miette_handler = MietteHandlerOpts::new()
|
let miette_handler = MietteHandlerOpts::new()
|
||||||
|
|
|
@ -49,15 +49,23 @@ fn main() -> miette::Result<()> {
|
||||||
|
|
||||||
let mut project = Project::new(config, project_path);
|
let mut project = Project::new(config, project_path);
|
||||||
|
|
||||||
if let Err(err) = project.build() {
|
let build_result = project.build();
|
||||||
match err {
|
|
||||||
|
for warning in project.warnings {
|
||||||
|
eprintln!("Warning: {:?}", warning)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = build_result {
|
||||||
|
match &err {
|
||||||
error::Error::List(errors) => {
|
error::Error::List(errors) => {
|
||||||
for error in errors {
|
for error in errors {
|
||||||
eprintln!("Error: {:?}", error)
|
eprintln!("Error: {:?}", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rest => Err(rest)?,
|
rest => eprintln!("Error: {:?}", rest),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
miette::bail!("failed: {} errors", err.total());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub struct Project {
|
||||||
module_types: HashMap<String, tipo::Module>,
|
module_types: HashMap<String, tipo::Module>,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
sources: Vec<Source>,
|
sources: Vec<Source>,
|
||||||
warnings: Vec<Warning>,
|
pub warnings: Vec<Warning>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
|
|
|
@ -181,6 +181,38 @@ pub struct FieldMap {
|
||||||
pub fields: HashMap<String, usize>,
|
pub fields: HashMap<String, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FieldMap {
|
||||||
|
pub fn new(arity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
arity,
|
||||||
|
fields: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
label: String,
|
||||||
|
index: usize,
|
||||||
|
location: &Span,
|
||||||
|
) -> Result<(), tipo::error::Error> {
|
||||||
|
match self.fields.insert(label.clone(), index) {
|
||||||
|
Some(_) => Err(tipo::error::Error::DuplicateField {
|
||||||
|
label,
|
||||||
|
location: *location,
|
||||||
|
}),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_option(self) -> Option<Self> {
|
||||||
|
if self.fields.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct RecordConstructor<T> {
|
pub struct RecordConstructor<T> {
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
|
@ -261,17 +293,84 @@ pub enum Annotation {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Tuple {
|
|
||||||
location: Span,
|
|
||||||
elems: Vec<Self>,
|
|
||||||
},
|
|
||||||
|
|
||||||
Hole {
|
Hole {
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Annotation {
|
||||||
|
pub fn location(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Annotation::Fn { location, .. }
|
||||||
|
| Annotation::Var { location, .. }
|
||||||
|
| Annotation::Hole { location, .. }
|
||||||
|
| Annotation::Constructor { location, .. } => *location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_logically_equal(&self, other: &Annotation) -> bool {
|
||||||
|
match self {
|
||||||
|
Annotation::Constructor {
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
location: _,
|
||||||
|
} => match other {
|
||||||
|
Annotation::Constructor {
|
||||||
|
module: o_module,
|
||||||
|
name: o_name,
|
||||||
|
arguments: o_arguments,
|
||||||
|
location: _,
|
||||||
|
} => {
|
||||||
|
module == o_module
|
||||||
|
&& name == o_name
|
||||||
|
&& arguments.len() == o_arguments.len()
|
||||||
|
&& arguments
|
||||||
|
.iter()
|
||||||
|
.zip(o_arguments)
|
||||||
|
.all(|a| a.0.is_logically_equal(a.1))
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Annotation::Fn {
|
||||||
|
arguments,
|
||||||
|
ret,
|
||||||
|
location: _,
|
||||||
|
} => match other {
|
||||||
|
Annotation::Fn {
|
||||||
|
arguments: o_arguments,
|
||||||
|
ret: o_return,
|
||||||
|
location: _,
|
||||||
|
} => {
|
||||||
|
arguments.len() == o_arguments.len()
|
||||||
|
&& arguments
|
||||||
|
.iter()
|
||||||
|
.zip(o_arguments)
|
||||||
|
.all(|a| a.0.is_logically_equal(a.1))
|
||||||
|
&& ret.is_logically_equal(o_return)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Annotation::Var { name, location: _ } => match other {
|
||||||
|
Annotation::Var {
|
||||||
|
name: o_name,
|
||||||
|
location: _,
|
||||||
|
} => name == o_name,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
|
||||||
|
Annotation::Hole { name, location: _ } => match other {
|
||||||
|
Annotation::Hole {
|
||||||
|
name: o_name,
|
||||||
|
location: _,
|
||||||
|
} => name == o_name,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Layer {
|
pub enum Layer {
|
||||||
Value,
|
Value,
|
||||||
|
@ -284,7 +383,7 @@ impl Default for Layer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum BinOp {
|
pub enum BinOp {
|
||||||
// Boolean logic
|
// Boolean logic
|
||||||
And,
|
And,
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
tipo: int(),
|
tipo: int(),
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -46,7 +46,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
tipo: byte_array(),
|
tipo: byte_array(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -93,7 +93,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
tipo: bool(),
|
tipo: bool(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -106,7 +106,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
parameters: vec![list_parameter.clone()],
|
parameters: vec![list_parameter.clone()],
|
||||||
tipo: list(list_parameter),
|
tipo: list(list_parameter),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -118,7 +118,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
tipo: string(),
|
tipo: string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -145,7 +145,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
tipo: nil(),
|
tipo: nil(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -160,7 +160,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
origin: Span::empty(),
|
origin: Span::empty(),
|
||||||
parameters: vec![result_value.clone(), result_error.clone()],
|
parameters: vec![result_value.clone(), result_error.clone()],
|
||||||
tipo: result(result_value, result_error),
|
tipo: result(result_value, result_error),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -211,7 +211,7 @@ pub fn int() -> Arc<Type> {
|
||||||
Arc::new(Type::App {
|
Arc::new(Type::App {
|
||||||
public: true,
|
public: true,
|
||||||
name: INT.to_string(),
|
name: INT.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ pub fn byte_array() -> Arc<Type> {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
public: true,
|
public: true,
|
||||||
name: BYTE_ARRAY.to_string(),
|
name: BYTE_ARRAY.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ pub fn bool() -> Arc<Type> {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
public: true,
|
public: true,
|
||||||
name: BOOL.to_string(),
|
name: BOOL.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ pub fn list(t: Arc<Type>) -> Arc<Type> {
|
||||||
Arc::new(Type::App {
|
Arc::new(Type::App {
|
||||||
public: true,
|
public: true,
|
||||||
name: LIST.to_string(),
|
name: LIST.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
args: vec![t],
|
args: vec![t],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ pub fn string() -> Arc<Type> {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
public: true,
|
public: true,
|
||||||
name: STRING.to_string(),
|
name: STRING.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ pub fn nil() -> Arc<Type> {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
public: true,
|
public: true,
|
||||||
name: NIL.to_string(),
|
name: NIL.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ pub fn result(a: Arc<Type>, e: Arc<Type>) -> Arc<Type> {
|
||||||
Arc::new(Type::App {
|
Arc::new(Type::App {
|
||||||
public: true,
|
public: true,
|
||||||
name: RESULT.to_string(),
|
name: RESULT.to_string(),
|
||||||
module: vec![],
|
module: "".to_string(),
|
||||||
args: vec![a, e],
|
args: vec![a, e],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -279,3 +279,9 @@ pub fn generic_var(id: u64) -> Arc<Type> {
|
||||||
|
|
||||||
Arc::new(Type::Var { tipo })
|
Arc::new(Type::Var { tipo })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unbound_var(id: u64) -> Arc<Type> {
|
||||||
|
let tipo = Arc::new(RefCell::new(TypeVar::Unbound { id }));
|
||||||
|
|
||||||
|
Arc::new(Type::Var { tipo })
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::{
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
|
@ -9,9 +12,9 @@ pub mod parser;
|
||||||
pub mod tipo;
|
pub mod tipo;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct IdGenerator {
|
pub struct IdGenerator {
|
||||||
id: AtomicU64,
|
id: Arc<AtomicU64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdGenerator {
|
impl IdGenerator {
|
||||||
|
|
|
@ -2,7 +2,9 @@ use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant};
|
use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant};
|
||||||
|
|
||||||
|
mod environment;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
mod hydrator;
|
||||||
pub mod infer;
|
pub mod infer;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -17,7 +19,7 @@ pub enum Type {
|
||||||
///
|
///
|
||||||
App {
|
App {
|
||||||
public: bool,
|
public: bool,
|
||||||
module: Vec<String>,
|
module: String,
|
||||||
name: String,
|
name: String,
|
||||||
args: Vec<Arc<Type>>,
|
args: Vec<Arc<Type>>,
|
||||||
},
|
},
|
||||||
|
@ -32,12 +34,11 @@ pub enum Type {
|
||||||
/// A type variable. See the contained `TypeVar` enum for more information.
|
/// A type variable. See the contained `TypeVar` enum for more information.
|
||||||
///
|
///
|
||||||
Var { tipo: Arc<RefCell<TypeVar>> },
|
Var { tipo: Arc<RefCell<TypeVar>> },
|
||||||
|
// /// A tuple is an ordered collection of 0 or more values, each of which
|
||||||
/// A tuple is an ordered collection of 0 or more values, each of which
|
// /// can have a different type, so the `tuple` type is the sum of all the
|
||||||
/// can have a different type, so the `tuple` type is the sum of all the
|
// /// contained types.
|
||||||
/// contained types.
|
// ///
|
||||||
///
|
// Tuple { elems: Vec<Arc<Type>> },
|
||||||
Tuple { elems: Vec<Arc<Type>> },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -67,6 +68,12 @@ pub enum TypeVar {
|
||||||
Generic { id: u64 },
|
Generic { id: u64 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TypeVar {
|
||||||
|
pub fn is_unbound(&self) -> bool {
|
||||||
|
matches!(self, Self::Unbound { .. })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ValueConstructor {
|
pub struct ValueConstructor {
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
|
@ -100,7 +107,7 @@ pub enum ValueConstructorVariant {
|
||||||
ModuleFn {
|
ModuleFn {
|
||||||
name: String,
|
name: String,
|
||||||
field_map: Option<FieldMap>,
|
field_map: Option<FieldMap>,
|
||||||
module: Vec<String>,
|
module: String,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
@ -131,7 +138,7 @@ pub struct Module {
|
||||||
pub struct TypeConstructor {
|
pub struct TypeConstructor {
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
pub origin: Span,
|
pub origin: Span,
|
||||||
pub module: Vec<String>,
|
pub module: String,
|
||||||
pub parameters: Vec<Arc<Type>>,
|
pub parameters: Vec<Arc<Type>>,
|
||||||
pub tipo: Arc<Type>,
|
pub tipo: Arc<Type>,
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,12 +2,157 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
|
|
||||||
use crate::ast::{Span, TodoKind};
|
use crate::ast::{BinOp, Span, TodoKind};
|
||||||
|
|
||||||
use super::Type;
|
use super::Type;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, Diagnostic)]
|
#[derive(Debug, thiserror::Error, Diagnostic)]
|
||||||
pub enum Error {}
|
pub enum Error {
|
||||||
|
#[error("duplicate const {name}")]
|
||||||
|
DuplicateConstName {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
#[label]
|
||||||
|
previous_location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("duplicate import {name}")]
|
||||||
|
DuplicateImport {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
#[label]
|
||||||
|
previous_location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("duplicate name {label}")]
|
||||||
|
DuplicateField {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
label: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("duplicate name {name}")]
|
||||||
|
DuplicateName {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
#[label]
|
||||||
|
previous_location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("duplicate type name {name}")]
|
||||||
|
DuplicateTypeName {
|
||||||
|
location: Span,
|
||||||
|
previous_location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("{name} has incorrect type arity expected {expected} but given {given}")]
|
||||||
|
IncorrectTypeArity {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
expected: usize,
|
||||||
|
given: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("{name} contains keyword {keyword}")]
|
||||||
|
KeywordInModuleName { name: String, keyword: String },
|
||||||
|
|
||||||
|
#[error("{name} is a reserved module name")]
|
||||||
|
ReservedModuleName { name: String },
|
||||||
|
|
||||||
|
#[error("unexpected type hole")]
|
||||||
|
UnexpectedTypeHole {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("unknown module {name}")]
|
||||||
|
UnknownModule {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
imported_modules: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("unknown module field {name} in module {module_name}")]
|
||||||
|
UnknownModuleField {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
module_name: String,
|
||||||
|
value_constructors: Vec<String>,
|
||||||
|
type_constructors: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
UnknownType {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
types: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
UnknownTypeConstructorType {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
type_constructors: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
UnknownTypeConstructorModule {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
imported_modules: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
UnknownTypeConstructorModuleType {
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
module_name: Vec<String>,
|
||||||
|
type_constructors: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
CouldNotUnify {
|
||||||
|
location: Span,
|
||||||
|
expected: Arc<Type>,
|
||||||
|
given: Arc<Type>,
|
||||||
|
situation: Option<UnifyErrorSituation>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
ExtraVarInAlternativePattern { location: Span, name: String },
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
MissingVarInAlternativePattern { location: Span, name: String },
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
DuplicateVarInPattern { location: Span, name: String },
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
RecursiveType { location: Span },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn flip_unify(self) -> Error {
|
||||||
|
match self {
|
||||||
|
Error::CouldNotUnify {
|
||||||
|
location,
|
||||||
|
expected,
|
||||||
|
given,
|
||||||
|
situation: note,
|
||||||
|
} => Error::CouldNotUnify {
|
||||||
|
location,
|
||||||
|
expected: given,
|
||||||
|
given: expected,
|
||||||
|
situation: note,
|
||||||
|
},
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Warning {
|
pub enum Warning {
|
||||||
|
@ -70,3 +215,24 @@ pub enum Warning {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum UnifyErrorSituation {
|
||||||
|
/// Clauses in a case expression were found to return different types.
|
||||||
|
CaseClauseMismatch,
|
||||||
|
|
||||||
|
/// A function was found to return a value that did not match its return
|
||||||
|
/// annotation.
|
||||||
|
ReturnAnnotationMismatch,
|
||||||
|
|
||||||
|
PipeTypeMismatch,
|
||||||
|
|
||||||
|
/// The operands of a binary operator were incorrect.
|
||||||
|
Operator(BinOp),
|
||||||
|
|
||||||
|
/// A try expression returned a different error type to the previous try.
|
||||||
|
TryErrorMismatch,
|
||||||
|
|
||||||
|
/// The final value of a try expression was not a Result.
|
||||||
|
TryReturnResult,
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{ast::Annotation, builtins::function};
|
||||||
|
|
||||||
|
use super::{environment::Environment, error::Error, Type, TypeConstructor};
|
||||||
|
|
||||||
|
/// The Hydrator takes an AST representing a type (i.e. a type annotation
|
||||||
|
/// for a function argument) and returns a Type for that annotation.
|
||||||
|
///
|
||||||
|
/// If a valid Type cannot be constructed it returns an error.
|
||||||
|
///
|
||||||
|
/// It keeps track of any type variables created. This is useful for:
|
||||||
|
///
|
||||||
|
/// - Determining if a generic type variable should be made into an
|
||||||
|
/// unbound type varable during type instantiation.
|
||||||
|
/// - Ensuring that the same type is constructed if the programmer
|
||||||
|
/// uses the same name for a type variable multiple times.
|
||||||
|
///
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Hydrator {
|
||||||
|
created_type_variables: HashMap<String, Arc<Type>>,
|
||||||
|
/// A rigid type is a generic type that was specified as being generic in
|
||||||
|
/// an annotation. As such it should never be instantiated into an unbound
|
||||||
|
/// variable. This type_id => name map is used for reporting the original
|
||||||
|
/// annotated name on error.
|
||||||
|
rigid_type_names: HashMap<u64, String>,
|
||||||
|
permit_new_type_variables: bool,
|
||||||
|
permit_holes: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScopeResetData {
|
||||||
|
created_type_variables: HashMap<String, Arc<Type>>,
|
||||||
|
rigid_type_names: HashMap<u64, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hydrator {
|
||||||
|
pub fn new() -> Hydrator {
|
||||||
|
Hydrator {
|
||||||
|
created_type_variables: HashMap::new(),
|
||||||
|
rigid_type_names: HashMap::new(),
|
||||||
|
permit_new_type_variables: true,
|
||||||
|
permit_holes: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_new_scope(&mut self) -> ScopeResetData {
|
||||||
|
let created_type_variables = self.created_type_variables.clone();
|
||||||
|
let rigid_type_names = self.rigid_type_names.clone();
|
||||||
|
|
||||||
|
ScopeResetData {
|
||||||
|
created_type_variables,
|
||||||
|
rigid_type_names,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close_scope(&mut self, data: ScopeResetData) {
|
||||||
|
self.created_type_variables = data.created_type_variables;
|
||||||
|
self.rigid_type_names = data.rigid_type_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disallow_new_type_variables(&mut self) {
|
||||||
|
self.permit_new_type_variables = false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn permit_holes(&mut self, flag: bool) {
|
||||||
|
self.permit_holes = flag
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A rigid type is a generic type that was specified as being generic in
|
||||||
|
/// an annotation. As such it should never be instantiated into an unbound
|
||||||
|
/// variable.
|
||||||
|
pub fn is_rigid(&self, id: &u64) -> bool {
|
||||||
|
self.rigid_type_names.contains_key(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rigid_names(&self) -> HashMap<u64, String> {
|
||||||
|
self.rigid_type_names.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_from_option_annotation<'a>(
|
||||||
|
&mut self,
|
||||||
|
ast: &Option<Annotation>,
|
||||||
|
environment: &mut Environment<'a>,
|
||||||
|
) -> Result<Arc<Type>, Error> {
|
||||||
|
match ast {
|
||||||
|
Some(ast) => self.type_from_annotation(ast, environment),
|
||||||
|
None => Ok(environment.new_unbound_var()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a Type from an AST Type annotation.
|
||||||
|
///
|
||||||
|
pub fn type_from_annotation<'a>(
|
||||||
|
&mut self,
|
||||||
|
annotation: &Annotation,
|
||||||
|
environment: &mut Environment<'a>,
|
||||||
|
) -> Result<Arc<Type>, Error> {
|
||||||
|
match annotation {
|
||||||
|
Annotation::Constructor {
|
||||||
|
location,
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
arguments: args,
|
||||||
|
} => {
|
||||||
|
// Hydrate the type argument AST into types
|
||||||
|
let mut argument_types = Vec::with_capacity(args.len());
|
||||||
|
for t in args {
|
||||||
|
let typ = self.type_from_annotation(t, environment)?;
|
||||||
|
argument_types.push((t.location(), typ));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up the constructor
|
||||||
|
let TypeConstructor {
|
||||||
|
parameters,
|
||||||
|
tipo: return_type,
|
||||||
|
..
|
||||||
|
} = environment
|
||||||
|
.get_type_constructor(module, name, *location)?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
// Register the type constructor as being used if it is unqualifed.
|
||||||
|
// We do not track use of qualified type constructors as they may be
|
||||||
|
// used in another module.
|
||||||
|
if module.is_none() {
|
||||||
|
environment.increment_usage(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the correct number of arguments have been given to the constructor
|
||||||
|
if args.len() != parameters.len() {
|
||||||
|
return Err(Error::IncorrectTypeArity {
|
||||||
|
location: *location,
|
||||||
|
name: name.to_string(),
|
||||||
|
expected: parameters.len(),
|
||||||
|
given: args.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate the constructor type for this specific usage
|
||||||
|
let mut type_vars = HashMap::new();
|
||||||
|
#[allow(clippy::needless_collect)] // Not needless, used for size effects
|
||||||
|
let parameter_types: Vec<_> = parameters
|
||||||
|
.into_iter()
|
||||||
|
.map(|typ| environment.instantiate(typ, &mut type_vars, self))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let return_type = environment.instantiate(return_type, &mut type_vars, self);
|
||||||
|
|
||||||
|
// Unify argument types with instantiated parameter types so that the correct types
|
||||||
|
// are inserted into the return type
|
||||||
|
for (parameter, (location, argument)) in
|
||||||
|
parameter_types.into_iter().zip(argument_types)
|
||||||
|
{
|
||||||
|
environment.unify(parameter, argument, location)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(return_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotation::Fn { arguments, ret, .. } => {
|
||||||
|
let mut args = Vec::with_capacity(arguments.len());
|
||||||
|
|
||||||
|
for arg in arguments {
|
||||||
|
let arg = self.type_from_annotation(arg, environment)?;
|
||||||
|
|
||||||
|
args.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = self.type_from_annotation(ret, environment)?;
|
||||||
|
|
||||||
|
Ok(function(args, ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotation::Var { name, location, .. } => match self.created_type_variables.get(name) {
|
||||||
|
Some(var) => Ok(var.clone()),
|
||||||
|
|
||||||
|
None if self.permit_new_type_variables => {
|
||||||
|
let var = environment.new_generic_var();
|
||||||
|
|
||||||
|
self.rigid_type_names
|
||||||
|
.insert(environment.previous_uid(), name.clone());
|
||||||
|
|
||||||
|
self.created_type_variables
|
||||||
|
.insert(name.clone(), var.clone());
|
||||||
|
|
||||||
|
Ok(var)
|
||||||
|
}
|
||||||
|
|
||||||
|
None => Err(Error::UnknownType {
|
||||||
|
name: name.to_string(),
|
||||||
|
location: *location,
|
||||||
|
types: environment
|
||||||
|
.module_types
|
||||||
|
.keys()
|
||||||
|
.map(|t| t.to_string())
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
Annotation::Hole { .. } if self.permit_holes => Ok(environment.new_unbound_var()),
|
||||||
|
|
||||||
|
Annotation::Hole { location, .. } => Err(Error::UnexpectedTypeHole {
|
||||||
|
location: *location,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,12 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{ModuleKind, TypedModule, UntypedModule},
|
ast::{ModuleKind, TypedModule, UntypedModule},
|
||||||
|
token::Token,
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
environment::Environment,
|
||||||
error::{Error, Warning},
|
error::{Error, Warning},
|
||||||
Module,
|
Module,
|
||||||
};
|
};
|
||||||
|
@ -18,5 +20,72 @@ pub fn module(
|
||||||
modules: &HashMap<String, Module>,
|
modules: &HashMap<String, Module>,
|
||||||
warnings: &mut Vec<Warning>,
|
warnings: &mut Vec<Warning>,
|
||||||
) -> Result<TypedModule, Error> {
|
) -> Result<TypedModule, Error> {
|
||||||
|
let name = module.name.clone();
|
||||||
|
let docs = std::mem::take(&mut module.docs);
|
||||||
|
let mut environment = Environment::new(id_gen.clone(), &name, modules, warnings);
|
||||||
|
|
||||||
|
validate_module_name(&name)?;
|
||||||
|
|
||||||
|
let mut type_names = HashMap::with_capacity(module.definitions.len());
|
||||||
|
let mut value_names = HashMap::with_capacity(module.definitions.len());
|
||||||
|
let mut hydrators = HashMap::with_capacity(module.definitions.len());
|
||||||
|
|
||||||
|
// Register any modules, types, and values being imported
|
||||||
|
// We process imports first so that anything imported can be referenced
|
||||||
|
// anywhere in the module.
|
||||||
|
for def in module.definitions() {
|
||||||
|
environment.register_import(def)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register types so they can be used in constructors and functions
|
||||||
|
// earlier in the module.
|
||||||
|
for def in module.definitions() {
|
||||||
|
environment.register_types(def, &name, &mut hydrators, &mut type_names)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register values so they can be used in functions earlier in the module.
|
||||||
|
for def in module.definitions() {
|
||||||
|
environment.register_values(def, &name, &mut hydrators, &mut value_names)?;
|
||||||
|
}
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_module_name(name: &str) -> Result<(), Error> {
|
||||||
|
if name == "aiken" {
|
||||||
|
return Err(Error::ReservedModuleName {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for segment in name.split('/') {
|
||||||
|
if str_to_keyword(segment).is_some() {
|
||||||
|
return Err(Error::KeywordInModuleName {
|
||||||
|
name: name.to_string(),
|
||||||
|
keyword: segment.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str_to_keyword(word: &str) -> Option<Token> {
|
||||||
|
// Alphabetical keywords:
|
||||||
|
match word {
|
||||||
|
"as" => Some(Token::As),
|
||||||
|
"assert" => Some(Token::Assert),
|
||||||
|
"when" => Some(Token::When),
|
||||||
|
"const" => Some(Token::Const),
|
||||||
|
"fn" => Some(Token::Fn),
|
||||||
|
"if" => Some(Token::If),
|
||||||
|
"use" => Some(Token::Use),
|
||||||
|
"let" => Some(Token::Let),
|
||||||
|
"opaque" => Some(Token::Opaque),
|
||||||
|
"pub" => Some(Token::Pub),
|
||||||
|
"todo" => Some(Token::Todo),
|
||||||
|
"try" => Some(Token::Try),
|
||||||
|
"type" => Some(Token::Type),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue