use brink_syntax::ast::{self, AstNode};
use crate::hir::types::{AwaitStmt, LogicBlock};
use crate::provenance::NodeClass;
use crate::{AssignOp, Assignment, DiagnosticCode, Expr, Return, ReturnKind, Stmt, TempDecl};
use super::super::context::{LowerScope, LowerSink, Lowered};
use super::super::expr::LowerExpr;
use super::super::helpers::{expr_contains_call, name_from_ident};
use super::super::types::lower_type_annotation;
use super::LowerBody;
use super::logic_block::{lower_await_stmt, lower_stmt_block};
pub enum LogicLineOutput {
Return(Return),
TempDecl(TempDecl),
Assignment(Assignment),
ExprStmt(Expr),
Block(LogicBlock),
Await(AwaitStmt),
}
impl LogicLineOutput {
pub fn has_call(&self) -> bool {
match self {
Self::ExprStmt(expr) => expr_contains_call(expr),
Self::TempDecl(td) => td.value.as_ref().is_some_and(expr_contains_call),
Self::Assignment(a) => expr_contains_call(&a.value),
Self::Return(_) | Self::Block(_) | Self::Await(_) => false,
}
}
pub fn into_stmt(self) -> Stmt {
match self {
Self::Return(r) => Stmt::Return(r),
Self::TempDecl(td) => Stmt::TempDecl(td),
Self::Assignment(a) => Stmt::Assignment(a),
Self::ExprStmt(e) => Stmt::ExprStmt(e),
Self::Block(lb) => Stmt::LogicBlock(lb),
Self::Await(a) => Stmt::Await(a),
}
}
}
impl LowerBody for ast::LogicLine {
type Output = LogicLineOutput;
fn lower_body(
&self,
scope: &LowerScope,
sink: &mut impl LowerSink,
) -> Lowered<LogicLineOutput> {
let range = self.syntax().text_range();
if let Some(block) = self.stmt_block() {
let stmts = lower_stmt_block(&block, scope, sink);
return Ok(LogicLineOutput::Block(LogicBlock {
ptr: scope.prov(NodeClass::LogicBlock, block.syntax()),
stmts,
scope: crate::LogicBlockScope::Standalone,
}));
}
if let Some(await_stmt) = self.await_stmt() {
return Ok(LogicLineOutput::Await(lower_await_stmt(
&await_stmt,
scope,
sink,
)));
}
if let Some(ret) = self.return_stmt() {
let value = ret.value().and_then(|e| e.lower_expr(scope, sink).ok());
return Ok(LogicLineOutput::Return(Return {
ptr: Some(scope.prov(NodeClass::Return, ret.syntax())),
kind: ReturnKind::Explicit,
value,
onwards_args: Vec::new(),
}));
}
if let Some(temp) = self.temp_decl() {
let ident = temp
.identifier()
.ok_or_else(|| sink.diagnose(range, DiagnosticCode::E014))?;
let name = name_from_ident(&ident)
.ok_or_else(|| sink.diagnose(range, DiagnosticCode::E014))?;
let value = temp.value().and_then(|e| e.lower_expr(scope, sink).ok());
let annotation = temp
.type_annotation()
.and_then(|ta| lower_type_annotation(&ta));
return Ok(LogicLineOutput::TempDecl(TempDecl {
ptr: scope.prov(NodeClass::TempDecl, temp.syntax()),
name,
value,
annotation,
synthetic: false,
}));
}
if let Some(assign) = self.assignment() {
let target = assign
.target()
.ok_or_else(|| sink.diagnose(range, DiagnosticCode::E014))
.and_then(|e| e.lower_expr(scope, sink))?;
let value = assign
.value()
.ok_or_else(|| sink.diagnose(range, DiagnosticCode::E014))
.and_then(|e| e.lower_expr(scope, sink))?;
let op = assign
.op_token()
.map_or(AssignOp::Set, |tok| match tok.kind() {
brink_syntax::SyntaxKind::PLUS_EQ => AssignOp::Add,
brink_syntax::SyntaxKind::MINUS_EQ => AssignOp::Sub,
_ => AssignOp::Set,
});
return Ok(LogicLineOutput::Assignment(Assignment {
ptr: scope.prov(NodeClass::Assignment, assign.syntax()),
target,
op,
value,
}));
}
for child in self.syntax().children() {
if let Some(expr) = ast::Expr::cast(child) {
let e = expr.lower_expr(scope, sink)?;
return Ok(LogicLineOutput::ExprStmt(e));
}
}
Err(sink.diagnose(range, DiagnosticCode::E014))
}
}