authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-02-01 19:59:47-08:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-02-01 19:59:47-08:00
log3bff5162173309e7eb6aab40e0fa93c689f99468
tree3b855158c70b71ea2895b94f21ccd7718e7847b9
parent1aaca7fa9509918bee08db12d51f2aef670b69fc
signature Commit is signed but in an unrecognized format.

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 {...@@ -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(
5252
53 // If the last statement is an expression, we need53 // 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 }
6668
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<
9494
95 ExprData::If(_) => hoist_if_expr(ctx, expr),95 ExprData::If(_) => hoist_if_expr(ctx, expr),
9696
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 }
3232
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 case23 // 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);
2726
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);
3743
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 ));
7077
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)]
110pub enum JsStatementData {110pub 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> {
1212
13 let mut statements = Vec::new();13 let mut statements = Vec::new();
1414
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 = p19 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(p52 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(p65 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};
99
10pub fn parse_expr_basic(p: &mut Parser) -> Result<Expr> {10pub 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 }
6464
65 Some(Token::Return) => {65 Some(Token::Return) => {
66 if p.allow_return {66 if p.is_top_level {
67 return Err(p67 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 }
7977
...@@ -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 }
113111
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 };
117114
118 // Parse function calls and field accesses115 // 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 }
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 }
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 };
8686
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;
8989
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 };
110110
111 p.allow_return = was_allow_return;111 p.is_in_fn = was_in_fn;
112112
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,
5454
55 /// Whether or not the parser is at the top level55 /// 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,
5761
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 }
7479
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;