| author | |
| committer | |
| log | c4d73c341e177bab25226dc3f9676b455e9f2b62 |
| tree | 3b855158c70b71ea2895b94f21ccd7718e7847b9 |
| parent | fec1b3637ceb5db50f87fa1e78e0fa968191bf12 |
| parent | d95ececd7b3f2344eae046bc983420081d156fc1 |
fix various `return` and `pub` related issues12 files changed, 101 insertions(+), 53 deletions(-)
crates/ast/ast.rs+1-1| ... | ... | @@ -162,7 +162,7 @@ pub enum ExprData { |
| 162 | 162 | arguments: Vec<Expr>, |
| 163 | 163 | }, |
| 164 | 164 | If(If), |
| 165 | Return(Box<Expr>), | |
| 165 | Return(Option<Box<Expr>>), | |
| 166 | 166 | Literal(Literal), |
| 167 | 167 | Identifier(String), |
| 168 | 168 | } |
crates/js_ast/convert/hoist_block_to_js_block.rs+11-9| ... | ... | @@ -52,16 +52,18 @@ pub fn hoist_block_to_js_block( |
| 52 | 52 | |
| 53 | 53 | // If the last statement is an expression, we need |
| 54 | 54 | // to assign to the temporary variable. |
| 55 | match child.take_last_statement_if_expr() { | |
| 56 | Some(expr) => { | |
| 57 | *did_assign = true; | |
| 58 | child.add_statement(new_js_assignment_statement( | |
| 59 | expr, | |
| 60 | target.get(ctx), | |
| 61 | loc.clone(), | |
| 62 | )); | |
| 55 | if block.has_implicit_return { | |
| 56 | match child.take_last_statement_if_expr() { | |
| 57 | Some(expr) => { | |
| 58 | *did_assign = true; | |
| 59 | child.add_statement(new_js_assignment_statement( | |
| 60 | expr, | |
| 61 | target.get(ctx), | |
| 62 | loc.clone(), | |
| 63 | )); | |
| 64 | } | |
| 65 | _ => {} | |
| 63 | 66 | } |
| 64 | _ => {} | |
| 65 | 67 | } |
| 66 | 68 | |
| 67 | 69 | child.into() |
crates/js_ast/convert/hoist_expr.rs+5-2| ... | ... | @@ -94,9 +94,12 @@ pub fn hoist_expr_contents(ctx: &mut BlockConvertContext, expr: Expr) -> Option< |
| 94 | 94 | |
| 95 | 95 | ExprData::If(_) => hoist_if_expr(ctx, expr), |
| 96 | 96 | |
| 97 | ExprData::Return(expr) => { | |
| 97 | ExprData::Return(maybe_expr) => { | |
| 98 | 98 | let loc = expr.loc.clone(); |
| 99 | let return_value = hoist_expr_if_needed(ctx, *expr)?; | |
| 99 | let return_value = match maybe_expr { | |
| 100 | Some(expr) => Some(hoist_expr_if_needed(ctx, *expr)?), | |
| 101 | None => None, | |
| 102 | }; | |
| 100 | 103 | ctx.add_statement(JsStatement::new( |
| 101 | 104 | JsStatementData::Return(return_value), |
| 102 | 105 | loc, |
crates/js_ast/convert/process_function.rs+9-7| ... | ... | @@ -30,18 +30,20 @@ pub fn process_function_to_arrow( |
| 30 | 30 | }; |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | if let Some(expr) = child.take_last_statement_if_expr() { | |
| 34 | child.add_statement(JsStatement::new( | |
| 35 | JsStatementData::Return(expr), | |
| 36 | loc.clone(), | |
| 37 | None, | |
| 38 | )); | |
| 33 | if block.has_implicit_return { | |
| 34 | if let Some(expr) = child.take_last_statement_if_expr() { | |
| 35 | child.add_statement(JsStatement::new( | |
| 36 | JsStatementData::Return(Some(expr)), | |
| 37 | loc.clone(), | |
| 38 | None, | |
| 39 | )); | |
| 40 | } | |
| 39 | 41 | } |
| 40 | 42 | } |
| 41 | 43 | _ => { |
| 42 | 44 | if let Some(expr) = hoist_expr_if_needed(&mut child, body) { |
| 43 | 45 | child.add_statement(JsStatement::new( |
| 44 | JsStatementData::Return(expr), | |
| 46 | JsStatementData::Return(Some(expr)), | |
| 45 | 47 | loc.clone(), |
| 46 | 48 | None, |
| 47 | 49 | )); |
crates/js_ast/convert/process_statement.rs+26-13| ... | ... | @@ -22,18 +22,24 @@ pub fn process_statement(ctx: &mut BlockConvertContext, statement: Statement) -> |
| 22 | 22 | // and `a` will be looked up before it is re-declared. |
| 23 | 23 | // we will also return in the noreturn case |
| 24 | 24 | let init = hoist_expr_contents(ctx, decl.init)?; |
| 25 | ||
| 26 | 25 | let name = ctx.declare_identifier(decl.name); |
| 27 | 26 | |
| 28 | ctx.add_statement(JsStatement::new( | |
| 29 | JsStatementData::Declaration(JSDeclaration { | |
| 30 | name: name.get(&ctx), | |
| 31 | init: Some(init), | |
| 32 | is_constant: decl.is_constant, | |
| 33 | }), | |
| 27 | let decl_data = JSDeclaration { | |
| 28 | name: name.get(&ctx), | |
| 29 | init: Some(init), | |
| 30 | is_constant: decl.is_constant, | |
| 31 | }; | |
| 32 | let stmt = JsStatement::new( | |
| 33 | if decl.is_pub { | |
| 34 | JsStatementData::Export(JsExport::Declaration(Box::new(decl_data))) | |
| 35 | } else { | |
| 36 | JsStatementData::Declaration(decl_data) | |
| 37 | }, | |
| 34 | 38 | decl.loc, |
| 35 | 39 | decl.comment.map(|x| x.into()), |
| 36 | )); | |
| 40 | ); | |
| 41 | ||
| 42 | ctx.add_statement(stmt); | |
| 37 | 43 | |
| 38 | 44 | Some(()) |
| 39 | 45 | } |
| ... | ... | @@ -56,6 +62,7 @@ pub fn process_statement(ctx: &mut BlockConvertContext, statement: Statement) -> |
| 56 | 62 | body, |
| 57 | 63 | loc, |
| 58 | 64 | comment, |
| 65 | is_pub, | |
| 59 | 66 | .. |
| 60 | 67 | }) => { |
| 61 | 68 | let name = ctx.declare_identifier(name); |
| ... | ... | @@ -68,12 +75,18 @@ pub fn process_statement(ctx: &mut BlockConvertContext, statement: Statement) -> |
| 68 | 75 | None, |
| 69 | 76 | )); |
| 70 | 77 | |
| 78 | let decl_data = JSDeclaration { | |
| 79 | name: name.get(&ctx), | |
| 80 | init: processed, | |
| 81 | is_constant: true, | |
| 82 | }; | |
| 83 | ||
| 71 | 84 | ctx.add_statement(JsStatement::new( |
| 72 | JsStatementData::Declaration(JSDeclaration { | |
| 73 | name: name.get(&ctx), | |
| 74 | init: processed, | |
| 75 | is_constant: true, | |
| 76 | }), | |
| 85 | if is_pub { | |
| 86 | JsStatementData::Export(JsExport::Declaration(Box::new(decl_data))) | |
| 87 | } else { | |
| 88 | JsStatementData::Declaration(decl_data) | |
| 89 | }, | |
| 77 | 90 | loc, |
| 78 | 91 | comment.map(|x| x.into()), |
| 79 | 92 | )); |
crates/js_ast/js_ast.rs+1-1| ... | ... | @@ -109,7 +109,7 @@ pub enum JsElse { |
| 109 | 109 | #[derive(Debug, PartialEq, Clone)] |
| 110 | 110 | pub enum JsStatementData { |
| 111 | 111 | Expression(JsExpr), |
| 112 | Return(JsExpr), | |
| 112 | Return(Option<JsExpr>), | |
| 113 | 113 | Export(JsExport), |
| 114 | 114 | Declaration(JSDeclaration), |
| 115 | 115 | Function(JSFunctionDeclaration), |
crates/js_printer/lib.rs+7-2| ... | ... | @@ -53,8 +53,13 @@ impl Print for JsStatement { |
| 53 | 53 | write!(w, ";") |
| 54 | 54 | } |
| 55 | 55 | js_ast::JsStatementData::Return(expr) => { |
| 56 | write!(w, "return ")?; | |
| 57 | expr.print(w) | |
| 56 | write!(w, "return")?; | |
| 57 | if let Some(expr) = expr { | |
| 58 | write!(w, " ")?; | |
| 59 | expr.print(w)?; | |
| 60 | } | |
| 61 | write!(w, ";")?; | |
| 62 | Ok(()) | |
| 58 | 63 | } |
| 59 | 64 | js_ast::JsStatementData::Export(export) => export.print(w), |
| 60 | 65 | js_ast::JsStatementData::Declaration(decl) => decl.print(w), |
crates/parser/parse_block.rs+8| ... | ... | @@ -12,6 +12,9 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 12 | 12 | |
| 13 | 13 | let mut statements = Vec::new(); |
| 14 | 14 | |
| 15 | let was_top_level = p.is_top_level; | |
| 16 | p.is_top_level = false; | |
| 17 | ||
| 15 | 18 | 'outer: loop { |
| 16 | 19 | let is_statement_like_expression = p |
| 17 | 20 | .peek() |
| ... | ... | @@ -20,6 +23,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 20 | 23 | let statement = match parse_statement(p) { |
| 21 | 24 | Ok(statement) => statement, |
| 22 | 25 | Err(err) => { |
| 26 | p.is_top_level = was_top_level; | |
| 23 | 27 | return Err(err); |
| 24 | 28 | } |
| 25 | 29 | }; |
| ... | ... | @@ -31,6 +35,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 31 | 35 | match p.peek() { |
| 32 | 36 | Some(Token::ClosedBrace) => { |
| 33 | 37 | p.next(); |
| 38 | p.is_top_level = was_top_level; | |
| 34 | 39 | return Ok(Block { |
| 35 | 40 | statements, |
| 36 | 41 | has_implicit_return: false, |
| ... | ... | @@ -41,6 +46,8 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 41 | 46 | }, |
| 42 | 47 | Some(Token::ClosedBrace) => { |
| 43 | 48 | p.next(); |
| 49 | p.is_top_level = was_top_level; | |
| 50 | ||
| 44 | 51 | if !statement.is_expression() { |
| 45 | 52 | return Err(p |
| 46 | 53 | .error("Expected semicolon or '}' after statement".to_string()) |
| ... | ... | @@ -54,6 +61,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> { |
| 54 | 61 | } |
| 55 | 62 | _ => { |
| 56 | 63 | if statement.require_semicolon(is_statement_like_expression) { |
| 64 | p.is_top_level = was_top_level; | |
| 57 | 65 | return Err(p |
| 58 | 66 | .error("Expected semicolon or '}' after statement".to_string()) |
| 59 | 67 | .into()); |
crates/parser/parse_expr_basic.rs+17-10| ... | ... | @@ -7,7 +7,7 @@ use crate::{ |
| 7 | 7 | parse_expr_if::parse_expr_if, Parser, |
| 8 | 8 | }; |
| 9 | 9 | |
| 10 | pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> { | |
| 10 | pub fn maybe_parse_expr_basic(p: &mut Parser) -> Result<Option<Expr>> { | |
| 11 | 11 | let loc = p.loc(); |
| 12 | 12 | let comments = p.take_comments(); |
| 13 | 13 | let mut expr = match p.peek() { |
| ... | ... | @@ -63,17 +63,15 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> { |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | Some(Token::Return) => { |
| 66 | if p.allow_return { | |
| 66 | if p.is_top_level { | |
| 67 | 67 | return Err(p |
| 68 | 68 | .error("'return' cannot be used at top level".to_string()) |
| 69 | 69 | .into()); |
| 70 | 70 | } else { |
| 71 | 71 | _ = p.next(); |
| 72 | return Ok(Expr::new( | |
| 73 | ExprData::Return(Box::new(parse_expr(p)?)), | |
| 74 | loc, | |
| 75 | comments, | |
| 76 | )); | |
| 72 | let inner = maybe_parse_expr_basic(p)?.map(Box::new); | |
| 73 | ||
| 74 | return Ok(Some(Expr::new(ExprData::Return(inner), loc, comments))); | |
| 77 | 75 | } |
| 78 | 76 | } |
| 79 | 77 | |
| ... | ... | @@ -111,8 +109,7 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> { |
| 111 | 109 | ) |
| 112 | 110 | } |
| 113 | 111 | |
| 114 | Some(t) => return Err(p.error(format!("Unexpected '{}'", t)).into()), | |
| 115 | None => return Err(p.error("Unexpected end of file".to_string()).into()), | |
| 112 | Some(_) | None => return Ok(None), | |
| 116 | 113 | }; |
| 117 | 114 | |
| 118 | 115 | // Parse function calls and field accesses |
| ... | ... | @@ -176,5 +173,15 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> { |
| 176 | 173 | } |
| 177 | 174 | } |
| 178 | 175 | |
| 179 | Ok(expr) | |
| 176 | Ok(Some(expr)) | |
| 177 | } | |
| 178 | ||
| 179 | pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> { | |
| 180 | match maybe_parse_expr_basic(p)? { | |
| 181 | Some(expr) => Ok(expr), | |
| 182 | None => match p.peek() { | |
| 183 | Some(token) => Err(p.error(format!("Unexpected '{}'", token)).into()), | |
| 184 | None => Err(p.error("Unexpected end of file".to_string()).into()), | |
| 185 | }, | |
| 186 | } | |
| 180 | 187 | } |
crates/parser/parse_fn_declaration.rs+3-3| ... | ... | @@ -84,8 +84,8 @@ pub fn parse_fn_declaration( |
| 84 | 84 | _ => Type::Infer, |
| 85 | 85 | }; |
| 86 | 86 | |
| 87 | let was_allow_return = p.allow_return; | |
| 88 | p.allow_return = true; | |
| 87 | let was_in_fn = p.is_in_fn; | |
| 88 | p.is_in_fn = true; | |
| 89 | 89 | |
| 90 | 90 | let body = match p.peek() { |
| 91 | 91 | Some(Token::OpenBrace) => { |
| ... | ... | @@ -108,7 +108,7 @@ pub fn parse_fn_declaration( |
| 108 | 108 | } |
| 109 | 109 | }; |
| 110 | 110 | |
| 111 | p.allow_return = was_allow_return; | |
| 111 | p.is_in_fn = was_in_fn; | |
| 112 | 112 | |
| 113 | 113 | Ok(FnDecl { |
| 114 | 114 | name, |
crates/parser/parse_statement.rs+5| ... | ... | @@ -26,6 +26,11 @@ pub fn parse_statement(p: &mut Parser) -> Result<Statement> { |
| 26 | 26 | match p.peek() { |
| 27 | 27 | Some(Token::DocComment(_)) => unreachable!(), |
| 28 | 28 | Some(Token::Pub) => { |
| 29 | if !p.is_top_level { | |
| 30 | return Err(p | |
| 31 | .error("Public declarations are not allowed inside of functions".to_string()) | |
| 32 | .into()); | |
| 33 | } | |
| 29 | 34 | _ = p.next(); |
| 30 | 35 | match p.peek() { |
| 31 | 36 | Some(Token::Var) | Some(Token::Let) => Ok(Statement::VarDecl(parse_declaration( |
crates/parser/parser.rs+8-5| ... | ... | @@ -52,8 +52,12 @@ pub struct Parser { |
| 52 | 52 | tokens: Vec<Token>, |
| 53 | 53 | cursor: usize, |
| 54 | 54 | |
| 55 | /// Whether or not the parser is at the top level | |
| 56 | pub allow_return: bool, | |
| 55 | /// Whether or not the parser is at the top level. | |
| 56 | /// Currently controls the validity of `pub` keywords. | |
| 57 | pub is_top_level: bool, | |
| 58 | /// Whether or not the parser is inside a function. | |
| 59 | /// Controls the validity of the `return` keyword. | |
| 60 | pub is_in_fn: bool, | |
| 57 | 61 | |
| 58 | 62 | comments: Vec<String>, |
| 59 | 63 | } |
| ... | ... | @@ -63,7 +67,8 @@ impl Parser { |
| 63 | 67 | Parser { |
| 64 | 68 | tokens, |
| 65 | 69 | cursor: 0, |
| 66 | allow_return: false, | |
| 70 | is_top_level: true, | |
| 71 | is_in_fn: false, | |
| 67 | 72 | comments: vec![], |
| 68 | 73 | } |
| 69 | 74 | } |
| ... | ... | @@ -72,8 +77,6 @@ impl Parser { |
| 72 | 77 | self.cursor == self.tokens.len() |
| 73 | 78 | } |
| 74 | 79 | |
| 75 | // TODO(@paperdave): fix parser token stuff. its very messy rn | |
| 76 | ||
| 77 | 80 | pub fn next(&mut self) -> Option<Token> { |
| 78 | 81 | if self.cursor == self.tokens.len() { |
| 79 | 82 | return None; |