gloss 0.0.2 → 0.0.3

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