diff --git a/examples/tests/valid/optionals.w b/examples/tests/valid/optionals.w index 3771351ec5b..0ca09eb0423 100644 --- a/examples/tests/valid/optionals.w +++ b/examples/tests/valid/optionals.w @@ -58,6 +58,13 @@ let tryParseName = (fullName: str): Name? => { }; }; +// if lets reassignable +let a: num? = 1; +if let var z = a { + assert(z == 1); + z = 2; + assert(z == 2); +} // Nested if lets if let parsedName = tryParseName("Good Name") { diff --git a/libs/tree-sitter-wing/grammar.js b/libs/tree-sitter-wing/grammar.js index 91b74e5eacf..c9d8ae40ad5 100644 --- a/libs/tree-sitter-wing/grammar.js +++ b/libs/tree-sitter-wing/grammar.js @@ -274,8 +274,8 @@ module.exports = grammar({ if_let_statement: ($) => seq( - // TODO: support "if let var" "if let", + optional(field("reassignable", $.reassignable)), field("name", $.identifier), "=", field("value", $.expression), diff --git a/libs/tree-sitter-wing/test/corpus/statements/statements.txt b/libs/tree-sitter-wing/test/corpus/statements/statements.txt index 6d8f23e0e40..db2456efda2 100644 --- a/libs/tree-sitter-wing/test/corpus/statements/statements.txt +++ b/libs/tree-sitter-wing/test/corpus/statements/statements.txt @@ -401,6 +401,22 @@ if let x = y {} else {} block: (block) else_block: (block))) +================================================================================ +If Let Var +================================================================================ + +if let var x = y {} + +-------------------------------------------------------------------------------- + +(source + (if_let_statement + reassignable: (reassignable) + name: (identifier) + value: (reference + (reference_identifier)) + block: (block))) + ================================================================================ Debug symbol env ================================================================================ diff --git a/libs/wingc/src/ast.rs b/libs/wingc/src/ast.rs index 5b4b0690f0f..cb3b13fcf42 100644 --- a/libs/wingc/src/ast.rs +++ b/libs/wingc/src/ast.rs @@ -451,6 +451,7 @@ pub enum StmtKind { statements: Scope, }, IfLet { + reassignable: bool, var_name: Symbol, value: Expr, statements: Scope, diff --git a/libs/wingc/src/fold.rs b/libs/wingc/src/fold.rs index 40842424243..c424fc50f31 100644 --- a/libs/wingc/src/fold.rs +++ b/libs/wingc/src/fold.rs @@ -116,11 +116,13 @@ where StmtKind::IfLet { value, statements, + reassignable, var_name, else_statements, } => StmtKind::IfLet { value: f.fold_expr(value), statements: f.fold_scope(statements), + reassignable, var_name: f.fold_symbol(var_name), else_statements: else_statements.map(|statements| f.fold_scope(statements)), }, diff --git a/libs/wingc/src/jsify.rs b/libs/wingc/src/jsify.rs index 4216affb148..ef0eb9798a3 100644 --- a/libs/wingc/src/jsify.rs +++ b/libs/wingc/src/jsify.rs @@ -853,6 +853,7 @@ impl<'a> JSifier<'a> { StmtKind::Break => CodeMaker::one_line("break;"), StmtKind::Continue => CodeMaker::one_line("continue;"), StmtKind::IfLet { + reassignable, value, statements, var_name, @@ -894,7 +895,11 @@ impl<'a> JSifier<'a> { self.jsify_expression(value, ctx) )); code.open(format!("if ({if_let_value} != undefined) {{")); - code.line(format!("const {} = {};", var_name, if_let_value)); + if *reassignable { + code.line(format!("let {} = {};", var_name, if_let_value)); + } else { + code.line(format!("const {} = {};", var_name, if_let_value)); + } code.add_code(self.jsify_scope_body(statements, ctx)); code.close("}"); diff --git a/libs/wingc/src/parser.rs b/libs/wingc/src/parser.rs index 75fdea74a93..ab3889608e4 100644 --- a/libs/wingc/src/parser.rs +++ b/libs/wingc/src/parser.rs @@ -568,6 +568,7 @@ impl<'s> Parser<'s> { fn build_if_let_statement(&self, statement_node: &Node, phase: Phase) -> DiagnosticResult { let if_block = self.build_scope(&statement_node.child_by_field_name("block").unwrap(), phase); + let reassignable = statement_node.child_by_field_name("reassignable").is_some(); let value = self.build_expression(&statement_node.child_by_field_name("value").unwrap(), phase)?; let name = self.check_reserved_symbol(&statement_node.child_by_field_name("name").unwrap())?; let else_block = if let Some(else_block) = statement_node.child_by_field_name("else_block") { @@ -577,6 +578,7 @@ impl<'s> Parser<'s> { }; Ok(StmtKind::IfLet { var_name: name, + reassignable, value, statements: if_block, else_statements: else_block, diff --git a/libs/wingc/src/type_check.rs b/libs/wingc/src/type_check.rs index 2bdd1036546..96fae56336d 100644 --- a/libs/wingc/src/type_check.rs +++ b/libs/wingc/src/type_check.rs @@ -2808,6 +2808,7 @@ impl<'a> TypeChecker<'a> { StmtKind::IfLet { value, statements, + reassignable, var_name, else_statements, } => { @@ -2838,7 +2839,7 @@ impl<'a> TypeChecker<'a> { // Add the variable to if block scope match stmt_env.define( var_name, - SymbolKind::make_free_variable(var_name.clone(), var_type, false, env.phase), + SymbolKind::make_free_variable(var_name.clone(), var_type, *reassignable, env.phase), StatementIdx::Top, ) { Err(type_error) => { diff --git a/libs/wingc/src/visit.rs b/libs/wingc/src/visit.rs index fa09a8149c5..9121219f176 100644 --- a/libs/wingc/src/visit.rs +++ b/libs/wingc/src/visit.rs @@ -134,6 +134,7 @@ where StmtKind::IfLet { value, statements, + reassignable: _, var_name, else_statements, } => { diff --git a/tools/hangar/__snapshots__/test_corpus/valid/optionals.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/optionals.w_compile_tf-aws.md index ad04fccacd1..738f4856944 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/optionals.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/optionals.w_compile_tf-aws.md @@ -481,6 +481,16 @@ class $Root extends $stdlib.std.Resource { } return ({"first": (parts.at(0)),"last": (parts.at(1))}); }); + const a = 1; + { + const $IF_LET_VALUE = a; + if ($IF_LET_VALUE != undefined) { + let z = $IF_LET_VALUE; + {((cond) => {if (!cond) throw new Error("assertion failed: z == 1")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(z,1)))}; + z = 2; + {((cond) => {if (!cond) throw new Error("assertion failed: z == 2")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(z,2)))}; + } + } { const $IF_LET_VALUE = (tryParseName("Good Name")); if ($IF_LET_VALUE != undefined) {