| author | |
| committer | |
| log | 42c364acbbbd54bf8afefa0ff82034928e83f06e |
| tree | e820c08f8d97f3bee28a0908a3853fe7481a548e |
| parent | c164266ac2d05ed0a0f7ab09b8db76b5b81eb964 |
| signature | Commit is signed but in an unrecognized format. |
8 files changed, 125 insertions(+), 124 deletions(-)
crates/ast/ast.rs+9-19| ... | ... | @@ -186,13 +186,6 @@ impl BuiltinId { |
| 186 | 186 | } |
| 187 | 187 | |
| 188 | 188 | impl Statement { |
| 189 | pub fn is_expression(&self) -> bool { | |
| 190 | match &self { | |
| 191 | Statement::Expr(_) => true, | |
| 192 | _ => false, | |
| 193 | } | |
| 194 | } | |
| 195 | ||
| 196 | 189 | pub fn to_top_level(self) -> TopLevelStatement { |
| 197 | 190 | match self { |
| 198 | 191 | Statement::Expr(expr) => TopLevelStatement::Expr(expr), |
| ... | ... | @@ -201,18 +194,22 @@ impl Statement { |
| 201 | 194 | } |
| 202 | 195 | } |
| 203 | 196 | |
| 204 | pub fn is_comment(&self) -> bool { | |
| 205 | match self { | |
| 206 | Statement::Comment(_) => true, | |
| 197 | /// This is stupid function im sorry | |
| 198 | pub fn require_semicolon(&self, semicolon_hint: bool) -> bool { | |
| 199 | match &self { | |
| 200 | Statement::Expr(_) => !semicolon_hint, | |
| 201 | Statement::Decl(_) => true, | |
| 207 | 202 | _ => false, |
| 208 | 203 | } |
| 209 | 204 | } |
| 210 | 205 | } |
| 211 | 206 | |
| 212 | 207 | impl TopLevelStatement { |
| 213 | pub fn is_expression(&self) -> bool { | |
| 208 | /// This is stupid function im sorry | |
| 209 | pub fn require_semicolon(&self, earlier_hint: bool) -> bool { | |
| 214 | 210 | match &self { |
| 215 | TopLevelStatement::Expr(_) => true, | |
| 211 | TopLevelStatement::Expr(_) => !earlier_hint, | |
| 212 | TopLevelStatement::Decl(_) => true, | |
| 216 | 213 | _ => false, |
| 217 | 214 | } |
| 218 | 215 | } |
| ... | ... | @@ -224,11 +221,4 @@ impl TopLevelStatement { |
| 224 | 221 | TopLevelStatement::Comment(comment) => Statement::Comment(comment), |
| 225 | 222 | } |
| 226 | 223 | } |
| 227 | ||
| 228 | pub fn is_comment(&self) -> bool { | |
| 229 | match self { | |
| 230 | TopLevelStatement::Comment(_) => true, | |
| 231 | _ => false, | |
| 232 | } | |
| 233 | } | |
| 234 | 224 | } |
crates/js_ast/hoist.rs+61-37| ... | ... | @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; |
| 2 | 2 | use std::vec; |
| 3 | 3 | |
| 4 | 4 | use ast::{BinaryOperator, BlockOrIf, BlockWithLoc, Expr, Statement}; |
| 5 | use has_side_effects::HasSideEffects; | |
| 5 | 6 | use tokenizer::Loc; |
| 6 | 7 | |
| 7 | 8 | use crate::*; |
| ... | ... | @@ -156,9 +157,7 @@ impl HoistContext { |
| 156 | 157 | pub fn process_statement(&mut self, statement: Statement) -> Option<()> { |
| 157 | 158 | if statement.is_valid_js_expression() { |
| 158 | 159 | let result = convert_valid_statement(self, statement); |
| 159 | if !result.is_literal() { | |
| 160 | self.add_statement(result); | |
| 161 | } | |
| 160 | self.add_statement(result); | |
| 162 | 161 | return Some(()); |
| 163 | 162 | } |
| 164 | 163 | |
| ... | ... | @@ -189,13 +188,11 @@ impl HoistContext { |
| 189 | 188 | Statement::Expr(expr) => { |
| 190 | 189 | if let Some(expr) = self.hoist_expression_contents(expr) { |
| 191 | 190 | let loc = expr.loc.clone(); |
| 192 | if !expr.is_literal() { | |
| 193 | self.add_statement(JsStatement::new( | |
| 194 | JsStatementData::Expression(expr), | |
| 195 | loc, | |
| 196 | None, | |
| 197 | )) | |
| 198 | } | |
| 191 | self.add_statement(JsStatement::new( | |
| 192 | JsStatementData::Expression(expr), | |
| 193 | loc, | |
| 194 | None, | |
| 195 | )); | |
| 199 | 196 | Some(()) |
| 200 | 197 | } else { |
| 201 | 198 | None |
| ... | ... | @@ -209,9 +206,9 @@ impl HoistContext { |
| 209 | 206 | /// and returns an identifier expression for it. |
| 210 | 207 | pub fn hoist_expression_to_temporary(&mut self, expr: Expr) -> Option<JsExpr> { |
| 211 | 208 | // TODO |
| 212 | // if !expr.has_side_effects() { | |
| 213 | // return Some(self.hoist_expression_contents(expr)?); | |
| 214 | // } | |
| 209 | if !expr.has_side_effects() { | |
| 210 | return Some(self.hoist_expression_contents(expr)?); | |
| 211 | } | |
| 215 | 212 | |
| 216 | 213 | let init = self.hoist_expression_contents(expr)?; |
| 217 | 214 | |
| ... | ... | @@ -334,7 +331,7 @@ impl HoistContext { |
| 334 | 331 | let mut did_assign = false; |
| 335 | 332 | |
| 336 | 333 | let js_if = |
| 337 | self.hoist_js_if(data.yes, data.no, condition, &mut did_assign, &temporary)?; | |
| 334 | self.hoist_js_if(data.yes, data.no, condition, &mut did_assign, &temporary); | |
| 338 | 335 | |
| 339 | 336 | self.add_statement(JsStatement::new( |
| 340 | 337 | JsStatementData::If(js_if), |
| ... | ... | @@ -396,7 +393,7 @@ impl HoistContext { |
| 396 | 393 | b: BlockWithLoc, |
| 397 | 394 | temporary: &String, |
| 398 | 395 | did_assign: &mut bool, |
| 399 | ) -> Option<JsBlock> { | |
| 396 | ) -> JsBlock { | |
| 400 | 397 | let loc = b.loc.clone(); |
| 401 | 398 | |
| 402 | 399 | // A non-block will become a block with a single expression: |
| ... | ... | @@ -405,21 +402,22 @@ impl HoistContext { |
| 405 | 402 | // We will also apply this transformation to blocks that are valid |
| 406 | 403 | // expressions, because that is more concise. |
| 407 | 404 | if b.is_valid_js_expression() { |
| 408 | return Some(JsBlock::new( | |
| 405 | return JsBlock::new( | |
| 409 | 406 | vec![new_js_assignment_statement( |
| 410 | 407 | convert_valid_block(self, b), |
| 411 | 408 | temporary.clone(), |
| 412 | 409 | loc.clone(), |
| 413 | 410 | )], |
| 414 | 411 | loc, |
| 415 | )); | |
| 412 | ); | |
| 416 | 413 | } |
| 417 | 414 | |
| 418 | 415 | let mut child = self.new_child(); |
| 419 | 416 | |
| 420 | 417 | for statement in b.block.statements { |
| 421 | 418 | if let None = child.process_statement(statement) { |
| 422 | return None; | |
| 419 | tree_shake_statement_list(&mut child.statements); | |
| 420 | return JsBlock::new(child.statements, loc); | |
| 423 | 421 | }; |
| 424 | 422 | } |
| 425 | 423 | |
| ... | ... | @@ -449,7 +447,9 @@ impl HoistContext { |
| 449 | 447 | _ => {} |
| 450 | 448 | } |
| 451 | 449 | |
| 452 | Some(JsBlock::new(child.statements, loc)) | |
| 450 | tree_shake_statement_list(&mut child.statements); | |
| 451 | ||
| 452 | JsBlock::new(child.statements, loc) | |
| 453 | 453 | } |
| 454 | 454 | |
| 455 | 455 | pub fn hoist_block_to_arrow_fn_body(&mut self, b: BlockWithLoc) -> JsArrowBody { |
| ... | ... | @@ -494,6 +494,8 @@ impl HoistContext { |
| 494 | 494 | _ => {} |
| 495 | 495 | } |
| 496 | 496 | |
| 497 | tree_shake_statement_list(&mut child.statements); | |
| 498 | ||
| 497 | 499 | JsArrowBody::Block(JsBlock::new(child.statements, loc)) |
| 498 | 500 | } |
| 499 | 501 | |
| ... | ... | @@ -513,16 +515,9 @@ impl HoistContext { |
| 513 | 515 | // to assign to the temporary variable. |
| 514 | 516 | match self.last_statement().map(|x| &x.data) { |
| 515 | 517 | Some(JsStatementData::Expression(_)) if b.block.has_implicit_return => { |
| 516 | let last = self.statements.pop().unwrap(); | |
| 517 | match last { | |
| 518 | JsStatement { | |
| 519 | data: JsStatementData::Expression(expr), | |
| 520 | .. | |
| 521 | } => expr, | |
| 522 | _ => { | |
| 523 | self.statements.push(last); | |
| 524 | JsExpr::new(JsExprData::Null, loc.clone(), None) | |
| 525 | } | |
| 518 | match self.statements.pop().unwrap().data { | |
| 519 | JsStatementData::Expression(expr) => expr, | |
| 520 | _ => unreachable!(), | |
| 526 | 521 | } |
| 527 | 522 | } |
| 528 | 523 | _ => JsExpr::new(JsExprData::Null, loc.clone(), None), |
| ... | ... | @@ -536,15 +531,15 @@ impl HoistContext { |
| 536 | 531 | condition: JsExpr, |
| 537 | 532 | did_assign: &mut bool, |
| 538 | 533 | temporary: &TempId, |
| 539 | ) -> Option<JsIf> { | |
| 540 | let yes = self.hoist_block_to_js_block(yes, &temporary.get(&self), did_assign)?; | |
| 534 | ) -> JsIf { | |
| 535 | let yes = self.hoist_block_to_js_block(yes, &temporary.get(&self), did_assign); | |
| 541 | 536 | |
| 542 | 537 | let no = match no { |
| 543 | 538 | Some(BlockOrIf::Block(no)) => Some(JsElse::Block(self.hoist_block_to_js_block( |
| 544 | 539 | no, |
| 545 | 540 | &temporary.get(&self), |
| 546 | 541 | did_assign, |
| 547 | )?)), | |
| 542 | ))), | |
| 548 | 543 | Some(BlockOrIf::If(else_if)) => { |
| 549 | 544 | // If the condition of this block has a side effect, we need to |
| 550 | 545 | // emit a block so that we can assign to the temporary variable. |
| ... | ... | @@ -558,11 +553,12 @@ impl HoistContext { |
| 558 | 553 | condition, |
| 559 | 554 | did_assign, |
| 560 | 555 | temporary, |
| 561 | )?))) | |
| 556 | )))) | |
| 562 | 557 | } else { |
| 563 | 558 | let mut temp_block = self.new_child(); |
| 564 | let condition = | |
| 565 | temp_block.hoist_expression_to_temporary(*else_if.condition.clone())?; | |
| 559 | let condition = temp_block | |
| 560 | .hoist_expression_to_temporary(*else_if.condition.clone()) | |
| 561 | .unwrap(); | |
| 566 | 562 | |
| 567 | 563 | temp_block.add_statement(JsStatement::new( |
| 568 | 564 | JsStatementData::If(self.hoist_js_if( |
| ... | ... | @@ -571,11 +567,13 @@ impl HoistContext { |
| 571 | 567 | condition, |
| 572 | 568 | did_assign, |
| 573 | 569 | temporary, |
| 574 | )?), | |
| 570 | )), | |
| 575 | 571 | Loc::EMPTY, |
| 576 | 572 | None, |
| 577 | 573 | )); |
| 578 | 574 | |
| 575 | tree_shake_statement_list(&mut temp_block.statements); | |
| 576 | ||
| 579 | 577 | Some(JsElse::Block(JsBlock::new( |
| 580 | 578 | temp_block.statements, |
| 581 | 579 | Loc::EMPTY, |
| ... | ... | @@ -585,7 +583,7 @@ impl HoistContext { |
| 585 | 583 | None => None, |
| 586 | 584 | }; |
| 587 | 585 | |
| 588 | Some(JsIf { condition, yes, no }) | |
| 586 | JsIf { condition, yes, no } | |
| 589 | 587 | } |
| 590 | 588 | } |
| 591 | 589 | |
| ... | ... | @@ -612,3 +610,29 @@ pub fn new_js_assignment_expr(expr: JsExpr, target: String, loc: Loc) -> JsExpr |
| 612 | 610 | None, |
| 613 | 611 | ) |
| 614 | 612 | } |
| 613 | ||
| 614 | /// Remove | |
| 615 | /// - Discarded literals | |
| 616 | pub fn tree_shake_statement_list(list: &mut Vec<JsStatement>) { | |
| 617 | let mut set_i = 0; | |
| 618 | for i in 0..list.len() { | |
| 619 | let stmt = list.get(i).unwrap(); | |
| 620 | let should_keep = match &stmt.data { | |
| 621 | JsStatementData::Expression(expr) => !expr.is_literal(), | |
| 622 | JsStatementData::Return(_) => true, | |
| 623 | JsStatementData::Export(_) => true, | |
| 624 | JsStatementData::Declaration(_) => true, | |
| 625 | JsStatementData::Function(_) => true, | |
| 626 | JsStatementData::If(_) => true, | |
| 627 | // | |
| 628 | }; | |
| 629 | ||
| 630 | if should_keep { | |
| 631 | if set_i != i { | |
| 632 | list.swap(i, set_i) | |
| 633 | } | |
| 634 | set_i += 1; | |
| 635 | } | |
| 636 | } | |
| 637 | list.truncate(set_i) | |
| 638 | } |
crates/js_ast/js_ast.rs-7| ... | ... | @@ -176,13 +176,6 @@ impl JsStatement { |
| 176 | 176 | pub fn new(data: JsStatementData, loc: Loc, comment: Option<Comment>) -> Self { |
| 177 | 177 | Self { data, loc, comment } |
| 178 | 178 | } |
| 179 | ||
| 180 | pub(crate) fn is_literal(&self) -> bool { | |
| 181 | match &self.data { | |
| 182 | JsStatementData::Expression(expr) => expr.is_literal(), | |
| 183 | _ => false, | |
| 184 | } | |
| 185 | } | |
| 186 | 179 | } |
| 187 | 180 | |
| 188 | 181 | #[derive(Debug, PartialEq, Clone)] |
crates/parser/parse_block.rs+2-8| ... | ... | @@ -46,10 +46,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 46 | 46 | }, |
| 47 | 47 | Some(Token::ClosedBrace) => { |
| 48 | 48 | p.next(); |
| 49 | if !statement.is_expression() | |
| 50 | && !statement.is_comment() | |
| 51 | && is_statement_like_expression | |
| 52 | { | |
| 49 | if statement.require_semicolon(is_statement_like_expression) { | |
| 53 | 50 | p.is_top_level = was_top_level; |
| 54 | 51 | return Err(p |
| 55 | 52 | .error("Expected semicolon or '}' after statement".to_string()) |
| ... | ... | @@ -63,10 +60,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 63 | 60 | }); |
| 64 | 61 | } |
| 65 | 62 | _ => { |
| 66 | if !statement.is_expression() | |
| 67 | && !statement.is_comment() | |
| 68 | && is_statement_like_expression | |
| 69 | { | |
| 63 | if statement.require_semicolon(is_statement_like_expression) { | |
| 70 | 64 | p.is_top_level = was_top_level; |
| 71 | 65 | return Err(p |
| 72 | 66 | .error("Expected semicolon or '}' after statement".to_string()) |
crates/parser/parse_module.rs+1-1| ... | ... | @@ -24,7 +24,7 @@ pub fn parse_module(p: &mut Parser) -> Result<Module> { |
| 24 | 24 | } |
| 25 | 25 | } |
| 26 | 26 | _ => { |
| 27 | if !statement.is_comment() && is_statement_like_expression { | |
| 27 | if statement.require_semicolon(is_statement_like_expression) { | |
| 28 | 28 | return Err(p |
| 29 | 29 | .error("Expected semicolon after statement".to_string()) |
| 30 | 30 | .into()); |
crates/sema/ast_type_coersion.rs+51-51| ... | ... | @@ -1,54 +1,54 @@ |
| 1 | 1 | // use ast::{BinaryOperator, ExprData, Type}; |
| 2 | 2 | |
| 3 | // impl TypeCoerceable for ExprData { | |
| 4 | // fn to_type(&self) -> Option<Type> { | |
| 5 | // match self { | |
| 6 | // ExprData::Block(block) => { | |
| 7 | // if (block.has_implicit_return) { | |
| 8 | // block.statements.last().unwrap().to_type() | |
| 9 | // } else { | |
| 10 | // Some(Type::Void) | |
| 11 | // } | |
| 12 | // } | |
| 13 | // ExprData::Binary { op, left, right } => { | |
| 14 | // // TODO: this likely has to do further type checking | |
| 15 | // let left_type = left.to_type(); | |
| 16 | // match op { | |
| 17 | // BinaryOperator::Add => left_type, | |
| 18 | // BinaryOperator::Sub => left_type, | |
| 19 | // BinaryOperator::Mul => left_type, | |
| 20 | // BinaryOperator::Div => left_type, | |
| 21 | // BinaryOperator::Pow => left_type, | |
| 22 | // BinaryOperator::Eq => Some(Type::Bool), | |
| 23 | // BinaryOperator::Gt => Some(Type::Bool), | |
| 24 | // BinaryOperator::Lt => Some(Type::Bool), | |
| 25 | // BinaryOperator::Gte => Some(Type::Bool), | |
| 26 | // BinaryOperator::Lte => Some(Type::Bool), | |
| 27 | // BinaryOperator::Lshift => Some(Type::Int), | |
| 28 | // BinaryOperator::Rshift => Some(Type::Int), | |
| 29 | // BinaryOperator::BitAnd => Some(Type::Int), | |
| 30 | // BinaryOperator::BitOr => Some(Type::Int), | |
| 31 | // BinaryOperator::BitXor => Some(Type::Int), | |
| 32 | // BinaryOperator::Mod => Some(Type::Int), | |
| 33 | // BinaryOperator::NotEq => Some(Type::Int), | |
| 3 | impl TypeCoerceable for ExprData { | |
| 4 | fn to_type(&self) -> Option<Type> { | |
| 5 | match self { | |
| 6 | ExprData::Block(block) => { | |
| 7 | if (block.has_implicit_return) { | |
| 8 | block.statements.last().unwrap().to_type() | |
| 9 | } else { | |
| 10 | Some(Type::Void) | |
| 11 | } | |
| 12 | } | |
| 13 | ExprData::Binary { op, left, right } => { | |
| 14 | // TODO: this likely has to do further type checking | |
| 15 | let left_type = left.to_type(); | |
| 16 | match op { | |
| 17 | BinaryOperator::Add => left_type, | |
| 18 | BinaryOperator::Sub => left_type, | |
| 19 | BinaryOperator::Mul => left_type, | |
| 20 | BinaryOperator::Div => left_type, | |
| 21 | BinaryOperator::Pow => left_type, | |
| 22 | BinaryOperator::Eq => Some(Type::Bool), | |
| 23 | BinaryOperator::Gt => Some(Type::Bool), | |
| 24 | BinaryOperator::Lt => Some(Type::Bool), | |
| 25 | BinaryOperator::Gte => Some(Type::Bool), | |
| 26 | BinaryOperator::Lte => Some(Type::Bool), | |
| 27 | BinaryOperator::Lshift => Some(Type::Int), | |
| 28 | BinaryOperator::Rshift => Some(Type::Int), | |
| 29 | BinaryOperator::BitAnd => Some(Type::Int), | |
| 30 | BinaryOperator::BitOr => Some(Type::Int), | |
| 31 | BinaryOperator::BitXor => Some(Type::Int), | |
| 32 | BinaryOperator::Mod => Some(Type::Int), | |
| 33 | BinaryOperator::NotEq => Some(Type::Int), | |
| 34 | 34 | |
| 35 | // BinaryOperator::And => Some(Type::Bool), | |
| 36 | // BinaryOperator::Or => Some(Type::Bool), | |
| 37 | // } | |
| 38 | // } | |
| 39 | // ExprData::Unary { op, value } => todo!(), | |
| 40 | // ExprData::Function { | |
| 41 | // name, | |
| 42 | // params, | |
| 43 | // return_type, | |
| 44 | // block, | |
| 45 | // } => Some(return_type), | |
| 46 | // ExprData::Call { base, arguments } => None, // oh shit. yeah this trait isn't implementable | |
| 47 | // ExprData::Builtin { id, arguments } => todo!(), | |
| 48 | // ExprData::If(_) => todo!(), | |
| 49 | // ExprData::Return(_) => todo!(), | |
| 50 | // ExprData::Literal(_) => todo!(), | |
| 51 | // ExprData::Identifier(_) => todo!(), | |
| 52 | // } | |
| 53 | // } | |
| 54 | // } | |
| 35 | BinaryOperator::And => Some(Type::Bool), | |
| 36 | BinaryOperator::Or => Some(Type::Bool), | |
| 37 | } | |
| 38 | } | |
| 39 | ExprData::Unary { op, value } => todo!(), | |
| 40 | ExprData::Function { | |
| 41 | name, | |
| 42 | params, | |
| 43 | return_type, | |
| 44 | block, | |
| 45 | } => Some(return_type), | |
| 46 | ExprData::Call { base, arguments } => None, // oh shit. yeah this trait isn't implementable | |
| 47 | ExprData::Builtin { id, arguments } => todo!(), | |
| 48 | ExprData::If(_) => todo!(), | |
| 49 | ExprData::Return(_) => todo!(), | |
| 50 | ExprData::Literal(_) => todo!(), | |
| 51 | ExprData::Identifier(_) => todo!(), | |
| 52 | } | |
| 53 | } | |
| 54 | } |
crates/sema/sema_scope.rs createdcrates/tokenizer/tokenizer.rs+1-1| ... | ... | @@ -79,7 +79,7 @@ pub enum Token { |
| 79 | 79 | impl Token { |
| 80 | 80 | pub fn is_start_of_statement_like_expression(&self) -> bool { |
| 81 | 81 | match &self { |
| 82 | Token::If | Token::OpenBrace | Token::Fn => false, | |
| 82 | Token::If | Token::OpenBrace => false, | |
| 83 | 83 | _ => true, |
| 84 | 84 | } |
| 85 | 85 | } |