gloss 0.0.1 → 0.0.2
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/crystal.yml +26 -0
- data/.github/workflows/ruby.yml +33 -0
- data/.gitignore +2 -1
- data/Gemfile +0 -1
- data/Gemfile.lock +5 -11
- data/LICENSE +21 -0
- data/README.md +23 -2
- data/Rakefile +4 -0
- data/ext/gloss/Makefile +27 -5
- data/ext/gloss/extconf.rb +3 -25
- data/ext/gloss/spec/parser_spec.cr +91 -0
- data/ext/gloss/spec/spec_helper.cr +2 -0
- data/ext/gloss/src/cr_ast.cr +23 -9
- data/ext/gloss/src/gloss.cr +11 -17
- data/ext/gloss/src/parser.cr +544 -250
- data/ext/gloss/src/rb_ast.cr +34 -2
- data/gloss.gemspec +1 -0
- data/lib/gloss.rb +3 -1
- data/lib/gloss/builder.rb +24 -73
- data/lib/gloss/cli.rb +4 -1
- data/lib/gloss/parser.rb +18 -0
- data/lib/gloss/type_checker.rb +75 -0
- data/lib/gloss/version.rb +1 -1
- metadata +23 -3
- data/lib/gloss.bundle.dwarf +0 -0
data/ext/gloss/src/rb_ast.cr
CHANGED
@@ -114,15 +114,17 @@ module Rb
|
|
114
114
|
|
115
115
|
class Arg < Node
|
116
116
|
@info : NamedTuple(type: String, name: String, external_name: String, default_value: Node?,
|
117
|
-
restriction: Node
|
117
|
+
restriction: Node?, keyword_arg: Bool)
|
118
118
|
|
119
|
-
def initialize(name : String, external_name : String, restriction : Node?, default_value :
|
119
|
+
def initialize(name : String, external_name : String, restriction : Node?, default_value :
|
120
|
+
Node?, keyword_arg)
|
120
121
|
@info = {
|
121
122
|
type: self.class.name.split("::").last,
|
122
123
|
name: name,
|
123
124
|
restriction: restriction,
|
124
125
|
default_value: default_value,
|
125
126
|
external_name: external_name,
|
127
|
+
keyword_arg: keyword_arg
|
126
128
|
}
|
127
129
|
end
|
128
130
|
|
@@ -236,6 +238,9 @@ module Rb
|
|
236
238
|
class InstanceVar < Var
|
237
239
|
end
|
238
240
|
|
241
|
+
class GlobalVar < Var
|
242
|
+
end
|
243
|
+
|
239
244
|
abstract class Conditional < Node
|
240
245
|
@info : NamedTuple(type: String, condition: Node, then: Node, else: Node)
|
241
246
|
|
@@ -487,5 +492,32 @@ module Rb
|
|
487
492
|
|
488
493
|
delegate :to_json, to: @info
|
489
494
|
end
|
495
|
+
|
496
|
+
class Union < Node
|
497
|
+
@info : NamedTuple(type: String, types: Array(Node))
|
498
|
+
|
499
|
+
def initialize(types)
|
500
|
+
@info = {
|
501
|
+
type: self.class.name.split("::").last,
|
502
|
+
types: types
|
503
|
+
}
|
504
|
+
end
|
505
|
+
|
506
|
+
delegate :to_json, to: @info
|
507
|
+
end
|
508
|
+
|
509
|
+
class Generic < Node
|
510
|
+
@info : NamedTuple(type: String, name: Node, args: Array(Node))
|
511
|
+
|
512
|
+
def initialize(name, args)
|
513
|
+
@info = {
|
514
|
+
type: self.class.name.split("::").last,
|
515
|
+
name: name,
|
516
|
+
args: args
|
517
|
+
}
|
518
|
+
end
|
519
|
+
|
520
|
+
delegate :to_json, to: @info
|
521
|
+
end
|
490
522
|
end
|
491
523
|
end
|
data/gloss.gemspec
CHANGED
data/lib/gloss.rb
CHANGED
@@ -8,6 +8,8 @@ require "fast_blank"
|
|
8
8
|
require "gloss/version"
|
9
9
|
require "gloss/cli"
|
10
10
|
require "gloss/watcher"
|
11
|
+
require "gloss/type_checker"
|
12
|
+
require "gloss/parser"
|
11
13
|
require "gloss/initializer"
|
12
14
|
require "gloss/config"
|
13
15
|
require "gloss/writer"
|
@@ -16,7 +18,7 @@ require "gloss/scope"
|
|
16
18
|
require "gloss/builder"
|
17
19
|
require "gloss/errors"
|
18
20
|
|
19
|
-
require "
|
21
|
+
require "gls" unless ENV["CI"] # a bit of a hack for now
|
20
22
|
|
21
23
|
EMPTY_ARRAY = [].freeze
|
22
24
|
EMPTY_HASH = {}.freeze
|
data/lib/gloss/builder.rb
CHANGED
@@ -4,87 +4,21 @@ module Gloss
|
|
4
4
|
class Builder
|
5
5
|
attr_reader :tree
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(tree_hash, type_checker = nil)
|
8
8
|
@indent_level = 0
|
9
9
|
@inside_macro = false
|
10
10
|
@eval_vars = false
|
11
|
-
tree_json = Gloss.parse_buffer str
|
12
|
-
begin
|
13
|
-
@tree = JSON.parse tree_json, symbolize_names: true
|
14
|
-
rescue JSON::ParserError
|
15
|
-
raise Errors::ParserError, tree_json
|
16
|
-
end
|
17
11
|
@current_scope = nil
|
18
|
-
@
|
19
|
-
|
20
|
-
options: Steep::Project::Options.new,
|
21
|
-
source_patterns: ["gloss"],
|
22
|
-
ignore_patterns: [],
|
23
|
-
signature_patterns: []
|
24
|
-
)
|
25
|
-
Dir.glob("sig/**/*.rbs").each do |fp|
|
26
|
-
next if !@steep_target.possible_signature_file?(fp) || @steep_target.signature_file?(fp)
|
27
|
-
|
28
|
-
Steep.logger.info { "Adding signature file: #{fp}" }
|
29
|
-
@steep_target.add_signature path, (Pathname(".") + fp).cleanpath.read
|
30
|
-
end
|
31
|
-
@top_level_decls = {}
|
12
|
+
@tree = tree_hash
|
13
|
+
@type_checker = type_checker
|
32
14
|
end
|
33
15
|
|
34
16
|
def run
|
35
17
|
rb_output = visit_node(@tree)
|
36
18
|
rb_output = "# frozen_string_literal: true\n#{rb_output}" if Config.frozen_string_literals
|
37
|
-
|
38
|
-
unless check_types(rb_output)
|
39
|
-
raise Errors::TypeError,
|
40
|
-
@steep_target.errors.map { |e|
|
41
|
-
case e
|
42
|
-
when Steep::Errors::NoMethod
|
43
|
-
"Unknown method :#{e.method}, location: #{e.type.location.inspect}"
|
44
|
-
when Steep::Errors::MethodBodyTypeMismatch
|
45
|
-
"Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
|
46
|
-
when Steep::Errors::IncompatibleArguments
|
47
|
-
"Invalid argmuents - method type: #{e.method_type}, receiver type: #{e.receiver_type}"
|
48
|
-
when Steep::Errors::ReturnTypeMismatch
|
49
|
-
"Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
|
50
|
-
when Steep::Errors::IncompatibleAssignment
|
51
|
-
"Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
|
52
|
-
else
|
53
|
-
e.inspect
|
54
|
-
end
|
55
|
-
}.join("\n")
|
56
|
-
end
|
57
|
-
|
58
19
|
rb_output
|
59
20
|
end
|
60
21
|
|
61
|
-
def check_types(rb_str)
|
62
|
-
env_loader = RBS::EnvironmentLoader.new
|
63
|
-
env = RBS::Environment.from_loader(env_loader)
|
64
|
-
|
65
|
-
@top_level_decls.each do |_, decl|
|
66
|
-
env << decl
|
67
|
-
end
|
68
|
-
env = env.resolve_type_names
|
69
|
-
|
70
|
-
@steep_target.instance_variable_set("@environment", env)
|
71
|
-
@steep_target.add_source("gloss", rb_str)
|
72
|
-
|
73
|
-
definition_builder = RBS::DefinitionBuilder.new(env: env)
|
74
|
-
factory = Steep::AST::Types::Factory.new(builder: definition_builder)
|
75
|
-
check = Steep::Subtyping::Check.new(factory: factory)
|
76
|
-
validator = Steep::Signature::Validator.new(checker: check)
|
77
|
-
validator.validate
|
78
|
-
|
79
|
-
raise Errors::TypeValidationError, validator.each_error.to_a.join("\n") unless validator.no_error?
|
80
|
-
|
81
|
-
@steep_target.run_type_check(env, check, Time.now)
|
82
|
-
|
83
|
-
@steep_target.status.is_a?(Steep::Project::Target::TypeCheckStatus) &&
|
84
|
-
@steep_target.no_error? &&
|
85
|
-
@steep_target.errors.empty?
|
86
|
-
end
|
87
|
-
|
88
22
|
def visit_node(node, scope = Scope.new)
|
89
23
|
src = Source.new(@indent_level)
|
90
24
|
case node[:type]
|
@@ -121,7 +55,9 @@ module Gloss
|
|
121
55
|
src.write_ln "end"
|
122
56
|
|
123
57
|
@current_scope = old_parent_scope
|
124
|
-
|
58
|
+
if @type_checker
|
59
|
+
@type_checker.top_level_decls[class_type.name.name] = class_type unless @current_scope
|
60
|
+
end
|
125
61
|
when "ModuleNode"
|
126
62
|
module_name = visit_node node[:name]
|
127
63
|
src.write_ln "module #{module_name}"
|
@@ -146,7 +82,9 @@ module Gloss
|
|
146
82
|
indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
|
147
83
|
|
148
84
|
@current_scope = old_parent_scope
|
149
|
-
|
85
|
+
if @type_checker
|
86
|
+
@type_checker.top_level_decls[module_type.name.name] = module_type unless @current_scope
|
87
|
+
end
|
150
88
|
src.write_ln "end"
|
151
89
|
when "DefNode"
|
152
90
|
args = render_args(node)
|
@@ -199,7 +137,7 @@ module Gloss
|
|
199
137
|
if @current_scope
|
200
138
|
@current_scope.members << method_definition
|
201
139
|
else
|
202
|
-
@type_env << method_definition # should be new class declaration for Object with method_definition as private method
|
140
|
+
@type_checker.type_env << method_definition if @type_checker # should be new class declaration for Object with method_definition as private method
|
203
141
|
end
|
204
142
|
|
205
143
|
indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
|
@@ -278,9 +216,20 @@ module Gloss
|
|
278
216
|
|
279
217
|
src.write node[:name]
|
280
218
|
|
219
|
+
when "GlobalVar"
|
220
|
+
|
221
|
+
src.write node[:name]
|
222
|
+
|
281
223
|
when "Arg"
|
224
|
+
val = node[:external_name]
|
225
|
+
if node[:keyword_arg]
|
226
|
+
val += ":"
|
227
|
+
val += " #{visit_node(node[:default_value])}" if node[:default_value]
|
228
|
+
elsif node[:default_value]
|
229
|
+
val += " = #{visit_node(node[:default_value])}"
|
230
|
+
end
|
282
231
|
|
283
|
-
src.write
|
232
|
+
src.write val
|
284
233
|
|
285
234
|
when "UnaryExpr"
|
286
235
|
|
@@ -386,6 +335,8 @@ module Gloss
|
|
386
335
|
src.write_ln visit_node(node[:ensure])
|
387
336
|
end
|
388
337
|
src.write_ln "end"
|
338
|
+
when "Generic"
|
339
|
+
src.write "#{node[:name]}[#{node[:args].map { |a| visit_node a }.join(", ")}]"
|
389
340
|
when "EmptyNode"
|
390
341
|
# pass
|
391
342
|
else
|
data/lib/gloss/cli.rb
CHANGED
@@ -17,9 +17,12 @@ module Gloss
|
|
17
17
|
(files.empty? ? Dir.glob("#{Config.src_dir}/**/*.rb") : files).each do |fp|
|
18
18
|
puts "=====> Building #{fp}"
|
19
19
|
content = File.read(fp)
|
20
|
+
tree_hash = Parser.new(content).run
|
21
|
+
type_checker = TypeChecker.new
|
22
|
+
rb_output = Builder.new(tree_hash, type_checker)
|
20
23
|
|
21
24
|
puts "=====> Writing #{fp}"
|
22
|
-
Writer.new(
|
25
|
+
Writer.new(rb_output, fp).run
|
23
26
|
end
|
24
27
|
when "init"
|
25
28
|
force = false
|
data/lib/gloss/parser.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gloss
|
4
|
+
class Parser
|
5
|
+
def initialize(str)
|
6
|
+
@str = str
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
tree_json = Gloss.parse_buffer(@str)
|
11
|
+
begin
|
12
|
+
JSON.parse tree_json, symbolize_names: true
|
13
|
+
rescue JSON::ParserError
|
14
|
+
raise Errors::ParserError, tree_json
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gloss
|
4
|
+
class TypeChecker
|
5
|
+
attr_reader :steep_target, :top_level_decls
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@steep_target = Steep::Project::Target.new(
|
9
|
+
name: "gloss",
|
10
|
+
options: Steep::Project::Options.new,
|
11
|
+
source_patterns: ["gloss"],
|
12
|
+
ignore_patterns: [],
|
13
|
+
signature_patterns: []
|
14
|
+
)
|
15
|
+
@top_level_decls = {}
|
16
|
+
Dir.glob("sig/**/*.rbs").each do |fp|
|
17
|
+
next if !@steep_target.possible_signature_file?(fp) || @steep_target.signature_file?(fp)
|
18
|
+
|
19
|
+
Steep.logger.info { "Adding signature file: #{fp}" }
|
20
|
+
@steep_target.add_signature path, (Pathname(".") + fp).cleanpath.read
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run(rb_str)
|
25
|
+
unless check_types(rb_str)
|
26
|
+
raise Errors::TypeError,
|
27
|
+
@steep_target.errors.map { |e|
|
28
|
+
case e
|
29
|
+
when Steep::Errors::NoMethod
|
30
|
+
"Unknown method :#{e.method}, location: #{e.type.location.inspect}"
|
31
|
+
when Steep::Errors::MethodBodyTypeMismatch
|
32
|
+
"Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
|
33
|
+
when Steep::Errors::IncompatibleArguments
|
34
|
+
"Invalid argmuents - method type: #{e.method_type}, receiver type: #{e.receiver_type}"
|
35
|
+
when Steep::Errors::ReturnTypeMismatch
|
36
|
+
"Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
|
37
|
+
when Steep::Errors::IncompatibleAssignment
|
38
|
+
"Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
|
39
|
+
else
|
40
|
+
e.inspect
|
41
|
+
end
|
42
|
+
}.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_types(rb_str)
|
49
|
+
env_loader = RBS::EnvironmentLoader.new
|
50
|
+
env = RBS::Environment.from_loader(env_loader)
|
51
|
+
|
52
|
+
@top_level_decls.each do |_, decl|
|
53
|
+
env << decl
|
54
|
+
end
|
55
|
+
env = env.resolve_type_names
|
56
|
+
|
57
|
+
@steep_target.instance_variable_set("@environment", env)
|
58
|
+
@steep_target.add_source("gloss", rb_str)
|
59
|
+
|
60
|
+
definition_builder = RBS::DefinitionBuilder.new(env: env)
|
61
|
+
factory = Steep::AST::Types::Factory.new(builder: definition_builder)
|
62
|
+
check = Steep::Subtyping::Check.new(factory: factory)
|
63
|
+
validator = Steep::Signature::Validator.new(checker: check)
|
64
|
+
validator.validate
|
65
|
+
|
66
|
+
raise Errors::TypeValidationError, validator.each_error.to_a.join("\n") unless validator.no_error?
|
67
|
+
|
68
|
+
@steep_target.run_type_check(env, check, Time.now)
|
69
|
+
|
70
|
+
@steep_target.status.is_a?(Steep::Project::Target::TypeCheckStatus) &&
|
71
|
+
@steep_target.no_error? &&
|
72
|
+
@steep_target.errors.empty?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/gloss/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gloss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- johansenja
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fast_blank
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rbs
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: steep
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,11 +131,14 @@ extensions:
|
|
117
131
|
- ext/gloss/extconf.rb
|
118
132
|
extra_rdoc_files: []
|
119
133
|
files:
|
134
|
+
- ".github/workflows/crystal.yml"
|
135
|
+
- ".github/workflows/ruby.yml"
|
120
136
|
- ".gitignore"
|
121
137
|
- ".gloss.yml"
|
122
138
|
- ".rubocop.yml"
|
123
139
|
- Gemfile
|
124
140
|
- Gemfile.lock
|
141
|
+
- LICENSE
|
125
142
|
- README.md
|
126
143
|
- Rakefile
|
127
144
|
- bin/console
|
@@ -129,6 +146,8 @@ files:
|
|
129
146
|
- ext/gloss/Makefile
|
130
147
|
- ext/gloss/extconf.rb
|
131
148
|
- ext/gloss/shard.yml
|
149
|
+
- ext/gloss/spec/parser_spec.cr
|
150
|
+
- ext/gloss/spec/spec_helper.cr
|
132
151
|
- ext/gloss/src/cr_ast.cr
|
133
152
|
- ext/gloss/src/gloss.cr
|
134
153
|
- ext/gloss/src/lexer.cr
|
@@ -136,15 +155,16 @@ files:
|
|
136
155
|
- ext/gloss/src/parser.cr
|
137
156
|
- ext/gloss/src/rb_ast.cr
|
138
157
|
- gloss.gemspec
|
139
|
-
- lib/gloss.bundle.dwarf
|
140
158
|
- lib/gloss.rb
|
141
159
|
- lib/gloss/builder.rb
|
142
160
|
- lib/gloss/cli.rb
|
143
161
|
- lib/gloss/config.rb
|
144
162
|
- lib/gloss/errors.rb
|
145
163
|
- lib/gloss/initializer.rb
|
164
|
+
- lib/gloss/parser.rb
|
146
165
|
- lib/gloss/scope.rb
|
147
166
|
- lib/gloss/source.rb
|
167
|
+
- lib/gloss/type_checker.rb
|
148
168
|
- lib/gloss/version.rb
|
149
169
|
- lib/gloss/watcher.rb
|
150
170
|
- lib/gloss/writer.rb
|
data/lib/gloss.bundle.dwarf
DELETED
Binary file
|