From daa5853a60ba6bd3bfacfbdb3b6077433c494ae6 Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Thu, 10 Oct 2024 17:27:43 -0400 Subject: [PATCH 1/2] Implement translation for variable pinning --- parser/prism/Translator.cc | 11 +- ...case_match_variable_binding.parse-tree.exp | 444 ++++++++++-------- .../case_match_variable_binding.rb | 14 +- 3 files changed, 272 insertions(+), 197 deletions(-) diff --git a/parser/prism/Translator.cc b/parser/prism/Translator.cc index 7f29cdc5cbc..0559d737c60 100644 --- a/parser/prism/Translator.cc +++ b/parser/prism/Translator.cc @@ -1012,7 +1012,8 @@ unique_ptr Translator::translate(pm_node_t *node) { case PM_ARRAY_PATTERN_NODE: // An array pattern such as the `[head, *tail]` in the `a in [head, *tail]` case PM_FIND_PATTERN_NODE: // A find pattern such as the `[*, middle, *]` in the `a in [*, middle, *]` case PM_HASH_PATTERN_NODE: // An hash pattern such as the `{ k: Integer }` in the `h in { k: Integer }` - case PM_IN_NODE: // An `in` pattern such as in a `case` statement, or as a standalone expression. + case PM_IN_NODE: // An `in` pattern such as in a `case` statement, or as a standalone expression. + case PM_PINNED_VARIABLE_NODE: // A "pinned" variable, like `^x` in `in ^x` unreachable( "These pattern-match related nodes are handled separately in `Translator::patternTranslate()`."); @@ -1038,7 +1039,6 @@ unique_ptr Translator::translate(pm_node_t *node) { case PM_NUMBERED_PARAMETERS_NODE: case PM_NUMBERED_REFERENCE_READ_NODE: case PM_PINNED_EXPRESSION_NODE: - case PM_PINNED_VARIABLE_NODE: case PM_POST_EXECUTION_NODE: case PM_PRE_EXECUTION_NODE: case PM_RESCUE_NODE: @@ -1211,6 +1211,13 @@ unique_ptr Translator::patternTranslate(pm_node_t *node) { return make_unique(location, gs.enterNameUTF8(name)); } + case PM_PINNED_VARIABLE_NODE: { // A "pinned" variable, like `^x` in `in ^x` + auto pinnedVarNode = reinterpret_cast(node); + + auto variable = translate(pinnedVarNode->variable); + + return make_unique(location, move(variable)); + } default: { return translate(node); } diff --git a/test/prism_regression/case_match_variable_binding.parse-tree.exp b/test/prism_regression/case_match_variable_binding.parse-tree.exp index 316c9398738..e7b02706e14 100644 --- a/test/prism_regression/case_match_variable_binding.parse-tree.exp +++ b/test/prism_regression/case_match_variable_binding.parse-tree.exp @@ -1,246 +1,302 @@ -CaseMatch { - expr = Send { - receiver = NULL - method = - args = [ - ] - } - inBodies = [ - InPattern { - pattern = ArrayPattern { - elts = [ - MatchVar { - name = - } - ] +Begin { + stmts = [ + Assign { + lhs = LVarLhs { + name = } - guard = NULL - body = DString { - nodes = [ - String { - val = - } - Begin { - stmts = [ - LVar { - name = - } - ] - } - ] + rhs = Integer { + val = "1" } } - InPattern { - pattern = HashPattern { - pairs = [ - Pair { - key = Symbol { - val = - } - value = MatchVar { - name = - } - } + CaseMatch { + expr = Send { + receiver = NULL + method = + args = [ ] } - guard = NULL - body = DString { - nodes = [ - String { - val = - } - Begin { - stmts = [ - LVar { + inBodies = [ + InPattern { + pattern = ArrayPattern { + elts = [ + MatchVar { name = } ] } - ] - } - } - InPattern { - pattern = ArrayPattern { - elts = [ - ArrayPattern { - elts = [ - MatchVar { - name = + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] } ] } - MatchRest { - var = MatchVar { - name = - } - } - ] - } - guard = NULL - body = DString { - nodes = [ - String { - val = - } - Begin { - stmts = [ - LVar { - name = + } + InPattern { + pattern = HashPattern { + pairs = [ + Pair { + key = Symbol { + val = + } + value = MatchVar { + name = + } } ] } - String { - val = - } - Begin { - stmts = [ - LVar { - name = + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] } ] } - ] - } - } - InPattern { - pattern = HashPattern { - pairs = [ - Pair { - key = Symbol { - val = - } - value = ArrayPattern { - elts = [ - MatchVar { - name = + } + InPattern { + pattern = ArrayPattern { + elts = [ + ArrayPattern { + elts = [ + MatchVar { + name = + } + ] + } + MatchRest { + var = MatchVar { + name = } - ] - } - } - ] - } - guard = NULL - body = DString { - nodes = [ - String { - val = + } + ] } - Begin { - stmts = [ - LVar { - name = + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] + } + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] } ] } - ] - } - } - InPattern { - pattern = ArrayPattern { - elts = [ - HashPattern { + } + InPattern { + pattern = HashPattern { pairs = [ Pair { key = Symbol { val = } - value = MatchVar { - name = + value = ArrayPattern { + elts = [ + MatchVar { + name = + } + ] } } ] } - MatchRest { - var = MatchVar { - name = - } - } - ] - } - guard = NULL - body = DString { - nodes = [ - String { - val = + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] + } + ] } - Begin { - stmts = [ - LVar { - name = + } + InPattern { + pattern = ArrayPattern { + elts = [ + HashPattern { + pairs = [ + Pair { + key = Symbol { + val = + } + value = MatchVar { + name = + } + } + ] + } + MatchRest { + var = MatchVar { + name = + } } ] } - String { - val = + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] + } + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] + } + ] } - Begin { - stmts = [ - LVar { - name = + } + InPattern { + pattern = HashPattern { + pairs = [ + Pair { + key = Symbol { + val = + } + value = HashPattern { + pairs = [ + Pair { + key = Symbol { + val = + } + value = MatchVar { + name = + } + } + ] + } } ] } - ] - } - } - InPattern { - pattern = HashPattern { - pairs = [ - Pair { - key = Symbol { - val = - } - value = HashPattern { - pairs = [ - Pair { - key = Symbol { - val = - } - value = MatchVar { + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { name = } - } - ] + ] + } + ] + } + } + InPattern { + pattern = Pin { + var = LVar { + name = } } - ] - } - guard = NULL - body = DString { - nodes = [ - String { - val = + guard = NULL + body = String { + val = } - Begin { - stmts = [ - LVar { - name = - } - ] + } + InPattern { + pattern = Pin { + var = IVar { + name = + } } - ] - } - } - InPattern { - pattern = MatchVar { - name = - } - guard = NULL - body = DString { - nodes = [ - String { - val = - } - Begin { - stmts = [ - LVar { - name = + guard = NULL + body = String { + val = + } + } + InPattern { + pattern = Pin { + var = CVar { + name = + } + } + guard = NULL + body = String { + val = + } + } + InPattern { + pattern = Pin { + var = GVar { + name = + } + } + guard = NULL + body = String { + val = + } + } + InPattern { + pattern = MatchVar { + name = + } + guard = NULL + body = DString { + nodes = [ + String { + val = + } + Begin { + stmts = [ + LVar { + name = + } + ] } ] } - ] - } + } + ] + elseBody = NULL } ] - elseBody = NULL } diff --git a/test/prism_regression/case_match_variable_binding.rb b/test/prism_regression/case_match_variable_binding.rb index 396df62c5d7..70f1bfd3dcb 100644 --- a/test/prism_regression/case_match_variable_binding.rb +++ b/test/prism_regression/case_match_variable_binding.rb @@ -1,5 +1,7 @@ # typed: false +lvar = 1 + case foo in [x] # Variable binding nested in an Array pattern "An Array-like thing that only contains #{x}" @@ -16,6 +18,16 @@ in { k: { k2: value } } # A Hash pattern inside a Hash pattern "A hash-like whose key `:k` has a one-element Hash value containing k2: #{value}" -in x +# "Variable pinning", which matches against the value of that variable +in ^lvar # `lvar` must already exist. Does *not* bind a new variable `lvar`, like `in x` below. + "Has the same value as the preexisting variable `x`" +in ^@ivar + "Has the same value as `@ivar`" +in ^@@cvar + "Has the same value as `@@cvar`" +in ^$global + "Has the same value as `$global`" + +in x # Binds any value to a new variable `x`. "Some other value: #{x}" end From de3a00b838c223b42615b0b03d80e5eccdc1943a Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Thu, 10 Oct 2024 17:03:27 -0400 Subject: [PATCH 2/2] Implement translation for expression pinning --- parser/prism/Translator.cc | 19 +++++++++++---- ...case_match_variable_binding.parse-tree.exp | 23 +++++++++++++++++++ .../case_match_variable_binding.rb | 4 ++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/parser/prism/Translator.cc b/parser/prism/Translator.cc index 0559d737c60..914ea777b7c 100644 --- a/parser/prism/Translator.cc +++ b/parser/prism/Translator.cc @@ -1012,8 +1012,9 @@ unique_ptr Translator::translate(pm_node_t *node) { case PM_ARRAY_PATTERN_NODE: // An array pattern such as the `[head, *tail]` in the `a in [head, *tail]` case PM_FIND_PATTERN_NODE: // A find pattern such as the `[*, middle, *]` in the `a in [*, middle, *]` case PM_HASH_PATTERN_NODE: // An hash pattern such as the `{ k: Integer }` in the `h in { k: Integer }` - case PM_IN_NODE: // An `in` pattern such as in a `case` statement, or as a standalone expression. - case PM_PINNED_VARIABLE_NODE: // A "pinned" variable, like `^x` in `in ^x` + case PM_IN_NODE: // An `in` pattern such as in a `case` statement, or as a standalone expression. + case PM_PINNED_EXPRESSION_NODE: // A "pinned" expression, like `^(1 + 2)` in `in ^(1 + 2)` + case PM_PINNED_VARIABLE_NODE: // A "pinned" variable, like `^x` in `in ^x` unreachable( "These pattern-match related nodes are handled separately in `Translator::patternTranslate()`."); @@ -1038,7 +1039,6 @@ unique_ptr Translator::translate(pm_node_t *node) { case PM_MULTI_TARGET_NODE: case PM_NUMBERED_PARAMETERS_NODE: case PM_NUMBERED_REFERENCE_READ_NODE: - case PM_PINNED_EXPRESSION_NODE: case PM_POST_EXECUTION_NODE: case PM_PRE_EXECUTION_NODE: case PM_RESCUE_NODE: @@ -1211,6 +1211,18 @@ unique_ptr Translator::patternTranslate(pm_node_t *node) { return make_unique(location, gs.enterNameUTF8(name)); } + case PM_PINNED_EXPRESSION_NODE: { // A "pinned" expression, like `^(1 + 2)` in `in ^(1 + 2)` + auto pinnedExprNode = reinterpret_cast(node); + + auto expr = translate(pinnedExprNode->expression); + + // Sorbet's parser always wraps the pinned expression in a `Begin` node. + NodeVec statements; + statements.emplace_back(move(expr)); + auto beginNode = make_unique(translateLoc(pinnedExprNode->base.location), move(statements)); + + return make_unique(location, move(beginNode)); + } case PM_PINNED_VARIABLE_NODE: { // A "pinned" variable, like `^x` in `in ^x` auto pinnedVarNode = reinterpret_cast(node); @@ -1386,5 +1398,4 @@ unique_ptr Translator::translateConst(PrismLhsNode *node) { template unique_ptr Translator::translateSimpleKeyword(pm_node_t *node) { return make_unique(translateLoc(node->location)); } - }; // namespace sorbet::parser::Prism diff --git a/test/prism_regression/case_match_variable_binding.parse-tree.exp b/test/prism_regression/case_match_variable_binding.parse-tree.exp index e7b02706e14..b1abd69a930 100644 --- a/test/prism_regression/case_match_variable_binding.parse-tree.exp +++ b/test/prism_regression/case_match_variable_binding.parse-tree.exp @@ -275,6 +275,29 @@ Begin { val = } } + InPattern { + pattern = Pin { + var = Begin { + stmts = [ + Send { + receiver = Integer { + val = "1" + } + method = + args = [ + Integer { + val = "2" + } + ] + } + ] + } + } + guard = NULL + body = String { + val = + } + } InPattern { pattern = MatchVar { name = diff --git a/test/prism_regression/case_match_variable_binding.rb b/test/prism_regression/case_match_variable_binding.rb index 70f1bfd3dcb..3f4f42f66c6 100644 --- a/test/prism_regression/case_match_variable_binding.rb +++ b/test/prism_regression/case_match_variable_binding.rb @@ -28,6 +28,10 @@ in ^$global "Has the same value as `$global`" +# "Expression pinning", which match the result of the expression +in ^(1 + 2) + "Has the same value as `1 + 2`" + in x # Binds any value to a new variable `x`. "Some other value: #{x}" end