mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
block expression
This commit is contained in:
parent
e87c6dccc1
commit
646f9f6bd2
7
hh.hlm
7
hh.hlm
|
@ -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
19
ret.hlm
|
@ -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
25
sketch.hlm
Normal 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;
|
||||||
|
}
|
|
@ -7,7 +7,11 @@ pub mod typing;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let src = "
|
let src = "
|
||||||
(\\x : num, y : num, z : num -> x)()
|
{
|
||||||
|
let x: num = 1;
|
||||||
|
let y: num = 2;
|
||||||
|
x
|
||||||
|
}
|
||||||
".to_string();
|
".to_string();
|
||||||
let filename = "?".to_string();
|
let filename = "?".to_string();
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,7 @@ pub enum Expr<'src> {
|
||||||
Assign(Vec<Binding<'src>>),
|
Assign(Vec<Binding<'src>>),
|
||||||
Block {
|
Block {
|
||||||
exprs: Vec<Spanned<Box<Self>>>,
|
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()
|
let block = expr.clone()
|
||||||
.map(boxspan)
|
.map(boxspan)
|
||||||
.separated_by(just(Token::Semicolon))
|
.then_ignore(just(Token::Semicolon))
|
||||||
.allow_trailing()
|
.repeated()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
.then(expr.clone()
|
||||||
|
.map(boxspan)
|
||||||
|
.or_not())
|
||||||
.delimited_by(
|
.delimited_by(
|
||||||
just(Token::Open(Delim::Brace)),
|
just(Token::Open(Delim::Brace)),
|
||||||
just(Token::Close(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
|
let atom = lit
|
||||||
.or(ident)
|
.or(ident)
|
||||||
|
|
|
@ -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!(),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -292,7 +292,7 @@ fn type_expr<'src>(
|
||||||
for (name, maybe_ty, expr) in bindings {
|
for (name, maybe_ty, expr) in bindings {
|
||||||
let ty = match maybe_ty {
|
let ty = match maybe_ty {
|
||||||
Some(ty) => 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(&mut new_env, unbox!(expr))?;
|
||||||
|
|
||||||
|
@ -326,17 +326,14 @@ fn type_expr<'src>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Assign(bindings) => {
|
Expr::Assign(bindings) => {
|
||||||
// Create a new type environment.
|
|
||||||
let mut new_env = env.clone();
|
|
||||||
|
|
||||||
// Type check the bindings.
|
// Type check the bindings.
|
||||||
let mut tbindings = Vec::new();
|
let mut tbindings = Vec::new();
|
||||||
for (name, maybe_ty, expr) in bindings {
|
for (name, maybe_ty, expr) in bindings {
|
||||||
let ty = match maybe_ty {
|
let ty = match maybe_ty {
|
||||||
Some(ty) => 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.
|
// Check if the type of the binding matches the type of the expression.
|
||||||
if texpr.0.ty() != &ty {
|
if texpr.0.ty() != &ty {
|
||||||
|
@ -354,13 +351,35 @@ fn type_expr<'src>(
|
||||||
}
|
}
|
||||||
|
|
||||||
tbindings.push((name, ty.clone(), sbox!(texpr)));
|
tbindings.push((name, ty.clone(), sbox!(texpr)));
|
||||||
new_env.bind(name, ty);
|
env.bind(name, ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the typed assign expression.
|
// Return the typed assign expression.
|
||||||
oks!(TExpr::Assign(tbindings))
|
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!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,8 @@ pub enum TExpr<'src> {
|
||||||
},
|
},
|
||||||
Assign(Vec<TypedBinding<'src>>),
|
Assign(Vec<TypedBinding<'src>>),
|
||||||
Block {
|
Block {
|
||||||
exprs: Vec<Self>,
|
exprs: Vec<Spanned<Self>>,
|
||||||
|
void: bool,
|
||||||
ret_ty: Type,
|
ret_ty: Type,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue