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