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.
@@ -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