gloss 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0bcae87434234732f58cc6d0bb9a1408a72fae1f51b9fe0c1dcf86249784fe2
4
- data.tar.gz: d3db73f4c6e907a0b11eaba71f4428680372b56df1e3e6d8df973e80c8346e26
3
+ metadata.gz: 3a1461d26e97ea9693ee00d63016e0fa1e34ea719de672ae0d4c303e4efb5144
4
+ data.tar.gz: 2370827768056a2575c1987d661a140d7247b68629e049aa58b0bc9d1de0bc31
5
5
  SHA512:
6
- metadata.gz: d58d4583ec2c703c023741bc6fd211fd146d81e3e90b3c882b5c4e37aa4cb12de2ad8cf46f6c26a7bf392cfd6accc1372cbaa01be8eb763468918d0a60d05eba
7
- data.tar.gz: ab6244066db5797def1d7225598c87e5bd9cde3385b303854e176332a96c16f8fbbe6f4a531dadb6fe6e22e9d860152daab4b5d4282af79646f7421ce4cde0e3
6
+ metadata.gz: 17d5c598e8199b375599112a4365cb14691e651783a86880844dd35ff312dc2ca2ed0bb632e0a51897dcd3ba774231ae9c547710f19ed134318a39e098224a37
7
+ data.tar.gz: 6626ff60cab24dacfc6e6780368a719e04c45c80c1f4e8b89db76bfdc0848f725e33d907058b6c9991fc80c1b97b518503eb42a47435c733f43a4e0d0d49eb0e
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gloss (0.0.2)
4
+ gloss (0.0.3)
5
5
  fast_blank
6
6
  listen
7
7
  rbs
@@ -10,7 +10,7 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- activesupport (6.1.0)
13
+ activesupport (6.1.1)
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
15
  i18n (>= 1.6, < 2)
16
16
  minitest (>= 5.1)
@@ -26,14 +26,14 @@ GEM
26
26
  diff-lcs (1.4.4)
27
27
  fast_blank (1.0.0)
28
28
  ffi (1.14.2)
29
- i18n (1.8.6)
29
+ i18n (1.8.7)
30
30
  concurrent-ruby (~> 1.0)
31
31
  language_server-protocol (3.15.0.1)
32
- listen (3.4.0)
32
+ listen (3.4.1)
33
33
  rb-fsevent (~> 0.10, >= 0.10.3)
34
34
  rb-inotify (~> 0.9, >= 0.9.10)
35
35
  method_source (1.0.0)
36
- minitest (5.14.2)
36
+ minitest (5.14.3)
37
37
  parallel (1.20.1)
38
38
  parser (2.7.2.0)
39
39
  ast (~> 2.4.1)
@@ -44,40 +44,40 @@ GEM
44
44
  byebug (~> 11.0)
45
45
  pry (~> 0.13.0)
46
46
  rainbow (3.0.0)
47
- rake (13.0.1)
47
+ rake (13.0.3)
48
48
  rake-compiler (1.1.1)
49
49
  rake
50
50
  rb-fsevent (0.10.4)
51
51
  rb-inotify (0.10.1)
52
52
  ffi (~> 1.0)
53
53
  rbs (1.0.0)
54
- regexp_parser (2.0.0)
54
+ regexp_parser (2.0.3)
55
55
  rexml (3.2.4)
56
56
  rspec (3.10.0)
57
57
  rspec-core (~> 3.10.0)
58
58
  rspec-expectations (~> 3.10.0)
59
59
  rspec-mocks (~> 3.10.0)
60
- rspec-core (3.10.0)
60
+ rspec-core (3.10.1)
61
61
  rspec-support (~> 3.10.0)
62
- rspec-expectations (3.10.0)
62
+ rspec-expectations (3.10.1)
63
63
  diff-lcs (>= 1.2.0, < 2.0)
64
64
  rspec-support (~> 3.10.0)
65
- rspec-mocks (3.10.0)
65
+ rspec-mocks (3.10.1)
66
66
  diff-lcs (>= 1.2.0, < 2.0)
67
67
  rspec-support (~> 3.10.0)
68
- rspec-support (3.10.0)
69
- rubocop (1.5.1)
68
+ rspec-support (3.10.1)
69
+ rubocop (1.7.0)
70
70
  parallel (~> 1.10)
71
71
  parser (>= 2.7.1.5)
72
72
  rainbow (>= 2.2.2, < 4.0)
73
- regexp_parser (>= 2.0)
73
+ regexp_parser (>= 1.8, < 3.0)
74
74
  rexml
75
- rubocop-ast (>= 1.2.0)
75
+ rubocop-ast (>= 1.2.0, < 2.0)
76
76
  ruby-progressbar (~> 1.7)
77
77
  unicode-display_width (>= 1.4.0, < 2.0)
78
- rubocop-ast (1.3.0)
78
+ rubocop-ast (1.4.0)
79
79
  parser (>= 2.7.1.5)
80
- ruby-progressbar (1.10.1)
80
+ ruby-progressbar (1.11.0)
81
81
  steep (0.39.0)
82
82
  activesupport (>= 5.1)
83
83
  ast_utils (~> 0.3.0)
@@ -103,4 +103,4 @@ DEPENDENCIES
103
103
  rubocop
104
104
 
105
105
  BUNDLED WITH
106
- 2.1.4
106
+ 2.2.3
@@ -88,4 +88,37 @@ module Gloss
88
88
  hsh : Hash[String, String] = { "hello" => "world" }
89
89
  GLS
90
90
  end
91
+
92
+ it "parses method calls in case statements" do
93
+ expected =
94
+ %q|{"type":"Case","condition":{"type":"LiteralNode","value":"\"abc\"","rb_type":"String"},"whens":[{"type":"When","conditions":[{"type":"Proc","function":{"type":"DefNode","name":"->","body":{"type":"Call","name":"start_with?","args":[{"type":"LiteralNode","value":"\"a\"","rb_type":"String"}],"object":{"type":"Var","name":"x"},"block":null,"block_arg":null},"rp_args":[{"type":"Arg","name":"x","external_name":"x","default_value":null,"restriction":null,"keyword_arg":false}],"receiver":null,"return_type":null,"rest_kw_args":null}}],"body":{"type":"LiteralNode","value":"1","rb_type":"Integer"},"exhaustive":false}],"else":{"type":"LiteralNode","value":"0","rb_type":"Integer"},"exhaustive":false}|
95
+ Gloss.parse_string(<<-GLS).should eq expected
96
+ case "abc"
97
+ when .start_with? 'a'
98
+ 1
99
+ else
100
+ 0
101
+ end
102
+ GLS
103
+ end
104
+
105
+ it "allows constant methods" do
106
+ Gloss.parse_string(<<-GLS).should be_truthy
107
+ def Hello(arg = nil)
108
+ end
109
+
110
+ Hello()
111
+
112
+ Hello("a")
113
+ GLS
114
+ end
115
+
116
+ it "requires constant methods to be called with ()" do
117
+ Gloss.parse_string(<<-GLS).should be_falsey
118
+ def Hello(arg = nil)
119
+ end
120
+
121
+ Hello
122
+ GLS
123
+ end
91
124
  end
@@ -62,7 +62,7 @@ module Crystal
62
62
 
63
63
  class SymbolLiteral < ASTNode
64
64
  def to_rb
65
- Rb::AST::LiteralNode.new(":#{@value.to_s}", Rb::AST::RbLiteral::Symbol)
65
+ Rb::AST::LiteralNode.new(%{:"#{@value.to_s}"}, Rb::AST::RbLiteral::Symbol)
66
66
  end
67
67
  end
68
68
 
@@ -74,13 +74,13 @@ module Crystal
74
74
 
75
75
  class HashLiteral < ASTNode
76
76
  def to_rb
77
- Rb::AST::HashLiteral.new(@entries.map { |e| { e.key.to_rb, e.value.to_rb }})
77
+ Rb::AST::HashLiteral.new(@entries.map { |e| {e.key.to_rb, e.value.to_rb} })
78
78
  end
79
79
  end
80
80
 
81
81
  class NamedTupleLiteral < ASTNode
82
82
  def to_rb
83
- Rb::AST::HashLiteral.new(@entries.map { |e| { e.key, e.value.to_rb }}, frozen: true)
83
+ Rb::AST::HashLiteral.new(@entries.map { |e| {e.key, e.value.to_rb} }, frozen: true)
84
84
  end
85
85
  end
86
86
 
@@ -92,7 +92,7 @@ module Crystal
92
92
 
93
93
  class RegexLiteral < ASTNode
94
94
  def to_rb
95
- Rb::AST::RegexLiteral.new("//")
95
+ Rb::AST::RegexLiteral.new(@value.to_rb)
96
96
  end
97
97
  end
98
98
 
@@ -105,7 +105,7 @@ module Crystal
105
105
  class Def < ASTNode
106
106
  def to_rb
107
107
  Rb::AST::DefNode.new(@name, @args.map(&.to_rb), @body.to_rb, receiver.try(&.to_rb),
108
- return_type.try(&.to_rb), @double_splat.try(&.to_rb))
108
+ return_type.try(&.to_rb), @double_splat.try(&.to_rb))
109
109
  end
110
110
  end
111
111
 
@@ -135,8 +135,14 @@ module Crystal
135
135
 
136
136
  class Call < ASTNode
137
137
  def to_rb
138
- Rb::AST::Call.new(@obj.try(&.to_rb), @name, @args.map(&.to_rb), @block.try(&.to_rb),
139
- @block_arg.try(&.to_rb))
138
+ Rb::AST::Call.new(
139
+ @obj.try(&.to_rb),
140
+ @name,
141
+ @args.map(&.to_rb),
142
+ @named_args.try(&.map(&.to_rb.as(Rb::AST::Arg))),
143
+ @block.try(&.to_rb),
144
+ @block_arg.try(&.to_rb)
145
+ )
140
146
  end
141
147
  end
142
148
 
@@ -145,13 +151,13 @@ module Crystal
145
151
 
146
152
  def to_rb
147
153
  Rb::AST::Arg.new(@name, @external_name, @restriction.try(&.to_rb),
148
- @default_value.try(&.to_rb), @keyword_arg)
154
+ @default_value.try(&.to_rb), @keyword_arg)
149
155
  end
150
156
  end
151
157
 
152
158
  class NamedArgument < ASTNode
153
159
  def to_rb
154
- Rb::AST::EmptyNode.new(self.class.name)
160
+ Rb::AST::Arg.new(@name, @name, nil, @value.to_rb, true)
155
161
  end
156
162
  end
157
163
 
@@ -161,6 +167,12 @@ module Crystal
161
167
  end
162
168
  end
163
169
 
170
+ class Unless < ASTNode
171
+ def to_rb
172
+ Rb::AST::Unless.new(@cond.to_rb, @then.to_rb, @else.to_rb)
173
+ end
174
+ end
175
+
164
176
  class Assign < ASTNode
165
177
  def to_rb
166
178
  Rb::AST::Assign.new(@target.to_rb, @value.to_rb)
@@ -295,7 +307,20 @@ module Crystal
295
307
  class When < ASTNode
296
308
  def to_rb
297
309
  Rb::AST::When.new(
298
- @conds.map(&.to_rb),
310
+ @conds.map do |c|
311
+ if c.is_a? Call
312
+ arg_name = "x"
313
+ ProcLiteral.new(
314
+ Def.new(
315
+ "->",
316
+ [Arg.new(arg_name)],
317
+ c.tap { |call| call.obj = Var.new(arg_name) }
318
+ )
319
+ ).to_rb
320
+ else
321
+ c.to_rb
322
+ end
323
+ end,
299
324
  @body.to_rb,
300
325
  @exhaustive
301
326
  )
@@ -351,7 +376,7 @@ module Crystal
351
376
  class ExceptionHandler < ASTNode
352
377
  def to_rb
353
378
  Rb::AST::ExceptionHandler.new(@body.to_rb, @rescues.try(&.map(&.to_rb)), @else.try(&.to_rb),
354
- @ensure.try(&.to_rb))
379
+ @ensure.try(&.to_rb))
355
380
  end
356
381
  end
357
382
 
@@ -373,11 +398,41 @@ module Crystal
373
398
  end
374
399
  end
375
400
 
376
- {% for class_name in %w[ProcNotation Macro OffsetOf VisibilityModifier IsA RespondsTo
401
+ class ProcLiteral < ASTNode
402
+ def to_rb
403
+ Rb::AST::Proc.new(@def.to_rb)
404
+ end
405
+ end
406
+
407
+ class Include < ASTNode
408
+ def to_rb
409
+ Rb::AST::Include.new(@name.to_rb)
410
+ end
411
+ end
412
+
413
+ class Extend < ASTNode
414
+ def to_rb
415
+ Rb::AST::Extend.new(@name.to_rb)
416
+ end
417
+ end
418
+
419
+ class IsA < ASTNode
420
+ def to_rb
421
+ Rb::AST::Call.new(
422
+ @obj.to_rb,
423
+ "is_a?",
424
+ [@const.to_rb],
425
+ nil,
426
+ nil,
427
+ nil
428
+ )
429
+ end
430
+ end
431
+
432
+ {% for class_name in %w[ProcNotation Macro OffsetOf VisibilityModifier RespondsTo
377
433
  Select ImplicitObj AnnotationDef While Until UninitializedVar
378
- ProcLiteral ProcPointer Self Yield Include
379
- Extend LibDef FunDef TypeDef CStructOrUnionDef ExternalVar Alias
380
- Metaclass Cast NilableCast TypeOf Annotation
434
+ ProcPointer Self Yield LibDef FunDef TypeDef CStructOrUnionDef
435
+ ExternalVar Alias Metaclass Cast NilableCast TypeOf Annotation
381
436
  Underscore MagicConstant Asm AsmOperand] %}
382
437
  class {{class_name.id}} < ASTNode
383
438
  def to_rb
@@ -17,10 +17,10 @@ module Gloss
17
17
  line = @line_number
18
18
  column = @token.column_number
19
19
 
20
- # next_token_skip_space
21
- # next_token_skip_space_or_newline
22
- # of = nil
23
- Crystal::ArrayLiteral.new([] of Crystal::ASTNode) # .at_end(of)
20
+ next_token_skip_space
21
+ next_token_skip_space_or_newline
22
+ of = nil
23
+ Crystal::ArrayLiteral.new([] of Crystal::ASTNode).at_end(of)
24
24
  end
25
25
 
26
26
  def new_hash_literal(entries, line, column, end_location, allow_of = true)
@@ -113,16 +113,16 @@ module Rb
113
113
  end
114
114
 
115
115
  class Arg < Node
116
- @info : NamedTuple(type: String, name: String, external_name: String, default_value: Node?,
116
+ @info : NamedTuple(type: String, name: String, external_name: String, value: Node?,
117
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?, value :
120
120
  Node?, keyword_arg)
121
121
  @info = {
122
122
  type: self.class.name.split("::").last,
123
123
  name: name,
124
124
  restriction: restriction,
125
- default_value: default_value,
125
+ value: value,
126
126
  external_name: external_name,
127
127
  keyword_arg: keyword_arg
128
128
  }
@@ -200,7 +200,17 @@ module Rb
200
200
  delegate :to_json, to: @info
201
201
  end
202
202
 
203
- class RegexLiteral < NodeWithValue
203
+ class RegexLiteral < Node
204
+ @info : NamedTuple(type: String, value: Node)
205
+
206
+ def initialize(value)
207
+ @info = {
208
+ type: self.class.name.split("::").last,
209
+ value: value,
210
+ }
211
+ end
212
+
213
+ delegate :to_json, to: @info
204
214
  end
205
215
 
206
216
  class Nop < Node
@@ -308,9 +318,10 @@ module Rb
308
318
  end
309
319
 
310
320
  class Call < Node
311
- @info : NamedTuple(type: String, name: String, args: Array(Node), object: Node?, block: Block?, block_arg: Node?)
321
+ @info : NamedTuple(type: String, name: String, args: Array(Node), object: Node?, block:
322
+ Block?, block_arg: Node?, named_args: Array(Arg)?)
312
323
 
313
- def initialize(object : Node?, name : String, args : Array(Node), block, block_arg)
324
+ def initialize(object : Node?, name : String, args : Array(Node), named_args, block, block_arg)
314
325
  @info = {
315
326
  type: self.class.name.split("::").last,
316
327
  name: name,
@@ -318,6 +329,7 @@ module Rb
318
329
  object: object,
319
330
  block: block,
320
331
  block_arg: block_arg,
332
+ named_args: named_args,
321
333
  }
322
334
  end
323
335
 
@@ -519,5 +531,37 @@ module Rb
519
531
 
520
532
  delegate :to_json, to: @info
521
533
  end
534
+
535
+ class Proc < Node
536
+ @info : NamedTuple(type: String, function: DefNode)
537
+
538
+ def initialize(function)
539
+ @info = {
540
+ type: self.class.name.split("::").last,
541
+ function: function
542
+ }
543
+ end
544
+
545
+ delegate :to_json, to: @info
546
+ end
547
+
548
+ class NodeWithNameNode < Node
549
+ @info : NamedTuple(type: String, name: Node)
550
+
551
+ def initialize(name)
552
+ @info = {
553
+ type: self.class.name.split("::").last,
554
+ name: name
555
+ }
556
+ end
557
+
558
+ delegate :to_json, to: @info
559
+ end
560
+
561
+ class Extend < NodeWithNameNode
562
+ end
563
+
564
+ class Include < NodeWithNameNode
565
+ end
522
566
  end
523
567
  end
@@ -15,8 +15,13 @@ module Gloss
15
15
 
16
16
  def run
17
17
  rb_output = visit_node(@tree)
18
- rb_output = "# frozen_string_literal: true\n#{rb_output}" if Config.frozen_string_literals
19
- rb_output
18
+ <<~RUBY
19
+ #{"# frozen_string_literal: true\n" if Config.frozen_string_literals}
20
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
21
+ ##### See #{Config.src_dir}/ to make changes
22
+
23
+ #{rb_output}
24
+ RUBY
20
25
  end
21
26
 
22
27
  def visit_node(node, scope = Scope.new)
@@ -24,24 +29,39 @@ module Gloss
24
29
  case node[:type]
25
30
  when "ClassNode"
26
31
  class_name = visit_node(node[:name])
27
- superclass = if node[:superclass]
28
- @eval_vars = true
29
- visit_node(node[:superclass])
30
- @eval_vars = false
31
- else
32
- nil
33
- end
32
+ current_namespace = @current_scope ? @current_scope.name.to_namespace : RBS::Namespace.root
33
+ superclass_type = nil
34
+ superclass_output = nil
35
+ if node[:superclass]
36
+ @eval_vars = true
37
+ superclass_output = visit_node(node[:superclass])
38
+ @eval_vars = false
39
+ ns = if superclass_output.start_with? '::'
40
+ RBS::Namespace.root
41
+ elsif superclass_output.include? '::'
42
+ current_namespace
43
+ else
44
+ RBS::Namespace.empty
45
+ end
46
+ superclass_type = RBS::AST::Declarations::Class::Super.new(
47
+ name: RBS::TypeName.new(
48
+ name: superclass_output.to_sym,
49
+ namespace: ns
50
+ ),
51
+ args: [],
52
+ location: nil
53
+ )
54
+ end
34
55
 
35
- src.write_ln "class #{class_name}#{" < #{superclass}" if superclass}"
56
+ src.write_ln "class #{class_name}#{" < #{superclass_output}" if superclass_output}"
36
57
 
37
- current_namespace = @current_scope ? @current_scope.name.to_namespace : RBS::Namespace.root
38
58
  class_type = RBS::AST::Declarations::Class.new(
39
59
  name: RBS::TypeName.new(
40
60
  namespace: current_namespace,
41
61
  name: class_name.to_sym
42
62
  ),
43
63
  type_params: RBS::AST::Declarations::ModuleTypeParams.new, # responds to #add to add params
44
- super_class: superclass ? RBS::AST::Declarations::Class::Super.new(name: RBS::Typename.new(name: super_class.to_sym, namespace: RBS::Namespace.root), args: [], location: nil) : nil,
64
+ super_class: superclass_type,
45
65
  members: [],
46
66
  annotations: [],
47
67
  location: node[:location],
@@ -55,6 +75,9 @@ module Gloss
55
75
  src.write_ln "end"
56
76
 
57
77
  @current_scope = old_parent_scope
78
+
79
+ @current_scope.members << class_type if @current_scope
80
+
58
81
  if @type_checker
59
82
  @type_checker.top_level_decls[class_type.name.name] = class_type unless @current_scope
60
83
  end
@@ -62,7 +85,7 @@ module Gloss
62
85
  module_name = visit_node node[:name]
63
86
  src.write_ln "module #{module_name}"
64
87
 
65
- current_namespace = RBS::Namespace.root # RBS::Namespace.new(path: [module_name.to_sym], absolute: false)
88
+ current_namespace = @current_scope ? @current_scope.name.to_namespace : RBS::Namespace.root
66
89
 
67
90
  module_type = RBS::AST::Declarations::Module.new(
68
91
  name: RBS::TypeName.new(
@@ -82,6 +105,9 @@ module Gloss
82
105
  indented(src) { src.write_ln visit_node(node[:body]) if node[:body] }
83
106
 
84
107
  @current_scope = old_parent_scope
108
+
109
+ @current_scope.members << module_type if @current_scope
110
+
85
111
  if @type_checker
86
112
  @type_checker.top_level_decls[module_type.name.name] = module_type unless @current_scope
87
113
  end
@@ -107,12 +133,17 @@ module Gloss
107
133
  RBS::MethodType.new(
108
134
  type_params: [],
109
135
  type: RBS::Types::Function.new(
110
- required_positionals: [],
111
- optional_positionals: [],
112
- rest_positionals: nil,
136
+ required_positionals: node[:rp_args]&.map do |a|
137
+ RBS::Types::Function::Param.new(
138
+ name: visit_node(a).to_sym,
139
+ type: RBS::Types::Bases::Any.new(location: nil)
140
+ )
141
+ end || EMPTY_ARRAY,
142
+ optional_positionals: node[:op_args] || EMPTY_ARRAY,
143
+ rest_positionals: node[:rest_p_args],
113
144
  trailing_positionals: [],
114
- required_keywords: {},
115
- optional_keywords: {},
145
+ required_keywords: node[:req_kw_args] || EMPTY_HASH,
146
+ optional_keywords: node[:opt_kw_args] || EMPTY_HASH,
116
147
  rest_keywords: node[:rest_kw_args] ?
117
148
  RBS::Types::Function::Param.new(
118
149
  name: visit_node(node[:rest_kw_args]).to_sym,
@@ -148,6 +179,7 @@ module Gloss
148
179
  when "Call"
149
180
  obj = node[:object] ? "#{visit_node(node[:object], scope)}." : ""
150
181
  args = node[:args] || EMPTY_ARRAY
182
+ args += node[:named_args] if node[:named_args]
151
183
  args = if !args.empty? || node[:block_arg]
152
184
  "(#{args.map { |a| visit_node(a, scope).strip }.reject(&:blank?).join(", ")}#{"&#{visit_node(node[:block_arg]).strip}" if node[:block_arg]})"
153
185
  else
@@ -224,9 +256,9 @@ module Gloss
224
256
  val = node[:external_name]
225
257
  if node[:keyword_arg]
226
258
  val += ":"
227
- val += " #{visit_node(node[:default_value])}" if node[:default_value]
228
- elsif node[:default_value]
229
- val += " = #{visit_node(node[:default_value])}"
259
+ val += " #{visit_node(node[:value])}" if node[:value]
260
+ elsif node[:value]
261
+ val += " = #{visit_node(node[:value])}"
230
262
  end
231
263
 
232
264
  src.write val
@@ -272,6 +304,16 @@ module Gloss
272
304
  end
273
305
 
274
306
  src.write_ln "end)"
307
+ when "Unless"
308
+ src.write_ln "unless #{visit_node node[:condition]}"
309
+ indented(src) { src.write_ln visit_node(node[:then]) }
310
+
311
+ if node[:else]
312
+ src.write_ln "else"
313
+ indented(src) { src.write_ln visit_node(node[:else]) }
314
+ end
315
+
316
+ src.write_ln "end"
275
317
  when "Case"
276
318
  src.write "case"
277
319
  src.write " #{visit_node(node[:condition]).strip}\n" if node[:condition]
@@ -321,22 +363,34 @@ module Gloss
321
363
  src.write_ln "#{visit_node(node[:var])} = #{visit_node(node[:value])}"
322
364
  when "ExceptionHandler"
323
365
  src.write_ln "begin"
324
- src.write_ln visit_node(node[:body])
325
- node[:rescues]&.each do |r|
326
- src.write_ln "rescue #{r[:types].map { |n| visit_node n }.join(", ") if r[:types]}#{" => #{r[:name]}" if r[:name]}"
327
- src.write_ln visit_node(r[:body]) if r[:body]
328
- end
329
- if node[:else]
330
- src.write_ln "else"
331
- src.write_ln visit_node(node[:else])
332
- end
333
- if node[:ensure]
334
- src.write_ln "ensure"
335
- src.write_ln visit_node(node[:ensure])
366
+ indented src do
367
+ src.write_ln visit_node(node[:body])
336
368
  end
369
+ node[:rescues]&.each do |r|
370
+ src.write_ln "rescue #{r[:types].map { |n| visit_node n }.join(", ") if r[:types]}#{" => #{r[:name]}" if r[:name]}"
371
+ indented(src) { src.write_ln visit_node(r[:body]) } if r[:body]
372
+ end
373
+ if node[:else]
374
+ src.write_ln "else"
375
+ indented(src) { src.write_ln visit_node(node[:else]) }
376
+ end
377
+ if node[:ensure]
378
+ src.write_ln "ensure"
379
+ intended(src) { src.write_ln visit_node(node[:ensure]) }
380
+ end
337
381
  src.write_ln "end"
338
382
  when "Generic"
339
383
  src.write "#{node[:name]}[#{node[:args].map { |a| visit_node a }.join(", ")}]"
384
+ when "Proc"
385
+ fn = node[:function]
386
+ src.write "->#{render_args(fn)} { #{visit_node fn[:body]} }"
387
+ when "Include"
388
+ src.write_ln "include #{visit_node node[:name]}"
389
+ when "Extend"
390
+ src.write_ln "extend #{visit_node node[:name]}"
391
+ when "RegexLiteral"
392
+ contents = visit_node node[:value]
393
+ src.write Regexp.new(contents.undump).inspect
340
394
  when "EmptyNode"
341
395
  # pass
342
396
  else
@@ -14,12 +14,13 @@ module Gloss
14
14
  when "watch"
15
15
  Watcher.new.watch
16
16
  when "build"
17
- (files.empty? ? Dir.glob("#{Config.src_dir}/**/*.rb") : files).each do |fp|
17
+ (files.empty? ? Dir.glob("#{Config.src_dir}/**/*.gl") : files).each do |fp|
18
18
  puts "=====> Building #{fp}"
19
19
  content = File.read(fp)
20
20
  tree_hash = Parser.new(content).run
21
21
  type_checker = TypeChecker.new
22
- rb_output = Builder.new(tree_hash, type_checker)
22
+ rb_output = Builder.new(tree_hash, type_checker).run
23
+ type_checker.run(rb_output)
23
24
 
24
25
  puts "=====> Writing #{fp}"
25
26
  Writer.new(rb_output, fp).run
@@ -1,15 +1,17 @@
1
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
5
+
3
6
  require "ostruct"
4
7
  require "yaml"
5
-
6
8
  module Gloss
7
9
  user_config = YAML.safe_load(File.read(".gloss.yml"))
8
- Config = OpenStruct.new(
9
- default_config: {
10
- frozen_string_literals: true,
11
- src_dir: "src",
12
- }.freeze
13
- )
14
- Config.default_config.each { |k, v| Config.send(:"#{k}=", user_config[k.to_s] || v) }
10
+ Config = OpenStruct.new(default_config: {:frozen_string_literals => true,
11
+ :src_dir => "src"}.freeze)
12
+ Config.default_config
13
+ .each { |k, v|
14
+ Config.send(:"#{k}=", user_config.[](k.to_s) || v)
15
+ }
15
16
  end
17
+
@@ -1,11 +1,18 @@
1
- module Gloss
2
- module Errors
3
- class BaseGlossError < StandardError; end
4
-
5
- class TypeValidationError < BaseGlossError; end
1
+ # frozen_string_literal: true
6
2
 
7
- class TypeError < BaseGlossError; end
3
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
4
+ ##### See src/ to make changes
8
5
 
9
- class ParserError < BaseGlossError; end
6
+ module Gloss
7
+ module Errors
8
+ class BaseGlossError < StandardError
9
+ end
10
+ class TypeValidationError < BaseGlossError
11
+ end
12
+ class TypeError < BaseGlossError
13
+ end
14
+ class ParserError < BaseGlossError
15
+ end
10
16
  end
11
17
  end
18
+
@@ -1,4 +1,8 @@
1
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
+
2
6
  require "yaml"
3
7
  module Gloss
4
8
  class Initializer
@@ -18,3 +22,4 @@ module Gloss
18
22
  end
19
23
  end
20
24
  end
25
+
@@ -1,18 +1,21 @@
1
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
5
+
3
6
  module Gloss
4
7
  class Parser
5
8
  def initialize(str)
6
9
  @str = str
7
10
  end
8
-
9
- def run
11
+ def run()
10
12
  tree_json = Gloss.parse_buffer(@str)
11
13
  begin
12
- JSON.parse tree_json, symbolize_names: true
14
+ JSON.parse(tree_json, symbolize_names: true)
13
15
  rescue JSON::ParserError
14
- raise Errors::ParserError, tree_json
16
+ raise(Errors::ParserError, tree_json)
15
17
  end
16
18
  end
17
19
  end
18
20
  end
21
+
@@ -1,75 +1,61 @@
1
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
5
+
3
6
  module Gloss
4
7
  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
- )
8
+ Project = Struct.new(:targets)
9
+ attr_reader(:steep_target, :top_level_decls)
10
+ def initialize()
11
+ @steep_target = Steep::Project::Target.new(name: "gloss", options: Steep::Project::Options.new
12
+ .tap { |o|
13
+ o.allow_unknown_constant_assignment=(true)
14
+ }, source_patterns: ["gloss.rb"], ignore_patterns: Array.new, signature_patterns: ["sig"])
15
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
16
  end
23
-
24
17
  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")
18
+ unless check_types(rb_str)
19
+ raise(Errors::TypeError, @steep_target.errors
20
+ .map { |e|
21
+ case e
22
+ when Steep::Errors::NoMethod
23
+ "Unknown method :#{e.method}, location: #{e.type
24
+ .location
25
+ .inspect}"
26
+ when Steep::Errors::MethodBodyTypeMismatch
27
+ "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
28
+ when Steep::Errors::IncompatibleArguments
29
+ "Invalid argmuents - method type: #{e.method_type}, receiver type: #{e.receiver_type}"
30
+ when Steep::Errors::ReturnTypeMismatch
31
+ "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
32
+ when Steep::Errors::IncompatibleAssignment
33
+ "Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
34
+ end
35
+ }
36
+ .join("\n"))
43
37
  end
44
-
45
- true
38
+ true
46
39
  end
47
-
48
40
  def check_types(rb_str)
49
41
  env_loader = RBS::EnvironmentLoader.new
50
42
  env = RBS::Environment.from_loader(env_loader)
51
-
52
- @top_level_decls.each do |_, decl|
53
- env << decl
54
- end
43
+ project = Steep::Project.new(steepfile_path: Pathname.new(Config.src_dir)
44
+ .realpath)
45
+ project.targets
46
+ .<<(@steep_target)
47
+ loader = Steep::Project::FileLoader.new(project: project)
48
+ loader.load_signatures
49
+ @steep_target.add_source("gloss.rb", rb_str)
50
+ @top_level_decls.each { |_, decl|
51
+ env.<<(decl)
52
+ }
55
53
  env = env.resolve_type_names
56
-
57
54
  @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?
55
+ @steep_target.type_check
56
+ @steep_target.status
57
+ .is_a?(Steep::Project::Target::TypeCheckStatus) && @steep_target.no_error? && @steep_target.errors
58
+ .empty?
73
59
  end
74
60
  end
75
61
  end
@@ -1,3 +1,9 @@
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
+
1
6
  module Gloss
2
- VERSION = "0.0.2"
7
+ VERSION = "0.0.3"
3
8
  end
9
+
@@ -1,32 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "listen"
3
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
4
+ ##### See src/ to make changes
4
5
 
6
+ require "listen"
5
7
  module Gloss
6
8
  class Watcher
7
- def initialize
8
- @paths = %w[src/]
9
+ def initialize()
10
+ @paths = ["src/"]
9
11
  end
10
-
11
- def watch
12
- puts "=====> Now listening for changes in #{@paths.join(', ')}"
13
- listener = Listen.to(*@paths, latency: 2) do |modified, added, removed|
14
- (modified + added).each do |f|
12
+ def watch()
13
+ puts("=====> Now listening for changes in #{@paths.join(", ")}")
14
+ listener = Listen.to(*@paths, latency: 2) { |modified, added, removed|
15
+ modified.+(added)
16
+ .each { |f|
15
17
  content = File.read(f)
16
- Writer.new(Builder.new(content).run, f).run
17
- end
18
- removed.each do |f|
18
+ Writer.new(Builder.new(content)
19
+ .run, f)
20
+ .run
21
+ }
22
+ removed.each { |f|
19
23
  out_path = Utils.src_path_to_output_path(f)
20
- File.delete out_path if File.exist? out_path
21
- end
22
- end
24
+ (if File.exist?(out_path)
25
+ File.delete(out_path)
26
+ end)
27
+ }
28
+ }
23
29
  listener.start
24
30
  begin
25
- loop { sleep 10 }
31
+ loop { ||
32
+ sleep(10)
33
+ }
26
34
  rescue Interrupt
27
- puts "=====> Interrupt signal received, shutting down"
28
- exit 0
35
+ puts("=====> Interrupt signal received, shutting down")
36
+ exit(0)
29
37
  end
30
38
  end
31
39
  end
32
40
  end
41
+
@@ -1,26 +1,34 @@
1
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
5
+
6
+ require "pathname"
7
+ require "fileutils"
3
8
  module Gloss
4
9
  module Utils
5
10
  module_function
6
-
7
11
  def src_path_to_output_path(src_path)
8
- src_path.sub(%r{\A(?:\./)?#{Config.src_dir}/?}, "")
12
+ src_path.sub(/\A(?:\.\/)?#{Config.src_dir}\/?/, "")
13
+ .sub(/\.gl$/, ".rb")
9
14
  end
10
15
  end
11
-
12
16
  class Writer
13
17
  include Utils
14
-
15
- def initialize(content, src_path, output_path = nil)
16
- @content, @src_path = content, src_path
17
- @output_path = output_path || src_path_to_output_path(src_path)
18
+ def initialize(content, src_path, output_path = Pathname.new(src_path_to_output_path(src_path))
19
+ )
20
+ @content = content
21
+ @output_path = output_path
18
22
  end
19
-
20
- def run
21
- File.open(@output_path, "wb") do |file|
22
- file << @content
23
+ def run()
24
+ unless @output_path.parent
25
+ .exist?
26
+ FileUtils.mkdir_p(@output_path.parent)
23
27
  end
28
+ File.open(@output_path, "wb") { |file|
29
+ file.<<(@content)
30
+ }
24
31
  end
25
32
  end
26
33
  end
34
+
@@ -0,0 +1,3 @@
1
+ module Gloss
2
+ def self.parse_buffer: (String) -> String
3
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+ require "yaml"
5
+
6
+ module Gloss
7
+ user_config = YAML.safe_load(File.read(".gloss.yml"))
8
+ Config = OpenStruct.new(
9
+ default_config: {
10
+ frozen_string_literals: true,
11
+ src_dir: "src",
12
+ }
13
+ )
14
+ Config.default_config.each { |k, v| Config.send(:"#{k}=", user_config[k.to_s] || v) }
15
+ end
@@ -0,0 +1,11 @@
1
+ module Gloss
2
+ module Errors
3
+ abstract class BaseGlossError < StandardError; end
4
+
5
+ class TypeValidationError < BaseGlossError; end
6
+
7
+ class TypeError < BaseGlossError; end
8
+
9
+ class ParserError < BaseGlossError; end
10
+ end
11
+ end
File without changes
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gloss
4
+ class Parser
5
+ def initialize(@str : String)
6
+ end
7
+
8
+ def run : String
9
+ tree_json = Gloss.parse_buffer(@str)
10
+ begin
11
+ JSON.parse tree_json, symbolize_names: true
12
+ rescue JSON::ParserError
13
+ # if parsing fails then tree is invalid and most likely an error message from the parser in
14
+ # crystal
15
+ raise Errors::ParserError, tree_json
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gloss
4
+ class TypeChecker
5
+ Project = Struct.new :targets
6
+
7
+ attr_reader :steep_target, :top_level_decls
8
+
9
+ def initialize
10
+ @steep_target = Steep::Project::Target.new(
11
+ name: "gloss",
12
+ options: Steep::Project::Options.new.tap do |o|
13
+ o.allow_unknown_constant_assignment = true
14
+ end,
15
+ source_patterns: ["gloss.rb"],
16
+ ignore_patterns: Array.new,
17
+ signature_patterns: ["sig"]
18
+ )
19
+ @top_level_decls = {}
20
+ end
21
+
22
+ def run(rb_str)
23
+ unless check_types(rb_str)
24
+ raise Errors::TypeError,
25
+ @steep_target.errors.map { |e|
26
+ case e
27
+ when Steep::Errors::NoMethod
28
+ "Unknown method :#{e.method}, location: #{e.type.location.inspect}"
29
+ when Steep::Errors::MethodBodyTypeMismatch
30
+ "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
31
+ when Steep::Errors::IncompatibleArguments
32
+ "Invalid argmuents - method type: #{e.method_type}, receiver type: #{e.receiver_type}"
33
+ when Steep::Errors::ReturnTypeMismatch
34
+ "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
35
+ when Steep::Errors::IncompatibleAssignment
36
+ "Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
37
+ else
38
+ e.inspect
39
+ end
40
+ }.join("\n")
41
+ end
42
+
43
+ true
44
+ end
45
+
46
+ def check_types(rb_str)
47
+ env_loader = RBS::EnvironmentLoader.new
48
+ env = RBS::Environment.from_loader(env_loader)
49
+ project = Steep::Project.new(steepfile_path: Pathname.new(Config.src_dir).realpath)
50
+ project.targets << @steep_target
51
+ loader = Steep::Project::FileLoader.new(project: project)
52
+ loader.load_signatures
53
+
54
+ @steep_target.add_source("gloss.rb", rb_str)
55
+
56
+ @top_level_decls.each do |_, decl|
57
+ env << decl
58
+ end
59
+ env = env.resolve_type_names
60
+
61
+ @steep_target.instance_variable_set("@environment", env)
62
+
63
+ @steep_target.type_check
64
+
65
+ @steep_target.status.is_a?(Steep::Project::Target::TypeCheckStatus) &&
66
+ @steep_target.no_error? &&
67
+ @steep_target.errors.empty?
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module Gloss
2
+ VERSION = "0.0.3"
3
+ end
File without changes
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "fileutils"
5
+
6
+ module Gloss
7
+ module Utils
8
+ module_function
9
+
10
+ def src_path_to_output_path(src_path)
11
+ src_path.sub(%r{\A(?:\./)?#{Config.src_dir}/?}, "")
12
+ .sub(/\.gl$/, ".rb")
13
+ end
14
+ end
15
+
16
+ class Writer
17
+ include Utils
18
+
19
+ def initialize(
20
+ @content,
21
+ src_path : String?,
22
+ @output_path : Pathname = Pathname.new(src_path_to_output_path(src_path))
23
+ )
24
+ end
25
+
26
+ def run
27
+ FileUtils.mkdir_p(@output_path.parent) unless @output_path.parent.exist?
28
+ File.open(@output_path, "wb") do |file|
29
+ file << @content
30
+ end
31
+ end
32
+ end
33
+ 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.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - johansenja
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-03 00:00:00.000000000 Z
11
+ date: 2021-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_blank
@@ -168,9 +168,16 @@ files:
168
168
  - lib/gloss/version.rb
169
169
  - lib/gloss/watcher.rb
170
170
  - lib/gloss/writer.rb
171
+ - sig/gloss.rbs
171
172
  - sig/listen.rbs
172
- - src/lib/hrb/initializer.gl
173
- - src/lib/hrb/watcher.gl
173
+ - src/lib/gloss/config.gl
174
+ - src/lib/gloss/errors.gl
175
+ - src/lib/gloss/initializer.gl
176
+ - src/lib/gloss/parser.gl
177
+ - src/lib/gloss/type_checker.gl
178
+ - src/lib/gloss/version.gl
179
+ - src/lib/gloss/watcher.gl
180
+ - src/lib/gloss/writer.gl
174
181
  homepage:
175
182
  licenses:
176
183
  - MIT
@@ -190,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
197
  - !ruby/object:Gem::Version
191
198
  version: '0'
192
199
  requirements: []
193
- rubygems_version: 3.1.2
200
+ rubygems_version: 3.2.3
194
201
  signing_key:
195
202
  specification_version: 4
196
203
  summary: A superset of ruby