block expression

tc
azur 2023-03-29 20:47:48 +07:00
parent e87c6dccc1
commit 646f9f6bd2
8 changed files with 74 additions and 233 deletions

7
hh.hlm
View File

@ -1,7 +0,0 @@
\x -> x // => x : t0
\x -> x + 1 // => x : Int
(\x -> x)(1) // => x : Int
let a : num = 1,
b : num = a + 1,
c : num = b + 1;

19
ret.hlm
View File

@ -1,19 +0,0 @@
let something: bool = true;
let a: num = 4 in
println(a);
fun foo(x: bool): num = {
fun id(a: T): T = id;
if !id(x) then
return 1;
42
};
fun main(): () = {
let r: num = foo(something);
println(r);
};

25
sketch.hlm Normal file
View File

@ -0,0 +1,25 @@
mut ret_ty: Option<Ty>
if type_check(expr) = Return {
if ret_ty.is_some() && ret_ty != this_ty {
error
} else {
ret_ty = this_ty
}
}
=====
{
if true {
return 1;
}
if false {
return "Hello";
}
do_something();
return 4;
}

View File

@ -7,7 +7,11 @@ pub mod typing;
fn main() {
let src = "
(\\x : num, y : num, z : num -> x)()
{
let x: num = 1;
let y: num = 2;
x
}
".to_string();
let filename = "?".to_string();

View File

@ -206,6 +206,7 @@ pub enum Expr<'src> {
Assign(Vec<Binding<'src>>),
Block {
exprs: Vec<Spanned<Box<Self>>>,
void: bool, // True if last expression is discarded (ends with semicolon).
},
}
@ -307,14 +308,26 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
let block = expr.clone()
.map(boxspan)
.separated_by(just(Token::Semicolon))
.allow_trailing()
.then_ignore(just(Token::Semicolon))
.repeated()
.collect::<Vec<_>>()
.then(expr.clone()
.map(boxspan)
.or_not())
.delimited_by(
just(Token::Open(Delim::Brace)),
just(Token::Close(Delim::Brace)),
)
.map(|exprs| Expr::Block { exprs });
.map(|(mut exprs, end)| {
let void = end.is_none();
if let Some(end) = end {
exprs.push(end);
}
Expr::Block {
exprs,
void,
}
});
let atom = lit
.or(ident)

View File

@ -1,195 +0,0 @@
use super::{
ty::Type,
typed::TExpr,
};
use crate::{parse::{
ptree::*,
span::*,
}, span};
#[derive(Clone, Debug)]
struct TypeEnv {
bindings: Vec<(String, Type)>,
funcs: Vec<(String, Vec<Type>, Type)>,
}
impl TypeEnv {
fn new() -> Self {
Self {
bindings: Vec::new(),
funcs: Vec::new(),
}
}
fn bind(&mut self, name: String, ty: Type) {
self.bindings.push((name, ty));
}
fn bind_func(&mut self, name: String, args: Vec<Type>, ret_ty: Type) {
self.funcs.push((name, args, ret_ty));
}
fn lookup(&self, name: &str) -> Option<Type> {
self.bindings.iter()
.rev()
.find(|(n, _)| *n == name)
.map(|(_, t)| t.clone())
}
fn lookup_func(&self, name: &str) -> Option<(Vec<Type>, Type)> {
self.funcs.iter()
.rev()
.find(|(n, _, _)| *n == name)
.map(|(_, args, ret_ty)| (args.clone(), ret_ty.clone()))
}
}
struct TypeError {
msg: String,
loc: Span,
}
fn type_expr(env: &mut TypeEnv, expr: Spanned<Expr>) -> Result<Spanned<TExpr>, TypeError> {
match expr.value {
Expr::Lit(lit) => match lit {
Lit::Unit => Ok(span!(TExpr::Lit(Lit::Unit), expr.span)),
Lit::Bool(x) => Ok(span!(TExpr::Lit(Lit::Bool(x)), expr.span)),
Lit::Num(x) => Ok(span!(TExpr::Lit(Lit::Num(x)), expr.span)),
Lit::Str(x) => Ok(span!(TExpr::Lit(Lit::Str(x)), expr.span)),
},
Expr::Ident(name) => {
let ty = env.lookup(&name)
.ok_or(TypeError {
msg: format!("unknown identifier `{}`", name),
loc: expr.span.clone(),
})?;
Ok(span!(TExpr::Ident(name, ty), expr.span))
},
Expr::Unary(op, expr) => {
let span = expr.span.clone();
let texpr = type_expr(env, *expr)?;
let ret_ty = match op {
UnaryOp::Neg => Type::Num,
UnaryOp::Not => Type::Bool,
};
Ok(span!(
TExpr::Unary { op, expr: Box::new(texpr), ret_ty },
span
))
},
Expr::Binary(op, lhs, rhs) => {
let span = lhs.span.clone();
let tlhs = type_expr(env, *lhs)?;
let trhs = type_expr(env, *rhs)?;
let ret_ty = match op {
BinaryOp::Add
| BinaryOp::Sub
| BinaryOp::Mul
| BinaryOp::Div
| BinaryOp::Rem => Type::Num,
BinaryOp::And
| BinaryOp::Or => Type::Bool,
BinaryOp::Eq
| BinaryOp::Ne
| BinaryOp::Lt
| BinaryOp::Le
| BinaryOp::Gt
| BinaryOp::Ge => Type::Bool,
};
Ok(span!(
TExpr::Binary { op, lhs: Box::new(tlhs), rhs: Box::new(trhs), ret_ty },
span
))
},
Expr::Call(func, args) => {
let span = func.span.clone();
match func.value {
Expr::Ident(name) => {
// Get the function's argument and return types
let (arg_tys, ret_ty) = env.lookup_func(&name)
.ok_or(TypeError {
msg: format!("unknown function `{}`", name),
loc: span.clone(),
})?;
// Create a typed identifier
let tfunc = TExpr::Ident(
name,
Type::Func(arg_tys.clone(), Box::new(ret_ty.clone()))
);
// Check that the number of arguments matches
if arg_tys.len() != args.len() {
return Err(TypeError {
msg: format!(
"expected {} arguments, got {}",
arg_tys.len(), args.len()
),
loc: span,
});
}
// Type check the arguments
let mut targs = Vec::new();
for (arg, ty) in args.into_iter().zip(arg_tys) {
let targ = type_expr(env, arg)?;
if targ.value.ty() != &ty {
return Err(TypeError {
msg: format!(
"expected argument of type `{}`, got `{}`",
ty, targ.value.ty()
),
loc: targ.span,
});
}
targs.push(targ);
}
Ok(span!(
TExpr::Call {
func: Box::new(span!(tfunc, span.clone())),
args: targs,
ret_ty
},
span
))
},
Expr::Lambda(args, body) => {
// Create a new type environment
let mut new_env = env.clone();
// Bind the arguments to the new environment and also infer their types
let mut arg_tys = Vec::new();
for (arg, maybe_ty) in args {
let ty = match maybe_ty {
Some(ty) => ty,
None => todo!(), // TODO: infer the type
};
arg_tys.push((arg.clone(), ty.clone()));
env.bind(arg, ty);
}
// Type check the body
let tbody = type_expr(&mut new_env, *body)?;
// Return the typed lambda expression
Ok(span!(
TExpr::Lambda {
params: arg_tys,
body: Box::new(tbody.clone()),
ret_ty: tbody.value.ty().clone(),
},
span
))
},
_ => todo!(),
}
},
_ => todo!(),
}
}

View File

@ -292,7 +292,7 @@ fn type_expr<'src>(
for (name, maybe_ty, expr) in bindings {
let ty = match maybe_ty {
Some(ty) => ty,
None => todo!(), // TODO: infer.
None => todo!("Type inferrence"), // TODO: infer.
};
let texpr = type_expr(&mut new_env, unbox!(expr))?;
@ -326,17 +326,14 @@ fn type_expr<'src>(
}
Expr::Assign(bindings) => {
// Create a new type environment.
let mut new_env = env.clone();
// Type check the bindings.
let mut tbindings = Vec::new();
for (name, maybe_ty, expr) in bindings {
let ty = match maybe_ty {
Some(ty) => ty,
None => todo!(), // TODO: infer.
None => todo!("Type inferrence"), // TODO: infer.
};
let texpr = type_expr(&mut new_env, unbox!(expr))?;
let texpr = type_expr(env, unbox!(expr))?;
// Check if the type of the binding matches the type of the expression.
if texpr.0.ty() != &ty {
@ -354,13 +351,35 @@ fn type_expr<'src>(
}
tbindings.push((name, ty.clone(), sbox!(texpr)));
new_env.bind(name, ty);
env.bind(name, ty);
}
// Return the typed assign expression.
oks!(TExpr::Assign(tbindings))
}
Expr::Block { exprs, void } => {
let texprs = exprs
.into_iter()
.map(|e| type_expr(env, unbox!(e)))
.collect::<Result<Vec<_>, _>>()?;
let ret_ty = if void {
Type::Unit
} else if let Some(texpr) = texprs.last() {
texpr.0.ty().clone()
} else {
Type::Unit
};
oks!(TExpr::Block {
exprs: texprs,
void,
ret_ty,
})
}
#[allow(unreachable_patterns)]
_ => todo!(),
}
}

View File

@ -49,7 +49,8 @@ pub enum TExpr<'src> {
},
Assign(Vec<TypedBinding<'src>>),
Block {
exprs: Vec<Self>,
exprs: Vec<Spanned<Self>>,
void: bool,
ret_ty: Type,
},
}