gloss 0.0.3 → 0.1.1
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/.gitattributes +3 -0
- data/.github/workflows/{crystal.yml → crystal_specs.yml} +1 -1
- data/.github/workflows/{ruby.yml → ruby_specs.yml} +2 -2
- data/.gloss.yml +1 -0
- data/.rspec +1 -0
- data/Gemfile.lock +10 -12
- data/README.md +36 -5
- data/Rakefile +1 -1
- data/exe/gloss +13 -2
- data/ext/gloss/Makefile +8 -19
- data/ext/gloss/{src/lib → lib}/cr_ruby.cr +0 -0
- data/ext/gloss/lib/rbs_types.cr +3 -0
- data/ext/gloss/spec/parser_spec.cr +83 -83
- data/ext/gloss/src/cr_ast.cr +96 -77
- data/ext/gloss/src/gloss.cr +2 -2
- data/ext/gloss/src/lexer.cr +59 -1
- data/ext/gloss/src/rb_ast.cr +114 -63
- data/lib/gloss.rb +15 -7
- data/lib/gloss/cli.rb +85 -28
- data/lib/gloss/config.rb +13 -7
- data/lib/gloss/errors.rb +3 -4
- data/lib/gloss/initializer.rb +9 -9
- data/lib/gloss/logger.rb +29 -0
- data/lib/gloss/parser.rb +19 -5
- data/lib/gloss/prog_loader.rb +141 -0
- data/lib/gloss/scope.rb +7 -2
- data/lib/gloss/source.rb +17 -14
- data/lib/gloss/type_checker.rb +86 -33
- data/lib/gloss/utils.rb +44 -0
- data/lib/gloss/version.rb +1 -2
- data/lib/gloss/visitor.rb +667 -0
- data/lib/gloss/watcher.rb +51 -16
- data/lib/gloss/writer.rb +24 -15
- data/sig/core.rbs +2 -0
- data/sig/fast_blank.rbs +4 -0
- data/sig/{gloss.rbs → gls.rbs} +0 -0
- data/sig/listen.rbs +1 -0
- data/sig/optparse.rbs +6 -0
- data/sig/rubygems.rbs +9 -0
- data/sig/yaml.rbs +3 -0
- data/src/exe/gloss +19 -0
- data/src/lib/gloss.gl +26 -0
- data/src/lib/gloss/cli.gl +70 -0
- data/src/lib/gloss/config.gl +9 -3
- data/src/lib/gloss/initializer.gl +4 -6
- data/src/lib/gloss/logger.gl +21 -0
- data/src/lib/gloss/parser.gl +17 -5
- data/src/lib/gloss/prog_loader.gl +133 -0
- data/src/lib/gloss/scope.gl +7 -0
- data/src/lib/gloss/source.gl +32 -0
- data/src/lib/gloss/type_checker.gl +85 -36
- data/src/lib/gloss/utils.gl +38 -0
- data/src/lib/gloss/version.gl +1 -1
- data/src/lib/gloss/visitor.gl +575 -0
- data/src/lib/gloss/watcher.gl +44 -10
- data/src/lib/gloss/writer.gl +16 -14
- metadata +28 -8
- data/lib/gloss/builder.rb +0 -447
data/lib/gloss.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
# frozen_string_literal: true
|
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
|
2
5
|
|
3
6
|
require "rbs"
|
4
7
|
require "json"
|
5
8
|
require "steep"
|
6
9
|
require "fast_blank"
|
7
|
-
|
8
10
|
require "gloss/version"
|
9
11
|
require "gloss/cli"
|
10
12
|
require "gloss/watcher"
|
@@ -15,10 +17,16 @@ require "gloss/config"
|
|
15
17
|
require "gloss/writer"
|
16
18
|
require "gloss/source"
|
17
19
|
require "gloss/scope"
|
18
|
-
require "gloss/
|
20
|
+
require "gloss/visitor"
|
19
21
|
require "gloss/errors"
|
20
|
-
|
21
|
-
require "
|
22
|
-
|
23
|
-
|
22
|
+
require "gloss/logger"
|
23
|
+
require "gloss/prog_loader"
|
24
|
+
require "gloss/utils"
|
25
|
+
is_ci = ENV.fetch("CI") { ||
|
26
|
+
false}
|
27
|
+
unless is_ci
|
28
|
+
require "gls"
|
29
|
+
end
|
30
|
+
EMPTY_ARRAY = Array.new
|
31
|
+
.freeze
|
24
32
|
EMPTY_HASH = {}.freeze
|
data/lib/gloss/cli.rb
CHANGED
@@ -1,39 +1,96 @@
|
|
1
|
-
# frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
+
##### See src/ to make changes
|
4
5
|
|
6
|
+
require "optparse"
|
5
7
|
module Gloss
|
6
8
|
class CLI
|
7
9
|
def initialize(argv)
|
8
10
|
@argv = argv
|
9
11
|
end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
12
|
+
def run()
|
13
|
+
command = @argv.first
|
14
|
+
files = @argv.[](((1)..(-1)))
|
15
|
+
err_msg = catch(:"error") { ||
|
16
|
+
case command
|
17
|
+
when "init"
|
18
|
+
force = false
|
19
|
+
OptionParser.new() { |opt|
|
20
|
+
opt.on("--force", "-f") { ||
|
21
|
+
force = true
|
22
|
+
}
|
23
|
+
}
|
24
|
+
.parse(@argv)
|
25
|
+
Initializer.new(force)
|
26
|
+
.run
|
27
|
+
when "version", "--version", "-v"
|
28
|
+
puts(Gloss::VERSION)
|
29
|
+
when "watch", "build"
|
30
|
+
type_checker = ProgLoader.new
|
31
|
+
.run
|
32
|
+
(if command.==("watch")
|
33
|
+
files = files.map() { |f|
|
34
|
+
path = (if Pathname.new(f)
|
35
|
+
.absolute?
|
36
|
+
f
|
37
|
+
else
|
38
|
+
File.join(Dir.pwd, f)
|
39
|
+
end)
|
40
|
+
(if Pathname.new(path)
|
41
|
+
.exist?
|
42
|
+
path
|
43
|
+
else
|
44
|
+
throw(:"error", "Pathname #{f} does not exist")
|
45
|
+
end)
|
46
|
+
}
|
47
|
+
Watcher.new(files)
|
48
|
+
.watch
|
49
|
+
else
|
50
|
+
(if command.==("build")
|
51
|
+
entry_tree = Parser.new(File.read(Config.entrypoint))
|
52
|
+
.run
|
53
|
+
Visitor.new(entry_tree, type_checker)
|
54
|
+
.run
|
55
|
+
(if files.empty?
|
56
|
+
files = Dir.glob("#{Config.src_dir}/**/*.gl")
|
57
|
+
end)
|
58
|
+
files.each() { |fp|
|
59
|
+
fp = File.absolute_path(fp)
|
60
|
+
preloaded_output = OUTPUT_BY_PATH.fetch(fp) { ||
|
61
|
+
nil }
|
62
|
+
(if preloaded_output
|
63
|
+
rb_output = preloaded_output
|
64
|
+
else
|
65
|
+
Gloss.logger
|
66
|
+
.info("Building #{fp}")
|
67
|
+
content = File.read(fp)
|
68
|
+
tree_hash = Parser.new(content)
|
69
|
+
.run
|
70
|
+
rb_output = Visitor.new(tree_hash, type_checker)
|
71
|
+
.run
|
72
|
+
end)
|
73
|
+
Gloss.logger
|
74
|
+
.info("Type checking #{fp}")
|
75
|
+
type_checker.run(fp, rb_output)
|
76
|
+
}
|
77
|
+
files.each() { |fp|
|
78
|
+
fp = File.absolute_path(fp)
|
79
|
+
rb_output = OUTPUT_BY_PATH.fetch(fp)
|
80
|
+
Gloss.logger
|
81
|
+
.info("Writing #{fp}")
|
82
|
+
Writer.new(rb_output, fp)
|
83
|
+
.run
|
84
|
+
}
|
85
|
+
end)
|
86
|
+
end)
|
87
|
+
else
|
88
|
+
throw(:"error", "Gloss doesn't know how to #{command}")
|
27
89
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end.parse(@argv)
|
33
|
-
Initializer.new(force).run
|
34
|
-
else
|
35
|
-
abort "Gloss doesn't know how to #{command}"
|
36
|
-
end
|
90
|
+
nil }
|
91
|
+
(if err_msg
|
92
|
+
abort(err_msg)
|
93
|
+
end)
|
37
94
|
end
|
38
95
|
end
|
39
96
|
end
|
data/lib/gloss/config.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
|
-
# frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See src/ to make changes
|
3
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
+
##### See src/ to make changes
|
5
5
|
|
6
6
|
require "ostruct"
|
7
7
|
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"
|
11
|
+
:src_dir => "src",
|
12
|
+
:entrypoint => nil,
|
13
|
+
:strict_require => false}.freeze)
|
14
|
+
user_config = (if File.exist?(CONFIG_PATH)
|
15
|
+
YAML.safe_load(File.read(CONFIG_PATH))
|
16
|
+
else
|
17
|
+
Config.default_config
|
18
|
+
end)
|
12
19
|
Config.default_config
|
13
|
-
.each { |k, v|
|
20
|
+
.each() { |k, v|
|
14
21
|
Config.send(:"#{k}=", user_config.[](k.to_s) || v)
|
15
22
|
}
|
16
23
|
end
|
17
|
-
|
data/lib/gloss/errors.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See src/ to make changes
|
3
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
+
##### See src/ to make changes
|
5
5
|
|
6
6
|
module Gloss
|
7
7
|
module Errors
|
@@ -15,4 +15,3 @@ module Gloss
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
data/lib/gloss/initializer.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See src/ to make changes
|
3
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
+
##### See src/ to make changes
|
5
5
|
|
6
6
|
require "yaml"
|
7
7
|
module Gloss
|
@@ -10,16 +10,16 @@ module Gloss
|
|
10
10
|
@force = force
|
11
11
|
end
|
12
12
|
def run()
|
13
|
-
(if File.exist?(
|
14
|
-
|
13
|
+
(if File.exist?(CONFIG_PATH) && !@force
|
14
|
+
throw(:"error", "#{CONFIG_PATH} file already exists - aborting. Use --force to override.")
|
15
15
|
end)
|
16
|
-
File.open(
|
16
|
+
File.open(CONFIG_PATH, "wb") { |file|
|
17
17
|
file.puts(Config.default_config
|
18
|
-
.transform_keys(&:to_s)
|
18
|
+
.transform_keys(&:"to_s")
|
19
19
|
.to_yaml)
|
20
20
|
}
|
21
|
-
|
21
|
+
Gloss.logger
|
22
|
+
.info("Created #{CONFIG_PATH} with default preferences")
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
25
|
-
|
data/lib/gloss/logger.rb
ADDED
@@ -0,0 +1,29 @@
|
|
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
|
+
module Gloss
|
7
|
+
def self.logger()
|
8
|
+
(if @logger
|
9
|
+
@logger
|
10
|
+
else
|
11
|
+
env_log_level = ENV.fetch("LOG_LEVEL") { ||
|
12
|
+
"INFO" }
|
13
|
+
real_log_level = {"UNKNOWN" => Logger::UNKNOWN,
|
14
|
+
"FATAL" => Logger::FATAL,
|
15
|
+
"ERROR" => Logger::ERROR,
|
16
|
+
"WARN" => Logger::WARN,
|
17
|
+
"INFO" => Logger::INFO,
|
18
|
+
"DEBUG" => Logger::DEBUG,
|
19
|
+
"NIL" => nil,
|
20
|
+
nil => nil,
|
21
|
+
"" => nil}.fetch(env_log_level)
|
22
|
+
@logger = Logger.new((if real_log_level
|
23
|
+
STDOUT
|
24
|
+
else
|
25
|
+
IO::NULL
|
26
|
+
end))
|
27
|
+
end)
|
28
|
+
end
|
29
|
+
end
|
data/lib/gloss/parser.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# frozen_string_literal: true
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
-
##### See src/ to make changes
|
3
|
+
##### This file was generated by Gloss; any changes made here will be overwritten.
|
4
|
+
##### See src/ to make changes
|
5
5
|
|
6
6
|
module Gloss
|
7
7
|
class Parser
|
@@ -13,9 +13,23 @@ module Gloss
|
|
13
13
|
begin
|
14
14
|
JSON.parse(tree_json, symbolize_names: true)
|
15
15
|
rescue JSON::ParserError
|
16
|
-
|
16
|
+
error_message = tree_json
|
17
|
+
error_message.match(/.+\s:(\d+)$/)
|
18
|
+
(if $~.[](1)
|
19
|
+
line_number = $~.[](1)
|
20
|
+
.to_i
|
21
|
+
context = @str.lines
|
22
|
+
.[]((( line_number.-(2)
|
23
|
+
)..(line_number)))
|
24
|
+
.map
|
25
|
+
.with_index() { |line, index|
|
26
|
+
"#{index.-(1)
|
27
|
+
.+(line_number)}| #{line}" }
|
28
|
+
.join
|
29
|
+
error_message = "#{context.rstrip}\n\n#{error_message}\n"
|
30
|
+
end)
|
31
|
+
throw(:"error", error_message)
|
17
32
|
end
|
18
33
|
end
|
19
34
|
end
|
20
35
|
end
|
21
|
-
|
@@ -0,0 +1,141 @@
|
|
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
|
+
def initialize()
|
11
|
+
entrypoint = Config.entrypoint
|
12
|
+
(if entrypoint.==(nil) || entrypoint.==("")
|
13
|
+
throw(:"error", "Entrypoint is not yet set in .gloss.yml")
|
14
|
+
end)
|
15
|
+
@files_to_process = [Utils.absolute_path(Config.entrypoint), Utils.absolute_path(File.join(__dir__ || "", "..", "..", "sig", "core.rbs"))]
|
16
|
+
@processed_files = Set.new
|
17
|
+
@type_checker = TypeChecker.new
|
18
|
+
end
|
19
|
+
def run()
|
20
|
+
@files_to_process.each() { |path_string|
|
21
|
+
unless @processed_files.member?(path_string) || OUTPUT_BY_PATH.[](path_string)
|
22
|
+
Gloss.logger
|
23
|
+
.debug("Loading #{path_string}")
|
24
|
+
path = Utils.absolute_path(path_string)
|
25
|
+
file_contents = File.open(path)
|
26
|
+
.read
|
27
|
+
contents_tree = Parser.new(file_contents)
|
28
|
+
.run
|
29
|
+
on_new_file_referenced = proc() { |pa, relative|
|
30
|
+
(if relative
|
31
|
+
handle_require_relative(pa)
|
32
|
+
else
|
33
|
+
handle_require(pa)
|
34
|
+
end)
|
35
|
+
}
|
36
|
+
OUTPUT_BY_PATH.[]=(path_string, Visitor.new(contents_tree, @type_checker, on_new_file_referenced)
|
37
|
+
.run)
|
38
|
+
@processed_files.add(path_string)
|
39
|
+
end
|
40
|
+
}
|
41
|
+
@type_checker
|
42
|
+
end
|
43
|
+
STDLIB_TYPE_DEPENDENCIES = {"yaml" => ["pstore", "dbm"],
|
44
|
+
"rbs" => ["logger", "set", "tsort"],
|
45
|
+
"logger" => ["monitor"]}
|
46
|
+
private def handle_require(path)
|
47
|
+
(if path.start_with?(".")
|
48
|
+
base = File.join(Dir.pwd, path)
|
49
|
+
fp = base.+(".gl")
|
50
|
+
(if File.exist?(fp)
|
51
|
+
@files_to_process.<<(fp)
|
52
|
+
end)
|
53
|
+
return
|
54
|
+
end)
|
55
|
+
full = File.absolute_path("#{File.join(Config.src_dir, "lib", path)}.gl")
|
56
|
+
pathn = Pathname.new(full)
|
57
|
+
(if pathn.file?
|
58
|
+
@files_to_process.<<(pathn.to_s)
|
59
|
+
else
|
60
|
+
pathn = Pathname.new("#{File.join(Dir.pwd, "sig", path)}.rbs")
|
61
|
+
gem_path = Utils.gem_path_for(path)
|
62
|
+
(if gem_path
|
63
|
+
sig_files = Dir.glob(File.absolute_path(File.join(gem_path, "..", "..", "sig", "**", "*.rbs")))
|
64
|
+
(if sig_files.length
|
65
|
+
.positive?
|
66
|
+
sig_files.each() { |fp|
|
67
|
+
@type_checker.load_sig_path(fp)
|
68
|
+
}
|
69
|
+
@processed_files.add(path)
|
70
|
+
rbs_type_deps = STDLIB_TYPE_DEPENDENCIES.fetch(path) { ||
|
71
|
+
nil }
|
72
|
+
(if rbs_type_deps
|
73
|
+
rbs_type_deps.each() { |d|
|
74
|
+
handle_require(d)
|
75
|
+
}
|
76
|
+
end)
|
77
|
+
return
|
78
|
+
end)
|
79
|
+
end)
|
80
|
+
(if pathn.file?
|
81
|
+
@type_checker.load_sig_path(pathn.to_s)
|
82
|
+
@processed_files.add(pathn.to_s)
|
83
|
+
else
|
84
|
+
rbs_stdlib_dir = File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "..", "stdlib", path))
|
85
|
+
(if Pathname.new(rbs_stdlib_dir)
|
86
|
+
.exist?
|
87
|
+
load_rbs_from_require_path(path)
|
88
|
+
rbs_type_deps = STDLIB_TYPE_DEPENDENCIES.fetch(path) { ||
|
89
|
+
nil }
|
90
|
+
(if rbs_type_deps
|
91
|
+
rbs_type_deps.each() { |d|
|
92
|
+
load_rbs_from_require_path(d)
|
93
|
+
}
|
94
|
+
end)
|
95
|
+
else
|
96
|
+
(if Config.strict_require
|
97
|
+
throw(:"error", "Cannot resolve require path for #{path}")
|
98
|
+
else
|
99
|
+
Gloss.logger
|
100
|
+
.debug("No path found for #{path}")
|
101
|
+
end)
|
102
|
+
end)
|
103
|
+
end)
|
104
|
+
end)
|
105
|
+
end
|
106
|
+
private def handle_require_relative(path)
|
107
|
+
base = File.join(@filepath, "..", path)
|
108
|
+
# @type var pn: String?
|
109
|
+
pn = nil
|
110
|
+
Gem.suffixes
|
111
|
+
.each() { |ext|
|
112
|
+
full = File.absolute_path(base.+(ext))
|
113
|
+
(if File.exist?(full)
|
114
|
+
pn = full
|
115
|
+
end)
|
116
|
+
}
|
117
|
+
(if pn
|
118
|
+
unless @files_to_process.include?(pn)
|
119
|
+
@files_to_process.<<(pn)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
(if Config.strict_require
|
123
|
+
throw(:"error", "Cannot resolve require path for #{pn}")
|
124
|
+
else
|
125
|
+
Gloss.logger
|
126
|
+
.debug("No path found for #{path}")
|
127
|
+
end)
|
128
|
+
end)
|
129
|
+
end
|
130
|
+
private def rbs_stdlib_path_for(libr)
|
131
|
+
File.absolute_path(File.join(@type_checker.rbs_gem_dir, "..", "..", "stdlib", libr))
|
132
|
+
end
|
133
|
+
private def load_rbs_from_require_path(path)
|
134
|
+
Dir.glob(File.join(rbs_stdlib_path_for(path), "**", "*.rbs"))
|
135
|
+
.each() { |fp|
|
136
|
+
@type_checker.load_sig_path(fp)
|
137
|
+
@processed_files.add(fp)
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|