Skip to content

Commit

Permalink
Statements
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Apr 16, 2024
1 parent 4aaee44 commit f97a52d
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 50 deletions.
3 changes: 3 additions & 0 deletions crates/rue-compiler/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct Ops {
f: NodePtr,
r: NodePtr,
l: NodePtr,
x: NodePtr,
eq: NodePtr,
sha256: NodePtr,
strlen: NodePtr,
Expand All @@ -43,6 +44,7 @@ impl<'a> Codegen<'a> {
f: allocator.new_small_number(5).unwrap(),
r: allocator.new_small_number(6).unwrap(),
l: allocator.new_small_number(7).unwrap(),
x: allocator.new_small_number(8).unwrap(),
eq: allocator.new_small_number(9).unwrap(),
sha256: allocator.new_small_number(11).unwrap(),
strlen: allocator.new_small_number(13).unwrap(),
Expand Down Expand Up @@ -70,6 +72,7 @@ impl<'a> Codegen<'a> {
Lir::FunctionBody(body) => self.gen_quote(body),
Lir::First(value) => self.gen_first(value),
Lir::Rest(value) => self.gen_rest(value),
Lir::Raise => self.list(&[self.ops.x]),
Lir::Sha256(value) => self.gen_sha256(value),
Lir::IsCons(value) => self.gen_is_cons(value),
Lir::Strlen(value) => self.gen_strlen(value),
Expand Down
6 changes: 6 additions & 0 deletions crates/rue-compiler/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ pub enum DiagnosticInfo {

#[error("redundant check against same type `{0}`")]
RedundantTypeGuard(String),

#[error("implicit return is not allowed in if statements, use an explicit return statement")]
ImplicitReturnInIf,

#[error("explicit return is not allowed in expressions")]
ExplicitReturnInExpr,
}

/// Join a list of names into a string, wrapped in backticks.
Expand Down
1 change: 1 addition & 0 deletions crates/rue-compiler/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub enum Hir {
First(HirId),
Rest(HirId),
Not(HirId),
Raise,
Sha256(HirId),
IsCons(HirId),
Strlen(HirId),
Expand Down
1 change: 1 addition & 0 deletions crates/rue-compiler/src/lir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub enum Lir {
FunctionBody(LirId),
First(LirId),
Rest(LirId),
Raise,
Sha256(LirId),
IsCons(LirId),
Strlen(LirId),
Expand Down
197 changes: 162 additions & 35 deletions crates/rue-compiler/src/lowerer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rowan::TextRange;
use rue_parser::{
AstNode, BinaryExpr, BinaryOp, Block, CastExpr, ConstItem, EnumItem, Expr, FieldAccess,
FunctionCall, FunctionItem, FunctionType as AstFunctionType, GroupExpr, GuardExpr, IfExpr,
IndexAccess, InitializerExpr, InitializerField, Item, LambdaExpr, ListExpr, ListType,
IndexAccess, InitializerExpr, InitializerField, Item, LambdaExpr, LetStmt, ListExpr, ListType,
LiteralExpr, PairExpr, PairType, Path, PrefixExpr, PrefixOp, Root, Stmt, StructField,
StructItem, SyntaxKind, SyntaxToken, Type as AstType, TypeAliasItem,
};
Expand Down Expand Up @@ -279,7 +279,8 @@ impl<'a> Lowerer<'a> {
};

self.scope_stack.push(scope_id);
let output = self.compile_block_expr(body, None, Some(ty.return_type()));
let (output, _explicit_return) =
self.compile_block_expr(body, None, Some(ty.return_type()));
self.scope_stack.pop().unwrap();

self.type_check(
Expand Down Expand Up @@ -387,49 +388,140 @@ impl<'a> Lowerer<'a> {
}
}

fn compile_let_stmt(&mut self, let_stmt: LetStmt) -> Option<ScopeId> {
let expected_type = let_stmt.ty().map(|ty| self.compile_type(ty));

let value = let_stmt
.expr()
.map(|expr| self.compile_expr(expr, expected_type))
.unwrap_or(self.unknown());

if let Some(expected_type) = expected_type {
self.type_check(value.ty(), expected_type, let_stmt.syntax().text_range());
}

let Some(name) = let_stmt.name() else {
return None;
};

let symbol_id = self.db.alloc_symbol(Symbol::LetBinding {
type_id: expected_type.unwrap_or(value.ty()),
hir_id: value.hir(),
});

let mut let_scope = Scope::default();
let_scope.define_symbol(name.to_string(), symbol_id);
let scope_id = self.db.alloc_scope(let_scope);
self.scope_stack.push(scope_id);

Some(scope_id)
}

fn compile_block_expr(
&mut self,
block: Block,
scope_id: Option<ScopeId>,
expected_type: Option<TypeId>,
) -> Value {
) -> (Value, bool) {
if let Some(scope_id) = scope_id {
self.scope_stack.push(scope_id);
}

self.compile_items(block.items());

let mut let_scope_ids = Vec::new();
enum Statement {
Let(ScopeId),
If(HirId, HirId),
Return(Value),
}

let mut statements = Vec::new();

for stmt in block.stmts() {
match stmt {
Stmt::LetStmt(let_stmt) => {
let expected_type = let_stmt.ty().map(|ty| self.compile_type(ty));
let Some(scope_id) = self.compile_let_stmt(let_stmt) else {
continue;
};
statements.push(Statement::Let(scope_id));
}
Stmt::IfStmt(if_stmt) => {
let condition = if_stmt
.condition()
.map(|condition| self.compile_expr(condition, Some(self.bool_type)));

let value = let_stmt
.expr()
.map(|expr| self.compile_expr(expr, expected_type))
.unwrap_or(self.unknown());
if let Some(condition) = condition.as_ref() {
self.type_guards.push(condition.then_guards());
}

if let Some(expected_type) = expected_type {
self.type_check(value.ty(), expected_type, let_stmt.syntax().text_range());
let then_block = if_stmt
.then_block()
.map(|then_block| {
let scope_id = self.db.alloc_scope(Scope::default());
let (value, explicit_return) = self.compile_block_expr(
then_block.clone(),
Some(scope_id),
expected_type,
);
if !explicit_return {
self.error(
DiagnosticInfo::ImplicitReturnInIf,
then_block.syntax().text_range(),
);
}
value
})
.unwrap_or_else(|| self.unknown());

if condition.is_some() {
self.type_guards.pop().unwrap();
}

let Some(name) = let_stmt.name() else {
continue;
};
self.type_check(
then_block.ty(),
expected_type.unwrap_or(self.unknown_type),
block.syntax().text_range(),
);

let else_guards = condition
.as_ref()
.map(|condition| condition.else_guards())
.unwrap_or_default();

self.type_guards.push(else_guards);

statements.push(Statement::If(
condition
.map(|condition| condition.hir())
.unwrap_or(self.unknown_hir),
then_block.hir(),
));
}
Stmt::ReturnStmt(return_stmt) => {
let value = return_stmt
.expr()
.map(|expr| self.compile_expr(expr, expected_type))
.unwrap_or_else(|| self.unknown());
statements.push(Statement::Return(value));
}
Stmt::AssertStmt(assert_stmt) => {
let condition = assert_stmt
.expr()
.map(|condition| self.compile_expr(condition, Some(self.bool_type)))
.unwrap_or_else(|| self.unknown());

let symbol_id = self.db.alloc_symbol(Symbol::LetBinding {
type_id: expected_type.unwrap_or(value.ty()),
hir_id: value.hir(),
});
self.type_guards.push(condition.then_guards());

let mut let_scope = Scope::default();
let_scope.define_symbol(name.to_string(), symbol_id);
let scope_id = self.db.alloc_scope(let_scope);
self.scope_stack.push(scope_id);
self.type_check(
condition.ty(),
self.bool_type,
assert_stmt.syntax().text_range(),
);

let not_condition = self.db.alloc_hir(Hir::Not(condition.hir()));
let raise = self.db.alloc_hir(Hir::Raise);

let_scope_ids.push(scope_id);
statements.push(Statement::If(not_condition, raise))
}
}
}
Expand All @@ -439,22 +531,44 @@ impl<'a> Lowerer<'a> {
.map(|expr| self.compile_expr(expr, expected_type))
.unwrap_or(self.unknown());

for scope_id in let_scope_ids.into_iter().rev() {
body = Value::typed(
self.db.alloc_hir(Hir::Scope {
scope_id,
value: body.hir(),
}),
body.ty(),
);
self.scope_stack.pop().unwrap();
let mut explicit_return = false;

for statement in statements.into_iter().rev() {
match statement {
Statement::Let(scope_id) => {
body = Value::typed(
self.db.alloc_hir(Hir::Scope {
scope_id,
value: body.hir(),
}),
body.ty(),
);
self.scope_stack.pop().unwrap();
}
Statement::Return(value) => {
body = value;
explicit_return = true;
}
Statement::If(condition, then_block) => {
self.type_guards.pop().unwrap();

body = Value::typed(
self.db.alloc_hir(Hir::If {
condition,
then_block,
else_block: body.hir(),
}),
body.ty(),
);
}
}
}

if scope_id.is_some() {
self.scope_stack.pop().unwrap();
}

body
(body, explicit_return)
}

fn compile_expr(&mut self, expr: Expr, expected_type: Option<TypeId>) -> Value {
Expand All @@ -466,7 +580,15 @@ impl<'a> Lowerer<'a> {
Expr::PairExpr(pair) => self.compile_pair_expr(pair, expected_type),
Expr::Block(block) => {
let scope_id = self.db.alloc_scope(Scope::default());
self.compile_block_expr(block, Some(scope_id), expected_type)
let (value, explicit_return) =
self.compile_block_expr(block.clone(), Some(scope_id), expected_type);
if explicit_return {
self.error(
DiagnosticInfo::ExplicitReturnInExpr,
block.syntax().text_range(),
);
}
value
}
Expr::LambdaExpr(lambda) => self.compile_lambda_expr(lambda, expected_type),
Expr::PrefixExpr(prefix) => self.compile_prefix_expr(prefix),
Expand Down Expand Up @@ -1100,7 +1222,7 @@ impl<'a> Lowerer<'a> {
fn compile_if_expr(&mut self, if_expr: IfExpr, expected_type: Option<TypeId>) -> Value {
let condition = if_expr
.condition()
.map(|condition| self.compile_expr(condition, None));
.map(|condition| self.compile_expr(condition, Some(self.bool_type)));

if let Some(condition) = condition.as_ref() {
self.type_guards.push(condition.then_guards());
Expand All @@ -1109,6 +1231,7 @@ impl<'a> Lowerer<'a> {
let then_block = if_expr.then_block().map(|then_block| {
let scope_id = self.db.alloc_scope(Scope::default());
self.compile_block_expr(then_block, Some(scope_id), expected_type)
.0
});

if condition.is_some() {
Expand All @@ -1119,9 +1242,13 @@ impl<'a> Lowerer<'a> {
self.type_guards.push(condition.else_guards());
}

let expected_type =
expected_type.or_else(|| then_block.as_ref().map(|then_block| then_block.ty()));

let else_block = if_expr.else_block().map(|else_block| {
let scope_id = self.db.alloc_scope(Scope::default());
self.compile_block_expr(else_block, Some(scope_id), expected_type)
.0
});

if condition.is_some() {
Expand Down
3 changes: 2 additions & 1 deletion crates/rue-compiler/src/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl<'a> Optimizer<'a> {
fn compute_captures_hir(&mut self, scope_id: ScopeId, hir_id: HirId) {
match self.db.hir(hir_id).clone() {
Hir::Unknown => unreachable!(),
Hir::Atom(_) => {}
Hir::Atom(_) | Hir::Raise => {}
Hir::Reference(symbol_id) => self.compute_reference_captures(scope_id, symbol_id),
Hir::Scope {
scope_id: new_scope_id,
Expand Down Expand Up @@ -317,6 +317,7 @@ impl<'a> Optimizer<'a> {
Hir::First(value) => self.opt_first(scope_id, *value),
Hir::Rest(value) => self.opt_rest(scope_id, *value),
Hir::Not(value) => self.opt_not(scope_id, *value),
Hir::Raise => self.db.alloc_lir(Lir::Raise),
Hir::Sha256(value) => self.opt_sha256(scope_id, *value),
Hir::IsCons(value) => self.opt_is_cons(scope_id, *value),
Hir::Strlen(value) => self.opt_strlen(scope_id, *value),
Expand Down
3 changes: 3 additions & 0 deletions crates/rue-lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ impl<'a> Lexer<'a> {
"const" => TokenKind::Const,
"if" => TokenKind::If,
"else" => TokenKind::Else,
"return" => TokenKind::Return,
"assert" => TokenKind::Assert,
"nil" => TokenKind::Nil,
"true" => TokenKind::True,
"false" => TokenKind::False,
Expand Down Expand Up @@ -275,6 +277,7 @@ mod tests {
check("const", &[TokenKind::Const]);
check("if", &[TokenKind::If]);
check("else", &[TokenKind::Else]);
check("return", &[TokenKind::Return]);
check("true", &[TokenKind::True]);
check("false", &[TokenKind::False]);
check("nil", &[TokenKind::Nil]);
Expand Down
2 changes: 2 additions & 0 deletions crates/rue-lexer/src/token_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum TokenKind {
Const,
If,
Else,
Return,
Assert,
Nil,
True,
False,
Expand Down
Loading

0 comments on commit f97a52d

Please sign in to comment.