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 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let miette_handler = MietteHandlerOpts::new()
|
||||
|
|
|
@ -49,15 +49,23 @@ fn main() -> miette::Result<()> {
|
|||
|
||||
let mut project = Project::new(config, project_path);
|
||||
|
||||
if let Err(err) = project.build() {
|
||||
match err {
|
||||
let build_result = project.build();
|
||||
|
||||
for warning in project.warnings {
|
||||
eprintln!("Warning: {:?}", warning)
|
||||
}
|
||||
|
||||
if let Err(err) = build_result {
|
||||
match &err {
|
||||
error::Error::List(errors) => {
|
||||
for error in errors {
|
||||
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>,
|
||||
root: PathBuf,
|
||||
sources: Vec<Source>,
|
||||
warnings: Vec<Warning>,
|
||||
pub warnings: Vec<Warning>,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
|
|
|
@ -181,6 +181,38 @@ pub struct FieldMap {
|
|||
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)]
|
||||
pub struct RecordConstructor<T> {
|
||||
pub location: Span,
|
||||
|
@ -261,17 +293,84 @@ pub enum Annotation {
|
|||
name: String,
|
||||
},
|
||||
|
||||
Tuple {
|
||||
location: Span,
|
||||
elems: Vec<Self>,
|
||||
},
|
||||
|
||||
Hole {
|
||||
location: Span,
|
||||
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)]
|
||||
pub enum Layer {
|
||||
Value,
|
||||
|
@ -284,7 +383,7 @@ impl Default for Layer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BinOp {
|
||||
// Boolean logic
|
||||
And,
|
||||
|
|
|
@ -34,7 +34,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
parameters: vec![],
|
||||
tipo: int(),
|
||||
origin: Span::empty(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -46,7 +46,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
origin: Span::empty(),
|
||||
parameters: vec![],
|
||||
tipo: byte_array(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -93,7 +93,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
origin: Span::empty(),
|
||||
parameters: vec![],
|
||||
tipo: bool(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -106,7 +106,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
origin: Span::empty(),
|
||||
parameters: vec![list_parameter.clone()],
|
||||
tipo: list(list_parameter),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -118,7 +118,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
origin: Span::empty(),
|
||||
parameters: vec![],
|
||||
tipo: string(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -145,7 +145,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
origin: Span::empty(),
|
||||
parameters: vec![],
|
||||
tipo: nil(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -160,7 +160,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
|||
origin: Span::empty(),
|
||||
parameters: vec![result_value.clone(), result_error.clone()],
|
||||
tipo: result(result_value, result_error),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
@ -211,7 +211,7 @@ pub fn int() -> Arc<Type> {
|
|||
Arc::new(Type::App {
|
||||
public: true,
|
||||
name: INT.to_string(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
args: vec![],
|
||||
})
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ pub fn byte_array() -> Arc<Type> {
|
|||
args: vec![],
|
||||
public: true,
|
||||
name: BYTE_ARRAY.to_string(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -230,7 +230,7 @@ pub fn bool() -> Arc<Type> {
|
|||
args: vec![],
|
||||
public: true,
|
||||
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 {
|
||||
public: true,
|
||||
name: LIST.to_string(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
args: vec![t],
|
||||
})
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ pub fn string() -> Arc<Type> {
|
|||
args: vec![],
|
||||
public: true,
|
||||
name: STRING.to_string(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -257,7 +257,7 @@ pub fn nil() -> Arc<Type> {
|
|||
args: vec![],
|
||||
public: true,
|
||||
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 {
|
||||
public: true,
|
||||
name: RESULT.to_string(),
|
||||
module: vec![],
|
||||
module: "".to_string(),
|
||||
args: vec![a, e],
|
||||
})
|
||||
}
|
||||
|
@ -279,3 +279,9 @@ pub fn generic_var(id: u64) -> Arc<Type> {
|
|||
|
||||
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 builtins;
|
||||
|
@ -9,9 +12,9 @@ pub mod parser;
|
|||
pub mod tipo;
|
||||
pub mod token;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct IdGenerator {
|
||||
id: AtomicU64,
|
||||
id: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
impl IdGenerator {
|
||||
|
|
|
@ -2,7 +2,9 @@ use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
|||
|
||||
use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant};
|
||||
|
||||
mod environment;
|
||||
pub mod error;
|
||||
mod hydrator;
|
||||
pub mod infer;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -17,7 +19,7 @@ pub enum Type {
|
|||
///
|
||||
App {
|
||||
public: bool,
|
||||
module: Vec<String>,
|
||||
module: String,
|
||||
name: String,
|
||||
args: Vec<Arc<Type>>,
|
||||
},
|
||||
|
@ -32,12 +34,11 @@ pub enum Type {
|
|||
/// A type variable. See the contained `TypeVar` enum for more information.
|
||||
///
|
||||
Var { tipo: Arc<RefCell<TypeVar>> },
|
||||
|
||||
/// 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
|
||||
/// contained types.
|
||||
///
|
||||
Tuple { elems: Vec<Arc<Type>> },
|
||||
// /// 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
|
||||
// /// contained types.
|
||||
// ///
|
||||
// Tuple { elems: Vec<Arc<Type>> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -67,6 +68,12 @@ pub enum TypeVar {
|
|||
Generic { id: u64 },
|
||||
}
|
||||
|
||||
impl TypeVar {
|
||||
pub fn is_unbound(&self) -> bool {
|
||||
matches!(self, Self::Unbound { .. })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ValueConstructor {
|
||||
pub public: bool,
|
||||
|
@ -100,7 +107,7 @@ pub enum ValueConstructorVariant {
|
|||
ModuleFn {
|
||||
name: String,
|
||||
field_map: Option<FieldMap>,
|
||||
module: Vec<String>,
|
||||
module: String,
|
||||
arity: usize,
|
||||
location: Span,
|
||||
},
|
||||
|
@ -131,7 +138,7 @@ pub struct Module {
|
|||
pub struct TypeConstructor {
|
||||
pub public: bool,
|
||||
pub origin: Span,
|
||||
pub module: Vec<String>,
|
||||
pub module: String,
|
||||
pub parameters: Vec<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 crate::ast::{Span, TodoKind};
|
||||
use crate::ast::{BinOp, Span, TodoKind};
|
||||
|
||||
use super::Type;
|
||||
|
||||
#[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)]
|
||||
pub enum Warning {
|
||||
|
@ -70,3 +215,24 @@ pub enum Warning {
|
|||
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::{
|
||||
ast::{ModuleKind, TypedModule, UntypedModule},
|
||||
token::Token,
|
||||
IdGenerator,
|
||||
};
|
||||
|
||||
use super::{
|
||||
environment::Environment,
|
||||
error::{Error, Warning},
|
||||
Module,
|
||||
};
|
||||
|
@ -18,5 +20,72 @@ pub fn module(
|
|||
modules: &HashMap<String, Module>,
|
||||
warnings: &mut Vec<Warning>,
|
||||
) -> 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!()
|
||||
}
|
||||
|
||||
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