diff --git a/compiler/forget/crates/build-hir/src/build.rs b/compiler/forget/crates/build-hir/src/build.rs index 743117cc2d..8ca67ec204 100644 --- a/compiler/forget/crates/build-hir/src/build.rs +++ b/compiler/forget/crates/build-hir/src/build.rs @@ -1,14 +1,15 @@ use bumpalo::collections::{CollectIn, String}; use estree::{ - AssignmentTarget, ExpressionLike, FunctionDeclaration, IfStatement, Literal, LiteralValue, - Pattern, Statement, VariableDeclarationKind, + AssignmentTarget, BinaryExpression, ExpressionLike, ForInit, ForStatement, FunctionDeclaration, + IfStatement, Literal, LiteralValue, Pattern, Statement, VariableDeclarationKind, }; use hir::{ - ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind, - InstructionValue, LValue, LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue, + ArrayElement, BlockKind, BranchTerminal, Environment, ForTerminal, Function, GotoKind, + Identifier, InstructionKind, InstructionValue, LValue, LoadGlobal, LoadLocal, Place, + PrimitiveValue, TerminalValue, }; -use crate::builder::{Binding, Builder}; +use crate::builder::{Binding, Builder, LoopScope}; /// Converts a React function in ESTree format into HIR. Returns the HIR /// if it was constructed sucessfully, otherwise a list of diagnostics @@ -35,7 +36,7 @@ pub fn build<'a>( }), ); builder.terminate( - TerminalValue::ReturnTerminal(hir::ReturnTerminal { + TerminalValue::Return(hir::ReturnTerminal { value: implicit_return_value, }), hir::BlockKind::Block, @@ -55,7 +56,7 @@ fn lower_statement<'a>( env: &'a Environment<'a>, builder: &mut Builder<'a>, stmt: Statement, - _label: Option>, + label: Option>, ) -> Result<(), Diagnostic> { match stmt { Statement::BlockStatement(stmt) => { @@ -66,7 +67,7 @@ fn lower_statement<'a>( Statement::BreakStatement(stmt) => { let block = builder.resolve_break(stmt.label.as_ref())?; builder.terminate( - TerminalValue::GotoTerminal(hir::GotoTerminal { + TerminalValue::Goto(hir::GotoTerminal { block, kind: GotoKind::Break, }), @@ -76,7 +77,7 @@ fn lower_statement<'a>( Statement::ContinueStatement(stmt) => { let block = builder.resolve_continue(stmt.label.as_ref())?; builder.terminate( - TerminalValue::GotoTerminal(hir::GotoTerminal { + TerminalValue::Goto(hir::GotoTerminal { block, kind: GotoKind::Continue, }), @@ -95,7 +96,7 @@ fn lower_statement<'a>( ), }; builder.terminate( - TerminalValue::ReturnTerminal(hir::ReturnTerminal { value }), + TerminalValue::Return(hir::ReturnTerminal { value }), BlockKind::Block, ); } @@ -158,7 +159,7 @@ fn lower_statement<'a>( let consequent_block = builder.enter(BlockKind::Block, |builder| { lower_statement(env, builder, consequent, None).unwrap(); - TerminalValue::GotoTerminal(hir::GotoTerminal { + TerminalValue::Goto(hir::GotoTerminal { block: fallthrough_block.id, kind: GotoKind::Break, }) @@ -168,14 +169,14 @@ fn lower_statement<'a>( if let Some(alternate) = alternate { lower_statement(env, builder, alternate, None).unwrap(); } - TerminalValue::GotoTerminal(hir::GotoTerminal { + TerminalValue::Goto(hir::GotoTerminal { block: fallthrough_block.id, kind: GotoKind::Break, }) }); let test = lower_expression_to_temporary(env, builder, test); - let terminal = TerminalValue::IfTerminal(hir::IfTerminal { + let terminal = TerminalValue::If(hir::IfTerminal { test, consequent: consequent_block, alternate: alternate_block, @@ -183,6 +184,80 @@ fn lower_statement<'a>( }); builder.terminate_with_fallthrough(terminal, fallthrough_block); } + Statement::ForStatement(stmt) => { + let ForStatement { + init, + test, + update, + body, + .. + } = *stmt; + + // Block for the loop's test condition + let test_block = builder.reserve(BlockKind::Loop); + + // Block for code following the loop + let fallthrough_block = builder.reserve(BlockKind::Block); + + let init_block = builder.enter(BlockKind::Loop, |builder| { + if let Some(ForInit::VariableDeclaration(decl)) = init { + lower_statement(env, builder, Statement::VariableDeclaration(decl), None) + .unwrap(); + TerminalValue::Goto(hir::GotoTerminal { + block: test_block.id, + kind: GotoKind::Break, + }) + } else { + panic!("Expected for statement to have a variable declaration initializer") + } + }); + + let update_block = update.map(|update| { + builder.enter(BlockKind::Loop, |builder| { + lower_expression_to_temporary(env, builder, update); + TerminalValue::Goto(hir::GotoTerminal { + block: test_block.id, + kind: GotoKind::Break, + }) + }) + }); + + let body_block = builder.enter(BlockKind::Block, |builder| { + let loop_ = LoopScope { + label, + continue_block: update_block.unwrap_or(test_block.id), + break_block: fallthrough_block.id, + }; + builder.enter_loop(loop_, |builder| { + lower_statement(env, builder, body, None).unwrap(); + TerminalValue::Goto(hir::GotoTerminal { + block: update_block.unwrap_or(test_block.id), + kind: GotoKind::Continue, + }) + }) + }); + + let terminal = TerminalValue::For(ForTerminal { + body: body_block, + init: init_block, + test: test_block.id, + fallthrough: fallthrough_block.id, + update: update_block, + }); + builder.terminate_with_fallthrough(terminal, test_block); + + if let Some(test) = test { + let test_value = lower_expression_to_temporary(env, builder, test); + let terminal = TerminalValue::Branch(BranchTerminal { + test: test_value, + consequent: body_block, + alternate: fallthrough_block.id, + }); + builder.terminate_with_fallthrough(terminal, fallthrough_block); + } else { + panic!("Expected for statement to have a tesst block"); + } + } _ => todo!("Lower {stmt:#?}"), } Ok(()) @@ -240,6 +315,7 @@ fn lower_expression<'a>( .collect_in(env.allocator); InstructionValue::Array(hir::Array { elements }) } + ExpressionLike::AssignmentExpression(expr) => match expr.operator { estree::AssignmentOperator::Equals => { let right = lower_expression_to_temporary(env, builder, expr.right); @@ -248,6 +324,22 @@ fn lower_expression<'a>( _ => todo!("lower assignment expr {:#?}", expr), }, + ExpressionLike::BinaryExpression(expr) => { + let BinaryExpression { + left, + operator, + right, + .. + } = *expr; + let left = lower_expression_to_temporary(env, builder, left); + let right = lower_expression_to_temporary(env, builder, right); + InstructionValue::Binary(hir::Binary { + left, + operator, + right, + }) + } + // Cases that cannot appear in expression position but which are included in ExpressionLike // to make serialization easier ExpressionLike::SpreadElement(_) => { diff --git a/compiler/forget/crates/build-hir/src/builder.rs b/compiler/forget/crates/build-hir/src/builder.rs index fb0992ee9b..3dc358cbd5 100644 --- a/compiler/forget/crates/build-hir/src/builder.rs +++ b/compiler/forget/crates/build-hir/src/builder.rs @@ -1,4 +1,4 @@ -use bumpalo::collections::Vec; +use bumpalo::collections::{String, Vec}; use std::{cell::RefCell, collections::HashSet, rc::Rc}; use hir::{ @@ -27,6 +27,8 @@ pub(crate) struct Builder<'a> { wip: WipBlock<'a>, id_gen: InstructionIdGenerator, + + scopes: Vec<'a, ControlFlowScope<'a>>, } pub(crate) struct WipBlock<'a> { @@ -35,6 +37,48 @@ pub(crate) struct WipBlock<'a> { pub instructions: Vec<'a, Instruction<'a>>, } +pub(crate) enum Binding<'a> { + Local(Identifier<'a>), + Module(Identifier<'a>), + Global, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +enum ControlFlowScope<'a> { + Loop(LoopScope<'a>), + // Switch(SwitchScope<'a>), + Label(LabelScope<'a>), +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) struct LoopScope<'a> { + pub label: Option>, + pub continue_block: BlockId, + pub break_block: BlockId, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) struct LabelScope<'a> { + pub label: String<'a>, + pub block: BlockId, +} + +impl<'a> ControlFlowScope<'a> { + fn label(&self) -> Option<&String<'a>> { + match self { + Self::Loop(scope) => scope.label.as_ref(), + Self::Label(scope) => Some(&scope.label), + } + } + + fn break_block(&self) -> BlockId { + match self { + Self::Loop(scope) => scope.break_block, + Self::Label(scope) => scope.block, + } + } +} + impl<'a> Builder<'a> { pub(crate) fn new(environment: &'a Environment<'a>) -> Self { let entry = environment.next_block_id(); @@ -49,6 +93,7 @@ impl<'a> Builder<'a> { entry, wip: current, id_gen: InstructionIdGenerator::new(), + scopes: Vec::new_in(&environment.allocator), } } @@ -155,6 +200,17 @@ impl<'a> Builder<'a> { ); } + pub(crate) fn enter_loop(&mut self, scope: LoopScope<'a>, f: F) -> TerminalValue<'a> + where + F: FnOnce(&mut Self) -> TerminalValue<'a>, + { + self.scopes.push(ControlFlowScope::Loop(scope.clone())); + let terminal = f(self); + let last = self.scopes.pop().unwrap(); + assert_eq!(last, ControlFlowScope::Loop(scope)); + terminal + } + /// Returns a new temporary identifier pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> { hir::Identifier { @@ -173,9 +229,21 @@ impl<'a> Builder<'a> { /// provided but cannot be resolved. pub(crate) fn resolve_break( &self, - _label: Option<&estree::Identifier>, + label: Option<&estree::Identifier>, ) -> Result { - todo!() + for scope in self.scopes.iter().rev() { + match (label, scope.label()) { + // If this is an unlabeled break, return the most recent break target + (None, _) => return Ok(scope.break_block()), + // If the break is labeled and matches the current scope, return its break target + (Some(label), Some(scope_label)) if &label.name == scope_label => { + return Ok(scope.break_block()); + } + // Otherwise keep searching + _ => continue, + } + } + Err(()) } /// Resolves the target for the given continue label (if present), or returns the default @@ -183,9 +251,36 @@ impl<'a> Builder<'a> { /// provided but cannot be resolved. pub(crate) fn resolve_continue( &self, - _label: Option<&estree::Identifier>, + label: Option<&estree::Identifier>, ) -> Result { - todo!() + for scope in self.scopes.iter().rev() { + match scope { + ControlFlowScope::Loop(scope) => { + match (label, &scope.label) { + // If this is an unlabeled continue, return the first matching loop + (None, _) => return Ok(scope.continue_block), + // If the continue is labeled and matches the current scope, return its continue target + (Some(label), Some(scope_label)) + if label.name.as_str() == scope_label.as_str() => + { + return Ok(scope.continue_block); + } + // Otherwise keep searching + _ => continue, + } + } + _ => { + match (label, scope.label()) { + (Some(label), Some(scope_label)) if label.name.as_str() == scope_label => { + // Error, the continue referred to a label that is not a loop + return Err(()); + } + _ => continue, + } + } + } + } + Err(()) } pub(crate) fn resolve_binding( @@ -206,12 +301,6 @@ impl<'a> Builder<'a> { } } -pub(crate) enum Binding<'a> { - Local(Identifier<'a>), - Module(Identifier<'a>), - Global, -} - /// Modifies the HIR to put the blocks in reverse postorder, with predecessors before /// successors (except for the case of loops) fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) { @@ -230,20 +319,24 @@ fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) { let block = hir.block(block_id); let terminal = &block.terminal; match &terminal.value { - TerminalValue::IfTerminal(terminal) => { + TerminalValue::Branch(terminal) => { visit(terminal.alternate, hir, visited, postorder); visit(terminal.consequent, hir, visited, postorder); } - TerminalValue::ForTerminal(terminal) => { + TerminalValue::If(terminal) => { + visit(terminal.alternate, hir, visited, postorder); + visit(terminal.consequent, hir, visited, postorder); + } + TerminalValue::For(terminal) => { visit(terminal.init, hir, visited, postorder); } - TerminalValue::DoWhileTerminal(terminal) => { + TerminalValue::DoWhile(terminal) => { visit(terminal.body, hir, visited, postorder); } - TerminalValue::GotoTerminal(terminal) => { + TerminalValue::Goto(terminal) => { visit(terminal.block, hir, visited, postorder); } - TerminalValue::ReturnTerminal(..) => { /* no-op */ } + TerminalValue::Return(..) => { /* no-op */ } } postorder.push(block_id); } @@ -263,7 +356,7 @@ fn remove_unreachable_for_updates<'a>(hir: &mut HIR<'a>) { let block_ids: HashSet = hir.blocks.keys().cloned().collect(); for block in hir.blocks.values_mut() { - if let TerminalValue::ForTerminal(terminal) = &mut block.terminal.value { + if let TerminalValue::For(terminal) = &mut block.terminal.value { if let Some(update) = terminal.update { if !block_ids.contains(&update) { terminal.update = None; @@ -297,9 +390,9 @@ fn remove_unreachable_do_while_statements<'a>(hir: &mut HIR<'a>) { let block_ids: HashSet = hir.blocks.keys().cloned().collect(); for block in hir.blocks.values_mut() { - if let TerminalValue::DoWhileTerminal(terminal) = &mut block.terminal.value { + if let TerminalValue::DoWhile(terminal) = &mut block.terminal.value { if !block_ids.contains(&terminal.test) { - block.terminal.value = TerminalValue::GotoTerminal(hir::GotoTerminal { + block.terminal.value = TerminalValue::Goto(hir::GotoTerminal { block: terminal.body, kind: GotoKind::Break, }); @@ -353,7 +446,7 @@ fn mark_predecessors<'a>(hir: &mut HIR<'a>) { fn invariant(cond: bool, f: F) -> Result<(), Diagnostic> where - F: FnOnce() -> String, + F: FnOnce() -> std::string::String, { if !cond { let msg = f(); diff --git a/compiler/forget/crates/estree-swc/src/lib.rs b/compiler/forget/crates/estree-swc/src/lib.rs index bfcbcff00c..4cb6adc517 100644 --- a/compiler/forget/crates/estree-swc/src/lib.rs +++ b/compiler/forget/crates/estree-swc/src/lib.rs @@ -6,8 +6,8 @@ use swc_core::common::errors::Handler; use swc_core::common::source_map::Pos; use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS}; use swc_core::ecma::ast::{ - AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, ModuleItem, Pat, - PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr, + AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, MemberProp, + ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr, }; use swc_core::ecma::parser::Syntax; use swc_core::ecma::transforms::base::resolver; @@ -366,6 +366,9 @@ fn convert_expression(cx: &Context, expr: &Expr) -> estree::ExpressionLike { range: convert_span(&expr.span), })) } + Expr::Member(expr) => { + estree::ExpressionLike::MemberExpression(Box::new(convert_member_expression(cx, expr))) + } _ => todo!("translate expression {:#?}", expr), } } @@ -376,22 +379,40 @@ fn convert_assignment_target(cx: &Context, target: &PatOrExpr) -> estree::Assign estree::AssignmentTarget::Pattern(Box::new(convert_pattern(cx, target))) } PatOrExpr::Expr(target) => { - if let Expr::Member(target) = target.as_ref() { - estree::AssignmentTarget::MemberExpression(Box::new(convert_member_expression( - cx, target, - ))) - } else { - panic!( - "Invalid input, expected either a pattern or member expression, got {:#?}", - target - ) + match target.as_ref() { + Expr::Member(target) => estree::AssignmentTarget::MemberExpression(Box::new( + convert_member_expression(cx, target), + )), + Expr::Ident(target) => estree::AssignmentTarget::Pattern(Box::new( + estree::Pattern::Identifier(Box::new(convert_identifier(cx, target))), + )), + _ => { + panic!("Expected assignment target to be member expression or identifier, got {:#?}", target) + } } } } } -fn convert_member_expression(_cx: &Context, _expr: &MemberExpr) -> estree::MemberExpression { - todo!("convert member expression") +fn convert_member_expression(cx: &Context, expr: &MemberExpr) -> estree::MemberExpression { + let (is_computed, property) = match &expr.prop { + MemberProp::Ident(prop) => ( + false, + estree::ExpressionLike::Identifier(Box::new(convert_identifier(cx, prop))), + ), + MemberProp::Computed(prop) => (true, convert_expression(cx, &prop.expr)), + _ => { + panic!("PrivateName member expression properties are not supported") + } + }; + estree::MemberExpression { + object: convert_expression(cx, &expr.obj), + property, + is_computed, + is_optional: false, + loc: None, + range: convert_span(&expr.span), + } } fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator { diff --git a/compiler/forget/crates/estree/src/lib.rs b/compiler/forget/crates/estree/src/lib.rs index 22111383e3..a467714bab 100644 --- a/compiler/forget/crates/estree/src/lib.rs +++ b/compiler/forget/crates/estree/src/lib.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use static_assertions::assert_eq_size; -use std::num::NonZeroU32; +use std::{fmt::Display, num::NonZeroU32}; #[derive(Serialize, Deserialize, Debug)] pub struct SourceLocation { @@ -689,6 +689,17 @@ pub enum BinaryOperator { Instanceof, } +impl Display for BinaryOperator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + Self::Plus => "+", + Self::LessThan => "<", + _ => todo!("display for operator: {:#?}", self), + }; + f.write_str(name) + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct CallExpression { #[serde(rename = "optional")] diff --git a/compiler/forget/crates/fixtures/tests/fixtures/for-statement.js b/compiler/forget/crates/fixtures/tests/fixtures/for-statement.js new file mode 100644 index 0000000000..180dd4d759 --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/fixtures/for-statement.js @@ -0,0 +1,7 @@ +function foo() { + let x = 0; + for (let i = 0; i < 10; i = i + 1) { + x = x + i; + } + return x; +} diff --git a/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@for-statement.js.snap b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@for-statement.js.snap new file mode 100644 index 0000000000..ee9c1aef84 --- /dev/null +++ b/compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@for-statement.js.snap @@ -0,0 +1,46 @@ +--- +source: crates/fixtures/tests/fixtures_test.rs +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")" +input_file: crates/fixtures/tests/fixtures/for-statement.js +--- +Input: +function foo() { + let x = 0; + for (let i = 0; i < 10; i = i + 1) { + x = x + i; + } + return x; +} + + +Output: +entry bb0 +bb0 + [0] unknown $0 = 0 + [1] unknown $2 = StoreLocal Let unknown x$1 = unknown $0 + [2] For init=bb3 test=bb1 update=bb4 body=bb5 fallthrough=bb2 +bb3 + [3] unknown $3 = 0 + [4] unknown $5 = StoreLocal Let unknown i$4 = unknown $3 + [5] Goto bb1 +bb1 + [6] unknown $14 = LoadLocal unknown i$4 + [7] unknown $15 = 10 + [8] unknown $16 = Binary unknown $14 < unknown $15 + [9] Branch unknown $16 consequent=bb5 alternate=bb2 +bb5 + [10] unknown $10 = LoadLocal unknown x$1 + [11] unknown $11 = LoadLocal unknown i$4 + [12] unknown $12 = Binary unknown $10 + unknown $11 + [13] unknown $13 = StoreLocal Reassign unknown x$1 = unknown $12 + [14] Goto bb4 +bb4 + [15] unknown $6 = LoadLocal unknown i$4 + [16] unknown $7 = 1 + [17] unknown $8 = Binary unknown $6 + unknown $7 + [18] unknown $9 = StoreLocal Reassign unknown i$4 = unknown $8 + [19] Goto bb1 +bb2 + [20] unknown $17 = LoadLocal unknown x$1 + [21] Return unknown $17 + diff --git a/compiler/forget/crates/hir/src/instruction.rs b/compiler/forget/crates/hir/src/instruction.rs index 02e49d80e0..75dddb5ea6 100644 --- a/compiler/forget/crates/hir/src/instruction.rs +++ b/compiler/forget/crates/hir/src/instruction.rs @@ -1,6 +1,7 @@ use std::{cell::RefCell, fmt::Display, rc::Rc}; use bumpalo::collections::{String, Vec}; +use estree::BinaryOperator; use crate::{IdentifierId, InstructionId, ScopeId, Type}; @@ -15,7 +16,7 @@ pub struct Instruction<'a> { pub enum InstructionValue<'a> { Array(Array<'a>), // Await(Await<'a>), - // Binary(Binary<'a>), + Binary(Binary<'a>), // Call(Call<'a>), // ComputedDelete(ComputedDelete<'a>), // ComputedLoad(ComputedLoad<'a>), @@ -59,6 +60,13 @@ pub enum ArrayElement<'a> { Spread(Place<'a>), } +#[derive(Debug)] +pub struct Binary<'a> { + pub left: Place<'a>, + pub operator: BinaryOperator, + pub right: Place<'a>, +} + #[derive(Debug)] pub struct Primitive<'a> { pub value: PrimitiveValue<'a>, diff --git a/compiler/forget/crates/hir/src/print.rs b/compiler/forget/crates/hir/src/print.rs index 15fb1407ca..3bd83493fa 100644 --- a/compiler/forget/crates/hir/src/print.rs +++ b/compiler/forget/crates/hir/src/print.rs @@ -90,6 +90,12 @@ impl<'a> Print for InstructionValue<'a> { write!(out, "DeclareLocal ")?; value.lvalue.print(out)?; } + InstructionValue::Binary(value) => { + write!(out, "Binary ")?; + value.left.print(out)?; + write!(out, " {} ", value.operator)?; + value.right.print(out)?; + } _ => write!(out, "{:?}", self)?, } Ok(()) @@ -146,14 +152,14 @@ impl<'a> Print for Terminal<'a> { impl<'a> Print for TerminalValue<'a> { fn print(&self, out: &mut impl Write) -> Result { match self { - TerminalValue::ReturnTerminal(terminal) => { + TerminalValue::Return(terminal) => { write!(out, "Return ")?; terminal.value.print(out)?; } - TerminalValue::GotoTerminal(terminal) => { + TerminalValue::Goto(terminal) => { write!(out, "Goto {}", terminal.block)?; } - TerminalValue::IfTerminal(terminal) => { + TerminalValue::If(terminal) => { write!(out, "If ")?; terminal.test.print(out)?; write!( @@ -167,6 +173,29 @@ impl<'a> Print for TerminalValue<'a> { } )?; } + TerminalValue::Branch(terminal) => { + write!(out, "Branch ")?; + terminal.test.print(out)?; + write!( + out, + " consequent={} alternate={}", + terminal.consequent, terminal.alternate, + )?; + } + TerminalValue::For(terminal) => { + write!( + out, + "For init={} test={} update={} body={} fallthrough={}", + terminal.init, + terminal.test, + match terminal.update { + Some(fallthrough) => format!("{fallthrough}"), + None => "".to_string(), + }, + terminal.body, + terminal.fallthrough, + )?; + } _ => write!(out, "{:?}", self)?, } Ok(()) diff --git a/compiler/forget/crates/hir/src/terminal.rs b/compiler/forget/crates/hir/src/terminal.rs index 2be31a59ca..5d257352be 100644 --- a/compiler/forget/crates/hir/src/terminal.rs +++ b/compiler/forget/crates/hir/src/terminal.rs @@ -10,22 +10,22 @@ pub struct Terminal<'a> { #[derive(Debug)] pub enum TerminalValue<'a> { - // BranchTerminal(BranchTerminal), - DoWhileTerminal(DoWhileTerminal), - // ForOfTerminal(ForOfTerminal), - ForTerminal(ForTerminal), - GotoTerminal(GotoTerminal), - IfTerminal(IfTerminal<'a>), - // LabelTerminal(LabelTerminal), - // LogicalTerminal(LogicalTerminal), - // OptionalTerminal(OptionalTerminal), - ReturnTerminal(ReturnTerminal<'a>), - // SequenceTerminal(SequenceTerminal), - // SwitchTerminal(SwitchTerminal), - // TernaryTerminal(TernaryTerminal), - // ThrowTerminal(ThrowTerminal), - // UnsupportedTerminal(UnsupportedTerminal), - // WhileTerminal(WhileTerminal), + Branch(BranchTerminal<'a>), + DoWhile(DoWhileTerminal), + // ForOf(ForOfTerminal), + For(ForTerminal), + Goto(GotoTerminal), + If(IfTerminal<'a>), + // Label(LabelTerminal), + // Logical(LogicalTerminal), + // Optional(OptionalTerminal), + Return(ReturnTerminal<'a>), + // Sequence(SequenceTerminal), + // Switch(SwitchTerminal), + // Ternary(TernaryTerminal), + // Throw(ThrowTerminal), + // Unsupported(UnsupportedTerminal), + // While(WhileTerminal), } impl<'a> TerminalValue<'a> { @@ -34,43 +34,53 @@ impl<'a> TerminalValue<'a> { F: Fn(BlockId) -> Option, { match self { - Self::IfTerminal(terminal) => { + Self::If(terminal) => { terminal.fallthrough = match terminal.fallthrough { Some(fallthrough) => f(fallthrough), _ => None, } } - Self::DoWhileTerminal(DoWhileTerminal { fallthrough, .. }) - | Self::ForTerminal(ForTerminal { fallthrough, .. }) => { + Self::DoWhile(DoWhileTerminal { fallthrough, .. }) + | Self::For(ForTerminal { fallthrough, .. }) => { // statically detect if fallthrough is changed to Option so // that we can update to map the fallthrough w f() let _: BlockId = *fallthrough; } - Self::GotoTerminal(_) | Self::ReturnTerminal(_) => {} + Self::Branch(_) | Self::Goto(_) | Self::Return(_) => {} } } pub fn successors(&self) -> Vec { match self { - Self::IfTerminal(terminal) => { + Self::If(terminal) => { vec![terminal.consequent, terminal.alternate] } - Self::ForTerminal(terminal) => { + Self::Branch(terminal) => { + vec![terminal.consequent, terminal.alternate] + } + Self::For(terminal) => { vec![terminal.init] } - Self::DoWhileTerminal(terminal) => { + Self::DoWhile(terminal) => { vec![terminal.body] } - Self::GotoTerminal(terminal) => { + Self::Goto(terminal) => { vec![terminal.block] } - Self::ReturnTerminal(_) => { + Self::Return(_) => { vec![] } } } } +#[derive(Debug)] +pub struct BranchTerminal<'a> { + pub test: Place<'a>, + pub consequent: BlockId, + pub alternate: BlockId, +} + #[derive(Debug)] pub struct GotoTerminal { pub block: BlockId,