Compare commits
2 commits
22e2a45401
...
b959e11e51
| Author | SHA1 | Date | |
|---|---|---|---|
| b959e11e51 | |||
| 6a1362390c |
14 changed files with 682 additions and 70 deletions
3
simple.sui
Normal file
3
simple.sui
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
fn simple() -> int do
|
||||||
|
42
|
||||||
|
end
|
||||||
13
src/ast.rs
13
src/ast.rs
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::typechecker::Type;
|
use crate::typechecker::Type;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct BindingId(pub usize);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TypeAnnot {
|
pub enum TypeAnnot {
|
||||||
Var(String),
|
Var(String),
|
||||||
|
|
@ -306,7 +309,7 @@ pub enum TypedASTNodeKind {
|
||||||
pub struct TypedFunction {
|
pub struct TypedFunction {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub parameters: Vec<Parameter>,
|
pub parameters: Vec<Parameter>,
|
||||||
pub args: Vec<(String, Option<TypeAnnot>)>,
|
pub args: Vec<(BindingId, String, Option<TypeAnnot>)>,
|
||||||
pub return_type: Option<TypeAnnot>,
|
pub return_type: Option<TypeAnnot>,
|
||||||
pub body: TypedExpr,
|
pub body: TypedExpr,
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
|
|
@ -395,8 +398,8 @@ pub enum TypedExprKind {
|
||||||
Dot(Box<TypedExpr>, String),
|
Dot(Box<TypedExpr>, String),
|
||||||
EarlyReturn(Option<Box<TypedExpr>>),
|
EarlyReturn(Option<Box<TypedExpr>>),
|
||||||
OptionalChain(Option<Box<TypedExpr>>, String),
|
OptionalChain(Option<Box<TypedExpr>>, String),
|
||||||
Lambda(Vec<(String, Option<TypeAnnot>)>, Box<TypedExpr>),
|
Lambda(Vec<(BindingId, String, Option<TypeAnnot>)>, Box<TypedExpr>),
|
||||||
Let(String, BindingKind, Option<TypeAnnot>, Box<TypedExpr>),
|
Let(BindingId, String, BindingKind, Option<TypeAnnot>, Box<TypedExpr>),
|
||||||
Assign(Box<TypedExpr>, Box<TypedExpr>),
|
Assign(Box<TypedExpr>, Box<TypedExpr>),
|
||||||
Cast(Box<TypedExpr>, TypeAnnot),
|
Cast(Box<TypedExpr>, TypeAnnot),
|
||||||
If(Box<TypedExpr>, Box<TypedExpr>, Option<Box<TypedExpr>>),
|
If(Box<TypedExpr>, Box<TypedExpr>, Option<Box<TypedExpr>>),
|
||||||
|
|
@ -405,7 +408,7 @@ pub enum TypedExprKind {
|
||||||
Do(Vec<TypedExpr>),
|
Do(Vec<TypedExpr>),
|
||||||
BinOp(Box<TypedExpr>, BinOp, Box<TypedExpr>),
|
BinOp(Box<TypedExpr>, BinOp, Box<TypedExpr>),
|
||||||
UnOp(UnOp, Box<TypedExpr>),
|
UnOp(UnOp, Box<TypedExpr>),
|
||||||
For(String, Box<TypedExpr>, Box<TypedExpr>),
|
For(BindingId, String, Box<TypedExpr>, Box<TypedExpr>),
|
||||||
Range(Box<TypedExpr>, Box<TypedExpr>),
|
Range(Box<TypedExpr>, Box<TypedExpr>),
|
||||||
Return(Option<Box<TypedExpr>>),
|
Return(Option<Box<TypedExpr>>),
|
||||||
Break,
|
Break,
|
||||||
|
|
@ -422,7 +425,7 @@ pub struct TypedPattern {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TypedPatternKind {
|
pub enum TypedPatternKind {
|
||||||
Wildcard,
|
Wildcard,
|
||||||
Variable(String),
|
Variable(BindingId, String),
|
||||||
Literal(String),
|
Literal(String),
|
||||||
Tuple(Vec<TypedPattern>),
|
Tuple(Vec<TypedPattern>),
|
||||||
Struct(String, Vec<(String, TypedPattern)>),
|
Struct(String, Vec<(String, TypedPattern)>),
|
||||||
|
|
|
||||||
382
src/lambda_lower.rs
Normal file
382
src/lambda_lower.rs
Normal file
|
|
@ -0,0 +1,382 @@
|
||||||
|
use crate::ast::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// LambdaLowerer converts lambda expressions into generated functions
|
||||||
|
/// that are hoisted to the top level of the program.
|
||||||
|
pub struct LambdaLowerer {
|
||||||
|
lambda_counter: Rc<RefCell<usize>>,
|
||||||
|
generated_functions: Rc<RefCell<Vec<ASTNode>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LambdaLowerer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
LambdaLowerer {
|
||||||
|
lambda_counter: Rc::new(RefCell::new(0)),
|
||||||
|
generated_functions: Rc::new(RefCell::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn collect_free_vars(&self, expr: &Expr, lambda_params: &[String]) -> std::collections::HashSet<String> {
|
||||||
|
let mut free_vars = std::collections::HashSet::new();
|
||||||
|
let mut local_scope = std::collections::HashSet::new();
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, &mut free_vars, &mut local_scope);
|
||||||
|
free_vars
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_free_vars_expr(&self, expr: &Expr, lambda_params: &[String], free_vars: &mut std::collections::HashSet<String>, local_scope: &mut std::collections::HashSet<String>) {
|
||||||
|
match &expr.kind {
|
||||||
|
ExprKind::Variable(name) => {
|
||||||
|
if !lambda_params.contains(name) && !local_scope.contains(name) {
|
||||||
|
free_vars.insert(name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Lambda(args, body) => {
|
||||||
|
let param_names: Vec<String> = args.iter().map(|(name, _)| name.clone()).collect();
|
||||||
|
// For nested lambdas, we don't enter a new scope here since we're just collecting free vars
|
||||||
|
self.collect_free_vars_expr(body, ¶m_names, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Let(name, _, _, body) => {
|
||||||
|
// Collect from body before adding the binding
|
||||||
|
self.collect_free_vars_expr(body, lambda_params, free_vars, local_scope);
|
||||||
|
// Add to local scope
|
||||||
|
local_scope.insert(name.clone());
|
||||||
|
}
|
||||||
|
ExprKind::Call(func, args) => {
|
||||||
|
self.collect_free_vars_expr(func, lambda_params, free_vars, local_scope);
|
||||||
|
for arg in args {
|
||||||
|
self.collect_free_vars_expr(arg, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle other expression types that contain subexpressions
|
||||||
|
ExprKind::If(cond, then_expr, else_expr) => {
|
||||||
|
self.collect_free_vars_expr(cond, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(then_expr, lambda_params, free_vars, local_scope);
|
||||||
|
if let Some(else_expr) = else_expr {
|
||||||
|
self.collect_free_vars_expr(else_expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Match(scrutinee, arms) => {
|
||||||
|
self.collect_free_vars_expr(scrutinee, lambda_params, free_vars, local_scope);
|
||||||
|
for (_, expr) in arms {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::While(cond, body) => {
|
||||||
|
self.collect_free_vars_expr(cond, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(body, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::For(_, iter, body) => {
|
||||||
|
self.collect_free_vars_expr(iter, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(body, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Do(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::BinOp(left, _, right) => {
|
||||||
|
self.collect_free_vars_expr(left, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(right, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::UnOp(_, operand) => {
|
||||||
|
self.collect_free_vars_expr(operand, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Assign(target, value) => {
|
||||||
|
self.collect_free_vars_expr(target, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(value, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Cast(operand, _) => {
|
||||||
|
self.collect_free_vars_expr(operand, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Index(obj, index) => {
|
||||||
|
self.collect_free_vars_expr(obj, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(index, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Dot(obj, _) => {
|
||||||
|
self.collect_free_vars_expr(obj, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
ExprKind::EarlyReturn(expr) => {
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::OptionalChain(obj, _) => {
|
||||||
|
if let Some(obj) = obj {
|
||||||
|
self.collect_free_vars_expr(obj, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Return(expr) => {
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Array(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Tuple(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::StructLit(_, fields) => {
|
||||||
|
for (_, expr) in fields {
|
||||||
|
self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::EnumLit(_, _, args) => {
|
||||||
|
for arg in args {
|
||||||
|
self.collect_free_vars_expr(arg, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Range(start, end) => {
|
||||||
|
self.collect_free_vars_expr(start, lambda_params, free_vars, local_scope);
|
||||||
|
self.collect_free_vars_expr(end, lambda_params, free_vars, local_scope);
|
||||||
|
}
|
||||||
|
// Terminal expressions don't contain variables
|
||||||
|
ExprKind::Int(_) | ExprKind::Float(_) | ExprKind::Bool(_) | ExprKind::String(_) | ExprKind::Break | ExprKind::Continue => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lower all lambdas in a program by hoisting them to functions
|
||||||
|
pub fn lower_program(&self, nodes: &[ASTNode]) -> Result<Vec<ASTNode>, String> {
|
||||||
|
let mut lowered_nodes = Vec::new();
|
||||||
|
|
||||||
|
// Process each top-level node
|
||||||
|
for node in nodes {
|
||||||
|
let lowered = self.lower_node(node)?;
|
||||||
|
lowered_nodes.push(lowered);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all generated lambda functions to the end
|
||||||
|
let generated = self.generated_functions.borrow();
|
||||||
|
lowered_nodes.extend(generated.iter().cloned());
|
||||||
|
|
||||||
|
Ok(lowered_nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_node(&self, node: &ASTNode) -> Result<ASTNode, String> {
|
||||||
|
let new_kind = match &node.kind {
|
||||||
|
ASTNodeKind::Function(func) => {
|
||||||
|
let lowered_body = self.lower_expr(&func.body)?;
|
||||||
|
ASTNodeKind::Function(Function {
|
||||||
|
name: func.name.clone(),
|
||||||
|
parameters: func.parameters.clone(),
|
||||||
|
args: func.args.clone(),
|
||||||
|
return_type: func.return_type.clone(),
|
||||||
|
body: lowered_body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
other => other.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ASTNode {
|
||||||
|
kind: new_kind,
|
||||||
|
span: node.span.clone(),
|
||||||
|
attributes: node.attributes.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_expr(&self, expr: &Expr) -> Result<Expr, String> {
|
||||||
|
let new_kind = match &expr.kind {
|
||||||
|
ExprKind::Lambda(args, body) => {
|
||||||
|
// Collect free variables (captured variables)
|
||||||
|
let lambda_params: Vec<String> = args.iter().map(|(name, _)| name.clone()).collect();
|
||||||
|
let free_vars = self.collect_free_vars(body, &lambda_params);
|
||||||
|
|
||||||
|
if free_vars.is_empty() {
|
||||||
|
// No captures - hoist to function like the original implementation
|
||||||
|
let lambda_id = {
|
||||||
|
let mut counter = self.lambda_counter.borrow_mut();
|
||||||
|
*counter += 1;
|
||||||
|
*counter
|
||||||
|
};
|
||||||
|
let lambda_name = format!("__suic_gen_lambda_{}", lambda_id);
|
||||||
|
|
||||||
|
// Lower the lambda body recursively
|
||||||
|
let lowered_body = self.lower_expr(body)?;
|
||||||
|
|
||||||
|
// Create a new function for this lambda
|
||||||
|
let lambda_func = ASTNode {
|
||||||
|
kind: ASTNodeKind::Function(Function {
|
||||||
|
name: lambda_name.clone(),
|
||||||
|
parameters: Vec::new(), // No type parameters for now
|
||||||
|
args: args.clone(),
|
||||||
|
return_type: None, // Let typechecker infer return type
|
||||||
|
body: lowered_body,
|
||||||
|
}),
|
||||||
|
span: expr.span.clone(),
|
||||||
|
attributes: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store the generated function
|
||||||
|
self.generated_functions
|
||||||
|
.borrow_mut()
|
||||||
|
.push(lambda_func);
|
||||||
|
|
||||||
|
// Replace the lambda with a reference to the generated function
|
||||||
|
ExprKind::Variable(lambda_name)
|
||||||
|
} else {
|
||||||
|
// Has captures - keep as lambda, but recursively lower the body
|
||||||
|
let lowered_body = self.lower_expr(body)?;
|
||||||
|
ExprKind::Lambda(args.clone(), Box::new(lowered_body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Call(func, args) => {
|
||||||
|
let lowered_func = self.lower_expr(func)?;
|
||||||
|
let lowered_args = args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| self.lower_expr(arg))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ExprKind::Call(Box::new(lowered_func), lowered_args)
|
||||||
|
}
|
||||||
|
ExprKind::Let(name, kind, type_annot, body) => {
|
||||||
|
let lowered_body = self.lower_expr(body)?;
|
||||||
|
ExprKind::Let(
|
||||||
|
name.clone(),
|
||||||
|
kind.clone(),
|
||||||
|
type_annot.clone(),
|
||||||
|
Box::new(lowered_body),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ExprKind::If(cond, then_expr, else_expr) => {
|
||||||
|
let lowered_cond = self.lower_expr(cond)?;
|
||||||
|
let lowered_then = self.lower_expr(then_expr)?;
|
||||||
|
let lowered_else = else_expr
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| self.lower_expr(e))
|
||||||
|
.transpose()?;
|
||||||
|
ExprKind::If(
|
||||||
|
Box::new(lowered_cond),
|
||||||
|
Box::new(lowered_then),
|
||||||
|
lowered_else.map(Box::new),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ExprKind::Match(scrutinee, arms) => {
|
||||||
|
let lowered_scrutinee = self.lower_expr(scrutinee)?;
|
||||||
|
let mut lowered_arms = Vec::new();
|
||||||
|
for (pattern, expr) in arms {
|
||||||
|
let lowered_expr = self.lower_expr(expr)?;
|
||||||
|
lowered_arms.push((pattern.clone(), lowered_expr));
|
||||||
|
}
|
||||||
|
ExprKind::Match(Box::new(lowered_scrutinee), lowered_arms)
|
||||||
|
}
|
||||||
|
ExprKind::While(cond, body) => {
|
||||||
|
let lowered_cond = self.lower_expr(cond)?;
|
||||||
|
let lowered_body = self.lower_expr(body)?;
|
||||||
|
ExprKind::While(Box::new(lowered_cond), Box::new(lowered_body))
|
||||||
|
}
|
||||||
|
ExprKind::For(var, iter, body) => {
|
||||||
|
let lowered_iter = self.lower_expr(iter)?;
|
||||||
|
let lowered_body = self.lower_expr(body)?;
|
||||||
|
ExprKind::For(var.clone(), Box::new(lowered_iter), Box::new(lowered_body))
|
||||||
|
}
|
||||||
|
ExprKind::Do(exprs) => {
|
||||||
|
let lowered_exprs = exprs
|
||||||
|
.iter()
|
||||||
|
.map(|e| self.lower_expr(e))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ExprKind::Do(lowered_exprs)
|
||||||
|
}
|
||||||
|
ExprKind::BinOp(left, op, right) => {
|
||||||
|
let lowered_left = self.lower_expr(left)?;
|
||||||
|
let lowered_right = self.lower_expr(right)?;
|
||||||
|
ExprKind::BinOp(Box::new(lowered_left), op.clone(), Box::new(lowered_right))
|
||||||
|
}
|
||||||
|
ExprKind::UnOp(op, operand) => {
|
||||||
|
let lowered_operand = self.lower_expr(operand)?;
|
||||||
|
ExprKind::UnOp(op.clone(), Box::new(lowered_operand))
|
||||||
|
}
|
||||||
|
ExprKind::Assign(target, value) => {
|
||||||
|
let lowered_target = self.lower_expr(target)?;
|
||||||
|
let lowered_value = self.lower_expr(value)?;
|
||||||
|
ExprKind::Assign(Box::new(lowered_target), Box::new(lowered_value))
|
||||||
|
}
|
||||||
|
ExprKind::Cast(operand, type_annot) => {
|
||||||
|
let lowered_operand = self.lower_expr(operand)?;
|
||||||
|
ExprKind::Cast(Box::new(lowered_operand), type_annot.clone())
|
||||||
|
}
|
||||||
|
ExprKind::Index(obj, index) => {
|
||||||
|
let lowered_obj = self.lower_expr(obj)?;
|
||||||
|
let lowered_index = self.lower_expr(index)?;
|
||||||
|
ExprKind::Index(Box::new(lowered_obj), Box::new(lowered_index))
|
||||||
|
}
|
||||||
|
ExprKind::Dot(obj, field) => {
|
||||||
|
let lowered_obj = self.lower_expr(obj)?;
|
||||||
|
ExprKind::Dot(Box::new(lowered_obj), field.clone())
|
||||||
|
}
|
||||||
|
ExprKind::EarlyReturn(expr) => {
|
||||||
|
let lowered_expr = expr
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| self.lower_expr(e))
|
||||||
|
.transpose()?;
|
||||||
|
ExprKind::EarlyReturn(lowered_expr.map(Box::new))
|
||||||
|
}
|
||||||
|
ExprKind::OptionalChain(obj, field) => {
|
||||||
|
let lowered_obj = obj.as_ref().map(|e| self.lower_expr(e)).transpose()?;
|
||||||
|
ExprKind::OptionalChain(lowered_obj.map(Box::new), field.clone())
|
||||||
|
}
|
||||||
|
ExprKind::Return(expr) => {
|
||||||
|
let lowered_expr = expr
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| self.lower_expr(e))
|
||||||
|
.transpose()?;
|
||||||
|
ExprKind::Return(lowered_expr.map(Box::new))
|
||||||
|
}
|
||||||
|
ExprKind::Array(exprs) => {
|
||||||
|
let lowered_exprs = exprs
|
||||||
|
.iter()
|
||||||
|
.map(|e| self.lower_expr(e))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ExprKind::Array(lowered_exprs)
|
||||||
|
}
|
||||||
|
ExprKind::Tuple(exprs) => {
|
||||||
|
let lowered_exprs = exprs
|
||||||
|
.iter()
|
||||||
|
.map(|e| self.lower_expr(e))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ExprKind::Tuple(lowered_exprs)
|
||||||
|
}
|
||||||
|
ExprKind::StructLit(name, fields) => {
|
||||||
|
let mut lowered_fields = Vec::new();
|
||||||
|
for (field_name, field_expr) in fields {
|
||||||
|
let lowered_expr = self.lower_expr(field_expr)?;
|
||||||
|
lowered_fields.push((field_name.clone(), lowered_expr));
|
||||||
|
}
|
||||||
|
ExprKind::StructLit(name.clone(), lowered_fields)
|
||||||
|
}
|
||||||
|
ExprKind::EnumLit(enum_name, variant, args) => {
|
||||||
|
let lowered_args = args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| self.lower_expr(arg))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
ExprKind::EnumLit(enum_name.clone(), variant.clone(), lowered_args)
|
||||||
|
}
|
||||||
|
ExprKind::Range(start, end) => {
|
||||||
|
let lowered_start = self.lower_expr(start)?;
|
||||||
|
let lowered_end = self.lower_expr(end)?;
|
||||||
|
ExprKind::Range(Box::new(lowered_start), Box::new(lowered_end))
|
||||||
|
}
|
||||||
|
// Terminal expressions that don't contain other expressions
|
||||||
|
ExprKind::Int(_)
|
||||||
|
| ExprKind::Float(_)
|
||||||
|
| ExprKind::Bool(_)
|
||||||
|
| ExprKind::String(_)
|
||||||
|
| ExprKind::Variable(_)
|
||||||
|
| ExprKind::Break
|
||||||
|
| ExprKind::Continue => expr.kind.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Expr {
|
||||||
|
kind: new_kind,
|
||||||
|
span: expr.span.clone(),
|
||||||
|
attributes: expr.attributes.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,5 +3,6 @@ pub const EXTENSION: &str = ".sui";
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod lambda_lower;
|
||||||
pub mod typechecker;
|
pub mod typechecker;
|
||||||
pub mod monomorphize;
|
pub mod monomorphize;
|
||||||
|
|
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use logos::Logos;
|
use logos::Logos;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use suicmez::{
|
use suicmez::{
|
||||||
lexer::Token, parser::Parser, typechecker::TypeChecker,
|
lexer::Token, parser::Parser, lambda_lower::LambdaLowerer, typechecker::TypeChecker,
|
||||||
monomorphize::{Monomorphizer, check_no_typevars},
|
monomorphize::{Monomorphizer, check_no_typevars},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,9 +73,16 @@ fn run_file(filename: &str) -> Result<(), String> {
|
||||||
|
|
||||||
println!("Parsed {} AST nodes successfully", ast_nodes.len());
|
println!("Parsed {} AST nodes successfully", ast_nodes.len());
|
||||||
|
|
||||||
|
// Lower lambdas to generated functions
|
||||||
|
let lowerer = LambdaLowerer::new();
|
||||||
|
let lowered_nodes = lowerer.lower_program(&ast_nodes)
|
||||||
|
.map_err(|e| format!("Lambda lowering error: {}", e))?;
|
||||||
|
|
||||||
|
println!("Lambda lowering passed! {} nodes after lowering.", lowered_nodes.len());
|
||||||
|
|
||||||
// Typecheck the AST
|
// Typecheck the AST
|
||||||
let mut typechecker = TypeChecker::new();
|
let mut typechecker = TypeChecker::new();
|
||||||
let typed_nodes = typechecker.typecheck_program(&ast_nodes).map_err(|e| {
|
let typed_nodes = typechecker.typecheck_program(&lowered_nodes).map_err(|e| {
|
||||||
format!(
|
format!(
|
||||||
"Type error at {}:{}: {:?}",
|
"Type error at {}:{}: {:?}",
|
||||||
e.span.file, e.span.start, e.kind
|
e.span.file, e.span.start, e.kind
|
||||||
|
|
|
||||||
|
|
@ -439,10 +439,11 @@ impl Monomorphizer {
|
||||||
TypedExprKind::Lambda(params.clone(), Box::new(new_body))
|
TypedExprKind::Lambda(params.clone(), Box::new(new_body))
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedExprKind::Let(name, binding_kind, ty_annot, expr) => {
|
TypedExprKind::Let(id, name, binding_kind, ty_annot, expr) => {
|
||||||
let (new_expr, expr_needs) = self.monomorphize_expr(expr)?;
|
let (new_expr, expr_needs) = self.monomorphize_expr(expr)?;
|
||||||
needs.extend(expr_needs);
|
needs.extend(expr_needs);
|
||||||
TypedExprKind::Let(
|
TypedExprKind::Let(
|
||||||
|
*id,
|
||||||
name.clone(),
|
name.clone(),
|
||||||
binding_kind.clone(),
|
binding_kind.clone(),
|
||||||
ty_annot.clone(),
|
ty_annot.clone(),
|
||||||
|
|
@ -527,12 +528,12 @@ impl Monomorphizer {
|
||||||
TypedExprKind::UnOp(op.clone(), Box::new(new_operand))
|
TypedExprKind::UnOp(op.clone(), Box::new(new_operand))
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedExprKind::For(var, iter_expr, body) => {
|
TypedExprKind::For(id, var, iter_expr, body) => {
|
||||||
let (new_iter, iter_needs) = self.monomorphize_expr(iter_expr)?;
|
let (new_iter, iter_needs) = self.monomorphize_expr(iter_expr)?;
|
||||||
let (new_body, body_needs) = self.monomorphize_expr(body)?;
|
let (new_body, body_needs) = self.monomorphize_expr(body)?;
|
||||||
needs.extend(iter_needs);
|
needs.extend(iter_needs);
|
||||||
needs.extend(body_needs);
|
needs.extend(body_needs);
|
||||||
TypedExprKind::For(var.clone(), Box::new(new_iter), Box::new(new_body))
|
TypedExprKind::For(*id, var.clone(), Box::new(new_iter), Box::new(new_body))
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedExprKind::Range(start, end) => {
|
TypedExprKind::Range(start, end) => {
|
||||||
|
|
@ -682,7 +683,7 @@ impl Monomorphizer {
|
||||||
let mut new_args = Vec::new();
|
let mut new_args = Vec::new();
|
||||||
let mut needs = Vec::new();
|
let mut needs = Vec::new();
|
||||||
|
|
||||||
for (arg_name, arg_ty_opt) in &generic_func.args {
|
for (arg_id, arg_name, arg_ty_opt) in &generic_func.args {
|
||||||
let new_arg_ty = if let Some(arg_ty) = arg_ty_opt {
|
let new_arg_ty = if let Some(arg_ty) = arg_ty_opt {
|
||||||
let ty = self.substitute_in_type_annot(arg_ty, &subst_map)?;
|
let ty = self.substitute_in_type_annot(arg_ty, &subst_map)?;
|
||||||
self.collect_needs_from_type(&ty, &mut needs);
|
self.collect_needs_from_type(&ty, &mut needs);
|
||||||
|
|
@ -690,7 +691,7 @@ impl Monomorphizer {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
new_args.push((arg_name.clone(), new_arg_ty));
|
new_args.push((*arg_id, arg_name.clone(), new_arg_ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specialize return type
|
// Specialize return type
|
||||||
|
|
@ -986,7 +987,7 @@ fn check_function_for_typevars(func: &TypedFunction) -> Result<(), Monomorphizat
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, ty_opt) in &func.args {
|
for (_, _, ty_opt) in &func.args {
|
||||||
if let Some(ty) = ty_opt {
|
if let Some(ty) = ty_opt {
|
||||||
if has_typevars_in_type_annot(ty) {
|
if has_typevars_in_type_annot(ty) {
|
||||||
return Err(MonomorphizationError::new(
|
return Err(MonomorphizationError::new(
|
||||||
|
|
@ -1056,7 +1057,7 @@ fn check_enum_for_typevars(e: &TypedEnum) -> Result<(), MonomorphizationError> {
|
||||||
fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError> {
|
fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError> {
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
TypedExprKind::Lambda(params, body) => {
|
TypedExprKind::Lambda(params, body) => {
|
||||||
for (_, ty_opt) in params {
|
for (_, _, ty_opt) in params {
|
||||||
if let Some(ty) = ty_opt {
|
if let Some(ty) = ty_opt {
|
||||||
if has_typevars_in_type_annot(ty) {
|
if has_typevars_in_type_annot(ty) {
|
||||||
return Err(MonomorphizationError::new(
|
return Err(MonomorphizationError::new(
|
||||||
|
|
@ -1068,7 +1069,7 @@ fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError
|
||||||
}
|
}
|
||||||
check_expr_for_typevars(body)?;
|
check_expr_for_typevars(body)?;
|
||||||
}
|
}
|
||||||
TypedExprKind::Let(_, _, ty_opt, expr) => {
|
TypedExprKind::Let(_, _, _, ty_opt, expr) => {
|
||||||
if let Some(ty) = ty_opt {
|
if let Some(ty) = ty_opt {
|
||||||
if has_typevars_in_type_annot(ty) {
|
if has_typevars_in_type_annot(ty) {
|
||||||
return Err(MonomorphizationError::new(
|
return Err(MonomorphizationError::new(
|
||||||
|
|
@ -1160,7 +1161,7 @@ fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError
|
||||||
TypedExprKind::UnOp(_, operand) => {
|
TypedExprKind::UnOp(_, operand) => {
|
||||||
check_expr_for_typevars(operand)?;
|
check_expr_for_typevars(operand)?;
|
||||||
}
|
}
|
||||||
TypedExprKind::For(_, iter, body) => {
|
TypedExprKind::For(_, _, iter, body) => {
|
||||||
check_expr_for_typevars(iter)?;
|
check_expr_for_typevars(iter)?;
|
||||||
check_expr_for_typevars(body)?;
|
check_expr_for_typevars(body)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,14 +84,25 @@ pub enum TypeErrorKind {
|
||||||
Other(String),
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct VarInfo {
|
||||||
|
ty: Type,
|
||||||
|
kind: BindingKind,
|
||||||
|
name: String,
|
||||||
|
usage: usize,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TypeEnv {
|
struct TypeEnv {
|
||||||
vars: HashMap<String, (Type, BindingKind)>,
|
vars: HashMap<crate::ast::BindingId, VarInfo>,
|
||||||
|
name_to_id: HashMap<String, crate::ast::BindingId>,
|
||||||
types: HashMap<String, TypeInfo>,
|
types: HashMap<String, TypeInfo>,
|
||||||
functions: HashMap<String, FunctionType>,
|
functions: HashMap<String, FunctionType>,
|
||||||
traits: HashMap<String, TraitInfo>,
|
traits: HashMap<String, TraitInfo>,
|
||||||
impls: Vec<ImplInfo>,
|
impls: Vec<ImplInfo>,
|
||||||
type_vars: HashMap<String, Type>,
|
type_vars: HashMap<String, Type>,
|
||||||
|
scopes: Vec<Vec<crate::ast::BindingId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -130,31 +141,100 @@ impl TypeEnv {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
TypeEnv {
|
TypeEnv {
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
|
name_to_id: HashMap::new(),
|
||||||
types: HashMap::new(),
|
types: HashMap::new(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
traits: HashMap::new(),
|
traits: HashMap::new(),
|
||||||
impls: Vec::new(),
|
impls: Vec::new(),
|
||||||
type_vars: HashMap::new(),
|
type_vars: HashMap::new(),
|
||||||
|
scopes: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_scope(&self) -> Self {
|
fn enter_scope(&mut self) {
|
||||||
TypeEnv {
|
self.scopes.push(Vec::new());
|
||||||
vars: self.vars.clone(),
|
}
|
||||||
types: self.types.clone(),
|
|
||||||
functions: self.functions.clone(),
|
fn exit_scope(&mut self) -> Result<(), TypeError> {
|
||||||
traits: self.traits.clone(),
|
if let Some(scope) = self.scopes.pop() {
|
||||||
impls: self.impls.clone(),
|
for &id in &scope {
|
||||||
type_vars: self.type_vars.clone(),
|
if let Some(var_info) = self.vars.get(&id) {
|
||||||
|
match var_info.kind {
|
||||||
|
BindingKind::Linear => {
|
||||||
|
if var_info.usage != 1 {
|
||||||
|
return Err(TypeError {
|
||||||
|
kind: TypeErrorKind::LinearityError(format!(
|
||||||
|
"Linear variable '{}' used {} times, must be exactly 1",
|
||||||
|
var_info.name, var_info.usage
|
||||||
|
)),
|
||||||
|
span: var_info.span.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BindingKind::Affine => {
|
||||||
|
if var_info.usage > 1 {
|
||||||
|
return Err(TypeError {
|
||||||
|
kind: TypeErrorKind::LinearityError(format!(
|
||||||
|
"Affine variable '{}' used {} times, must be at most 1",
|
||||||
|
var_info.name, var_info.usage
|
||||||
|
)),
|
||||||
|
span: var_info.span.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let name = var_info.name.clone();
|
||||||
|
self.vars.remove(&id);
|
||||||
|
self.name_to_id.remove(&name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_var(
|
||||||
|
&mut self,
|
||||||
|
id: crate::ast::BindingId,
|
||||||
|
name: String,
|
||||||
|
ty: Type,
|
||||||
|
kind: BindingKind,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
let var_info = VarInfo {
|
||||||
|
ty,
|
||||||
|
kind,
|
||||||
|
name: name.clone(),
|
||||||
|
usage: 0,
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
self.vars.insert(id, var_info);
|
||||||
|
if let Some(current) = self.scopes.last_mut() {
|
||||||
|
current.push(id);
|
||||||
|
}
|
||||||
|
self.name_to_id.insert(name, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var(&self, id: &crate::ast::BindingId) -> Option<&VarInfo> {
|
||||||
|
self.vars.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var_by_name(&self, name: &str) -> Option<(crate::ast::BindingId, &VarInfo)> {
|
||||||
|
if let Some(id) = self.name_to_id.get(name) {
|
||||||
|
if let Some(var_info) = self.vars.get(id) {
|
||||||
|
Some((*id, var_info))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_var(&mut self, name: String, ty: Type, kind: BindingKind) {
|
fn increment_usage(&mut self, id: &crate::ast::BindingId) {
|
||||||
self.vars.insert(name, (ty, kind));
|
if let Some(var_info) = self.vars.get_mut(id) {
|
||||||
}
|
var_info.usage += 1;
|
||||||
|
}
|
||||||
fn get_var(&self, name: &str) -> Option<&(Type, BindingKind)> {
|
|
||||||
self.vars.get(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_type(&mut self, name: String, info: TypeInfo) {
|
fn add_type(&mut self, name: String, info: TypeInfo) {
|
||||||
|
|
@ -176,15 +256,23 @@ impl TypeEnv {
|
||||||
|
|
||||||
pub struct TypeChecker {
|
pub struct TypeChecker {
|
||||||
env: TypeEnv,
|
env: TypeEnv,
|
||||||
|
binding_id_counter: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeChecker {
|
impl TypeChecker {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
TypeChecker {
|
TypeChecker {
|
||||||
env: TypeEnv::new(),
|
env: TypeEnv::new(),
|
||||||
|
binding_id_counter: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_binding_id(&mut self) -> crate::ast::BindingId {
|
||||||
|
let id = crate::ast::BindingId(self.binding_id_counter);
|
||||||
|
self.binding_id_counter += 1;
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn typecheck_program(&mut self, nodes: &[ASTNode]) -> Result<Vec<TypedASTNode>, TypeError> {
|
pub fn typecheck_program(&mut self, nodes: &[ASTNode]) -> Result<Vec<TypedASTNode>, TypeError> {
|
||||||
// First pass: collect all type definitions, function signatures, etc.
|
// First pass: collect all type definitions, function signatures, etc.
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
|
|
@ -192,10 +280,12 @@ impl TypeChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass: typecheck everything
|
// Second pass: typecheck everything
|
||||||
|
self.env.enter_scope();
|
||||||
let mut typed_nodes = Vec::new();
|
let mut typed_nodes = Vec::new();
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
typed_nodes.push(self.typecheck_node(node)?);
|
typed_nodes.push(self.typecheck_node(node)?);
|
||||||
}
|
}
|
||||||
|
self.env.exit_scope()?;
|
||||||
|
|
||||||
Ok(typed_nodes)
|
Ok(typed_nodes)
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +330,13 @@ impl TypeChecker {
|
||||||
.return_type
|
.return_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| self.type_annot_to_type(t))
|
.map(|t| self.type_annot_to_type(t))
|
||||||
.unwrap_or(Type::Unit);
|
.unwrap_or_else(|| {
|
||||||
|
if f.name.starts_with("__suic_gen_lambda_") {
|
||||||
|
Type::Unknown
|
||||||
|
} else {
|
||||||
|
Type::Unit
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let func_type = FunctionType {
|
let func_type = FunctionType {
|
||||||
type_params: f.parameters.iter().map(|p| p.name.clone()).collect(),
|
type_params: f.parameters.iter().map(|p| p.name.clone()).collect(),
|
||||||
|
|
@ -325,11 +421,15 @@ impl TypeChecker {
|
||||||
kind: TypedASTNodeKind::Struct(TypedStruct {
|
kind: TypedASTNodeKind::Struct(TypedStruct {
|
||||||
name: s.name.clone(),
|
name: s.name.clone(),
|
||||||
parameters: s.parameters.clone(),
|
parameters: s.parameters.clone(),
|
||||||
fields: s.fields.iter().map(|f| TypedField {
|
fields: s
|
||||||
name: f.name.clone(),
|
.fields
|
||||||
field_type: f.field_type.clone(),
|
.iter()
|
||||||
span: f.span.clone(),
|
.map(|f| TypedField {
|
||||||
}).collect(),
|
name: f.name.clone(),
|
||||||
|
field_type: f.field_type.clone(),
|
||||||
|
span: f.span.clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
}),
|
}),
|
||||||
span: node.span.clone(),
|
span: node.span.clone(),
|
||||||
attributes: node.attributes.clone(),
|
attributes: node.attributes.clone(),
|
||||||
|
|
@ -342,11 +442,15 @@ impl TypeChecker {
|
||||||
kind: TypedASTNodeKind::Enum(TypedEnum {
|
kind: TypedASTNodeKind::Enum(TypedEnum {
|
||||||
name: e.name.clone(),
|
name: e.name.clone(),
|
||||||
parameters: e.parameters.clone(),
|
parameters: e.parameters.clone(),
|
||||||
variants: e.variants.iter().map(|v| TypedVariant {
|
variants: e
|
||||||
name: v.name.clone(),
|
.variants
|
||||||
fields: v.fields.clone(),
|
.iter()
|
||||||
span: v.span.clone(),
|
.map(|v| TypedVariant {
|
||||||
}).collect(),
|
name: v.name.clone(),
|
||||||
|
fields: v.fields.clone(),
|
||||||
|
span: v.span.clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
}),
|
}),
|
||||||
span: node.span.clone(),
|
span: node.span.clone(),
|
||||||
attributes: node.attributes.clone(),
|
attributes: node.attributes.clone(),
|
||||||
|
|
@ -424,11 +528,8 @@ impl TypeChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typecheck_function(&mut self, func: &Function) -> Result<TypedFunction, TypeError> {
|
fn typecheck_function(&mut self, func: &Function) -> Result<TypedFunction, TypeError> {
|
||||||
// Save the original environment
|
|
||||||
let original_env = self.env.clone();
|
|
||||||
|
|
||||||
// Enter new scope for function
|
// Enter new scope for function
|
||||||
self.env = self.env.enter_scope();
|
self.env.enter_scope();
|
||||||
|
|
||||||
// Add type parameters to environment
|
// Add type parameters to environment
|
||||||
for param in &func.parameters {
|
for param in &func.parameters {
|
||||||
|
|
@ -439,28 +540,41 @@ impl TypeChecker {
|
||||||
|
|
||||||
// Add function parameters to environment
|
// Add function parameters to environment
|
||||||
let mut param_types = Vec::new();
|
let mut param_types = Vec::new();
|
||||||
|
let mut typed_args = Vec::new();
|
||||||
for (arg_name, arg_type_annot) in &func.args {
|
for (arg_name, arg_type_annot) in &func.args {
|
||||||
|
let arg_id = self.next_binding_id();
|
||||||
let arg_type = arg_type_annot
|
let arg_type = arg_type_annot
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| self.type_annot_to_type(t))
|
.map(|t| self.type_annot_to_type(t))
|
||||||
.unwrap_or(Type::Unknown);
|
.unwrap_or(Type::Unknown);
|
||||||
param_types.push(arg_type.clone());
|
param_types.push(arg_type.clone());
|
||||||
self.env
|
self.env.add_var(
|
||||||
.add_var(arg_name.clone(), arg_type, BindingKind::Default);
|
arg_id,
|
||||||
|
arg_name.clone(),
|
||||||
|
arg_type,
|
||||||
|
BindingKind::Default,
|
||||||
|
func.body.span.clone(),
|
||||||
|
);
|
||||||
|
typed_args.push((arg_id, arg_name.clone(), arg_type_annot.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Typecheck function body
|
// Typecheck function body
|
||||||
let typed_body = self.typecheck_expr(&func.body)?;
|
let typed_body = self.typecheck_expr(&func.body)?;
|
||||||
|
|
||||||
// Restore the original environment
|
// Exit scope, checking usages
|
||||||
self.env = original_env;
|
self.env.exit_scope()?;
|
||||||
|
|
||||||
// Check return type
|
// Check return type
|
||||||
let expected_return = func
|
let expected_return =
|
||||||
.return_type
|
if func.name.starts_with("__suic_gen_lambda_") && func.return_type.is_none() {
|
||||||
.as_ref()
|
// For generated lambda functions, infer return type from body
|
||||||
.map(|t| self.type_annot_to_type(t))
|
typed_body.ty.clone()
|
||||||
.unwrap_or(Type::Unit);
|
} else {
|
||||||
|
func.return_type
|
||||||
|
.as_ref()
|
||||||
|
.map(|t| self.type_annot_to_type(t))
|
||||||
|
.unwrap_or(Type::Unit)
|
||||||
|
};
|
||||||
|
|
||||||
if !self.types_compatible(&typed_body.ty, &expected_return) {
|
if !self.types_compatible(&typed_body.ty, &expected_return) {
|
||||||
return Err(TypeError {
|
return Err(TypeError {
|
||||||
|
|
@ -474,7 +588,7 @@ impl TypeChecker {
|
||||||
Ok(TypedFunction {
|
Ok(TypedFunction {
|
||||||
name: func.name.clone(),
|
name: func.name.clone(),
|
||||||
parameters: func.parameters.clone(),
|
parameters: func.parameters.clone(),
|
||||||
args: func.args.clone(),
|
args: typed_args,
|
||||||
return_type: func.return_type.clone(),
|
return_type: func.return_type.clone(),
|
||||||
body: typed_body,
|
body: typed_body,
|
||||||
ty: func_type,
|
ty: func_type,
|
||||||
|
|
@ -490,8 +604,10 @@ impl TypeChecker {
|
||||||
|
|
||||||
ExprKind::Variable(name) => {
|
ExprKind::Variable(name) => {
|
||||||
// First check if it's a variable
|
// First check if it's a variable
|
||||||
if let Some((t, _)) = self.env.get_var(name) {
|
if let Some((id, var_info)) = self.env.get_var_by_name(name) {
|
||||||
(TypedExprKind::Variable(name.clone()), t.clone())
|
let ty = var_info.ty.clone();
|
||||||
|
self.env.increment_usage(&id);
|
||||||
|
(TypedExprKind::Variable(name.clone()), ty)
|
||||||
} else if let Some(func_type) = self.env.get_function(name) {
|
} else if let Some(func_type) = self.env.get_function(name) {
|
||||||
// If not a variable, check if it's a function
|
// If not a variable, check if it's a function
|
||||||
let func_type_clone = func_type.clone();
|
let func_type_clone = func_type.clone();
|
||||||
|
|
@ -689,11 +805,18 @@ impl TypeChecker {
|
||||||
typed_value.ty.clone()
|
typed_value.ty.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.env
|
let var_id = self.next_binding_id();
|
||||||
.add_var(name.clone(), var_type.clone(), binding_kind.clone());
|
self.env.add_var(
|
||||||
|
var_id,
|
||||||
|
name.clone(),
|
||||||
|
var_type.clone(),
|
||||||
|
binding_kind.clone(),
|
||||||
|
expr.span.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
(
|
(
|
||||||
TypedExprKind::Let(
|
TypedExprKind::Let(
|
||||||
|
var_id,
|
||||||
name.clone(),
|
name.clone(),
|
||||||
binding_kind.clone(),
|
binding_kind.clone(),
|
||||||
type_annot.clone(),
|
type_annot.clone(),
|
||||||
|
|
@ -776,13 +899,25 @@ impl TypeChecker {
|
||||||
_ => Type::Unknown,
|
_ => Type::Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.env = self.env.enter_scope();
|
self.env.enter_scope();
|
||||||
self.env
|
let var_id = self.next_binding_id();
|
||||||
.add_var(var.clone(), element_type, BindingKind::Default);
|
self.env.add_var(
|
||||||
|
var_id,
|
||||||
|
var.clone(),
|
||||||
|
element_type,
|
||||||
|
BindingKind::Default,
|
||||||
|
expr.span.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let typed_body = self.typecheck_expr(body)?;
|
let typed_body = self.typecheck_expr(body)?;
|
||||||
|
self.env.exit_scope()?;
|
||||||
(
|
(
|
||||||
TypedExprKind::For(var.clone(), Box::new(typed_iterable), Box::new(typed_body)),
|
TypedExprKind::For(
|
||||||
|
var_id,
|
||||||
|
var.clone(),
|
||||||
|
Box::new(typed_iterable),
|
||||||
|
Box::new(typed_body),
|
||||||
|
),
|
||||||
Type::Unit,
|
Type::Unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1007,8 +1142,10 @@ impl TypeChecker {
|
||||||
let mut result_type = Type::Unknown;
|
let mut result_type = Type::Unknown;
|
||||||
|
|
||||||
for (i, (pattern, body)) in arms.iter().enumerate() {
|
for (i, (pattern, body)) in arms.iter().enumerate() {
|
||||||
|
self.env.enter_scope();
|
||||||
let typed_pattern = self.typecheck_pattern(pattern, &typed_scrutinee.ty)?;
|
let typed_pattern = self.typecheck_pattern(pattern, &typed_scrutinee.ty)?;
|
||||||
let typed_body = self.typecheck_expr(body)?;
|
let typed_body = self.typecheck_expr(body)?;
|
||||||
|
self.env.exit_scope()?;
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
result_type = typed_body.ty.clone();
|
result_type = typed_body.ty.clone();
|
||||||
|
|
@ -1029,29 +1166,62 @@ impl TypeChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprKind::Lambda(params, body) => {
|
ExprKind::Lambda(params, body) => {
|
||||||
self.env = self.env.enter_scope();
|
// Enter scope for lambda parameters
|
||||||
|
self.env.enter_scope();
|
||||||
let mut param_types = Vec::new();
|
let mut param_types = Vec::new();
|
||||||
|
let mut typed_params = Vec::new();
|
||||||
for (param_name, param_type_annot) in params {
|
for (param_name, param_type_annot) in params {
|
||||||
|
let param_id = self.next_binding_id();
|
||||||
let param_type = param_type_annot
|
let param_type = param_type_annot
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|t| self.type_annot_to_type(t))
|
.map(|t| self.type_annot_to_type(t))
|
||||||
.unwrap_or(Type::Unknown);
|
.unwrap_or(Type::Unknown);
|
||||||
param_types.push(param_type.clone());
|
param_types.push(param_type.clone());
|
||||||
self.env
|
self.env.add_var(
|
||||||
.add_var(param_name.clone(), param_type, BindingKind::Default);
|
param_id,
|
||||||
|
param_name.clone(),
|
||||||
|
param_type,
|
||||||
|
BindingKind::Default,
|
||||||
|
expr.span.clone(),
|
||||||
|
);
|
||||||
|
typed_params.push((param_id, param_name.clone(), param_type_annot.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let typed_body = self.typecheck_expr(body)?;
|
let typed_body = self.typecheck_expr(body)?;
|
||||||
let func_type = Type::Function(param_types, Box::new(typed_body.ty.clone()));
|
let func_type = Type::Function(param_types, Box::new(typed_body.ty.clone()));
|
||||||
|
|
||||||
|
// Exit scope, checking usages
|
||||||
|
self.env.exit_scope()?;
|
||||||
|
|
||||||
(
|
(
|
||||||
TypedExprKind::Lambda(params.clone(), Box::new(typed_body)),
|
TypedExprKind::Lambda(typed_params, Box::new(typed_body)),
|
||||||
func_type,
|
func_type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprKind::Assign(lhs, rhs) => {
|
ExprKind::Assign(lhs, rhs) => {
|
||||||
|
// Check if lhs is a mutable variable
|
||||||
|
if let ExprKind::Variable(name) = &lhs.kind {
|
||||||
|
if let Some((_, var_info)) = self.env.get_var_by_name(name) {
|
||||||
|
if var_info.kind != BindingKind::Mutable {
|
||||||
|
return Err(TypeError {
|
||||||
|
kind: TypeErrorKind::MutableityError(format!(
|
||||||
|
"Cannot assign to immutable variable '{}'",
|
||||||
|
name
|
||||||
|
)),
|
||||||
|
span: lhs.span.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(TypeError {
|
||||||
|
kind: TypeErrorKind::MutableityError(
|
||||||
|
"Invalid left-hand side of assignment".to_string(),
|
||||||
|
),
|
||||||
|
span: lhs.span.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let typed_lhs = self.typecheck_expr(lhs)?;
|
let typed_lhs = self.typecheck_expr(lhs)?;
|
||||||
let typed_rhs = self.typecheck_expr(rhs)?;
|
let typed_rhs = self.typecheck_expr(rhs)?;
|
||||||
|
|
||||||
|
|
@ -1170,10 +1340,16 @@ impl TypeChecker {
|
||||||
PatternKind::Wildcard => (TypedPatternKind::Wildcard, scrutinee_type.clone()),
|
PatternKind::Wildcard => (TypedPatternKind::Wildcard, scrutinee_type.clone()),
|
||||||
|
|
||||||
PatternKind::Variable(name) => {
|
PatternKind::Variable(name) => {
|
||||||
self.env
|
let var_id = self.next_binding_id();
|
||||||
.add_var(name.clone(), scrutinee_type.clone(), BindingKind::Default);
|
self.env.add_var(
|
||||||
|
var_id,
|
||||||
|
name.clone(),
|
||||||
|
scrutinee_type.clone(),
|
||||||
|
BindingKind::Default,
|
||||||
|
pattern.span.clone(),
|
||||||
|
);
|
||||||
(
|
(
|
||||||
TypedPatternKind::Variable(name.clone()),
|
TypedPatternKind::Variable(var_id, name.clone()),
|
||||||
scrutinee_type.clone(),
|
scrutinee_type.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
test_affine.sui
Normal file
5
test_affine.sui
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn test_affine() -> int do
|
||||||
|
let uniq x = 5
|
||||||
|
x + 1
|
||||||
|
x + 2
|
||||||
|
end
|
||||||
5
test_binding.sui
Normal file
5
test_binding.sui
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn test_binding_id() -> int do
|
||||||
|
let x = 5
|
||||||
|
let x = x + 1 # shadowing
|
||||||
|
x
|
||||||
|
end
|
||||||
4
test_linear.sui
Normal file
4
test_linear.sui
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn test_linear() -> int do
|
||||||
|
let once y = 10
|
||||||
|
5
|
||||||
|
end
|
||||||
4
test_linear_ok.sui
Normal file
4
test_linear_ok.sui
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn test_linear_ok() -> int do
|
||||||
|
let once z = 10
|
||||||
|
z + 5
|
||||||
|
end
|
||||||
7
test_scoping.sui
Normal file
7
test_scoping.sui
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn test_scoping() -> int do
|
||||||
|
let x = 5
|
||||||
|
let f = lambda (y) y + 1 # simple lambda
|
||||||
|
f = lambda (z) z+1
|
||||||
|
let x = 10 # shadow x
|
||||||
|
f(3) # should work
|
||||||
|
end
|
||||||
5
test_shadow.sui
Normal file
5
test_shadow.sui
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn test_shadow() -> int do
|
||||||
|
let x = 5
|
||||||
|
let x = 10
|
||||||
|
x
|
||||||
|
end
|
||||||
9
tests/lamba.sui
Normal file
9
tests/lamba.sui
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# OCaml my Caml, our fearful trip is `done`
|
||||||
|
|
||||||
|
fn main(_) do
|
||||||
|
let mylamba = lambda (x) x+1;
|
||||||
|
mylamba(5)
|
||||||
|
let id = lambda (y) y;
|
||||||
|
id("hi")
|
||||||
|
id(1)
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue