First tests for typechecking
This commit is contained in:
parent
f0ed63ccf3
commit
bcf48d0ecb
@ -169,8 +169,8 @@ fn typechecking(input: ast::AST, handle: &mut Schala, comp: Option<&mut Unfinish
|
|||||||
|
|
||||||
comp.map(|comp| {
|
comp.map(|comp| {
|
||||||
let artifact = TraceArtifact::new("type", match result {
|
let artifact = TraceArtifact::new("type", match result {
|
||||||
Ok(typestring) => typestring,
|
Ok(ty) => ty.to_string(),
|
||||||
Err(err) => format!("Type error: {}", err)
|
Err(err) => format!("Type error: {}", err.msg)
|
||||||
});
|
});
|
||||||
comp.add_artifact(artifact);
|
comp.add_artifact(artifact);
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::util::ScopeStack;
|
use crate::util::ScopeStack;
|
||||||
use crate::builtin::PrefixOp;
|
use crate::builtin::{PrefixOp, BinOp};
|
||||||
|
|
||||||
pub type TypeName = Rc<String>;
|
pub type TypeName = Rc<String>;
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ pub struct TypeContext<'a> {
|
|||||||
type InferResult<T> = Result<T, TypeError>;
|
type InferResult<T> = Result<T, TypeError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TypeError { msg: String }
|
pub struct TypeError { pub msg: String }
|
||||||
|
|
||||||
impl TypeError {
|
impl TypeError {
|
||||||
fn new<A>(msg: &str) -> InferResult<A> { //TODO make these kinds of error-producing functions CoW-ready
|
fn new<A>(msg: &str) -> InferResult<A> { //TODO make these kinds of error-producing functions CoW-ready
|
||||||
@ -27,7 +27,7 @@ impl TypeError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Const(TypeConst),
|
Const(TypeConst),
|
||||||
Arrow(Box<Type>, Box<Type>),
|
Arrow(Box<Type>, Box<Type>),
|
||||||
@ -57,7 +57,7 @@ macro_rules! ty {
|
|||||||
|
|
||||||
//TODO find a better way to capture the to/from string logic
|
//TODO find a better way to capture the to/from string logic
|
||||||
impl Type {
|
impl Type {
|
||||||
fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
use self::Type::*;
|
use self::Type::*;
|
||||||
use self::TypeConst::*;
|
use self::TypeConst::*;
|
||||||
match self {
|
match self {
|
||||||
@ -193,12 +193,12 @@ impl<'a> TypeContext<'a> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn typecheck(&mut self, ast: &AST) -> Result<String, String> {
|
pub fn typecheck(&mut self, ast: &AST) -> Result<Type, TypeError> {
|
||||||
let mut returned_type = Type::Const(TypeConst::Unit);
|
let mut returned_type = Type::Const(TypeConst::Unit);
|
||||||
for statement in ast.0.iter() {
|
for statement in ast.0.iter() {
|
||||||
returned_type = self.statement(statement.node()).map_err(|err| { err.msg })?
|
returned_type = self.statement(statement.node())?;
|
||||||
}
|
}
|
||||||
Ok(returned_type.to_string())
|
Ok(returned_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement(&mut self, statement: &Statement) -> InferResult<Type> {
|
fn statement(&mut self, statement: &Statement) -> InferResult<Type> {
|
||||||
@ -239,8 +239,10 @@ impl<'a> TypeContext<'a> {
|
|||||||
FloatLiteral(_) => ty!(Float),
|
FloatLiteral(_) => ty!(Float),
|
||||||
StringLiteral(_) => ty!(StringT),
|
StringLiteral(_) => ty!(StringT),
|
||||||
PrefixExp(op, expr) => self.prefix(op, expr.node())?,
|
PrefixExp(op, expr) => self.prefix(op, expr.node())?,
|
||||||
|
BinExp(op, lhs, rhs) => self.binexp(op, lhs.node(), rhs.node())?,
|
||||||
IfExpression { discriminator, body } => self.if_expr(discriminator, body)?,
|
IfExpression { discriminator, body } => self.if_expr(discriminator, body)?,
|
||||||
Value(val) => self.handle_value(val)?,
|
Value(val) => self.handle_value(val)?,
|
||||||
|
Lambda { params, type_anno, body } => self.lambda(params, type_anno, body)?,
|
||||||
_ => ty!(Unit),
|
_ => ty!(Unit),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -255,10 +257,20 @@ impl<'a> TypeContext<'a> {
|
|||||||
self.handle_apply(f, x)
|
self.handle_apply(f, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_apply(&mut self, f: Type, x: Type) -> InferResult<Type> {
|
fn binexp(&mut self, op: &BinOp, lhs: &Expression, rhs: &Expression) -> InferResult<Type> {
|
||||||
Ok(ty!(Unit))
|
Ok(ty!(Unit))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_apply(&mut self, tf: Type, tx: Type) -> InferResult<Type> {
|
||||||
|
Ok(match tf {
|
||||||
|
Type::Arrow(ref t1, ref t2) => {
|
||||||
|
let _ = self.unify(*t1.clone(), tx)?;
|
||||||
|
*t2.clone()
|
||||||
|
},
|
||||||
|
_ => return TypeError::new(&format!("Not a function"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn if_expr(&mut self, discriminator: &Discriminator, body: &IfExpressionBody) -> InferResult<Type> {
|
fn if_expr(&mut self, discriminator: &Discriminator, body: &IfExpressionBody) -> InferResult<Type> {
|
||||||
use self::Discriminator::*; use self::IfExpressionBody::*;
|
use self::Discriminator::*; use self::IfExpressionBody::*;
|
||||||
match (discriminator, body) {
|
match (discriminator, body) {
|
||||||
@ -279,6 +291,10 @@ impl<'a> TypeContext<'a> {
|
|||||||
self.unify(t2, t3)
|
self.unify(t2, t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lambda(&mut self, params: &Vec<FormalParam>, type_anno: &Option<TypeIdentifier>, body: &Block) -> InferResult<Type> {
|
||||||
|
Ok(ty!(Unit))
|
||||||
|
}
|
||||||
|
|
||||||
fn block(&mut self, block: &Block) -> InferResult<Type> {
|
fn block(&mut self, block: &Block) -> InferResult<Type> {
|
||||||
let mut output = ty!(Unit);
|
let mut output = ty!(Unit);
|
||||||
for s in block.iter() {
|
for s in block.iter() {
|
||||||
@ -303,3 +319,33 @@ impl<'a> TypeContext<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod typechecking_tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::ast::AST;
|
||||||
|
|
||||||
|
fn parse(input: &str) -> AST {
|
||||||
|
let tokens = crate::tokenizing::tokenize(input);
|
||||||
|
let mut parser = crate::parsing::Parser::new(tokens);
|
||||||
|
parser.parse().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_type_in_fresh_context {
|
||||||
|
($string:expr, $type:expr) => {
|
||||||
|
let mut tc = TypeContext::new();
|
||||||
|
let ref ast = parse($string);
|
||||||
|
let ty = tc.typecheck(ast).unwrap();
|
||||||
|
assert_eq!(ty, $type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_test() {
|
||||||
|
assert_type_in_fresh_context!("1", ty!(Nat));
|
||||||
|
assert_type_in_fresh_context!(r#""drugs""#, ty!(StringT));
|
||||||
|
assert_type_in_fresh_context!("true", ty!(Bool));
|
||||||
|
assert_type_in_fresh_context!("-1", ty!(Int));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user