gloss 0.1.1 → 0.1.6
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.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yml +41 -0
- data/.gloss.yml +1 -0
- data/Gemfile.lock +12 -10
- data/README.md +24 -27
- data/Rakefile +4 -2
- data/ext/gloss/Makefile +6 -10
- data/ext/gloss/lib/cr_ruby.cr +5 -4
- data/ext/gloss/src/gloss.cr +7 -2
- data/gloss.gemspec +4 -3
- data/lib/gloss.rb +4 -8
- data/lib/gloss/cli.rb +3 -3
- data/lib/gloss/config.rb +14 -11
- data/lib/gloss/errors.rb +2 -3
- data/lib/gloss/initializer.rb +2 -3
- data/lib/gloss/logger.rb +7 -3
- data/lib/gloss/parser.rb +2 -3
- data/lib/gloss/prog_loader.rb +55 -36
- data/lib/gloss/runtime.rb +25 -0
- data/lib/gloss/scope.rb +2 -3
- data/lib/gloss/source.rb +2 -3
- data/lib/gloss/type_checker.rb +18 -9
- data/lib/gloss/utils.rb +12 -14
- data/lib/gloss/version.rb +4 -5
- data/lib/gloss/visitor.rb +22 -8
- data/lib/gloss/watcher.rb +14 -8
- data/lib/gloss/writer.rb +38 -22
- data/logo.svg +6 -0
- data/src/lib/gloss.gl +2 -2
- data/src/lib/gloss/cli.gl +1 -0
- data/src/lib/gloss/config.gl +11 -7
- data/src/lib/gloss/logger.gl +3 -1
- data/src/lib/gloss/prog_loader.gl +38 -27
- data/src/lib/gloss/runtime.gl +20 -0
- data/src/lib/gloss/type_checker.gl +17 -6
- data/src/lib/gloss/utils.gl +7 -5
- data/src/lib/gloss/version.gl +1 -1
- data/src/lib/gloss/visitor.gl +18 -7
- data/src/lib/gloss/watcher.gl +10 -4
- data/src/lib/gloss/writer.gl +28 -12
- metadata +27 -11
- data/.github/workflows/crystal_specs.yml +0 -26
- data/.github/workflows/ruby_specs.yml +0 -33
data/lib/gloss/parser.rb
CHANGED
data/lib/gloss/prog_loader.rb
CHANGED
@@ -1,42 +1,58 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
|
3
2
|
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See
|
3
|
+
##### See / to make changes
|
5
4
|
|
6
5
|
require "rubygems/gem_runner"
|
7
6
|
module Gloss
|
8
7
|
OUTPUT_BY_PATH = Hash.new
|
9
8
|
class ProgLoader
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
attr_reader(:"type_checker")
|
10
|
+
def initialize(entrypoint_path = nil, entrypoint_contents = nil)
|
11
|
+
entrypoint_path ||= Config.entrypoint
|
12
|
+
(if entrypoint_path.==(nil) || entrypoint_path.==("")
|
13
13
|
throw(:"error", "Entrypoint is not yet set in .gloss.yml")
|
14
14
|
end)
|
15
|
-
|
15
|
+
entrypoint = (if entrypoint_path && entrypoint_contents
|
16
|
+
ep = (if entrypoint_path.==(Runtime::NON_EXISTENT_FILEPATH)
|
17
|
+
entrypoint_path
|
18
|
+
else
|
19
|
+
Utils.absolute_path(entrypoint_path)
|
20
|
+
end)
|
21
|
+
[ep, entrypoint_contents]
|
22
|
+
else
|
23
|
+
Utils.abs_path_with_contents(entrypoint_path)
|
24
|
+
end)
|
25
|
+
core_types = Utils.abs_path_with_contents(File.join(__dir__ || "", "..", "..", "sig", "core.rbs"))
|
26
|
+
@files_to_process = [entrypoint, core_types]
|
16
27
|
@processed_files = Set.new
|
17
28
|
@type_checker = TypeChecker.new
|
18
29
|
end
|
19
30
|
def run()
|
20
|
-
@files_to_process.each() { |
|
21
|
-
|
22
|
-
|
31
|
+
@files_to_process.each() { |__arg0|
|
32
|
+
path_string = __arg0.[](0)
|
33
|
+
file_contents = __arg0.[](1)
|
34
|
+
(if path_string.end_with?(".rbs")
|
35
|
+
@type_checker.load_sig_path(path_string)
|
36
|
+
else
|
37
|
+
(if !@processed_files.member?(path_string) || !OUTPUT_BY_PATH.[](path_string)
|
38
|
+
Gloss.logger
|
23
39
|
.debug("Loading #{path_string}")
|
24
|
-
|
25
|
-
file_contents = File.open(path)
|
26
|
-
.read
|
27
|
-
contents_tree = Parser.new(file_contents)
|
40
|
+
contents_tree = Parser.new(file_contents)
|
28
41
|
.run
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
42
|
+
on_new_file_referenced = proc() { |ps, relative|
|
43
|
+
ps.each() { |pa|
|
44
|
+
(if relative
|
45
|
+
handle_require_relative(pa, path_string)
|
46
|
+
else
|
47
|
+
handle_require(pa)
|
48
|
+
end)
|
49
|
+
}
|
50
|
+
}
|
51
|
+
OUTPUT_BY_PATH.[]=(path_string, Visitor.new(contents_tree, @type_checker, on_new_file_referenced)
|
37
52
|
.run)
|
38
|
-
|
39
|
-
|
53
|
+
@processed_files.add(path_string)
|
54
|
+
end)
|
55
|
+
end)
|
40
56
|
}
|
41
57
|
@type_checker
|
42
58
|
end
|
@@ -48,19 +64,19 @@ module Gloss
|
|
48
64
|
base = File.join(Dir.pwd, path)
|
49
65
|
fp = base.+(".gl")
|
50
66
|
(if File.exist?(fp)
|
51
|
-
@files_to_process.<<(fp)
|
67
|
+
@files_to_process.<<(Utils.abs_path_with_contents(fp))
|
52
68
|
end)
|
53
69
|
return
|
54
70
|
end)
|
55
71
|
full = File.absolute_path("#{File.join(Config.src_dir, "lib", path)}.gl")
|
56
72
|
pathn = Pathname.new(full)
|
57
73
|
(if pathn.file?
|
58
|
-
@files_to_process.<<(pathn.to_s)
|
74
|
+
@files_to_process.<<(Utils.abs_path_with_contents(pathn.to_s))
|
59
75
|
else
|
60
76
|
pathn = Pathname.new("#{File.join(Dir.pwd, "sig", path)}.rbs")
|
61
77
|
gem_path = Utils.gem_path_for(path)
|
62
78
|
(if gem_path
|
63
|
-
sig_files = Dir.glob(File.absolute_path(File.join(gem_path, "..", "
|
79
|
+
sig_files = Dir.glob(File.absolute_path(File.join(gem_path, "..", "sig", "**", "*.rbs")))
|
64
80
|
(if sig_files.length
|
65
81
|
.positive?
|
66
82
|
sig_files.each() { |fp|
|
@@ -81,7 +97,7 @@ return
|
|
81
97
|
@type_checker.load_sig_path(pathn.to_s)
|
82
98
|
@processed_files.add(pathn.to_s)
|
83
99
|
else
|
84
|
-
rbs_stdlib_dir = File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "
|
100
|
+
rbs_stdlib_dir = File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "stdlib", path))
|
85
101
|
(if Pathname.new(rbs_stdlib_dir)
|
86
102
|
.exist?
|
87
103
|
load_rbs_from_require_path(path)
|
@@ -103,24 +119,27 @@ nil }
|
|
103
119
|
end)
|
104
120
|
end)
|
105
121
|
end
|
106
|
-
private def handle_require_relative(path)
|
107
|
-
base = File.join(
|
122
|
+
private def handle_require_relative(path, source_file)
|
123
|
+
base = File.join(source_file, "..", path)
|
108
124
|
# @type var pn: String?
|
109
125
|
pn = nil
|
110
|
-
Gem.suffixes
|
111
|
-
.each() { |ext|
|
126
|
+
exts = [".gl"].concat(Gem.suffixes)
|
127
|
+
exts.each() { |ext|
|
112
128
|
full = File.absolute_path(base.+(ext))
|
113
129
|
(if File.exist?(full)
|
114
130
|
pn = full
|
115
131
|
end)
|
116
132
|
}
|
117
133
|
(if pn
|
118
|
-
unless @files_to_process.
|
119
|
-
|
134
|
+
unless @files_to_process.any?() { |__arg1|
|
135
|
+
fp = __arg1.[](0)
|
136
|
+
fp.==(pn)
|
137
|
+
}
|
138
|
+
@files_to_process.<<(Utils.abs_path_with_contents(pn))
|
120
139
|
end
|
121
140
|
else
|
122
141
|
(if Config.strict_require
|
123
|
-
throw(:"error", "Cannot resolve
|
142
|
+
throw(:"error", "Cannot resolve relative path for #{path}")
|
124
143
|
else
|
125
144
|
Gloss.logger
|
126
145
|
.debug("No path found for #{path}")
|
@@ -128,7 +147,7 @@ nil }
|
|
128
147
|
end)
|
129
148
|
end
|
130
149
|
private def rbs_stdlib_path_for(libr)
|
131
|
-
File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "
|
150
|
+
File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "stdlib", libr))
|
132
151
|
end
|
133
152
|
private def load_rbs_from_require_path(path)
|
134
153
|
Dir.glob(File.join(rbs_stdlib_path_for(path), "**", "*.rbs"))
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
3
|
+
##### See / to make changes
|
4
|
+
|
5
|
+
require "stringio"
|
6
|
+
module Gloss
|
7
|
+
class Runtime
|
8
|
+
NON_EXISTENT_FILEPATH = "__string__"
|
9
|
+
def self.process_string(str, options = Config.default_config)
|
10
|
+
out_io = StringIO.new
|
11
|
+
error_msg = catch(:"error") { ||
|
12
|
+
tree = Parser.new(str)
|
13
|
+
.run
|
14
|
+
tc = ProgLoader.new(NON_EXISTENT_FILEPATH, str)
|
15
|
+
.run
|
16
|
+
rb_output = Visitor.new(tree, tc)
|
17
|
+
.run
|
18
|
+
tc.run(NON_EXISTENT_FILEPATH, rb_output)
|
19
|
+
Writer.new(rb_output, NON_EXISTENT_FILEPATH, out_io)
|
20
|
+
.run
|
21
|
+
nil }
|
22
|
+
return [out_io.string, error_msg].freeze
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/gloss/scope.rb
CHANGED
data/lib/gloss/source.rb
CHANGED
data/lib/gloss/type_checker.rb
CHANGED
@@ -1,18 +1,27 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
|
3
2
|
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See
|
3
|
+
##### See / to make changes
|
5
4
|
|
6
5
|
require "set"
|
7
6
|
module Gloss
|
8
7
|
class TypeChecker
|
9
|
-
Project = Struct.new(:"targets")
|
10
8
|
attr_reader(:"steep_target", :"top_level_decls", :"env", :"rbs_gem_dir")
|
9
|
+
module Strictness
|
10
|
+
Strict = "strict"
|
11
|
+
Lenient = "lenient"
|
12
|
+
Default = "default"
|
13
|
+
end
|
11
14
|
def initialize()
|
12
|
-
|
13
|
-
.
|
14
|
-
|
15
|
-
|
15
|
+
options = Steep::Project::Options.new
|
16
|
+
case Config.type_checking_strictness
|
17
|
+
when Strictness::Strict
|
18
|
+
options.apply_strict_typing_options!
|
19
|
+
when Strictness::Lenient
|
20
|
+
options.apply_lenient_typing_options!
|
21
|
+
else
|
22
|
+
options.apply_default_typing_options!
|
23
|
+
end
|
24
|
+
@steep_target = Steep::Project::Target.new(name: "gloss", options: options, source_patterns: ["**/*.rb"], ignore_patterns: Array.new, signature_patterns: ["sig"])
|
16
25
|
@top_level_decls = Set.new
|
17
26
|
@rbs_gem_dir = Utils.gem_path_for("rbs")
|
18
27
|
env_loader = RBS::EnvironmentLoader.new
|
@@ -51,7 +60,7 @@ case e
|
|
51
60
|
when Steep::Diagnostic::Ruby::UnexpectedBlockGiven
|
52
61
|
"Unexpected block given"
|
53
62
|
else
|
54
|
-
"#{e.header_line}\n#{e
|
63
|
+
"#{e.header_line}\n#{e}"
|
55
64
|
end
|
56
65
|
}
|
57
66
|
.join("\n")
|
data/lib/gloss/utils.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
|
3
2
|
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See
|
3
|
+
##### See / to make changes
|
5
4
|
|
6
5
|
require "rubygems"
|
7
6
|
module Gloss
|
@@ -21,17 +20,11 @@ module Gloss
|
|
21
20
|
end)
|
22
21
|
end
|
23
22
|
def gem_path_for(gem_name)
|
24
|
-
|
25
|
-
|
26
|
-
.
|
27
|
-
|
28
|
-
|
29
|
-
Gem.ui
|
30
|
-
.outs
|
31
|
-
.string
|
32
|
-
rescue SystemExit => e
|
33
|
-
nil
|
34
|
-
end
|
23
|
+
spec = Gem::Specification.find_by_path(gem_name)
|
24
|
+
(if spec
|
25
|
+
spec.full_require_paths
|
26
|
+
.first
|
27
|
+
end)
|
35
28
|
end
|
36
29
|
def with_file_header(str)
|
37
30
|
"#{Visitor::FILE_HEADER}\n\n#{str}"
|
@@ -40,5 +33,10 @@ module Gloss
|
|
40
33
|
src_path.sub("#{Config.src_dir}/", "")
|
41
34
|
.sub(/\.gl$/, ".rb")
|
42
35
|
end
|
36
|
+
def abs_path_with_contents(path)
|
37
|
+
abs_path = absolute_path(path)
|
38
|
+
return [abs_path, File.open(abs_path)
|
39
|
+
.read].freeze
|
40
|
+
end
|
43
41
|
end
|
44
42
|
end
|
data/lib/gloss/version.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#####
|
4
|
-
##### See src/ to make changes
|
1
|
+
|
2
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
3
|
+
##### See / to make changes
|
5
4
|
|
6
5
|
module Gloss
|
7
|
-
VERSION = "0.1.
|
6
|
+
VERSION = "0.1.6"
|
8
7
|
end
|
data/lib/gloss/visitor.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
|
3
2
|
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See
|
3
|
+
##### See / to make changes
|
5
4
|
|
6
5
|
module Gloss
|
7
6
|
class Visitor
|
@@ -123,7 +122,7 @@ case node.[](:"type")
|
|
123
122
|
else
|
124
123
|
RBS::Types::Bases::Any.new(location: build_location(node))
|
125
124
|
end)
|
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")
|
125
|
+
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") || args.dig(:"types", :"block_arg")
|
127
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: build_location(node))), required: !!node.[](:"block_arg") || node.[](:"yield_arg_count"))
|
128
127
|
else
|
129
128
|
nil
|
@@ -184,6 +183,7 @@ EMPTY_ARRAY }
|
|
184
183
|
else
|
185
184
|
nil
|
186
185
|
end)
|
186
|
+
name = node.[](:"name")
|
187
187
|
block = (if node.[](:"block")
|
188
188
|
" #{visit_node(node.[](:"block"))}"
|
189
189
|
else
|
@@ -195,14 +195,21 @@ EMPTY_ARRAY }
|
|
195
195
|
else
|
196
196
|
nil
|
197
197
|
end)
|
198
|
-
name = node.[](:"name")
|
199
198
|
call = "#{obj}#{name}#{opening_delimiter}#{args}#{(if has_parens
|
200
199
|
")"
|
201
200
|
end)}#{block}"
|
202
201
|
case name
|
203
202
|
when "require_relative"
|
204
203
|
(if @on_new_file_referenced
|
205
|
-
|
204
|
+
paths = arg_arr.map() { |a|
|
205
|
+
unless a.[](:"type")
|
206
|
+
.==("LiteralNode")
|
207
|
+
throw(:"error", "Dynamic file paths are not allowed in require_relative")
|
208
|
+
end
|
209
|
+
eval(visit_node(a, scope)
|
210
|
+
.strip)
|
211
|
+
}
|
212
|
+
@on_new_file_referenced.call(paths, true)
|
206
213
|
end)
|
207
214
|
when "module_function"
|
208
215
|
@after_module_function = true
|
@@ -254,7 +261,7 @@ case name
|
|
254
261
|
path = node.[](:"value")
|
255
262
|
src.write_ln("require \"#{path}\"")
|
256
263
|
(if @on_new_file_referenced
|
257
|
-
@on_new_file_referenced.call(path, false)
|
264
|
+
@on_new_file_referenced.call([path], false)
|
258
265
|
end)
|
259
266
|
when "Assign", "OpAssign"
|
260
267
|
src.write_ln("#{visit_node(node.[](:"target"))} #{node.[](:"op")}= #{visit_node(node.[](:"value"))
|
@@ -608,7 +615,9 @@ EMPTY_HASH }
|
|
608
615
|
end)
|
609
616
|
# @type var rest_kw: Hash[Symbol, Any]?
|
610
617
|
rest_kw = node.[](:"rest_kw_args")
|
611
|
-
|
618
|
+
# @type var block_arg: Hash[Symbol, Any]?
|
619
|
+
block_arg = node.[](:"block_arg")
|
620
|
+
(if [rp, op, rkw, okw, rest_p, rest_kw, block_arg].all?() { |a|
|
612
621
|
a && a.empty? }
|
613
622
|
return nil
|
614
623
|
end)
|
@@ -626,6 +635,10 @@ a && a.empty? }
|
|
626
635
|
"**#{visit_node(rest_kw)}"
|
627
636
|
else
|
628
637
|
""
|
638
|
+
end), (if block_arg
|
639
|
+
"&#{visit_node(block_arg)}"
|
640
|
+
else
|
641
|
+
""
|
629
642
|
end)].reject(&:"empty?")
|
630
643
|
.flatten
|
631
644
|
.join(", ")
|
@@ -650,6 +663,7 @@ a && a.empty? }
|
|
650
663
|
:trailing_positionals => EMPTY_ARRAY,
|
651
664
|
:required_keywords => node.[](:"req_kw_args") || EMPTY_HASH,
|
652
665
|
:optional_keywords => node.[](:"opt_kw_args") || EMPTY_HASH,
|
666
|
+
:block_arg => true,
|
653
667
|
:rest_keywords => (if node.[](:"rest_kw_args")
|
654
668
|
RBS::Types::Function::Param.new(name: visit_node(node.[](:"rest_kw_args"))
|
655
669
|
.to_sym, type: RBS::Types::Bases::Any.new(location: build_location(node)))
|
data/lib/gloss/watcher.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
|
3
2
|
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See
|
3
|
+
##### See / to make changes
|
5
4
|
|
6
5
|
require "listen"
|
7
6
|
module Gloss
|
8
7
|
class Watcher
|
8
|
+
# @type var @listener: Listen?
|
9
|
+
@listener
|
9
10
|
def initialize(paths)
|
10
11
|
@paths = paths
|
11
12
|
(if @paths.empty?
|
@@ -32,7 +33,7 @@ module Gloss
|
|
32
33
|
def watch()
|
33
34
|
Gloss.logger
|
34
35
|
.info("Now listening for changes in #{@paths.join(", ")}")
|
35
|
-
listener
|
36
|
+
@listener ||= Listen.to(*@paths, latency: 2, only: @only) { |modified, added, removed|
|
36
37
|
modified.+(added)
|
37
38
|
.each() { |f|
|
38
39
|
Gloss.logger
|
@@ -64,13 +65,18 @@ nil }
|
|
64
65
|
}
|
65
66
|
}
|
66
67
|
begin
|
67
|
-
listener.start
|
68
|
+
@listener.start
|
68
69
|
sleep
|
69
70
|
rescue Interrupt
|
70
|
-
|
71
|
-
.info("Interrupt signal received, shutting down")
|
72
|
-
exit(0)
|
71
|
+
kill
|
73
72
|
end
|
74
73
|
end
|
74
|
+
def kill()
|
75
|
+
Gloss.logger
|
76
|
+
.info("Interrupt signal received, shutting down")
|
77
|
+
(if @listener
|
78
|
+
@listener.stop
|
79
|
+
end)
|
80
|
+
end
|
75
81
|
end
|
76
82
|
end
|