gloss 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|