yoda-language-server 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +78 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +85 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/client/atom/main.js +27 -0
  13. data/client/vscode/.gitignore +4 -0
  14. data/client/vscode/.vscode/launch.json +28 -0
  15. data/client/vscode/.vscode/settings.json +9 -0
  16. data/client/vscode/.vscode/tasks.json +20 -0
  17. data/client/vscode/.vscodeignore +8 -0
  18. data/client/vscode/CHANGELOG.md +7 -0
  19. data/client/vscode/README.md +65 -0
  20. data/client/vscode/package-lock.json +2688 -0
  21. data/client/vscode/package.json +39 -0
  22. data/client/vscode/src/extension.ts +42 -0
  23. data/client/vscode/src/test/extension.test.ts +22 -0
  24. data/client/vscode/src/test/index.ts +22 -0
  25. data/client/vscode/tsconfig.json +16 -0
  26. data/client/vscode/vsc-extension-quickstart.md +33 -0
  27. data/exe/yoda +27 -0
  28. data/lib/yoda.rb +11 -0
  29. data/lib/yoda/evaluation.rb +9 -0
  30. data/lib/yoda/evaluation/code_completion.rb +65 -0
  31. data/lib/yoda/evaluation/code_completion/base_provider.rb +57 -0
  32. data/lib/yoda/evaluation/code_completion/const_provider.rb +90 -0
  33. data/lib/yoda/evaluation/code_completion/method_provider.rb +82 -0
  34. data/lib/yoda/evaluation/code_completion/variable_provider.rb +18 -0
  35. data/lib/yoda/evaluation/comment_completion.rb +70 -0
  36. data/lib/yoda/evaluation/comment_completion/base_provider.rb +64 -0
  37. data/lib/yoda/evaluation/comment_completion/param_provider.rb +18 -0
  38. data/lib/yoda/evaluation/comment_completion/tag_provider.rb +41 -0
  39. data/lib/yoda/evaluation/comment_completion/type_provider.rb +58 -0
  40. data/lib/yoda/evaluation/current_node_explain.rb +70 -0
  41. data/lib/yoda/evaluation/evaluator.rb +103 -0
  42. data/lib/yoda/evaluation/signature_discovery.rb +83 -0
  43. data/lib/yoda/model.rb +12 -0
  44. data/lib/yoda/model/completion_item.rb +56 -0
  45. data/lib/yoda/model/descriptions.rb +10 -0
  46. data/lib/yoda/model/descriptions/base.rb +26 -0
  47. data/lib/yoda/model/descriptions/function_description.rb +40 -0
  48. data/lib/yoda/model/descriptions/value_description.rb +33 -0
  49. data/lib/yoda/model/descriptions/word_description.rb +32 -0
  50. data/lib/yoda/model/function_signatures.rb +13 -0
  51. data/lib/yoda/model/function_signatures/base.rb +68 -0
  52. data/lib/yoda/model/function_signatures/constructor.rb +70 -0
  53. data/lib/yoda/model/function_signatures/formatter.rb +82 -0
  54. data/lib/yoda/model/function_signatures/method.rb +67 -0
  55. data/lib/yoda/model/function_signatures/overload.rb +79 -0
  56. data/lib/yoda/model/function_signatures/parameter_list.rb +108 -0
  57. data/lib/yoda/model/function_signatures/type_builder.rb +101 -0
  58. data/lib/yoda/model/node_signature.rb +28 -0
  59. data/lib/yoda/model/path.rb +96 -0
  60. data/lib/yoda/model/scoped_path.rb +44 -0
  61. data/lib/yoda/model/types.rb +84 -0
  62. data/lib/yoda/model/types/any_type.rb +32 -0
  63. data/lib/yoda/model/types/base.rb +37 -0
  64. data/lib/yoda/model/types/duck_type.rb +41 -0
  65. data/lib/yoda/model/types/function_type.rb +174 -0
  66. data/lib/yoda/model/types/generic_type.rb +66 -0
  67. data/lib/yoda/model/types/instance_type.rb +42 -0
  68. data/lib/yoda/model/types/module_type.rb +42 -0
  69. data/lib/yoda/model/types/sequence_type.rb +53 -0
  70. data/lib/yoda/model/types/union_type.rb +56 -0
  71. data/lib/yoda/model/types/unknown_type.rb +40 -0
  72. data/lib/yoda/model/types/value_type.rb +58 -0
  73. data/lib/yoda/model/values.rb +9 -0
  74. data/lib/yoda/model/values/base.rb +32 -0
  75. data/lib/yoda/model/values/instance_value.rb +65 -0
  76. data/lib/yoda/model/values/module_value.rb +72 -0
  77. data/lib/yoda/parsing.rb +15 -0
  78. data/lib/yoda/parsing/ast_traversable.rb +18 -0
  79. data/lib/yoda/parsing/comment_tokenizer.rb +59 -0
  80. data/lib/yoda/parsing/location.rb +101 -0
  81. data/lib/yoda/parsing/node_objects.rb +10 -0
  82. data/lib/yoda/parsing/node_objects/const_node.rb +52 -0
  83. data/lib/yoda/parsing/node_objects/method_definition.rb +46 -0
  84. data/lib/yoda/parsing/node_objects/namespace.rb +104 -0
  85. data/lib/yoda/parsing/node_objects/send_node.rb +72 -0
  86. data/lib/yoda/parsing/parser.rb +27 -0
  87. data/lib/yoda/parsing/query.rb +11 -0
  88. data/lib/yoda/parsing/query/current_comment_query.rb +80 -0
  89. data/lib/yoda/parsing/query/current_comment_token_query.rb +153 -0
  90. data/lib/yoda/parsing/query/current_commenting_node_query.rb +68 -0
  91. data/lib/yoda/parsing/query/current_location_node_query.rb +51 -0
  92. data/lib/yoda/parsing/query/current_node_comment_query.rb +40 -0
  93. data/lib/yoda/parsing/range.rb +41 -0
  94. data/lib/yoda/parsing/scopes.rb +15 -0
  95. data/lib/yoda/parsing/scopes/base.rb +78 -0
  96. data/lib/yoda/parsing/scopes/builder.rb +60 -0
  97. data/lib/yoda/parsing/scopes/class_definition.rb +47 -0
  98. data/lib/yoda/parsing/scopes/meta_class_definition.rb +44 -0
  99. data/lib/yoda/parsing/scopes/meta_method_definition.rb +70 -0
  100. data/lib/yoda/parsing/scopes/method_definition.rb +69 -0
  101. data/lib/yoda/parsing/scopes/module_definition.rb +36 -0
  102. data/lib/yoda/parsing/scopes/root.rb +25 -0
  103. data/lib/yoda/parsing/source_analyzer.rb +59 -0
  104. data/lib/yoda/parsing/source_cutter.rb +231 -0
  105. data/lib/yoda/parsing/type_parser.rb +141 -0
  106. data/lib/yoda/runner.rb +6 -0
  107. data/lib/yoda/runner/infer.rb +50 -0
  108. data/lib/yoda/runner/setup.rb +26 -0
  109. data/lib/yoda/server.rb +191 -0
  110. data/lib/yoda/server/client_info.rb +98 -0
  111. data/lib/yoda/server/completion_provider.rb +78 -0
  112. data/lib/yoda/server/definition_provider.rb +36 -0
  113. data/lib/yoda/server/deserializer.rb +27 -0
  114. data/lib/yoda/server/hover_provider.rb +38 -0
  115. data/lib/yoda/server/signature_provider.rb +46 -0
  116. data/lib/yoda/store.rb +13 -0
  117. data/lib/yoda/store/actions.rb +10 -0
  118. data/lib/yoda/store/actions/import_core_library.rb +30 -0
  119. data/lib/yoda/store/actions/import_gems.rb +91 -0
  120. data/lib/yoda/store/actions/read_file.rb +36 -0
  121. data/lib/yoda/store/actions/read_project_files.rb +29 -0
  122. data/lib/yoda/store/adapters.rb +14 -0
  123. data/lib/yoda/store/adapters/base.rb +58 -0
  124. data/lib/yoda/store/adapters/leveldb_adapter.rb +80 -0
  125. data/lib/yoda/store/adapters/lmdb_adapter.rb +113 -0
  126. data/lib/yoda/store/objects.rb +46 -0
  127. data/lib/yoda/store/objects/addressable.rb +25 -0
  128. data/lib/yoda/store/objects/base.rb +116 -0
  129. data/lib/yoda/store/objects/class_object.rb +51 -0
  130. data/lib/yoda/store/objects/merger.rb +94 -0
  131. data/lib/yoda/store/objects/meta_class_object.rb +41 -0
  132. data/lib/yoda/store/objects/method_object.rb +94 -0
  133. data/lib/yoda/store/objects/module_object.rb +11 -0
  134. data/lib/yoda/store/objects/namespace_object.rb +67 -0
  135. data/lib/yoda/store/objects/overload.rb +51 -0
  136. data/lib/yoda/store/objects/patch.rb +46 -0
  137. data/lib/yoda/store/objects/patch_set.rb +80 -0
  138. data/lib/yoda/store/objects/tag.rb +62 -0
  139. data/lib/yoda/store/objects/value_object.rb +45 -0
  140. data/lib/yoda/store/project.rb +159 -0
  141. data/lib/yoda/store/query.rb +12 -0
  142. data/lib/yoda/store/query/associators.rb +10 -0
  143. data/lib/yoda/store/query/associators/associate_ancestors.rb +103 -0
  144. data/lib/yoda/store/query/associators/associate_methods.rb +38 -0
  145. data/lib/yoda/store/query/base.rb +16 -0
  146. data/lib/yoda/store/query/find_constant.rb +150 -0
  147. data/lib/yoda/store/query/find_meta_class.rb +18 -0
  148. data/lib/yoda/store/query/find_method.rb +74 -0
  149. data/lib/yoda/store/query/find_signature.rb +43 -0
  150. data/lib/yoda/store/registry.rb +67 -0
  151. data/lib/yoda/store/yard_importer.rb +260 -0
  152. data/lib/yoda/typing.rb +10 -0
  153. data/lib/yoda/typing/context.rb +96 -0
  154. data/lib/yoda/typing/environment.rb +35 -0
  155. data/lib/yoda/typing/evaluator.rb +256 -0
  156. data/lib/yoda/typing/lexical_scope.rb +26 -0
  157. data/lib/yoda/typing/relation.rb +15 -0
  158. data/lib/yoda/typing/traces.rb +9 -0
  159. data/lib/yoda/typing/traces/base.rb +26 -0
  160. data/lib/yoda/typing/traces/normal.rb +22 -0
  161. data/lib/yoda/typing/traces/send.rb +26 -0
  162. data/lib/yoda/version.rb +3 -0
  163. data/lib/yoda/yard_extensions.rb +11 -0
  164. data/lib/yoda/yard_extensions/sig_directive.rb +40 -0
  165. data/lib/yoda/yard_extensions/type_tag.rb +10 -0
  166. data/package.json +76 -0
  167. data/scripts/benchmark.rb +6 -0
  168. data/scripts/build_core_index.sh +16 -0
  169. data/yarn.lock +13 -0
  170. data/yoda-language-server.gemspec +40 -0
  171. metadata +424 -0
@@ -0,0 +1,82 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CodeCompletion
4
+ class MethodProvider < BaseProvider
5
+ # @return [true, false]
6
+ def providable?
7
+ !!(current_send)
8
+ end
9
+
10
+ # @return [Array<Model::CompletionItem>]
11
+ def candidates
12
+ method_candidates.map do |method_candidate|
13
+ Model::CompletionItem.new(
14
+ description: Model::Descriptions::FunctionDescription.new(method_candidate),
15
+ range: substitution_range,
16
+ kind: :method,
17
+ )
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ # @return [Range]
24
+ def substitution_range
25
+ return current_send.selector_range if current_send.on_selector?(location)
26
+ return Parsing::Range.new(current_send.next_location_to_dot, current_send.next_location_to_dot) if current_send.on_dot?(location)
27
+ nil
28
+ end
29
+
30
+ # @return [Array<Store::Objects::Method>]
31
+ def method_candidates
32
+ return [] unless providable?
33
+ receiver_values
34
+ .map { |value| Store::Query::FindSignature.new(registry).select(value, /\A#{Regexp.escape(index_word)}/, visibility: method_visibility_of_send_node(current_send)) }
35
+ .flatten
36
+ end
37
+
38
+ # @param send_node [Parsing::NodeObjects::SendNode]
39
+ # @return [Array<Symbol>]
40
+ def method_visibility_of_send_node(send_node)
41
+ if send_node.receiver_node
42
+ %i(public)
43
+ else
44
+ %i(public private protected)
45
+ end
46
+ end
47
+
48
+ # @return [Array<Store::Objects::Base>]
49
+ def receiver_values
50
+ @receiver_values ||= begin
51
+ if current_receiver_node
52
+ evaluator.calculate_values(current_receiver_node)
53
+ else
54
+ # implicit call for self
55
+ [evaluator.scope_constant]
56
+ end
57
+ end
58
+ end
59
+
60
+ # @return [Parsing::NodeObjects::SendNode, nil]
61
+ def current_send
62
+ @current_send ||= begin
63
+ node = source_analyzer.nodes_to_current_location_from_root.last
64
+ return nil unless node.type == :send
65
+ Parsing::NodeObjects::SendNode.new(node)
66
+ end
67
+ end
68
+
69
+ # @return [Parser::AST::Node, nil]
70
+ def current_receiver_node
71
+ current_send&.receiver_node
72
+ end
73
+
74
+ # @return [String, nil]
75
+ def index_word
76
+ return nil unless providable?
77
+ @index_word ||= current_send.on_selector?(location) ? current_send.selector_name.slice(0..current_send.offset_in_selector(location)) : ''
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,18 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CodeCompletion
4
+ # WIP
5
+ class VariableProvider < BaseProvider
6
+ # @return [true, false]
7
+ def providable?
8
+ false
9
+ end
10
+
11
+ # @return [Array<Model::CompletionItem>]
12
+ def candidates
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CommentCompletion
4
+ require 'yoda/evaluation/comment_completion/base_provider'
5
+ require 'yoda/evaluation/comment_completion/param_provider'
6
+ require 'yoda/evaluation/comment_completion/tag_provider'
7
+ require 'yoda/evaluation/comment_completion/type_provider'
8
+
9
+ # @type Store::Registry
10
+ attr_reader :registry
11
+
12
+ # @type ::Parser::AST::Node
13
+ attr_reader :ast
14
+
15
+ # @type Array<::Parser::Source::Comment>
16
+ attr_reader :comments
17
+
18
+ # @type Location
19
+ attr_reader :location
20
+
21
+ # @param registry [Store::Registry]
22
+ # @param ast [::Parser::AST::Node]
23
+ # @param comments [Array<::Parser::Source::Comment>]
24
+ # @param location [Location]
25
+ def initialize(registry, ast, comments, location)
26
+ @registry = registry
27
+ @ast = ast
28
+ @comments = comments
29
+ @location = location
30
+ end
31
+
32
+ # @return [true, false]
33
+ def available?
34
+ comment? && providers.any?(&:available?)
35
+ end
36
+
37
+ # @return [Array<Model::CompletionItem>]
38
+ def candidates
39
+ available? ? providers.select(&:available?).map(&:candidates).flatten : []
40
+ end
41
+
42
+ private
43
+
44
+ # @return [Array<CommentCompletion::BaseProvider>]
45
+ def providers
46
+ [param_provider, tag_provider, type_provider]
47
+ end
48
+
49
+ # @return [ParamProvider]
50
+ def param_provider
51
+ @param_provider ||= ParamProvider.new(registry, ast, comments, location)
52
+ end
53
+
54
+ # @return [TagProvider]
55
+ def tag_provider
56
+ @tag_provider ||= TagProvider.new(registry, ast, comments, location)
57
+ end
58
+
59
+ # @return [TypeProvider]
60
+ def type_provider
61
+ @type_provider ||= TypeProvider.new(registry, ast, comments, location)
62
+ end
63
+
64
+ def comment?
65
+ return @is_comment if instance_variable_defined?(:@is_comment)
66
+ @is_comment = !!Parsing::Query::CurrentCommentQuery.new(comments, location).current_comment
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,64 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CommentCompletion
4
+ # @abstract
5
+ class BaseProvider
6
+ # @type Store::Registry
7
+ attr_reader :registry
8
+
9
+ # @type ::Parser::AST::Node
10
+ attr_reader :ast
11
+
12
+ # @type Array<::Parser::Source::Comment>
13
+ attr_reader :comments
14
+
15
+ # @type Location
16
+ attr_reader :location
17
+
18
+ # @param registry [Store::Registry]
19
+ # @param ast [::Parser::AST::Node]
20
+ # @param comments [Array<::Parser::Source::Comment>]
21
+ # @param location [Location]
22
+ def initialize(registry, ast, comments, location)
23
+ @registry = registry
24
+ @ast = ast
25
+ @comments = comments
26
+ @location = location
27
+ end
28
+
29
+ # @abstract
30
+ # @return [true, false]
31
+ def available?
32
+ fail NotImplementedError
33
+ end
34
+
35
+ # @abstract
36
+ # @return [Array<Model::CompletionItem>]
37
+ def candidates
38
+ fail NotImplementedError
39
+ end
40
+
41
+ private
42
+
43
+ # @return [Parsing::Query::CurrentCommentTokenQuery]
44
+ def current_comment_token_query
45
+ @current_comment_token_query ||=
46
+ Parsing::Query::CurrentCommentTokenQuery.new(
47
+ current_comment_query.current_comment_block_text,
48
+ current_comment_query.location_in_current_comment_block,
49
+ )
50
+ end
51
+
52
+ # @return [Parsing::Query::CurrentCommentQuery]
53
+ def current_comment_query
54
+ @current_comment_query ||= Parsing::Query::CurrentCommentQuery.new(comments, location)
55
+ end
56
+
57
+ # @return [CurrentCommentingNodeQuery]
58
+ def current_commenting_node_query
59
+ Parsing::Query::CurrentCommentingNodeQuery.new(ast, comments, location)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CommentCompletion
4
+ # @note WIP
5
+ class ParamProvider < BaseProvider
6
+ # @return [true, false]
7
+ def available?
8
+ current_comment_token_query.current_state == :param
9
+ end
10
+
11
+ # @return [Array<Model::CompletionItem>]
12
+ def candidates
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CommentCompletion
4
+ class TagProvider < BaseProvider
5
+ # @return [true, false]
6
+ def available?
7
+ current_comment_token_query.current_state == :tag
8
+ end
9
+
10
+ # @return [Array<Model::CompletionItem>]
11
+ def candidates
12
+ description_candidates.map { |description| Model::CompletionItem.new(description: description, range: substitution_range) }
13
+ end
14
+
15
+ private
16
+
17
+ # @return [Array<Model::Descriptions::WordDescription>]
18
+ def description_candidates
19
+ return [] unless available?
20
+ tagnames.select { |tagname| tagname.start_with?(index_word) }.map { |obj| Model::Descriptions::WordDescription.new(obj) }
21
+ end
22
+
23
+ # @return [Parsing::Range, nil]
24
+ def substitution_range
25
+ return nil unless available?
26
+ current_comment_query.absolute_range(current_comment_token_query.current_range)
27
+ end
28
+
29
+ # @return [String]
30
+ def index_word
31
+ current_comment_token_query.current_word
32
+ end
33
+
34
+ # @return [Array<String>]
35
+ def tagnames
36
+ @tagnames ||= YARD::Tags::Library.labels.map { |tag_symbol, label| "@#{tag_symbol}" }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CommentCompletion
4
+ class TypeProvider < BaseProvider
5
+ # @return [true, false]
6
+ def available?
7
+ [:type, :type_tag_type].include?(current_comment_token_query.current_state)
8
+ end
9
+
10
+ # @return [Array<Model::CompletionItem>]
11
+ def candidates
12
+ description_candidates.map { |description| Model::CompletionItem.new(description: description, range: substitution_range) }
13
+ end
14
+
15
+ private
16
+
17
+ # @return [Array<Model::Descriptions::Base>]
18
+ def description_candidates
19
+ return [] unless available?
20
+ scoped_path = Model::ScopedPath.new(lexical_scope(namespace), index_word)
21
+ Store::Query::FindConstant.new(registry).select_with_prefix(scoped_path).map { |obj| Model::Descriptions::ValueDescription.new(obj) }
22
+ end
23
+
24
+ # @return [Parsing::Range, nil]
25
+ def substitution_range
26
+ return nil unless available?
27
+ # @todo Move this routine to Parsing module
28
+ if current_comment_token_query.current_range
29
+ range = current_comment_token_query.current_range.move(
30
+ row: current_comment_query.begin_point_of_current_comment_block.row - 1,
31
+ column: current_comment_query.begin_point_of_current_comment_block.column,
32
+ )
33
+ cut_point = current_comment_token_query.at_sign? ? 1 : (current_comment_token_query.current_word.rindex('::') || -2) + 2
34
+ Parsing::Range.new(range.begin_location.move(row: 0, column: cut_point), range.end_location)
35
+ else
36
+ Parsing::Range.new(location, location)
37
+ end
38
+ end
39
+
40
+ # @return [String]
41
+ def index_word
42
+ current_comment_token_query.at_sign? ? '' : current_comment_token_query.current_word
43
+ end
44
+
45
+ # @return [Parsing::NodeObjects::Namespace, nil]
46
+ def namespace
47
+ current_commenting_node_query.current_namespace
48
+ end
49
+
50
+ # @param namespace [Parsing::NodeObjects::Namespace]
51
+ # @return [Array<Path>]
52
+ def lexical_scope(namespace)
53
+ namespace.paths_from_root.reverse.map { |name| Model::Path.build(name.empty? ? 'Object' : name.gsub(/\A::/, '')) }
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,70 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class CurrentNodeExplain
4
+ # @return [Store::Registry]
5
+ attr_reader :registry
6
+
7
+ # @return [String]
8
+ attr_reader :source
9
+
10
+ # @return [Parsing::Location]
11
+ attr_reader :location
12
+
13
+ # @param registry [Store::Registry]
14
+ # @param source [String]
15
+ # @param location [Parsing::Location]
16
+ def initialize(registry, source, location)
17
+ @registry = registry
18
+ @source = source
19
+ @location = location
20
+ end
21
+
22
+ # @return [Model::NodeSignature, nil]
23
+ def current_node_signature
24
+ return nil if !valid? || !current_node_trace
25
+ @current_node_signature ||= Model::NodeSignature.new(current_node, current_node_trace)
26
+ end
27
+
28
+ # @return [true, false]
29
+ def valid?
30
+ !!(current_node)
31
+ end
32
+
33
+ # @return [Array<(String, Integer, Integer)>]
34
+ def defined_files
35
+ return [] if !valid? || !current_node_trace
36
+ case current_node.type
37
+ when :send
38
+ current_node_trace.functions.map { |function| function.primary_source }.compact
39
+ when :const
40
+ current_node_trace.values.map { |value| value.primary_source || value.sources.first }.compact
41
+ else
42
+ []
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # @return [Typing::Traces::Base, nil]
49
+ def current_node_trace
50
+ return nil unless valid?
51
+ @current_node_trace ||= evaluator.calculate_trace(current_node)
52
+ end
53
+
54
+ # @return [Parser::AST::Node]
55
+ def current_node
56
+ analyzer.nodes_to_current_location_from_root.last
57
+ end
58
+
59
+ # @return [Evaluator]
60
+ def evaluator
61
+ @evaluator ||= Evaluator.from_ast(registry, analyzer.ast, location)
62
+ end
63
+
64
+ # @return [SourceAnalyzer]
65
+ def analyzer
66
+ @analyzer ||= Parsing::SourceAnalyzer.from_source(source, location)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,103 @@
1
+ module Yoda
2
+ module Evaluation
3
+ class Evaluator
4
+ # @return [Parsing::Scopes::Base]
5
+ attr_reader :scope
6
+
7
+ # @return [Store::Registry]
8
+ attr_reader :registry
9
+
10
+ # @param registry [Store::Registry]
11
+ # @param ast [Parser::AST::Node]
12
+ # @param location [Parsing::Location]
13
+ # @return [Evaluator]
14
+ def self.from_ast(registry, ast, location)
15
+ from_root_scope(registry, Parsing::Scopes::Builder.new(ast).root_scope, location)
16
+ end
17
+
18
+ # @param registry [Store::Registry]
19
+ # @param root_scope [Parsing::Scopes::Root]
20
+ # @param location [Parsing::Location]
21
+ # @return [Evaluator]
22
+ def self.from_root_scope(registry, root_scope, location)
23
+ new(registry, root_scope.find_evaluation_root_scope(location) || root_scope)
24
+ end
25
+
26
+ # @param registry [Store::Registry]
27
+ # @param scope [Parsing::Scopes::Base]
28
+ def initialize(registry, scope)
29
+ @registry = registry
30
+ @scope = scope
31
+ end
32
+
33
+ # @param code_node [::Parser::AST::Node]
34
+ # @return [Model::Types::Base, nil]
35
+ def calculate_type(code_node)
36
+ calculate_trace(code_node)&.type
37
+ end
38
+
39
+ # @param code_node [::Parser::AST::Node]
40
+ # @return [Array<Store::Objects::Base>]
41
+ def calculate_values(code_node)
42
+ trace = calculate_trace(code_node)
43
+ trace ? trace.values : []
44
+ end
45
+
46
+ # @param code_node [::Parser::AST::Node, nil]
47
+ # @return [Typing::Traces::Base, nil]
48
+ def calculate_trace(code_node)
49
+ return nil unless code_node
50
+ evaluate
51
+ evaluator.find_trace(code_node)
52
+ end
53
+
54
+ # @return [Store::Objects::Base, nil]
55
+ def scope_constant
56
+ @scope_constant ||= begin
57
+ Store::Query::FindConstant.new(registry).find(scope.scope_name)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def evaluate
64
+ unless @evaluated
65
+ evaluator.process(scope.body_node)
66
+ @evaluated = true
67
+ end
68
+ end
69
+
70
+ # @return [Typing::Evaluator]
71
+ def evaluator
72
+ @evaluator ||= Typing::Evaluator.new(evaluation_context)
73
+ end
74
+
75
+ # @return [Typing::Context]
76
+ def evaluation_context
77
+ @evaluation_context ||= begin
78
+ fail RuntimeError, "The namespace #{scope.scope_name} (#{scope}) is not registered" unless scope_constant
79
+ lexical_scope = Typing::LexicalScope.new(scope_constant, scope.ancestor_scopes)
80
+ context = Typing::Context.new(registry: registry, caller_object: receiver, lexical_scope: lexical_scope)
81
+ context.env.bind_method_parameters(current_method_signature) if current_method_signature
82
+ context
83
+ end
84
+ end
85
+
86
+ # @return [Model::FunctionSignatures::Base, nil]
87
+ def current_method_signature
88
+ return unless scope.kind == :method
89
+ @current_method_signature ||= Store::Query::FindSignature.new(registry).select(scope_constant, scope.name.to_s)&.first
90
+ end
91
+
92
+ def receiver
93
+ @receiver ||= begin
94
+ if scope.kind == :method
95
+ Store::Query::FindConstant.new(registry).find(scope.scope_name)
96
+ else
97
+ Store::Query::FindMetaClass.new(registry).find(scope.scope_name)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end