You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
7
7
## [Unreleased]
8
8
9
+
### Added
10
+
11
+
- Every node now implements the `#copy(**)` method, which provides a copy of the node with the given attributes replaced.
12
+
- Every node now implements the `#===(other)` method, which checks if the given node matches the current node for all attributes except for comments and location.
13
+
- There is a new `SyntaxTree::Visitor::MutationVisitor` and its convenience method `SyntaxTree.mutation` which can be used to mutate a syntax tree. For details on how to use this visitor, check the README.
14
+
9
15
### Changed
10
16
11
17
- Nodes no longer have a `comments:` keyword on their initializers. By default, they initialize to an empty array. If you were previously passing comments into the initializer, you should now create the node first, then call `node.comments.concat` to add your comments.
@@ -332,6 +336,10 @@ This function takes an input string containing Ruby code and returns the syntax
332
336
333
337
This function takes an input string containing Ruby code, parses it into its underlying syntax tree, and formats it back out to a string. You can optionally pass a second argument to this method as well that is the maximum width to print. It defaults to `80`.
334
338
339
+
### SyntaxTree.mutation(&block)
340
+
341
+
This function yields a new mutation visitor to the block, and then returns the initialized visitor. It's effectively a shortcut for creating a `SyntaxTree::Visitor::MutationVisitor` without having to remember the class name. For more information on that visitor, see the definition below.
342
+
335
343
### SyntaxTree.search(source, query, &block)
336
344
337
345
This function takes an input string containing Ruby code, an input string containing a valid Ruby `in` clause expression that can be used to match against nodes in the tree (can be generated using `stree expr`, `stree match`, or `Node#construct_keys`), and a block. Each node that matches the given query will be yielded to the block. The block will receive the node as its only argument.
This method returns a copy of the node, with the given attributes replaced.
364
+
365
+
```ruby
366
+
program =SyntaxTree.parse("1 + 1")
367
+
368
+
binary = program.statements.body.first
369
+
# => (binary (int "1") + (int "1"))
370
+
371
+
binary.copy(operator::-)
372
+
# => (binary (int "1") - (int "1"))
373
+
```
374
+
353
375
### Pattern matching
354
376
355
377
Pattern matching is another way to descend the tree which is more specific than using `child_nodes`. Using Ruby's built-in pattern matching, you can extract the same information but be as specific about your constraints as you like. For example, with minimal constraints:
@@ -407,6 +429,18 @@ formatter.output.join
407
429
# => "1 + 1"
408
430
```
409
431
432
+
### ===(other)
433
+
434
+
Every node responds to `===`, which is used to check if the given other node matches all of the attributes of the current node except for location and comments. For example:
435
+
436
+
```ruby
437
+
program1 =SyntaxTree.parse("1 + 1")
438
+
program2 =SyntaxTree.parse("1 + 1")
439
+
440
+
program1 === program2
441
+
# => true
442
+
```
443
+
410
444
### construct_keys
411
445
412
446
Every node responds to `construct_keys`, which will return a string that contains a Ruby pattern-matching expression that could be used to match against the current node. It's meant to be used in tooling and through the CLI mostly.
@@ -495,6 +529,42 @@ end
495
529
496
530
The visitor defined above will error out unless it's only visiting a `SyntaxTree::Int` node. This is useful in a couple of ways, e.g., if you're trying to define a visitor to handle the whole tree but it's currently a work-in-progress.
497
531
532
+
### MutationVisitor
533
+
534
+
The `MutationVisitor` is a visitor that can be used to mutate the tree. It works by defining a default `visit_*` method that returns a copy of the given node with all of its attributes visited. This new node will replace the old node in the tree. Typically, you use the `#mutate` method on it to define mutations using patterns. For example:
535
+
536
+
```ruby
537
+
# Create a new visitor
538
+
visitor =SyntaxTree::Visitor::MutationVisitor.new
539
+
540
+
# Specify that it should mutate If nodes with assignments in their predicates
541
+
visitor.mutate("If[predicate: Assign | OpAssign]") do |node|
542
+
# Get the existing If's predicate node
543
+
predicate = node.predicate
544
+
545
+
# Create a new predicate node that wraps the existing predicate node
546
+
# in parentheses
547
+
predicate =
548
+
SyntaxTree::Paren.new(
549
+
lparen:SyntaxTree::LParen.default,
550
+
contents: predicate,
551
+
location: predicate.location
552
+
)
553
+
554
+
# Return a copy of this node with the new predicate
0 commit comments