gloss 0.0.6 → 0.1.4

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +41 -0
  3. data/.gloss.yml +2 -0
  4. data/Gemfile.lock +10 -10
  5. data/README.md +5 -7
  6. data/Rakefile +5 -3
  7. data/exe/gloss +10 -13
  8. data/ext/gloss/Makefile +6 -21
  9. data/ext/gloss/lib/cr_ruby.cr +5 -4
  10. data/ext/gloss/src/cr_ast.cr +61 -71
  11. data/ext/gloss/src/gloss.cr +7 -2
  12. data/ext/gloss/src/rb_ast.cr +37 -36
  13. data/gloss.gemspec +3 -3
  14. data/lib/gloss.rb +10 -7
  15. data/lib/gloss/cli.rb +61 -40
  16. data/lib/gloss/config.rb +14 -8
  17. data/lib/gloss/errors.rb +1 -1
  18. data/lib/gloss/logger.rb +6 -6
  19. data/lib/gloss/prog_loader.rb +144 -0
  20. data/lib/gloss/scope.rb +1 -1
  21. data/lib/gloss/source.rb +1 -1
  22. data/lib/gloss/type_checker.rb +60 -32
  23. data/lib/gloss/utils.rb +38 -0
  24. data/lib/gloss/version.rb +1 -1
  25. data/lib/gloss/{builder.rb → visitor.rb} +88 -53
  26. data/lib/gloss/watcher.rb +14 -7
  27. data/lib/gloss/writer.rb +21 -10
  28. data/logo.svg +6 -0
  29. data/sig/core.rbs +2 -0
  30. data/sig/fast_blank.rbs +4 -0
  31. data/sig/{gloss.rbs → gls.rbs} +0 -0
  32. data/sig/optparse.rbs +6 -0
  33. data/sig/rubygems.rbs +9 -0
  34. data/sig/yaml.rbs +3 -0
  35. data/src/exe/gloss +19 -0
  36. data/src/lib/gloss.gl +25 -0
  37. data/src/lib/gloss/cli.gl +41 -26
  38. data/src/lib/gloss/config.gl +13 -8
  39. data/src/lib/gloss/logger.gl +2 -5
  40. data/src/lib/gloss/prog_loader.gl +138 -0
  41. data/src/lib/gloss/scope.gl +0 -2
  42. data/src/lib/gloss/type_checker.gl +57 -28
  43. data/src/lib/gloss/utils.gl +35 -0
  44. data/src/lib/gloss/version.gl +1 -1
  45. data/src/lib/gloss/{builder.gl → visitor.gl} +82 -45
  46. data/src/lib/gloss/watcher.gl +13 -8
  47. data/src/lib/gloss/writer.gl +15 -13
  48. metadata +29 -18
  49. data/.github/workflows/crystal_specs.yml +0 -26
  50. data/.github/workflows/ruby_specs.yml +0 -33
data/lib/gloss/config.rb CHANGED
@@ -8,14 +8,20 @@ require "yaml"
8
8
  module Gloss
9
9
  CONFIG_PATH = ".gloss.yml"
10
10
  Config = OpenStruct.new(default_config: {:frozen_string_literals => true,
11
- :src_dir => "src"}.freeze)
12
- user_config = (if File.exist?(CONFIG_PATH)
13
- YAML.safe_load(File.read(CONFIG_PATH))
14
- else
11
+ :src_dir => "src",
12
+ :entrypoint => nil,
13
+ :strict_require => false,
14
+ :type_checking_strictness => "strict"}.freeze)
15
+ def self.load_config()
16
+ user_config = (if File.exist?(File.absolute_path(File.join(Dir.pwd, CONFIG_PATH)))
17
+ YAML.safe_load(File.read(CONFIG_PATH))
18
+ else
19
+ Config.default_config
20
+ end)
15
21
  Config.default_config
16
- end)
17
- Config.default_config
18
22
  .each() { |k, v|
19
- Config.send(:"#{k}=", user_config.[](k.to_s) || v)
20
- }
23
+ Config.send(:"#{k}=", user_config.[](k.to_s) || v)
24
+ }
25
+ end
26
+ load_config
21
27
  end
data/lib/gloss/errors.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- module Gloss
6
+ module Gloss
7
7
  module Errors
8
8
  class BaseGlossError < StandardError
9
9
  end
data/lib/gloss/logger.rb CHANGED
@@ -22,13 +22,13 @@ nil => nil,
22
22
  @logger = Logger.new((if real_log_level
23
23
  STDOUT
24
24
  else
25
- nil
25
+ IO::NULL
26
26
  end))
27
- formatter = Logger::Formatter.new
28
- @logger.formatter=(proc() { |severity, datetime, progname, msg|
29
- formatter.call(severity, datetime, progname, msg)
30
- })
31
- @logger
27
+ .tap() { |l|
28
+ (if real_log_level
29
+ l.level=(real_log_level)
30
+ end)
31
+ }
32
32
  end)
33
33
  end
34
34
  end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
4
+ ##### See src/ to make changes
5
+
6
+ require "rubygems/gem_runner"
7
+ module Gloss
8
+ OUTPUT_BY_PATH = Hash.new
9
+ class ProgLoader
10
+ attr_reader(:"type_checker")
11
+ def initialize()
12
+ entrypoint = Config.entrypoint
13
+ (if entrypoint.==(nil) || entrypoint.==("")
14
+ throw(:"error", "Entrypoint is not yet set in .gloss.yml")
15
+ end)
16
+ @files_to_process = [Utils.absolute_path(Config.entrypoint), Utils.absolute_path(File.join(__dir__ || "", "..", "..", "sig", "core.rbs"))]
17
+ @processed_files = Set.new
18
+ @type_checker = TypeChecker.new
19
+ end
20
+ def run()
21
+ @files_to_process.each() { |path_string|
22
+ unless @processed_files.member?(path_string) || OUTPUT_BY_PATH.[](path_string)
23
+ Gloss.logger
24
+ .debug("Loading #{path_string}")
25
+ path = Utils.absolute_path(path_string)
26
+ file_contents = File.open(path)
27
+ .read
28
+ contents_tree = Parser.new(file_contents)
29
+ .run
30
+ on_new_file_referenced = proc() { |ps, relative|
31
+ ps.each() { |pa|
32
+ (if relative
33
+ handle_require_relative(pa, path_string)
34
+ else
35
+ handle_require(pa)
36
+ end)
37
+ }
38
+ }
39
+ OUTPUT_BY_PATH.[]=(path_string, Visitor.new(contents_tree, @type_checker, on_new_file_referenced)
40
+ .run)
41
+ @processed_files.add(path_string)
42
+ end
43
+ }
44
+ @type_checker
45
+ end
46
+ STDLIB_TYPE_DEPENDENCIES = {"yaml" => ["pstore", "dbm"],
47
+ "rbs" => ["logger", "set", "tsort"],
48
+ "logger" => ["monitor"]}
49
+ private def handle_require(path)
50
+ (if path.start_with?(".")
51
+ base = File.join(Dir.pwd, path)
52
+ fp = base.+(".gl")
53
+ (if File.exist?(fp)
54
+ @files_to_process.<<(fp)
55
+ end)
56
+ return
57
+ end)
58
+ full = File.absolute_path("#{File.join(Config.src_dir, "lib", path)}.gl")
59
+ pathn = Pathname.new(full)
60
+ (if pathn.file?
61
+ @files_to_process.<<(pathn.to_s)
62
+ else
63
+ pathn = Pathname.new("#{File.join(Dir.pwd, "sig", path)}.rbs")
64
+ gem_path = Utils.gem_path_for(path)
65
+ (if gem_path
66
+ sig_files = Dir.glob(File.absolute_path(File.join(gem_path, "..", "sig", "**", "*.rbs")))
67
+ (if sig_files.length
68
+ .positive?
69
+ sig_files.each() { |fp|
70
+ @type_checker.load_sig_path(fp)
71
+ }
72
+ @processed_files.add(path)
73
+ rbs_type_deps = STDLIB_TYPE_DEPENDENCIES.fetch(path) { ||
74
+ nil }
75
+ (if rbs_type_deps
76
+ rbs_type_deps.each() { |d|
77
+ handle_require(d)
78
+ }
79
+ end)
80
+ return
81
+ end)
82
+ end)
83
+ (if pathn.file?
84
+ @type_checker.load_sig_path(pathn.to_s)
85
+ @processed_files.add(pathn.to_s)
86
+ else
87
+ rbs_stdlib_dir = File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "stdlib", path))
88
+ (if Pathname.new(rbs_stdlib_dir)
89
+ .exist?
90
+ load_rbs_from_require_path(path)
91
+ rbs_type_deps = STDLIB_TYPE_DEPENDENCIES.fetch(path) { ||
92
+ nil }
93
+ (if rbs_type_deps
94
+ rbs_type_deps.each() { |d|
95
+ load_rbs_from_require_path(d)
96
+ }
97
+ end)
98
+ else
99
+ (if Config.strict_require
100
+ throw(:"error", "Cannot resolve require path for #{path}")
101
+ else
102
+ Gloss.logger
103
+ .debug("No path found for #{path}")
104
+ end)
105
+ end)
106
+ end)
107
+ end)
108
+ end
109
+ private def handle_require_relative(path, source_file)
110
+ base = File.join(source_file, "..", path)
111
+ # @type var pn: String?
112
+ pn = nil
113
+ exts = [".gl"].concat(Gem.suffixes)
114
+ exts.each() { |ext|
115
+ full = File.absolute_path(base.+(ext))
116
+ (if File.exist?(full)
117
+ pn = full
118
+ end)
119
+ }
120
+ (if pn
121
+ unless @files_to_process.include?(pn)
122
+ @files_to_process.<<(pn)
123
+ end
124
+ else
125
+ (if Config.strict_require
126
+ throw(:"error", "Cannot resolve relative path for #{path}")
127
+ else
128
+ Gloss.logger
129
+ .debug("No path found for #{path}")
130
+ end)
131
+ end)
132
+ end
133
+ private def rbs_stdlib_path_for(libr)
134
+ File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "stdlib", libr))
135
+ end
136
+ private def load_rbs_from_require_path(path)
137
+ Dir.glob(File.join(rbs_stdlib_path_for(path), "**", "*.rbs"))
138
+ .each() { |fp|
139
+ @type_checker.load_sig_path(fp)
140
+ @processed_files.add(fp)
141
+ }
142
+ end
143
+ end
144
+ end
data/lib/gloss/scope.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- module Gloss
6
+ module Gloss
7
7
  class Scope < Hash
8
8
  def [](k)
9
9
  fetch(k) { ||
data/lib/gloss/source.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- module Gloss
6
+ module Gloss
7
7
  class Source < String
8
8
  def initialize(indent_level)
9
9
  @indent_level = indent_level
@@ -3,21 +3,39 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- require "pry-byebug"
6
+ require "set"
7
7
  module Gloss
8
8
  class TypeChecker
9
- Project = Struct.new(:"targets")
10
- attr_reader(:"steep_target", :"top_level_decls")
9
+ attr_reader(:"steep_target", :"top_level_decls", :"env", :"rbs_gem_dir")
10
+ module Strictness
11
+ Strict = "strict"
12
+ Lenient = "lenient"
13
+ Default = "default"
14
+ end
11
15
  def initialize()
12
- @steep_target = Steep::Project::Target.new(name: "gloss", options: Steep::Project::Options.new
13
- .tap() { |o|
14
- o.allow_unknown_constant_assignment=(true)
15
- }, source_patterns: ["gloss.rb"], ignore_patterns: Array.new, signature_patterns: ["sig"])
16
- @top_level_decls = {}
16
+ options = Steep::Project::Options.new
17
+ case Config.type_checking_strictness
18
+ when Strictness::Strict
19
+ options.apply_strict_typing_options!
20
+ when Strictness::Lenient
21
+ options.apply_lenient_typing_options!
22
+ else
23
+ options.apply_default_typing_options!
24
+ end
25
+ @steep_target = Steep::Project::Target.new(name: "gloss", options: options, source_patterns: ["**/*.rb"], ignore_patterns: Array.new, signature_patterns: ["sig"])
26
+ @top_level_decls = Set.new
27
+ @rbs_gem_dir = Utils.gem_path_for("rbs")
28
+ env_loader = RBS::EnvironmentLoader.new
29
+ @env = RBS::Environment.from_loader(env_loader)
30
+ project = Steep::Project.new(steepfile_path: Pathname.new(Config.src_dir)
31
+ .realpath)
32
+ project.targets
33
+ .<<(@steep_target)
34
+ loader = Steep::Project::FileLoader.new(project: project)
17
35
  end
18
- def run(rb_str)
36
+ def run(filepath, rb_str)
19
37
  begin
20
- valid_types = check_types(rb_str)
38
+ valid_types = check_types(filepath, rb_str)
21
39
  rescue ParseError => e
22
40
  throw(:"error", "")
23
41
  rescue => e
@@ -34,10 +52,8 @@ case e
34
52
  when Steep::Diagnostic::Ruby::MethodBodyTypeMismatch
35
53
  "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
36
54
  when Steep::Diagnostic::Ruby::IncompatibleArguments
37
- "Invalid argmuents - method type: #{e.method_type}\nmethod name: #{e.method_type
38
- .method_decls
39
- .first
40
- .method_name}"
55
+ "Invalid argmuents - method type: #{e.method_types
56
+ .first}\nmethod name: #{e.method_name}"
41
57
  when Steep::Diagnostic::Ruby::ReturnTypeMismatch
42
58
  "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
43
59
  when Steep::Diagnostic::Ruby::IncompatibleAssignment
@@ -45,7 +61,7 @@ case e
45
61
  when Steep::Diagnostic::Ruby::UnexpectedBlockGiven
46
62
  "Unexpected block given"
47
63
  else
48
- e.inspect
64
+ "#{e.header_line}\n#{e}"
49
65
  end
50
66
  }
51
67
  .join("\n")
@@ -53,30 +69,36 @@ case e
53
69
  end
54
70
  true
55
71
  end
56
- def check_types(rb_str)
57
- env_loader = RBS::EnvironmentLoader.new
58
- env = RBS::Environment.from_loader(env_loader)
59
- project = Steep::Project.new(steepfile_path: Pathname.new(Config.src_dir)
60
- .realpath)
61
- project.targets
62
- .<<(@steep_target)
63
- loader = Steep::Project::FileLoader.new(project: project)
64
- loader.load_signatures
65
- @steep_target.add_source("gloss.rb", rb_str)
66
- @top_level_decls.each() { |_, decl|
67
- env.<<(decl)
72
+ def ready_for_checking!()
73
+ @top_level_decls.each() { |decl|
74
+ @env.<<(decl)
68
75
  }
69
- env = env.resolve_type_names
70
- @steep_target.instance_variable_set("@environment", env)
76
+ @env = @env.resolve_type_names
77
+ @steep_target.instance_variable_set("@environment", @env)
78
+ end
79
+ def check_types(filepath, rb_str)
80
+ @steep_target.add_source(filepath, rb_str)
81
+ ready_for_checking!
71
82
  @steep_target.type_check
72
83
  (if @steep_target.status
73
84
  .is_a?(Steep::Project::Target::SignatureErrorStatus)
74
85
  throw(:"error", @steep_target.status
75
86
  .errors
76
87
  .map() { |e|
77
- " SignatureSyntaxError:\n Location: #{e.location}\n Message: \"#{e.exception
78
- .error_value
79
- .value}\"" }
88
+ msg = case e
89
+ when Steep::Diagnostic::Signature::UnknownTypeName
90
+ "Unknown type name: #{e.name
91
+ .name} (#{e.location
92
+ .source
93
+ .[](/^.*$/)})"
94
+ when Steep::Diagnostic::Signature::InvalidTypeApplication
95
+ "Invalid type application: #{e.header_line}"
96
+ when Steep::Diagnostic::Signature::DuplicatedMethodDefinition
97
+ "Duplicated method: #{e.header_line}"
98
+ else
99
+ e.header_line
100
+ end
101
+ " SignatureSyntaxError:\n Location: #{e.location}\n Message: \"#{msg}\"" }
80
102
  .join("\n"))
81
103
  end)
82
104
  @steep_target.source_files
@@ -92,5 +114,11 @@ true
92
114
  .is_a?(Steep::Project::Target::TypeCheckStatus) && @steep_target.no_error? && @steep_target.errors
93
115
  .empty?
94
116
  end
117
+ def load_sig_path(path)
118
+ Gloss.logger
119
+ .debug("Loading signature file for #{path}")
120
+ @steep_target.add_signature(path, File.open(path)
121
+ .read)
122
+ end
95
123
  end
96
124
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
4
+ ##### See src/ to make changes
5
+
6
+ require "rubygems"
7
+ module Gloss
8
+ module Utils
9
+ module_function
10
+ def absolute_path(path)
11
+ pn = Pathname.new(path)
12
+ (if pn.absolute?
13
+ pn.to_s
14
+ else
15
+ ap = File.absolute_path(path)
16
+ (if File.exist?(ap)
17
+ ap
18
+ else
19
+ throw(:"error", "File path #{path} does not exist (also looked for #{ap})")
20
+ end)
21
+ end)
22
+ end
23
+ def gem_path_for(gem_name)
24
+ spec = Gem::Specification.find_by_path(gem_name)
25
+ (if spec
26
+ spec.full_require_paths
27
+ .first
28
+ end)
29
+ end
30
+ def with_file_header(str)
31
+ "#{Visitor::FILE_HEADER}\n\n#{str}"
32
+ end
33
+ def src_path_to_output_path(src_path)
34
+ src_path.sub("#{Config.src_dir}/", "")
35
+ .sub(/\.gl$/, ".rb")
36
+ end
37
+ end
38
+ end
data/lib/gloss/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  ##### See src/ to make changes
5
5
 
6
6
  module Gloss
7
- VERSION = "0.0.6"
7
+ VERSION = "0.1.4"
8
8
  end
@@ -4,29 +4,24 @@
4
4
  ##### See src/ to make changes
5
5
 
6
6
  module Gloss
7
- module Utils
8
- module_function
9
- def with_file_header(str)
10
- "#{Builder::FILE_HEADER}\n\n#{str}"
11
- end
12
- end
13
- class Builder
7
+ class Visitor
14
8
  FILE_HEADER = " #{(if Config.frozen_string_literals
15
9
  "# frozen_string_literal: true\n"
16
10
  end)}\n ##### This file was generated by Gloss; any changes made here will be overwritten.\n ##### See #{Config.src_dir}/ to make changes"
17
- include Utils
18
11
  attr_reader(:"tree")
19
- def initialize(tree_hash, type_checker = nil)
12
+ def initialize(tree_hash, type_checker = nil, on_new_file_referenced = nil)
13
+ @on_new_file_referenced = on_new_file_referenced
20
14
  @indent_level = 0
21
15
  @inside_macro = false
22
16
  @eval_vars = false
23
17
  @current_scope = nil
24
18
  @tree = tree_hash
25
19
  @type_checker = type_checker
20
+ @after_module_function = false
26
21
  end
27
22
  def run()
28
23
  rb_output = visit_node(@tree)
29
- with_file_header(rb_output)
24
+ Utils.with_file_header(rb_output)
30
25
  end
31
26
  def visit_node(node, scope = Scope.new)
32
27
  src = Source.new(@indent_level)
@@ -40,21 +35,30 @@ case node.[](:"type")
40
35
  RBS::Namespace.root
41
36
  end)
42
37
  superclass_type = nil
43
- superclass_output = nil
38
+ superclass_output = ""
44
39
  (if node.[](:"superclass")
45
40
  @eval_vars = true
46
41
  superclass_output = visit_node(node.[](:"superclass"))
47
42
  @eval_vars = false
48
- superclass_type = RBS::Parser.parse_type(superclass_output)
43
+ args = Array.new
49
44
  (if node.dig(:"superclass", :"type")
50
45
  .==("Generic")
51
- superclass_output = superclass_output.[](/^[^\[]+/)
46
+ superclass_output = superclass_output.[](/^[^\[]+/) || superclass_output
47
+ args = node.dig(:"superclass", :"args")
48
+ .map() { |n|
49
+ RBS::Parser.parse_type(visit_node(n))
50
+ }
52
51
  end)
52
+ class_name_index = superclass_output.index(/[^(?:::)]+\z/) || 0
53
+ namespace = superclass_output.[](0, class_name_index)
54
+ superclass_name = superclass_output.[](/[^(?:::)]+\z/) || superclass_output
55
+ superclass_type = RBS::AST::Declarations::Class::Super.new(name: RBS::TypeName.new(namespace: method(:"Namespace")
56
+ .call(namespace), name: superclass_name.to_sym), args: args, location: build_location(node))
53
57
  end)
54
- src.write_ln("class #{class_name}#{(if superclass_output
58
+ src.write_ln("class #{class_name}#{unless superclass_output.blank?
55
59
  " < #{superclass_output}"
56
- end)}")
57
- class_type = RBS::AST::Declarations::Class.new(name: RBS::TypeName.new(namespace: current_namespace, name: class_name.to_sym), type_params: RBS::AST::Declarations::ModuleTypeParams.new, super_class: superclass_type, members: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
60
+ end}")
61
+ class_type = RBS::AST::Declarations::Class.new(name: RBS::TypeName.new(namespace: current_namespace, name: class_name.to_sym), type_params: RBS::AST::Declarations::ModuleTypeParams.new, super_class: superclass_type, members: Array.new, annotations: Array.new, location: build_location(node), comment: node.[](:"comment"))
58
62
  old_parent_scope = @current_scope
59
63
  @current_scope = class_type
60
64
  indented(src) { ||
@@ -68,14 +72,13 @@ case node.[](:"type")
68
72
  @current_scope.members
69
73
  .<<(class_type)
70
74
  end)
71
- (if @type_checker
72
- unless @current_scope
73
- @type_checker.top_level_decls
74
- .[]=(class_type.name
75
- .name, class_type)
76
- end
75
+ (if @type_checker && !@current_scope
76
+ @type_checker.top_level_decls
77
+ .add(class_type)
77
78
  end)
78
79
  when "ModuleNode"
80
+ existing_module_function_state = @after_module_function.dup
81
+ @after_module_function = false
79
82
  module_name = visit_node(node.[](:"name"))
80
83
  src.write_ln("module #{module_name}")
81
84
  current_namespace = (if @current_scope
@@ -84,7 +87,7 @@ case node.[](:"type")
84
87
  else
85
88
  RBS::Namespace.root
86
89
  end)
87
- module_type = RBS::AST::Declarations::Module.new(name: RBS::TypeName.new(namespace: current_namespace, name: module_name.to_sym), type_params: RBS::AST::Declarations::ModuleTypeParams.new, self_types: Array.new, members: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
90
+ module_type = RBS::AST::Declarations::Module.new(name: RBS::TypeName.new(namespace: current_namespace, name: module_name.to_sym), type_params: RBS::AST::Declarations::ModuleTypeParams.new, self_types: Array.new, members: Array.new, annotations: Array.new, location: build_location(node), comment: node.[](:"comment"))
88
91
  old_parent_scope = @current_scope
89
92
  @current_scope = module_type
90
93
  indented(src) { ||
@@ -97,14 +100,12 @@ case node.[](:"type")
97
100
  @current_scope.members
98
101
  .<<(module_type)
99
102
  end)
100
- (if @type_checker
101
- unless @current_scope
102
- @type_checker.top_level_decls
103
- .[]=(module_type.name
104
- .name, module_type)
105
- end
103
+ (if @type_checker && !@current_scope
104
+ @type_checker.top_level_decls
105
+ .add(module_type)
106
106
  end)
107
107
  src.write_ln("end")
108
+ @after_module_function = existing_module_function_state
108
109
  when "DefNode"
109
110
  args = render_args(node)
110
111
  receiver = (if node.[](:"receiver")
@@ -118,21 +119,25 @@ case node.[](:"type")
118
119
  return_type = (if node.[](:"return_type")
119
120
  RBS::Types::ClassInstance.new(name: RBS::TypeName.new(name: eval(visit_node(node.[](:"return_type")))
120
121
  .to_s
121
- .to_sym, namespace: RBS::Namespace.root), args: EMPTY_ARRAY, location: node.[](:"location"))
122
+ .to_sym, namespace: RBS::Namespace.root), args: EMPTY_ARRAY, location: build_location(node))
122
123
  else
123
- RBS::Types::Bases::Any.new(location: node.[](:"location"))
124
+ RBS::Types::Bases::Any.new(location: build_location(node))
124
125
  end)
125
126
  method_types = [RBS::MethodType.new(type_params: EMPTY_ARRAY, type: RBS::Types::Function.new(required_positionals: args.dig(:"types", :"required_positionals"), optional_positionals: args.dig(:"types", :"optional_positionals"), rest_positionals: args.dig(:"types", :"rest_positionals"), trailing_positionals: args.dig(:"types", :"trailing_positionals"), required_keywords: args.dig(:"types", :"required_keywords"), optional_keywords: args.dig(:"types", :"optional_keywords"), rest_keywords: args.dig(:"types", :"rest_keywords"), return_type: return_type), block: (if node.[](:"yield_arg_count")
126
- RBS::Types::Block.new(type: RBS::Types::Function.new(required_positionals: Array.new, optional_positionals: Array.new, rest_positionals: nil, trailing_positionals: Array.new, required_keywords: Hash.new, optional_keywords: Hash.new, rest_keywords: nil, return_type: RBS::Types::Bases::Any.new(location: node.[](:"location"))), required: !!node.[](:"block_arg") || node.[](:"yield_arg_count"))
127
+ RBS::Types::Block.new(type: RBS::Types::Function.new(required_positionals: Array.new, optional_positionals: Array.new, rest_positionals: nil, trailing_positionals: Array.new, required_keywords: Hash.new, optional_keywords: Hash.new, rest_keywords: nil, return_type: RBS::Types::Bases::Any.new(location: build_location(node))), required: !!node.[](:"block_arg") || node.[](:"yield_arg_count"))
127
128
  else
128
129
  nil
129
- end), location: node.[](:"location"))]
130
+ end), location: build_location(node))]
130
131
  method_definition = RBS::AST::Members::MethodDefinition.new(name: node.[](:"name")
131
- .to_sym, kind: (if receiver
132
- :"class"
132
+ .to_sym, kind: (if @after_module_function
133
+ :"singleton_instance"
133
134
  else
134
- :"instance"
135
- end), types: method_types, annotations: EMPTY_ARRAY, location: node.[](:"location"), comment: node.[](:"comment"), overload: false)
135
+ (if receiver
136
+ :"singleton"
137
+ else
138
+ :"instance"
139
+ end)
140
+ end), types: method_types, annotations: EMPTY_ARRAY, location: build_location(node), comment: node.[](:"comment"), overload: false)
136
141
  (if @current_scope
137
142
  @current_scope.members
138
143
  .<<(method_definition)
@@ -179,6 +184,7 @@ EMPTY_ARRAY }
179
184
  else
180
185
  nil
181
186
  end)
187
+ name = node.[](:"name")
182
188
  block = (if node.[](:"block")
183
189
  " #{visit_node(node.[](:"block"))}"
184
190
  else
@@ -190,9 +196,25 @@ EMPTY_ARRAY }
190
196
  else
191
197
  nil
192
198
  end)
193
- call = "#{obj}#{node.[](:"name")}#{opening_delimiter}#{args}#{(if has_parens
199
+ call = "#{obj}#{name}#{opening_delimiter}#{args}#{(if has_parens
194
200
  ")"
195
201
  end)}#{block}"
202
+ case name
203
+ when "require_relative"
204
+ (if @on_new_file_referenced
205
+ paths = arg_arr.map() { |a|
206
+ unless a.[](:"type")
207
+ .==("LiteralNode")
208
+ throw(:"error", "Dynamic file paths are not allowed in require_relative")
209
+ end
210
+ eval(visit_node(a, scope)
211
+ .strip)
212
+ }
213
+ @on_new_file_referenced.call(paths, true)
214
+ end)
215
+ when "module_function"
216
+ @after_module_function = true
217
+ end
196
218
  src.write_ln(call)
197
219
  when "Block"
198
220
  args = render_args(node)
@@ -237,7 +259,11 @@ EMPTY_ARRAY }
237
259
  when "Path"
238
260
  src.write(node.[](:"value"))
239
261
  when "Require"
240
- src.write_ln("require \"#{node.[](:"value")}\"")
262
+ path = node.[](:"value")
263
+ src.write_ln("require \"#{path}\"")
264
+ (if @on_new_file_referenced
265
+ @on_new_file_referenced.call([path], false)
266
+ end)
241
267
  when "Assign", "OpAssign"
242
268
  src.write_ln("#{visit_node(node.[](:"target"))} #{node.[](:"op")}= #{visit_node(node.[](:"value"))
243
269
  .strip}")
@@ -426,7 +452,12 @@ EMPTY_ARRAY }
426
452
  src.write("return#{val}")
427
453
  when "TypeDeclaration"
428
454
  src.write_ln("# @type var #{visit_node(node.[](:"var"))}: #{visit_node(node.[](:"declared_type"))}")
429
- src.write_ln("#{visit_node(node.[](:"var"))} = #{visit_node(node.[](:"value"))}")
455
+ value = (if node.[](:"value")
456
+ " = #{visit_node(node.[](:"value"))}"
457
+ else
458
+ nil
459
+ end)
460
+ src.write_ln("#{visit_node(node.[](:"var"))}#{value}")
430
461
  when "ExceptionHandler"
431
462
  src.write_ln("begin")
432
463
  indented(src) { ||
@@ -483,7 +514,7 @@ EMPTY_ARRAY }
483
514
  name = visit_node(node.[](:"name"))
484
515
  src.write_ln("include #{name}")
485
516
  type = RBS::AST::Members::Include.new(name: method(:"TypeName")
486
- .call(name), args: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
517
+ .call(name), args: Array.new, annotations: Array.new, location: build_location(node), comment: node.[](:"comment"))
487
518
  (if @current_scope
488
519
  @current_scope.members
489
520
  .<<(type)
@@ -501,7 +532,7 @@ EMPTY_ARRAY }
501
532
  name = visit_node(node.[](:"name"))
502
533
  src.write_ln("extend #{name}")
503
534
  type = RBS::AST::Members::Extend.new(name: method(:"TypeName")
504
- .call(name), args: Array.new, annotations: Array.new, location: node.[](:"location"), comment: node.[](:"comment"))
535
+ .call(name), args: Array.new, annotations: Array.new, location: build_location(node), comment: node.[](:"comment"))
505
536
  (if @current_scope
506
537
  @current_scope.members
507
538
  .<<(type)
@@ -607,34 +638,38 @@ a && a.empty? }
607
638
  .flatten
608
639
  .join(", ")
609
640
  representation = "(#{contents})"
610
- rp.map!() { |a|
641
+ rp_args = rp.map() { |a|
611
642
  RBS::Types::Function::Param.new(name: visit_node(a)
612
- .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
643
+ .to_sym, type: RBS::Types::Bases::Any.new(location: build_location(a)))
613
644
  }
614
- op.map!() { |a|
645
+ op_args = op.map() { |a|
615
646
  RBS::Types::Function::Param.new(name: visit_node(a)
616
- .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
647
+ .to_sym, type: RBS::Types::Bases::Any.new(location: build_location(a)))
617
648
  }
618
- rest_p = (if rpa = node.[](:"rest_p_args")
619
- RBS::Types::Function::Param.new(name: visit_node(rpa)
620
- .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
649
+ rpa = (if rest_p
650
+ RBS::Types::Function::Param.new(name: rest_p.to_sym, type: RBS::Types::Bases::Any.new(location: build_location(node)))
621
651
  else
622
652
  nil
623
653
  end)
624
654
  {:representation => representation,
625
- :types => {:required_positionals => rp,
626
- :optional_positionals => op,
627
- :rest_positionals => rest_p,
655
+ :types => {:required_positionals => rp_args,
656
+ :optional_positionals => op_args,
657
+ :rest_positionals => rpa,
628
658
  :trailing_positionals => EMPTY_ARRAY,
629
659
  :required_keywords => node.[](:"req_kw_args") || EMPTY_HASH,
630
660
  :optional_keywords => node.[](:"opt_kw_args") || EMPTY_HASH,
631
661
  :rest_keywords => (if node.[](:"rest_kw_args")
632
662
  RBS::Types::Function::Param.new(name: visit_node(node.[](:"rest_kw_args"))
633
- .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
663
+ .to_sym, type: RBS::Types::Bases::Any.new(location: build_location(node)))
634
664
  else
635
665
  nil
636
666
  end)
637
667
  }.freeze}.freeze
638
668
  end
669
+ def build_location(node)
670
+ unless node.[](:"location")
671
+ return nil
672
+ end
673
+ end
639
674
  end
640
675
  end