authorgravatar for 11367844+lino-levan@users.noreply.github.comLino Le Van <11367844+lino-levan@users.noreply.github.com> 2024-02-02 05:43:07+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-02 05:43:07+01:00
log4d577446b981a403ff6131f0b15451f125316af0
tree3b855158c70b71ea2895b94f21ccd7718e7847b9
parent1aaca7fa9509918bee08db12d51f2aef670b69fc
parent3bff5162173309e7eb6aab40e0fa93c689f99468
signature Signed by PGP key B5690EEEBB952194

Merge pull request #7 from lino-levan/dave/issue6

fix various `return` and `pub` related issues

12 files changed, 101 insertions(+), 53 deletions(-)

crates/ast/ast.rs+1-1
......@@ -162,7 +162,7 @@ pub enum ExprData {
162162 arguments: Vec<Expr>,
163163 },
164164 If(If),
165 Return(Box<Expr>),
165 Return(Option<Box<Expr>>),
166166 Literal(Literal),
167167 Identifier(String),
168168}
crates/js_ast/convert/hoist_block_to_js_block.rs+11-9
......@@ -52,16 +52,18 @@ pub fn hoist_block_to_js_block(
5252
5353 // If the last statement is an expression, we need
5454 // 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 _ => {}
6366 }
64 _ => {}
6567 }
6668
6769 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<
9494
9595 ExprData::If(_) => hoist_if_expr(ctx, expr),
9696
97 ExprData::Return(expr) => {
97 ExprData::Return(maybe_expr) => {
9898 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 };
100103 ctx.add_statement(JsStatement::new(
101104 JsStatementData::Return(return_value),
102105 loc,
crates/js_ast/convert/process_function.rs+9-7
......@@ -30,18 +30,20 @@ pub fn process_function_to_arrow(
3030 };
3131 }
3232
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 }
3941 }
4042 }
4143 _ => {
4244 if let Some(expr) = hoist_expr_if_needed(&mut child, body) {
4345 child.add_statement(JsStatement::new(
44 JsStatementData::Return(expr),
46 JsStatementData::Return(Some(expr)),
4547 loc.clone(),
4648 None,
4749 ));
crates/js_ast/convert/process_statement.rs+26-13
......@@ -22,18 +22,24 @@ pub fn process_statement(ctx: &mut BlockConvertContext, statement: Statement) ->
2222 // and `a` will be looked up before it is re-declared.
2323 // we will also return in the noreturn case
2424 let init = hoist_expr_contents(ctx, decl.init)?;
25
2625 let name = ctx.declare_identifier(decl.name);
2726
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 },
3438 decl.loc,
3539 decl.comment.map(|x| x.into()),
36 ));
40 );
41
42 ctx.add_statement(stmt);
3743
3844 Some(())
3945 }
......@@ -56,6 +62,7 @@ pub fn process_statement(ctx: &mut BlockConvertContext, statement: Statement) ->
5662 body,
5763 loc,
5864 comment,
65 is_pub,
5966 ..
6067 }) => {
6168 let name = ctx.declare_identifier(name);
......@@ -68,12 +75,18 @@ pub fn process_statement(ctx: &mut BlockConvertContext, statement: Statement) ->
6875 None,
6976 ));
7077
78 let decl_data = JSDeclaration {
79 name: name.get(&ctx),
80 init: processed,
81 is_constant: true,
82 };
83
7184 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 },
7790 loc,
7891 comment.map(|x| x.into()),
7992 ));
crates/js_ast/js_ast.rs+1-1
......@@ -109,7 +109,7 @@ pub enum JsElse {
109109#[derive(Debug, PartialEq, Clone)]
110110pub enum JsStatementData {
111111 Expression(JsExpr),
112 Return(JsExpr),
112 Return(Option<JsExpr>),
113113 Export(JsExport),
114114 Declaration(JSDeclaration),
115115 Function(JSFunctionDeclaration),
crates/js_printer/lib.rs+7-2
......@@ -53,8 +53,13 @@ impl Print for JsStatement {
5353 write!(w, ";")
5454 }
5555 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(())
5863 }
5964 js_ast::JsStatementData::Export(export) => export.print(w),
6065 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> {
1212
1313 let mut statements = Vec::new();
1414
15 let was_top_level = p.is_top_level;
16 p.is_top_level = false;
17
1518 'outer: loop {
1619 let is_statement_like_expression = p
1720 .peek()
......@@ -20,6 +23,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> {
2023 let statement = match parse_statement(p) {
2124 Ok(statement) => statement,
2225 Err(err) => {
26 p.is_top_level = was_top_level;
2327 return Err(err);
2428 }
2529 };
......@@ -31,6 +35,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> {
3135 match p.peek() {
3236 Some(Token::ClosedBrace) => {
3337 p.next();
38 p.is_top_level = was_top_level;
3439 return Ok(Block {
3540 statements,
3641 has_implicit_return: false,
......@@ -41,6 +46,8 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> {
4146 },
4247 Some(Token::ClosedBrace) => {
4348 p.next();
49 p.is_top_level = was_top_level;
50
4451 if !statement.is_expression() {
4552 return Err(p
4653 .error("Expected semicolon or '}' after statement".to_string())
......@@ -54,6 +61,7 @@ pub fn parse_block(p: &mut Parser) -> Result<Block> {
5461 }
5562 _ => {
5663 if statement.require_semicolon(is_statement_like_expression) {
64 p.is_top_level = was_top_level;
5765 return Err(p
5866 .error("Expected semicolon or '}' after statement".to_string())
5967 .into());
crates/parser/parse_expr_basic.rs+17-10
......@@ -7,7 +7,7 @@ use crate::{
77 parse_expr_if::parse_expr_if, Parser,
88};
99
10pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
10pub fn maybe_parse_expr_basic(p: &mut Parser) -> Result<Option<Expr>> {
1111 let loc = p.loc();
1212 let comments = p.take_comments();
1313 let mut expr = match p.peek() {
......@@ -63,17 +63,15 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
6363 }
6464
6565 Some(Token::Return) => {
66 if p.allow_return {
66 if p.is_top_level {
6767 return Err(p
6868 .error("'return' cannot be used at top level".to_string())
6969 .into());
7070 } else {
7171 _ = 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)));
7775 }
7876 }
7977
......@@ -111,8 +109,7 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
111109 )
112110 }
113111
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),
116113 };
117114
118115 // Parse function calls and field accesses
......@@ -176,5 +173,15 @@ pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {
176173 }
177174 }
178175
179 Ok(expr)
176 Ok(Some(expr))
177}
178
179pub 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 }
180187}
crates/parser/parse_fn_declaration.rs+3-3
......@@ -84,8 +84,8 @@ pub fn parse_fn_declaration(
8484 _ => Type::Infer,
8585 };
8686
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;
8989
9090 let body = match p.peek() {
9191 Some(Token::OpenBrace) => {
......@@ -108,7 +108,7 @@ pub fn parse_fn_declaration(
108108 }
109109 };
110110
111 p.allow_return = was_allow_return;
111 p.is_in_fn = was_in_fn;
112112
113113 Ok(FnDecl {
114114 name,
crates/parser/parse_statement.rs+5
......@@ -26,6 +26,11 @@ pub fn parse_statement(p: &mut Parser) -> Result<Statement> {
2626 match p.peek() {
2727 Some(Token::DocComment(_)) => unreachable!(),
2828 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 }
2934 _ = p.next();
3035 match p.peek() {
3136 Some(Token::Var) | Some(Token::Let) => Ok(Statement::VarDecl(parse_declaration(
crates/parser/parser.rs+8-5
......@@ -52,8 +52,12 @@ pub struct Parser {
5252 tokens: Vec<Token>,
5353 cursor: usize,
5454
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,
5761
5862 comments: Vec<String>,
5963}
......@@ -63,7 +67,8 @@ impl Parser {
6367 Parser {
6468 tokens,
6569 cursor: 0,
66 allow_return: false,
70 is_top_level: true,
71 is_in_fn: false,
6772 comments: vec![],
6873 }
6974 }
......@@ -72,8 +77,6 @@ impl Parser {
7277 self.cursor == self.tokens.len()
7378 }
7479
75 // TODO(@paperdave): fix parser token stuff. its very messy rn
76
7780 pub fn next(&mut self) -> Option<Token> {
7881 if self.cursor == self.tokens.len() {
7982 return None;