New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ruby: pattern matching #7154
base: main
Are you sure you want to change the base?
Ruby: pattern matching #7154
Conversation
2e9b57a
to
b602549
93ce829
to
157877e
5bd7bde
to
5ca8fe7
I haven't reviewed the tests yet, but looks very sensible so far. I found an issue in the upgrade script, plus some comment suggestions.
| final override string getAPrimaryQlClass() { result = "CaseMatch" } | ||
|
|
||
| /** | ||
| * Gets the expression being compared. For example, `foo` in the following example. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would 'matched' be a clearer way of phrasing it?
| * Gets the expression being compared. For example, `foo` in the following example. | |
| * Gets the expression being matched. For example, `foo` in the following example. |
| */ | ||
| final Expr getABranch() { result = this.getBranch(_) } | ||
|
|
||
| /** Gets a `in` clause of this case expression. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Gets a `in` clause of this case expression. */ | |
| /** Gets the `n`th `in` clause of this case expression. */ |
| /** Gets a `in` clause of this case expression. */ | ||
| final InClause getInClause(int n) { result = this.getBranch(n) } | ||
|
|
||
| /** Gets the `n`th `in` clause of this case expression. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Gets the `n`th `in` clause of this case expression. */ | |
| /** Gets an `in` clause of this case expression. */ |
|
|
||
| private Ruby::KeywordPattern keyValuePair(int n) { result = g.getChild(n) } | ||
|
|
||
| /** Get the key of the `n`th pair. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Get the key of the `n`th pair. */ | |
| /** Gets the key of the `n`th pair. */ |
| /** Get the key of the `n`th pair. */ | ||
| StringlikeLiteral getKey(int n) { toGenerated(result) = keyValuePair(n).getKey() } | ||
|
|
||
| /** Get the value of the `n`th pair. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Get the value of the `n`th pair. */ | |
| /** Gets the value of the `n`th pair. */ |
| /** Get the value of the `n`th pair. */ | ||
| CasePattern getValue(int n) { toGenerated(result) = keyValuePair(n).getValue() } | ||
|
|
||
| /** Get the value for a given key name. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Get the value for a given key name. */ | |
| /** Gets the value for a given key name. */ |
| exists(int i | key = this.getKey(i).getValueText() and result = this.getValue(i)) | ||
| } | ||
|
|
||
| /** Get the variable of the keyword rest token, if any, i.e. `name` in `**name`. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Get the variable of the keyword rest token, if any, i.e. `name` in `**name`. */ | |
| /** Gets the variable of the keyword rest token, if any, i.e. `name` in `**name`. */ |
| } | ||
|
|
||
| /** | ||
| * A pattern matching with a rename into specified local variable, for example `Integer => a` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence is a little hard to understand. Is my suggestion correct, and do you think it's any clearer?
| * A pattern matching with a rename into specified local variable, for example `Integer => a` | |
| * A pattern match that binds to the specified local variable, for example `Integer => a` |
|
|
||
| AsPattern() { this = TAsPattern(g) } | ||
|
|
||
| /** Get the underlying pattern. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** Get the underlying pattern. */ | |
| /** Gets the underlying pattern. */ |
|
|
||
| bindingset[old] | ||
| private int newKind(int old) { | ||
| old in [0 .. 7] and result = old |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.. is inclusive.
| old in [0 .. 7] and result = old | |
| old in [0 .. 6] and result = old | |
| `` |
Looks very solid to me!
As for the CFG inconsistencies: that is expected, so for now we should simply commit library-tests/ast/CONSISTENCY/CfgConsistency.expected and library-tests/ast/control/CONSISTENCY/CfgConsistency.ql. Then once we actually implement the CFG logic, those inconsistencies should disappear.
| or | ||
| pred = "getValue" and result = this.getValue() | ||
| or | ||
| pred = "getBranch" and result = this.getBranch(_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we include getInClause and getElseBranch here as well (they should be concatenated in the PrintAST query)?
| * following example, the pattern is `Point{ x:, y: }`. | ||
| * ```rb | ||
| * case foo | ||
| * in Point{ x:, y: } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we missing a then keyword here?
| * end | ||
| * ``` | ||
| */ | ||
| final PatternGuard getPatternGuard() { toGenerated(result) = g.getGuard() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call it getCondition instead?
| */ | ||
| final PatternGuard getPatternGuard() { toGenerated(result) = g.getGuard() } | ||
|
|
||
| final override string toString() { result = "in ..." } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps in ... then ... if the then keyword is required.
| or | ||
| pred = "getPattern" and result = this.getPattern() | ||
| or | ||
| pred = "getGuard" and result = this.getPatternGuard() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pred = "getPatternGuard" (or getCondition, if you agree with my suggestion above).
| TVariableReferencePattern; | ||
|
|
||
| private class TPattern = | ||
| TPatternNode or TLiteral or TLambda or TConstantAccess or TLocalVariableAccess or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is TLambda included?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that you have a test case in -> x { x == 10 }, so I guess that is why. But there are no sub classes of CasePattern that correspond to this case; should there be?
| @@ -223,6 +223,40 @@ private class FalseLiteral extends BooleanLiteral, TFalseLiteral { | |||
| final override predicate isFalse() { any() } | |||
| } | |||
|
|
|||
| /** | |||
| * The `__ENCODING__` literal. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An? And same for the two others below.
| or | ||
| casePattern(g) | ||
| or | ||
| exists(Ruby::ArrayPattern p | g = p.getClass()) | ||
| or | ||
| exists(Ruby::FindPattern p | g = p.getClass()) | ||
| or | ||
| exists(Ruby::HashPattern p | g = p.getClass()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is duplicated further down, so perhaps put into separate predicate.
| @@ -422,6 +422,189 @@ class WhenExpr extends Expr, TWhenExpr { | |||
| } | |||
| } | |||
|
|
|||
| /** | |||
| * A `case` statement used for pattern matching. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include an example.
| /** | ||
| * A `case` statement used for pattern matching. | ||
| */ | ||
| class CaseMatch extends ControlExpr, TCaseMatch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering whether CaseMatch and CaseExpr should be combined, but it looks like you can't actually combine when matching and pattern matching, e.g.
case foo
in 2
puts "2"
when 42
puts "42"
endStill, I would expect when matching to be just a simple form of pattern matching.
This pull request updates the Ruby grammar to tree-sitter/tree-sitter-ruby#193
The text was updated successfully, but these errors were encountered: