syntax_tree 5.3.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -1
  3. data/CHANGELOG.md +64 -1
  4. data/Gemfile.lock +2 -2
  5. data/README.md +28 -9
  6. data/Rakefile +12 -8
  7. data/bin/console +1 -0
  8. data/bin/whitequark +79 -0
  9. data/doc/changing_structure.md +16 -0
  10. data/lib/syntax_tree/basic_visitor.rb +44 -5
  11. data/lib/syntax_tree/cli.rb +2 -2
  12. data/lib/syntax_tree/dsl.rb +23 -11
  13. data/lib/syntax_tree/{visitor/field_visitor.rb → field_visitor.rb} +54 -55
  14. data/lib/syntax_tree/formatter.rb +1 -1
  15. data/lib/syntax_tree/index.rb +56 -54
  16. data/lib/syntax_tree/json_visitor.rb +55 -0
  17. data/lib/syntax_tree/language_server.rb +157 -2
  18. data/lib/syntax_tree/match_visitor.rb +120 -0
  19. data/lib/syntax_tree/mermaid.rb +177 -0
  20. data/lib/syntax_tree/mermaid_visitor.rb +69 -0
  21. data/lib/syntax_tree/{visitor/mutation_visitor.rb → mutation_visitor.rb} +27 -27
  22. data/lib/syntax_tree/node.rb +198 -107
  23. data/lib/syntax_tree/parser.rb +322 -118
  24. data/lib/syntax_tree/pretty_print_visitor.rb +83 -0
  25. data/lib/syntax_tree/reflection.rb +241 -0
  26. data/lib/syntax_tree/translation/parser.rb +3019 -0
  27. data/lib/syntax_tree/translation/rubocop_ast.rb +21 -0
  28. data/lib/syntax_tree/translation.rb +28 -0
  29. data/lib/syntax_tree/version.rb +1 -1
  30. data/lib/syntax_tree/with_scope.rb +244 -0
  31. data/lib/syntax_tree/yarv/basic_block.rb +53 -0
  32. data/lib/syntax_tree/yarv/calldata.rb +91 -0
  33. data/lib/syntax_tree/yarv/compiler.rb +110 -100
  34. data/lib/syntax_tree/yarv/control_flow_graph.rb +257 -0
  35. data/lib/syntax_tree/yarv/data_flow_graph.rb +338 -0
  36. data/lib/syntax_tree/yarv/decompiler.rb +1 -1
  37. data/lib/syntax_tree/yarv/disassembler.rb +104 -80
  38. data/lib/syntax_tree/yarv/instruction_sequence.rb +43 -18
  39. data/lib/syntax_tree/yarv/instructions.rb +203 -649
  40. data/lib/syntax_tree/yarv/legacy.rb +12 -24
  41. data/lib/syntax_tree/yarv/sea_of_nodes.rb +534 -0
  42. data/lib/syntax_tree/yarv.rb +18 -0
  43. data/lib/syntax_tree.rb +88 -56
  44. data/tasks/sorbet.rake +277 -0
  45. data/tasks/whitequark.rake +87 -0
  46. metadata +23 -11
  47. data/.gitmodules +0 -9
  48. data/lib/syntax_tree/language_server/inlay_hints.rb +0 -159
  49. data/lib/syntax_tree/visitor/environment.rb +0 -84
  50. data/lib/syntax_tree/visitor/json_visitor.rb +0 -55
  51. data/lib/syntax_tree/visitor/match_visitor.rb +0 -122
  52. data/lib/syntax_tree/visitor/pretty_print_visitor.rb +0 -85
  53. data/lib/syntax_tree/visitor/with_environment.rb +0 -140
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3261679bf963ba7263bb3ae21e826ed1fa7a28c6afbf2a2b65b42101be16b7ac
4
- data.tar.gz: 653c6c79a34db7686ceeadf83f0d8adb47093d1ab1d946636008ee4c31144258
3
+ metadata.gz: eb083ad4f07db18f2d09a8fef263962bd3593b8e93be19d241c7f4cc254a02bb
4
+ data.tar.gz: 68c448add2745ffbb26f5200b888e241f8ac56d2f8cb7c6ee6ae675dffb0f3b6
5
5
  SHA512:
6
- metadata.gz: 9cefe2a22c594efc74aa0fbe300c986a414c890efb7204633481345ceb9b0bd3578d836046ca64e289621ce31ea343f0da40db1f9b3d2d7dbd7f0a11b020c725
7
- data.tar.gz: 628ed098064cb9e6dc720666d59b5e9d8653f7e06cb7e0774887dfeb4ed4a5bdb217ea644b93f52a3730e2dfdc14334c0690018d145728a89fa99cbd041e4301
6
+ metadata.gz: 1e930dfe285bc15786b4d4de401144cc4b5ae90a1724ea9d5d9065b556ec7c9a238e4e9dfa47d72aff563bbc5a07269b2027916ab5e74bdf48896ff65c811e68
7
+ data.tar.gz: e148cc8409cbe08e1fcbc72f9cf5018d018264f996b79c9093fc51e588ea8836dbccbaa320f423a013341031369ba2047a0014c47116a65ca10f9b7add3ea9ec
data/.rubocop.yml CHANGED
@@ -8,7 +8,6 @@ AllCops:
8
8
  TargetRubyVersion: 2.7
9
9
  Exclude:
10
10
  - '{.git,.github,bin,coverage,pkg,spec,test/fixtures,vendor,tmp}/**/*'
11
- - test/ruby-syntax-fixtures/**/*
12
11
  - test.rb
13
12
 
14
13
  Gemspec/DevelopmentDependencies:
@@ -29,6 +28,9 @@ Lint/AmbiguousRange:
29
28
  Lint/BooleanSymbol:
30
29
  Enabled: false
31
30
 
31
+ Lint/Debugger:
32
+ Enabled: false
33
+
32
34
  Lint/DuplicateBranch:
33
35
  Enabled: false
34
36
 
@@ -80,6 +82,9 @@ Security/Eval:
80
82
  Style/AccessorGrouping:
81
83
  Enabled: false
82
84
 
85
+ Style/Alias:
86
+ Enabled: false
87
+
83
88
  Style/CaseEquality:
84
89
  Enabled: false
85
90
 
@@ -89,6 +94,9 @@ Style/CaseLikeIf:
89
94
  Style/ClassVars:
90
95
  Enabled: false
91
96
 
97
+ Style/CombinableLoops:
98
+ Enabled: false
99
+
92
100
  Style/DocumentDynamicEvalDefinition:
93
101
  Enabled: false
94
102
 
@@ -110,6 +118,9 @@ Style/FormatStringToken:
110
118
  Style/GuardClause:
111
119
  Enabled: false
112
120
 
121
+ Style/HashLikeCase:
122
+ Enabled: false
123
+
113
124
  Style/IdenticalConditionalBranches:
114
125
  Enabled: false
115
126
 
data/CHANGELOG.md CHANGED
@@ -6,6 +6,68 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [6.0.0] - 2023-02-10
10
+
11
+ ### Added
12
+
13
+ - `SyntaxTree::BasicVisitor::visit_methods` has been added to allow you to check multiple visit methods inside of a block. There _was_ a method called `visit_methods` previously, but it was undocumented because it was meant as a private API. That method has been renamed to `valid_visit_methods`.
14
+ - `rake sorbet:rbi` has been added as a task within the repository to generate an RBI file corresponding to the nodes in the tree. This can be used to help aid consumers of Syntax Tree that are using Sorbet.
15
+ - `SyntaxTree::Reflection` has been added to allow you to get information about the nodes in the tree. It is not required by default, since it takes a small amount of time to parse `node.rb` and get all of the information.
16
+ - `SyntaxTree::Node#to_mermaid` has been added to allow you to generate a Mermaid diagram of the node and its children. This is useful for debugging and understanding the structure of the tree.
17
+ - `SyntaxTree::Translation` has been added as an experimental API to transform the Syntax Tree syntax tree into the syntax trees represented by the whitequark/parser and rubocop/rubocop-ast gems.
18
+ - `SyntaxTree::Translation.to_parser(node, buffer)` will return a `Parser::AST::Node` object.
19
+ - `SyntaxTree::Translation.to_rubocop_ast(node, buffer)` will return a `RuboCop::AST::Node` object.
20
+ - `SyntaxTree::index` and `SyntaxTree::index_file` have been added to allow you to get a list of all of the classes, modules, and methods defined in a given source string or file.
21
+ - Various convenience methods have been added:
22
+ - `SyntaxTree::format_file` - which calls format with the result of reading the file
23
+ - `SyntaxTree::format_node` - which formats the node directly
24
+ - `SyntaxTree::parse_file` - which calls parse with the result of reading the file
25
+ - `SyntaxTree::search_file` - which calls search with the result of reading the file
26
+ - `SyntaxTree::Node#start_char` - which is the same as calling `node.location.start_char`
27
+ - `SyntaxTree::Node#end_char` - which is the same as calling `node.location.end_char`
28
+ - `SyntaxTree::Assoc` nodes can now be formatted on their own without a parent hash node.
29
+ - `SyntaxTree::BlockVar#arg0?` has been added to check if a single required block parameter is present and would potentially be expanded.
30
+ - More experimental APIs have been added to the `SyntaxTree::YARV` module, including:
31
+ - `SyntaxTree::YARV::ControlFlowGraph`
32
+ - `SyntaxTree::YARV::DataFlowGraph`
33
+ - `SyntaxTree::YARV::SeaOfNodes`
34
+
35
+ ### Changed
36
+
37
+ #### Major changes
38
+
39
+ - *BREAKING* Updates to `WithEnvironment`:
40
+ - The `WithEnvironment` module has been renamed to `WithScope`.
41
+ - The `current_environment` method has been renamed to `current_scope`.
42
+ - The `with_current_environment` method has been removed.
43
+ - Previously scopes were always able to look up the tree, as in: `a = 1; def foo; a = 2; end` would see only a single `a` variable. That has been corrected.
44
+ - Previously accessing variables from inside of blocks that were not shadowed would mark them as being local to the block only. This has been correct.
45
+ - *BREAKING* Lots of constants moved out of `SyntaxTree::Visitor` to just `SyntaxTree`:
46
+ * `SyntaxTree::Visitor::FieldVisitor` is now `SyntaxTree::FieldVisitor`
47
+ * `SyntaxTree::Visitor::JSONVisitor` is now `SyntaxTree::JSONVisitor`
48
+ * `SyntaxTree::Visitor::MatchVisitor` is now `SyntaxTree::MatchVisitor`
49
+ * `SyntaxTree::Visitor::MutationVisitor` is now `SyntaxTree::MutationVisitor`
50
+ * `SyntaxTree::Visitor::PrettyPrintVisitor` is now `SyntaxTree::PrettyPrintVisitor`
51
+ - *BREAKING* Lots of constants are now autoloaded instead of required by default. This is only particularly relevant if you are in a forking environment and want to preload constants before forking for better memory usage with copy-on-write.
52
+ - *BREAKING* The `SyntaxTree::Statements#initialize` method no longer accepts a parser as the first argument. It now mirrors the other nodes in that it accepts its children and location. As a result, Syntax Tree nodes are now marshalable (and therefore can be sent over DRb). Previously the `Statements` node was not able to be marshaled because it held a reference to the parser.
53
+
54
+ #### Minor changes
55
+
56
+ - Many places where embedded documents (`=begin` to `=end`) were being treated as real comments have been fixed for formatting.
57
+ - Dynamic symbols in keyword pattern matching now have better formatting.
58
+ - Endless method definitions used to have a `SyntaxTree::BodyStmt` node that had any kind of node as its `statements` field. That has been corrected to be more consistent such that now going from `def_node.bodystmt.statements` always returns a `SyntaxTree::Statements` node, which is more consistent.
59
+ - We no longer assume that `fiddle` is able to be required, and only require it when it is actually needed.
60
+
61
+ #### Tiny changes
62
+
63
+ - Empty parameter nodes within blocks now have more accurate location information.
64
+ - Pinned variables have more correct location information now. (Previously the location was just around the variable itself, but it now includes the pin.)
65
+ - Array patterns in pattern matching now have more accurate location information when they are using parentheses with a constant present.
66
+ - Find patterns in pattern matching now have more correct location information for their `left` and `right` fields.
67
+ - Lots of nodes have more correct types in the comments on their attributes.
68
+ - The expressions `break foo.bar :baz do |qux| qux end` and `next fun foo do end` now correctly parses as a control-flow statement with a method call that has a block attached, as opposed to a control-flow statement with a block attached.
69
+ - The expression `self::a, b = 1, 2` would previously yield a `SyntaxTree::ConstPathField` node for the first element of the left-hand-side of the multiple assignment. Semantically this is incorrect, and we have fixed this to now be a `SyntaxTree::Field` node instead.
70
+
9
71
  ## [5.3.0] - 2023-01-26
10
72
 
11
73
  ### Added
@@ -497,7 +559,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
497
559
 
498
560
  - 🎉 Initial release! 🎉
499
561
 
500
- [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.3.0...HEAD
562
+ [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.0...HEAD
563
+ [6.0.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.3.0...v6.0.0
501
564
  [5.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.2.0...v5.3.0
502
565
  [5.2.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.1.0...v5.2.0
503
566
  [5.1.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.0.1...v5.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- syntax_tree (5.3.0)
4
+ syntax_tree (6.0.0)
5
5
  prettier_print (>= 1.2.0)
6
6
 
7
7
  GEM
@@ -19,7 +19,7 @@ GEM
19
19
  rake (13.0.6)
20
20
  regexp_parser (2.6.2)
21
21
  rexml (3.2.5)
22
- rubocop (1.44.1)
22
+ rubocop (1.45.1)
23
23
  json (~> 2.3)
24
24
  parallel (~> 1.10)
25
25
  parser (>= 3.2.0.0)
data/README.md CHANGED
@@ -40,9 +40,10 @@ It is built with only standard library dependencies. It additionally ships with
40
40
  - [construct_keys](#construct_keys)
41
41
  - [Visitor](#visitor)
42
42
  - [visit_method](#visit_method)
43
+ - [visit_methods](#visit_methods)
43
44
  - [BasicVisitor](#basicvisitor)
44
45
  - [MutationVisitor](#mutationvisitor)
45
- - [WithEnvironment](#withenvironment)
46
+ - [WithScope](#withscope)
46
47
  - [Language server](#language-server)
47
48
  - [textDocument/formatting](#textdocumentformatting)
48
49
  - [textDocument/inlayHint](#textdocumentinlayhint)
@@ -340,7 +341,7 @@ This function takes an input string containing Ruby code, parses it into its und
340
341
 
341
342
  ### SyntaxTree.mutation(&block)
342
343
 
343
- 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.
344
+ 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::MutationVisitor` without having to remember the class name. For more information on that visitor, see the definition below.
344
345
 
345
346
  ### SyntaxTree.search(source, query, &block)
346
347
 
@@ -517,6 +518,26 @@ Did you mean? visit_binary
517
518
  from bin/console:8:in `<main>'
518
519
  ```
519
520
 
521
+ ### visit_methods
522
+
523
+ Similar to `visit_method`, `visit_methods` also checks that methods defined are valid visit methods. This variation however accepts a block and checks that all methods defined within that block are valid visit methods. It's meant to be used like:
524
+
525
+ ```ruby
526
+ class ArithmeticVisitor < SyntaxTree::Visitor
527
+ visit_methods do
528
+ def visit_binary(node)
529
+ # ...
530
+ end
531
+
532
+ def visit_int(node)
533
+ # ...
534
+ end
535
+ end
536
+ end
537
+ ```
538
+
539
+ This is only checked when the methods are defined and does not impose any kind of runtime overhead after that. It is very useful for upgrading versions of Syntax Tree in case these methods names change.
540
+
520
541
  ### BasicVisitor
521
542
 
522
543
  When you're defining your own visitor, by default it will walk down the tree even if you don't define `visit_*` methods. This is to ensure you can define a subset of the necessary methods in order to only interact with the nodes you're interested in. If you'd like to change this default to instead raise an error if you visit a node you haven't explicitly handled, you can instead inherit from `BasicVisitor`.
@@ -537,7 +558,7 @@ The `MutationVisitor` is a visitor that can be used to mutate the tree. It works
537
558
 
538
559
  ```ruby
539
560
  # Create a new visitor
540
- visitor = SyntaxTree::Visitor::MutationVisitor.new
561
+ visitor = SyntaxTree::MutationVisitor.new
541
562
 
542
563
  # Specify that it should mutate If nodes with assignments in their predicates
543
564
  visitor.mutate("IfNode[predicate: Assign | OpAssign]") do |node|
@@ -567,20 +588,18 @@ SyntaxTree::Formatter.format(source, program.accept(visitor))
567
588
  # => "if (a = 1)\nend\n"
568
589
  ```
569
590
 
570
- ### WithEnvironment
591
+ ### WithScope
571
592
 
572
- The `WithEnvironment` module can be included in visitors to automatically keep track of local variables and arguments
573
- defined inside each environment. A `current_environment` accessor is made available to the request, allowing it to find
574
- all usages and definitions of a local.
593
+ The `WithScope` module can be included in visitors to automatically keep track of local variables and arguments defined inside each scope. A `current_scope` accessor is made available to the request, allowing it to find all usages and definitions of a local.
575
594
 
576
595
  ```ruby
577
596
  class MyVisitor < Visitor
578
- include WithEnvironment
597
+ prepend WithScope
579
598
 
580
599
  def visit_ident(node)
581
600
  # find_local will return a Local for any local variables or arguments
582
601
  # present in the current environment or nil if the identifier is not a local
583
- local = current_environment.find_local(node)
602
+ local = current_scope.find_local(node)
584
603
 
585
604
  puts local.type # the type of the local (:variable or :argument)
586
605
  puts local.definitions # the array of locations where this local is defined
data/Rakefile CHANGED
@@ -4,6 +4,8 @@ require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
5
  require "syntax_tree/rake_tasks"
6
6
 
7
+ Rake.add_rakelib "tasks"
8
+
7
9
  Rake::TestTask.new(:test) do |t|
8
10
  t.libs << "test"
9
11
  t.libs << "lib"
@@ -14,7 +16,16 @@ task default: :test
14
16
 
15
17
  configure = ->(task) do
16
18
  task.source_files =
17
- FileList[%w[Gemfile Rakefile syntax_tree.gemspec lib/**/*.rb test/*.rb]]
19
+ FileList[
20
+ %w[
21
+ Gemfile
22
+ Rakefile
23
+ syntax_tree.gemspec
24
+ lib/**/*.rb
25
+ tasks/*.rake
26
+ test/*.rb
27
+ ]
28
+ ]
18
29
 
19
30
  # Since Syntax Tree supports back to Ruby 2.7.0, we need to make sure that we
20
31
  # format our code such that it's compatible with that version. This actually
@@ -26,10 +37,3 @@ end
26
37
 
27
38
  SyntaxTree::Rake::CheckTask.new(&configure)
28
39
  SyntaxTree::Rake::WriteTask.new(&configure)
29
-
30
- desc "Run mspec tests using YARV emulation"
31
- task :spec do
32
- Dir["./spec/ruby/language/**/*_spec.rb"].each do |filepath|
33
- sh "exe/yarv ./spec/mspec/bin/mspec-tag #{filepath}"
34
- end
35
- end
data/bin/console CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "bundler/setup"
5
5
  require "syntax_tree"
6
+ require "syntax_tree/reflection"
6
7
 
7
8
  require "irb"
8
9
  IRB.start(__FILE__)
data/bin/whitequark ADDED
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "parser/current"
6
+
7
+ $:.unshift(File.expand_path("../lib", __dir__))
8
+ require "syntax_tree"
9
+
10
+ # First, opt in to every AST feature.
11
+ Parser::Builders::Default.modernize
12
+
13
+ # Modify the source map == check so that it doesn't check against the node
14
+ # itself so we don't get into a recursive loop.
15
+ Parser::Source::Map.prepend(
16
+ Module.new {
17
+ def ==(other)
18
+ self.class == other.class &&
19
+ (instance_variables - %i[@node]).map do |ivar|
20
+ instance_variable_get(ivar) == other.instance_variable_get(ivar)
21
+ end.reduce(:&)
22
+ end
23
+ }
24
+ )
25
+
26
+ # Next, ensure that we're comparing the nodes and also comparing the source
27
+ # ranges so that we're getting all of the necessary information.
28
+ Parser::AST::Node.prepend(
29
+ Module.new {
30
+ def ==(other)
31
+ super && (location == other.location)
32
+ end
33
+ }
34
+ )
35
+
36
+ source = ARGF.read
37
+
38
+ parser = Parser::CurrentRuby.new
39
+ parser.diagnostics.all_errors_are_fatal = true
40
+
41
+ buffer = Parser::Source::Buffer.new("(string)", 1)
42
+ buffer.source = source.dup.force_encoding(parser.default_encoding)
43
+
44
+ stree = SyntaxTree::Translation.to_parser(SyntaxTree.parse(source), buffer)
45
+ ptree = parser.parse(buffer)
46
+
47
+ if stree == ptree
48
+ puts "Syntax trees are equivalent."
49
+ elsif stree.inspect == ptree.inspect
50
+ warn "Syntax tree locations are different."
51
+
52
+ queue = [[stree, ptree]]
53
+ while (left, right = queue.shift)
54
+ if left.location != right.location
55
+ warn "Different node:"
56
+ pp left
57
+
58
+ warn "Different location:"
59
+
60
+ warn "Syntax Tree:"
61
+ pp left.location
62
+
63
+ warn "whitequark/parser:"
64
+ pp right.location
65
+
66
+ exit
67
+ end
68
+
69
+ left.children.zip(right.children).each do |left_child, right_child|
70
+ queue << [left_child, right_child] if left_child.is_a?(Parser::AST::Node)
71
+ end
72
+ end
73
+ else
74
+ warn "Syntax Tree:"
75
+ pp stree
76
+
77
+ warn "whitequark/parser:"
78
+ pp ptree
79
+ end
@@ -0,0 +1,16 @@
1
+ # Changing structure
2
+
3
+ First and foremost, changing the structure of the tree in any way is a major breaking change. It forces the consumers to update their visitors, pattern matches, and method calls. It should not be taking lightly, and can only happen on a major version change. So keep that in mind.
4
+
5
+ That said, if you do want to change the structure of the tree, there are a few steps that you have to take. They are enumerated below.
6
+
7
+ 1. Change the structure in the required node classes. This could mean adding/removing classes or adding/removing fields. Be sure to also update the `copy` and `===` methods to be sure that they are correct.
8
+ 2. Update the parser to correctly create the new structure.
9
+ 3. Update any visitor methods that are affected by the change. For example, if adding a new node make sure to create the new visit method alias in the `Visitor` class.
10
+ 4. Update the `FieldVisitor` class to be sure that the various serializers, pretty printers, and matchers all get updated accordingly.
11
+ 5. Update the `DSL` module to be sure that folks can correctly create nodes with the new structure.
12
+ 6. Ensure the formatting of the code hasn't changed. This can mostly be done by running the tests, but if there's a corner case that we don't cover that is now exposed by your change be sure to add test cases.
13
+ 7. Update the translation visitors to ensure we're still translating into other ASTs correctly.
14
+ 8. Update the YARV compiler visitor to ensure we're still compiling correctly.
15
+ 9. Make sure we aren't referencing the previous structure in any documentation or tests.
16
+ 10. Be sure to update `CHANGELOG.md` with a description of the change that you made.
@@ -29,7 +29,7 @@ module SyntaxTree
29
29
  def corrections
30
30
  @corrections ||=
31
31
  DidYouMean::SpellChecker.new(
32
- dictionary: Visitor.visit_methods
32
+ dictionary: BasicVisitor.valid_visit_methods
33
33
  ).correct(visit_method)
34
34
  end
35
35
 
@@ -40,7 +40,40 @@ module SyntaxTree
40
40
  end
41
41
  end
42
42
 
43
+ # This module is responsible for checking all of the methods defined within
44
+ # a given block to ensure that they are valid visit methods.
45
+ class VisitMethodsChecker < Module
46
+ Status = Struct.new(:checking)
47
+
48
+ # This is the status of the checker. It's used to determine whether or not
49
+ # we should be checking the methods that are defined. It is kept as an
50
+ # instance variable so that it can be disabled later.
51
+ attr_reader :status
52
+
53
+ def initialize
54
+ # We need the status to be an instance variable so that it can be
55
+ # accessed by the disable! method, but also a local variable so that it
56
+ # can be captured by the define_method block.
57
+ status = @status = Status.new(true)
58
+
59
+ define_method(:method_added) do |name|
60
+ BasicVisitor.visit_method(name) if status.checking
61
+ super(name)
62
+ end
63
+ end
64
+
65
+ def disable!
66
+ status.checking = false
67
+ end
68
+ end
69
+
43
70
  class << self
71
+ # This is the list of all of the valid visit methods.
72
+ def valid_visit_methods
73
+ @valid_visit_methods ||=
74
+ Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
75
+ end
76
+
44
77
  # This method is here to help folks write visitors.
45
78
  #
46
79
  # It's not always easy to ensure you're writing the correct method name in
@@ -51,15 +84,21 @@ module SyntaxTree
51
84
  # name. It will raise an error if the visit method you're defining isn't
52
85
  # actually a method on the parent visitor.
53
86
  def visit_method(method_name)
54
- return if visit_methods.include?(method_name)
87
+ return if valid_visit_methods.include?(method_name)
55
88
 
56
89
  raise VisitMethodError, method_name
57
90
  end
58
91
 
59
- # This is the list of all of the valid visit methods.
92
+ # This method is here to help folks write visitors.
93
+ #
94
+ # Within the given block, every method that is defined will be checked to
95
+ # ensure it's a valid visit method using the BasicVisitor::visit_method
96
+ # method defined above.
60
97
  def visit_methods
61
- @visit_methods ||=
62
- Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
98
+ checker = VisitMethodsChecker.new
99
+ extend(checker)
100
+ yield
101
+ checker.disable!
63
102
  end
64
103
  end
65
104
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "etc"
3
4
  require "optparse"
4
5
 
5
6
  module SyntaxTree
@@ -238,7 +239,7 @@ module SyntaxTree
238
239
  # representation.
239
240
  class Json < Action
240
241
  def run(item)
241
- object = Visitor::JSONVisitor.new.visit(item.handler.parse(item.source))
242
+ object = item.handler.parse(item.source).accept(JSONVisitor.new)
242
243
  puts JSON.pretty_generate(object)
243
244
  end
244
245
  end
@@ -501,7 +502,6 @@ module SyntaxTree
501
502
  when "j", "json"
502
503
  Json.new(options)
503
504
  when "lsp"
504
- require "syntax_tree/language_server"
505
505
  LanguageServer.new(print_width: options.print_width).run
506
506
  return 0
507
507
  when "m", "match"
@@ -210,12 +210,17 @@ module SyntaxTree
210
210
  end
211
211
 
212
212
  # Create a new ClassDeclaration node.
213
- def ClassDeclaration(constant, superclass, bodystmt)
213
+ def ClassDeclaration(
214
+ constant,
215
+ superclass,
216
+ bodystmt,
217
+ location = Location.default
218
+ )
214
219
  ClassDeclaration.new(
215
220
  constant: constant,
216
221
  superclass: superclass,
217
222
  bodystmt: bodystmt,
218
- location: Location.default
223
+ location: location
219
224
  )
220
225
  end
221
226
 
@@ -225,12 +230,12 @@ module SyntaxTree
225
230
  end
226
231
 
227
232
  # Create a new Command node.
228
- def Command(message, arguments, block)
233
+ def Command(message, arguments, block, location = Location.default)
229
234
  Command.new(
230
235
  message: message,
231
236
  arguments: arguments,
232
237
  block: block,
233
- location: Location.default
238
+ location: location
234
239
  )
235
240
  end
236
241
 
@@ -247,8 +252,8 @@ module SyntaxTree
247
252
  end
248
253
 
249
254
  # Create a new Comment node.
250
- def Comment(value, inline)
251
- Comment.new(value: value, inline: inline, location: Location.default)
255
+ def Comment(value, inline, location = Location.default)
256
+ Comment.new(value: value, inline: inline, location: location)
252
257
  end
253
258
 
254
259
  # Create a new Const node.
@@ -285,14 +290,21 @@ module SyntaxTree
285
290
  end
286
291
 
287
292
  # Create a new DefNode node.
288
- def DefNode(target, operator, name, params, bodystmt)
293
+ def DefNode(
294
+ target,
295
+ operator,
296
+ name,
297
+ params,
298
+ bodystmt,
299
+ location = Location.default
300
+ )
289
301
  DefNode.new(
290
302
  target: target,
291
303
  operator: operator,
292
304
  name: name,
293
305
  params: params,
294
306
  bodystmt: bodystmt,
295
- location: Location.default
307
+ location: location
296
308
  )
297
309
  end
298
310
 
@@ -565,8 +577,8 @@ module SyntaxTree
565
577
  end
566
578
 
567
579
  # Create a new MethodAddBlock node.
568
- def MethodAddBlock(call, block)
569
- MethodAddBlock.new(call: call, block: block, location: Location.default)
580
+ def MethodAddBlock(call, block, location = Location.default)
581
+ MethodAddBlock.new(call: call, block: block, location: location)
570
582
  end
571
583
 
572
584
  # Create a new MLHS node.
@@ -779,7 +791,7 @@ module SyntaxTree
779
791
 
780
792
  # Create a new Statements node.
781
793
  def Statements(body)
782
- Statements.new(nil, body: body, location: Location.default)
794
+ Statements.new(body: body, location: Location.default)
783
795
  end
784
796
 
785
797
  # Create a new StringContent node.