gloss 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 : Node?)
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
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_runtime_dependency "fast_blank"
19
19
  s.add_runtime_dependency "listen"
20
+ s.add_runtime_dependency "rbs"
20
21
  s.add_runtime_dependency "steep"
21
22
 
22
23
  s.add_development_dependency "rake-compiler"
@@ -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 "gloss.bundle"
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
@@ -4,87 +4,21 @@ module Gloss
4
4
  class Builder
5
5
  attr_reader :tree
6
6
 
7
- def initialize(str)
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
- @steep_target = Steep::Project::Target.new(
19
- name: "gloss",
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
- @top_level_decls[class_type.name.name] = class_type unless @current_scope
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
- @top_level_decls[module_type.name.name] = module_type unless @current_scope
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 node[:external_name]
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
@@ -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(Builder.new(content).run, fp).run
25
+ Writer.new(rb_output, fp).run
23
26
  end
24
27
  when "init"
25
28
  force = false
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Gloss
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
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.1
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: 2020-12-30 00:00:00.000000000 Z
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
Binary file