From 363179084f36a63d5ba14aa8e0f31812531a1ef5 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Mon, 22 Jan 2024 02:14:22 -0800 Subject: [PATCH] fix some things --- crates/ast/ast.rs | 28 +++------ crates/js_ast/hoist.rs | 98 ++++++++++++++++++----------- crates/js_ast/js_ast.rs | 7 --- crates/parser/parse_block.rs | 10 +-- crates/parser/parse_module.rs | 2 +- crates/sema/ast_type_coersion.rs | 102 +++++++++++++++---------------- crates/sema/sema_scope.rs | 0 crates/tokenizer/tokenizer.rs | 2 +- 8 files changed, 125 insertions(+), 124 deletions(-) create mode 100644 crates/sema/sema_scope.rs diff --git a/crates/ast/ast.rs b/crates/ast/ast.rs index 46b9e6c60da841ff84f4af137d19f12dd3a34252..bdda2b34cd68a35b8c640bd1b18f1c6988ed6c5b 100644 --- a/crates/ast/ast.rs +++ b/crates/ast/ast.rs @@ -186,13 +186,6 @@ impl BuiltinId { } impl Statement { - pub fn is_expression(&self) -> bool { - match &self { - Statement::Expr(_) => true, - _ => false, - } - } - pub fn to_top_level(self) -> TopLevelStatement { match self { Statement::Expr(expr) => TopLevelStatement::Expr(expr), @@ -201,18 +194,22 @@ impl Statement { } } - pub fn is_comment(&self) -> bool { - match self { - Statement::Comment(_) => true, + /// This is stupid function im sorry + pub fn require_semicolon(&self, semicolon_hint: bool) -> bool { + match &self { + Statement::Expr(_) => !semicolon_hint, + Statement::Decl(_) => true, _ => false, } } } impl TopLevelStatement { - pub fn is_expression(&self) -> bool { + /// This is stupid function im sorry + pub fn require_semicolon(&self, earlier_hint: bool) -> bool { match &self { - TopLevelStatement::Expr(_) => true, + TopLevelStatement::Expr(_) => !earlier_hint, + TopLevelStatement::Decl(_) => true, _ => false, } } @@ -224,11 +221,4 @@ impl TopLevelStatement { TopLevelStatement::Comment(comment) => Statement::Comment(comment), } } - - pub fn is_comment(&self) -> bool { - match self { - TopLevelStatement::Comment(_) => true, - _ => false, - } - } } diff --git a/crates/js_ast/hoist.rs b/crates/js_ast/hoist.rs index 5351588aec2aae5653dcdebb524e2503df15aa9e..dfe289b9e0560009e5c5c475f686ecec097ddd0f 100644 --- a/crates/js_ast/hoist.rs +++ b/crates/js_ast/hoist.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::vec; use ast::{BinaryOperator, BlockOrIf, BlockWithLoc, Expr, Statement}; +use has_side_effects::HasSideEffects; use tokenizer::Loc; use crate::*; @@ -156,9 +157,7 @@ impl HoistContext { pub fn process_statement(&mut self, statement: Statement) -> Option<()> { if statement.is_valid_js_expression() { let result = convert_valid_statement(self, statement); - if !result.is_literal() { - self.add_statement(result); - } + self.add_statement(result); return Some(()); } @@ -189,13 +188,11 @@ impl HoistContext { Statement::Expr(expr) => { if let Some(expr) = self.hoist_expression_contents(expr) { let loc = expr.loc.clone(); - if !expr.is_literal() { - self.add_statement(JsStatement::new( - JsStatementData::Expression(expr), - loc, - None, - )) - } + self.add_statement(JsStatement::new( + JsStatementData::Expression(expr), + loc, + None, + )); Some(()) } else { None @@ -209,9 +206,9 @@ impl HoistContext { /// and returns an identifier expression for it. pub fn hoist_expression_to_temporary(&mut self, expr: Expr) -> Option { // TODO - // if !expr.has_side_effects() { - // return Some(self.hoist_expression_contents(expr)?); - // } + if !expr.has_side_effects() { + return Some(self.hoist_expression_contents(expr)?); + } let init = self.hoist_expression_contents(expr)?; @@ -334,7 +331,7 @@ impl HoistContext { let mut did_assign = false; let js_if = - self.hoist_js_if(data.yes, data.no, condition, &mut did_assign, &temporary)?; + self.hoist_js_if(data.yes, data.no, condition, &mut did_assign, &temporary); self.add_statement(JsStatement::new( JsStatementData::If(js_if), @@ -396,7 +393,7 @@ impl HoistContext { b: BlockWithLoc, temporary: &String, did_assign: &mut bool, - ) -> Option { + ) -> JsBlock { let loc = b.loc.clone(); // A non-block will become a block with a single expression: @@ -405,21 +402,22 @@ impl HoistContext { // We will also apply this transformation to blocks that are valid // expressions, because that is more concise. if b.is_valid_js_expression() { - return Some(JsBlock::new( + return JsBlock::new( vec![new_js_assignment_statement( convert_valid_block(self, b), temporary.clone(), loc.clone(), )], loc, - )); + ); } let mut child = self.new_child(); for statement in b.block.statements { if let None = child.process_statement(statement) { - return None; + tree_shake_statement_list(&mut child.statements); + return JsBlock::new(child.statements, loc); }; } @@ -449,7 +447,9 @@ impl HoistContext { _ => {} } - Some(JsBlock::new(child.statements, loc)) + tree_shake_statement_list(&mut child.statements); + + JsBlock::new(child.statements, loc) } pub fn hoist_block_to_arrow_fn_body(&mut self, b: BlockWithLoc) -> JsArrowBody { @@ -494,6 +494,8 @@ impl HoistContext { _ => {} } + tree_shake_statement_list(&mut child.statements); + JsArrowBody::Block(JsBlock::new(child.statements, loc)) } @@ -513,16 +515,9 @@ impl HoistContext { // to assign to the temporary variable. match self.last_statement().map(|x| &x.data) { Some(JsStatementData::Expression(_)) if b.block.has_implicit_return => { - let last = self.statements.pop().unwrap(); - match last { - JsStatement { - data: JsStatementData::Expression(expr), - .. - } => expr, - _ => { - self.statements.push(last); - JsExpr::new(JsExprData::Null, loc.clone(), None) - } + match self.statements.pop().unwrap().data { + JsStatementData::Expression(expr) => expr, + _ => unreachable!(), } } _ => JsExpr::new(JsExprData::Null, loc.clone(), None), @@ -536,15 +531,15 @@ impl HoistContext { condition: JsExpr, did_assign: &mut bool, temporary: &TempId, - ) -> Option { - let yes = self.hoist_block_to_js_block(yes, &temporary.get(&self), did_assign)?; + ) -> JsIf { + let yes = self.hoist_block_to_js_block(yes, &temporary.get(&self), did_assign); let no = match no { Some(BlockOrIf::Block(no)) => Some(JsElse::Block(self.hoist_block_to_js_block( no, &temporary.get(&self), did_assign, - )?)), + ))), Some(BlockOrIf::If(else_if)) => { // If the condition of this block has a side effect, we need to // emit a block so that we can assign to the temporary variable. @@ -558,11 +553,12 @@ impl HoistContext { condition, did_assign, temporary, - )?))) + )))) } else { let mut temp_block = self.new_child(); - let condition = - temp_block.hoist_expression_to_temporary(*else_if.condition.clone())?; + let condition = temp_block + .hoist_expression_to_temporary(*else_if.condition.clone()) + .unwrap(); temp_block.add_statement(JsStatement::new( JsStatementData::If(self.hoist_js_if( @@ -571,11 +567,13 @@ impl HoistContext { condition, did_assign, temporary, - )?), + )), Loc::EMPTY, None, )); + tree_shake_statement_list(&mut temp_block.statements); + Some(JsElse::Block(JsBlock::new( temp_block.statements, Loc::EMPTY, @@ -585,7 +583,7 @@ impl HoistContext { None => None, }; - Some(JsIf { condition, yes, no }) + JsIf { condition, yes, no } } } @@ -612,3 +610,29 @@ pub fn new_js_assignment_expr(expr: JsExpr, target: String, loc: Loc) -> JsExpr None, ) } + +/// Remove +/// - Discarded literals +pub fn tree_shake_statement_list(list: &mut Vec) { + let mut set_i = 0; + for i in 0..list.len() { + let stmt = list.get(i).unwrap(); + let should_keep = match &stmt.data { + JsStatementData::Expression(expr) => !expr.is_literal(), + JsStatementData::Return(_) => true, + JsStatementData::Export(_) => true, + JsStatementData::Declaration(_) => true, + JsStatementData::Function(_) => true, + JsStatementData::If(_) => true, + // + }; + + if should_keep { + if set_i != i { + list.swap(i, set_i) + } + set_i += 1; + } + } + list.truncate(set_i) +} diff --git a/crates/js_ast/js_ast.rs b/crates/js_ast/js_ast.rs index 6b6c2520d16f139d927522898af6f4bdee367b30..a580ac8502ce38df702844a319c825cd030a03ad 100644 --- a/crates/js_ast/js_ast.rs +++ b/crates/js_ast/js_ast.rs @@ -176,13 +176,6 @@ impl JsStatement { pub fn new(data: JsStatementData, loc: Loc, comment: Option) -> Self { Self { data, loc, comment } } - - pub(crate) fn is_literal(&self) -> bool { - match &self.data { - JsStatementData::Expression(expr) => expr.is_literal(), - _ => false, - } - } } #[derive(Debug, PartialEq, Clone)] diff --git a/crates/parser/parse_block.rs b/crates/parser/parse_block.rs index 3ab34ac6cc6474d351a0a944fabaa17ad2bc2775..a9ad16ed402ac0509be65f1a95ce8a4bd76ec980 100644 --- a/crates/parser/parse_block.rs +++ b/crates/parser/parse_block.rs @@ -46,10 +46,7 @@ pub fn parse_block(p: &mut Parser) -> Result { }, Some(Token::ClosedBrace) => { p.next(); - if !statement.is_expression() - && !statement.is_comment() - && is_statement_like_expression - { + if statement.require_semicolon(is_statement_like_expression) { p.is_top_level = was_top_level; return Err(p .error("Expected semicolon or '}' after statement".to_string()) @@ -63,10 +60,7 @@ pub fn parse_block(p: &mut Parser) -> Result { }); } _ => { - if !statement.is_expression() - && !statement.is_comment() - && is_statement_like_expression - { + if statement.require_semicolon(is_statement_like_expression) { p.is_top_level = was_top_level; return Err(p .error("Expected semicolon or '}' after statement".to_string()) diff --git a/crates/parser/parse_module.rs b/crates/parser/parse_module.rs index 97aef8e42d12836cc0d7e6c5ecde30167e67c57f..f3521bd641adcb0063461c9deee8e92593eff303 100644 --- a/crates/parser/parse_module.rs +++ b/crates/parser/parse_module.rs @@ -24,7 +24,7 @@ pub fn parse_module(p: &mut Parser) -> Result { } } _ => { - if !statement.is_comment() && is_statement_like_expression { + if statement.require_semicolon(is_statement_like_expression) { return Err(p .error("Expected semicolon after statement".to_string()) .into()); diff --git a/crates/sema/ast_type_coersion.rs b/crates/sema/ast_type_coersion.rs index 411442e62cbed296913068bdf7011e02e4eb4601..2fab10b08f06b009feda2523d100641b89dc0d52 100644 --- a/crates/sema/ast_type_coersion.rs +++ b/crates/sema/ast_type_coersion.rs @@ -1,54 +1,54 @@ // use ast::{BinaryOperator, ExprData, Type}; -// impl TypeCoerceable for ExprData { -// fn to_type(&self) -> Option { -// match self { -// ExprData::Block(block) => { -// if (block.has_implicit_return) { -// block.statements.last().unwrap().to_type() -// } else { -// Some(Type::Void) -// } -// } -// ExprData::Binary { op, left, right } => { -// // TODO: this likely has to do further type checking -// let left_type = left.to_type(); -// match op { -// BinaryOperator::Add => left_type, -// BinaryOperator::Sub => left_type, -// BinaryOperator::Mul => left_type, -// BinaryOperator::Div => left_type, -// BinaryOperator::Pow => left_type, -// BinaryOperator::Eq => Some(Type::Bool), -// BinaryOperator::Gt => Some(Type::Bool), -// BinaryOperator::Lt => Some(Type::Bool), -// BinaryOperator::Gte => Some(Type::Bool), -// BinaryOperator::Lte => Some(Type::Bool), -// BinaryOperator::Lshift => Some(Type::Int), -// BinaryOperator::Rshift => Some(Type::Int), -// BinaryOperator::BitAnd => Some(Type::Int), -// BinaryOperator::BitOr => Some(Type::Int), -// BinaryOperator::BitXor => Some(Type::Int), -// BinaryOperator::Mod => Some(Type::Int), -// BinaryOperator::NotEq => Some(Type::Int), +impl TypeCoerceable for ExprData { + fn to_type(&self) -> Option { + match self { + ExprData::Block(block) => { + if (block.has_implicit_return) { + block.statements.last().unwrap().to_type() + } else { + Some(Type::Void) + } + } + ExprData::Binary { op, left, right } => { + // TODO: this likely has to do further type checking + let left_type = left.to_type(); + match op { + BinaryOperator::Add => left_type, + BinaryOperator::Sub => left_type, + BinaryOperator::Mul => left_type, + BinaryOperator::Div => left_type, + BinaryOperator::Pow => left_type, + BinaryOperator::Eq => Some(Type::Bool), + BinaryOperator::Gt => Some(Type::Bool), + BinaryOperator::Lt => Some(Type::Bool), + BinaryOperator::Gte => Some(Type::Bool), + BinaryOperator::Lte => Some(Type::Bool), + BinaryOperator::Lshift => Some(Type::Int), + BinaryOperator::Rshift => Some(Type::Int), + BinaryOperator::BitAnd => Some(Type::Int), + BinaryOperator::BitOr => Some(Type::Int), + BinaryOperator::BitXor => Some(Type::Int), + BinaryOperator::Mod => Some(Type::Int), + BinaryOperator::NotEq => Some(Type::Int), -// BinaryOperator::And => Some(Type::Bool), -// BinaryOperator::Or => Some(Type::Bool), -// } -// } -// ExprData::Unary { op, value } => todo!(), -// ExprData::Function { -// name, -// params, -// return_type, -// block, -// } => Some(return_type), -// ExprData::Call { base, arguments } => None, // oh shit. yeah this trait isn't implementable -// ExprData::Builtin { id, arguments } => todo!(), -// ExprData::If(_) => todo!(), -// ExprData::Return(_) => todo!(), -// ExprData::Literal(_) => todo!(), -// ExprData::Identifier(_) => todo!(), -// } -// } -// } + BinaryOperator::And => Some(Type::Bool), + BinaryOperator::Or => Some(Type::Bool), + } + } + ExprData::Unary { op, value } => todo!(), + ExprData::Function { + name, + params, + return_type, + block, + } => Some(return_type), + ExprData::Call { base, arguments } => None, // oh shit. yeah this trait isn't implementable + ExprData::Builtin { id, arguments } => todo!(), + ExprData::If(_) => todo!(), + ExprData::Return(_) => todo!(), + ExprData::Literal(_) => todo!(), + ExprData::Identifier(_) => todo!(), + } + } +} diff --git a/crates/sema/sema_scope.rs b/crates/sema/sema_scope.rs new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/crates/tokenizer/tokenizer.rs b/crates/tokenizer/tokenizer.rs index b03a99236d2ecf981c8b866f060c73eaa9cdf97f..aee421347f0e4a1a7a044043c4748acaadfe5360 100644 --- a/crates/tokenizer/tokenizer.rs +++ b/crates/tokenizer/tokenizer.rs @@ -79,7 +79,7 @@ pub enum Token { impl Token { pub fn is_start_of_statement_like_expression(&self) -> bool { match &self { - Token::If | Token::OpenBrace | Token::Fn => false, + Token::If | Token::OpenBrace => false, _ => true, } } -- 2.54.0