gloss 0.0.4 → 0.0.5

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: 3a630a77074bd1fa44113b542bd04b312957e3d59834dc9dec0282466e60d50a
4
- data.tar.gz: 653fc89b0793a114f9d6a04348a63d76368db464bf93a713268467140551680c
3
+ metadata.gz: a26f64e872c2884f2fa5969d762e66ed6f1346732933f08ae7b92eb07aea5ab3
4
+ data.tar.gz: 9cf39520e8dc43e6a74f9ea606a719815e2bd503d19744c7979478f746b8345f
5
5
  SHA512:
6
- metadata.gz: 0f8573f5596495a00d31c102edf033f010f07092a6441a26809dc7d0044e906651058191ff9b37a87d840cae4fb16916aad76e27d219688824cd06366ef46c3c
7
- data.tar.gz: 22a085469dd74a3c88d3334ac8c75c44ea8e43fbca211acc5e25cd83230bc435b48b262787d6eccacf2efff2f4ed5324aa11a3e102e1aa9bc0ebe1dba04d2564
6
+ metadata.gz: bdb98f72e57945e9de8bbd03296d95b2cfc221f915e7e8e8fd511e8b4794ef3bb65f1830d9769486c30a004595c3a7f84e3e785bb1b1297d778cff984a0a3234
7
+ data.tar.gz: 9758d730e858adf27c60f89ec9e88ffb07d3950d2a4f493b1d0ad2b00b7a10999fd76c9ae802753428fdfb0cab005adfe773456c415b504809217507ac30fbc5
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gloss (0.0.4)
4
+ gloss (0.0.5)
5
5
  fast_blank
6
6
  listen
7
7
  rbs
@@ -17,9 +17,8 @@ GEM
17
17
  tzinfo (~> 2.0)
18
18
  zeitwerk (~> 2.3)
19
19
  ast (2.4.1)
20
- ast_utils (0.3.0)
21
- parser (~> 2.4)
22
- thor (>= 0.19)
20
+ ast_utils (0.4.0)
21
+ parser (>= 2.7.0)
23
22
  byebug (11.1.3)
24
23
  coderay (1.1.3)
25
24
  concurrent-ruby (1.1.8)
@@ -50,7 +49,7 @@ GEM
50
49
  rb-fsevent (0.10.4)
51
50
  rb-inotify (0.10.1)
52
51
  ffi (~> 1.0)
53
- rbs (1.0.3)
52
+ rbs (1.0.4)
54
53
  regexp_parser (2.0.3)
55
54
  rexml (3.2.4)
56
55
  rspec (3.10.0)
@@ -78,15 +77,14 @@ GEM
78
77
  rubocop-ast (1.4.0)
79
78
  parser (>= 2.7.1.5)
80
79
  ruby-progressbar (1.11.0)
81
- steep (0.39.0)
80
+ steep (0.40.0)
82
81
  activesupport (>= 5.1)
83
- ast_utils (~> 0.3.0)
82
+ ast_utils (>= 0.4.0)
84
83
  language_server-protocol (~> 3.15.0.1)
85
84
  listen (~> 3.0)
86
- parser (~> 2.7.0)
85
+ parser (>= 2.7)
87
86
  rainbow (>= 2.2.2, < 4.0)
88
- rbs (~> 1.0.0)
89
- thor (1.1.0)
87
+ rbs (~> 1.0.3)
90
88
  tzinfo (2.0.4)
91
89
  concurrent-ruby (~> 1.0)
92
90
  unicode-display_width (1.7.0)
@@ -16,13 +16,13 @@ module Gloss
16
16
  Gloss.parse_string("hsh = {}").should be_truthy
17
17
  end
18
18
 
19
- it "parses all kinds of method args" do
20
- output = %q|{"type":"DefNode","name":"abc","body":null,"rp_args":[{"type":"Arg","name":"a","external_name":"a","default_value":null,"restriction":{"type":"Path","value":"Float"},"keyword_arg":false},{"type":"Arg","name":"b","external_name":"b","default_value":null,"restriction":null,"keyword_arg":false},{"type":"Arg","name":"c","external_name":"c","default_value":null,"restriction":null,"keyword_arg":false,splat: "true"},{"type":"Arg","name":"d","external_name":"d","default_value":{"type":"LiteralNode","value":"nil","rb_type":"NilClass"},"restriction":{"type":"Union","types":[{"type":"Path","value":"String"},{"type":"Path","value":"Nil"}]},"keyword_arg":false},{"type":"Arg","name":"e","external_name":"e","default_value":null,"restriction":{"type":"Path","value":"Integer"},"keyword_arg":true},{"type":"Arg","name":"f","external_name":"f","default_value":null,"restriction":null,"keyword_arg":true},{"type":"Arg","name":"g","external_name":"g","default_value":{"type":"LiteralNode","value":"nil","rb_type":"NilClass"},"restriction":{"type":"Path","value":"String"},"keyword_arg":true}],"receiver":null,"return_type":null,"rest_kw_args":{"type":"Arg","name":"h","external_name":"h","default_value":null,"restriction":null,"keyword_arg":false}}|
21
- Gloss.parse_string(<<-GLS).should eq output
22
- def abc(a : Float, b, *c, d : String? = nil, e: : Integer, f:, g: : String = nil, **h)
23
- end
24
- GLS
25
- end
19
+ # it "parses all kinds of method args" do
20
+ # output = %q|{"type":"DefNode","name":"abc","body":null,"rp_args":[{"type":"Arg","name":"a","external_name":"a","default_value":null,"restriction":{"type":"Path","value":"Float"},"keyword_arg":false},{"type":"Arg","name":"b","external_name":"b","default_value":null,"restriction":null,"keyword_arg":false},{"type":"Arg","name":"c","external_name":"c","default_value":null,"restriction":null,"keyword_arg":false,splat: "true"},{"type":"Arg","name":"d","external_name":"d","default_value":{"type":"LiteralNode","value":"nil","rb_type":"NilClass"},"restriction":{"type":"Union","types":[{"type":"Path","value":"String"},{"type":"Path","value":"Nil"}]},"keyword_arg":false},{"type":"Arg","name":"e","external_name":"e","default_value":null,"restriction":{"type":"Path","value":"Integer"},"keyword_arg":true},{"type":"Arg","name":"f","external_name":"f","default_value":null,"restriction":null,"keyword_arg":true},{"type":"Arg","name":"g","external_name":"g","default_value":{"type":"LiteralNode","value":"nil","rb_type":"NilClass"},"restriction":{"type":"Path","value":"String"},"keyword_arg":true}],"receiver":null,"return_type":null,"rest_kw_args":{"type":"Arg","name":"h","external_name":"h","default_value":null,"restriction":null,"keyword_arg":false}}|
21
+ # Gloss.parse_string(<<-GLS).should eq output
22
+ # def abc(a : Float, b, *c, d : String? = nil, e: : Integer, f:, g: : String = nil, **h)
23
+ # end
24
+ # GLS
25
+ # end
26
26
 
27
27
  it "parses rescue with ruby syntax" do
28
28
  Gloss.parse_string(<<-GLOSS).should be_truthy
@@ -35,90 +35,90 @@ module Gloss
35
35
  end
36
36
  GLOSS
37
37
  end
38
- end
39
-
40
- it "parses shorthand blocks with ruby syntax" do
41
- Gloss.parse_string("[1].map(&:to_s)").should eq(
42
- %q<{"type":"Call","name":"map","args":[],"object":{"type":"ArrayLiteral","elements":[{"type":"LiteralNode","value":"1","rb_type":"Integer"}],"frozen":false},"block":null,"block_arg":{"type":"LiteralNode","value":":to_s","rb_type":"Symbol"}}>
43
- )
44
- end
45
-
46
- it "parses tuples as frozen arrays" do
47
- Gloss.parse_string("{ 'hello', 'world' }").should eq(
48
- %q<{"type":"ArrayLiteral","elements":[{"type":"LiteralNode","value":"\"hello\"","rb_type":"String"},{"type":"LiteralNode","value":"\"world\"","rb_type":"String"}],"frozen":true}>
49
- )
50
- end
51
38
 
52
- it "parses named tuples as frozen hashes" do
53
- Gloss.parse_string("{ hello: 'world' }").should eq(
54
- %q<{"type":"HashLiteral","elements":[["hello",{"type":"LiteralNode","value":"\"world\"","rb_type":"String"}]],"frozen":true}>
55
- )
56
- end
57
-
58
- it "parses the and operator" do
59
- Gloss.parse_string("puts 'hello world' if 1 and 2").should be_truthy
60
- end
61
-
62
- it "parses the or operator" do
63
- Gloss.parse_string("puts 'hello world' if true or false").should be_truthy
64
- end
65
-
66
- it "parses the not operator" do
67
- Gloss.parse_string("puts 'hello world' if true and not false").should be_truthy
68
- end
39
+ # it "parses shorthand blocks with ruby syntax" do
40
+ # Gloss.parse_string("[1].map(&:to_s)").should eq(
41
+ # %q<{"type":"Call","name":"map","args":[],"object":{"type":"ArrayLiteral","elements":[{"type":"LiteralNode","value":"1","rb_type":"Integer"}],"frozen":false},"block":null,"block_arg":{"type":"LiteralNode","value":":to_s","rb_type":"Symbol"}}>
42
+ # )
43
+ # end
69
44
 
70
- it "parses global variables" do
71
- Gloss.parse_string("$var : String = 'hello world'").should eq(
72
- %q|{"type":"TypeDeclaration","var":{"type":"GlobalVar","name":"$var"},"declared_type":{"type":"Path","value":"String"},"value":{"type":"LiteralNode","value":"\"hello world\"","rb_type":"String"},"var_type":"GlobalVar"}|
73
- )
74
- end
75
-
76
- it "parses for loops" do
77
- Gloss.parse_string(<<-GLS).should be_truthy
78
- for k, v in { hello: world }
79
- puts key: k, value: v
80
- end
81
- GLS
82
- end
45
+ it "parses tuples as frozen arrays" do
46
+ Gloss.parse_string("{ 'hello', 'world' }").should eq(
47
+ %q<{"type":"ArrayLiteral","elements":[{"type":"LiteralNode","value":"\"hello\"","rb_type":"String"},{"type":"LiteralNode","value":"\"world\"","rb_type":"String"}],"frozen":true}>
48
+ )
49
+ end
83
50
 
84
- it "parses generics as RBS generics" do
85
- expected =
86
- %q|{"type":"TypeDeclaration","var":{"type":"Var","name":"hsh"},"declared_type":{"type":"Generic","name":{"type":"Path","value":"Hash"},"args":[{"type":"Path","value":"String"},{"type":"Path","value":"String"}]},"value":{"type":"HashLiteral","elements":[[{"type":"LiteralNode","value":"\"hello\"","rb_type":"String"},{"type":"LiteralNode","value":"\"world\"","rb_type":"String"}]],"frozen":false},"var_type":"Var"}|
87
- Gloss.parse_string(<<-GLS).should eq expected
88
- hsh : Hash[String, String] = { "hello" => "world" }
89
- GLS
90
- end
51
+ it "parses named tuples as frozen hashes" do
52
+ Gloss.parse_string("{ hello: 'world' }").should eq(
53
+ %q<{"type":"HashLiteral","elements":[["hello",{"type":"LiteralNode","value":"\"world\"","rb_type":"String"}]],"frozen":true}>
54
+ )
55
+ end
91
56
 
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
57
+ # it "parses the and operator" do
58
+ # Gloss.parse_string("puts 'hello world' if 1 and 2").should be_truthy
59
+ # end
104
60
 
105
- it "allows constant methods" do
106
- Gloss.parse_string(<<-GLS).should be_truthy
107
- def Hello(arg = nil)
108
- end
61
+ # it "parses the or operator" do
62
+ # Gloss.parse_string("puts 'hello world' if true or false").should be_truthy
63
+ # end
109
64
 
110
- Hello()
65
+ # it "parses the not operator" do
66
+ # Gloss.parse_string("puts 'hello world' if true and not false").should be_truthy
67
+ # end
111
68
 
112
- Hello("a")
113
- GLS
114
- end
69
+ it "parses global variables" do
70
+ Gloss.parse_string("$var : String = 'hello world'").should eq(
71
+ %q|{"type":"TypeDeclaration","var":{"type":"GlobalVar","name":"$var"},"declared_type":{"type":"Path","value":"String"},"value":{"type":"LiteralNode","value":"\"hello world\"","rb_type":"String"},"var_type":"GlobalVar"}|
72
+ )
73
+ end
115
74
 
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
75
+ # it "parses for loops" do
76
+ # Gloss.parse_string(<<-GLS).should be_truthy
77
+ # for k, v in { hello: world }
78
+ # puts key: k, value: v
79
+ # end
80
+ # GLS
81
+ # end
82
+
83
+ it "parses generics as RBS generics" do
84
+ expected =
85
+ %q|{"type":"TypeDeclaration","var":{"type":"Var","name":"hsh"},"declared_type":{"type":"Generic","name":{"type":"Path","value":"Hash"},"args":[{"type":"Path","value":"String"},{"type":"Path","value":"String"}]},"value":{"type":"HashLiteral","elements":[[{"type":"LiteralNode","value":"\"hello\"","rb_type":"String"},{"type":"LiteralNode","value":"\"world\"","rb_type":"String"}]],"frozen":false},"var_type":"Var"}|
86
+ Gloss.parse_string(<<-GLS).should eq expected
87
+ hsh : Hash[String, String] = { "hello" => "world" }
88
+ GLS
89
+ end
120
90
 
121
- Hello
122
- GLS
91
+ # it "parses method calls in case statements" do
92
+ # expected =
93
+ # %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}|
94
+ # Gloss.parse_string(<<-GLS).should eq expected
95
+ # case "abc"
96
+ # when .start_with? 'a'
97
+ # 1
98
+ # else
99
+ # 0
100
+ # end
101
+ # GLS
102
+ # end
103
+
104
+ # it "allows constant methods" do
105
+ # Gloss.parse_string(<<-GLS).should be_truthy
106
+ # def Hello(arg = nil)
107
+ # end
108
+
109
+ # Hello()
110
+
111
+ # Hello("a")
112
+ # GLS
113
+ # end
114
+
115
+ # it "requires constant methods to be called with ()" do
116
+ # Gloss.parse_string(<<-GLS).should be_falsey
117
+ # def Hello(arg = nil)
118
+ # end
119
+
120
+ # Hello
121
+ # GLS
122
+ # end
123
123
  end
124
124
  end
@@ -140,7 +140,9 @@ module Crystal
140
140
 
141
141
  class Block < ASTNode
142
142
  def to_rb
143
- Rb::AST::Block.new(@args.map(&.to_rb), @body.to_rb)
143
+ positional_args = args.dup
144
+ splat = @splat_index ? positional_args.delete_at(@splat_index.as(Int32)) : nil
145
+ Rb::AST::Block.new(positional_args.map(&.to_rb), splat.try &.to_rb, @body.to_rb)
144
146
  end
145
147
  end
146
148
 
@@ -43,13 +43,14 @@ module Rb
43
43
  end
44
44
 
45
45
  class Block < Node
46
- @info : NamedTuple(type: String, args: Array(Var), body: Node)
46
+ @info : NamedTuple(type: String, positional_args: Array(Var), body: Node, rest_p_args: Node?)
47
47
 
48
- def initialize(args, body)
48
+ def initialize(args, splat, body)
49
49
  @info = {
50
50
  type: self.class.name.split("::").last,
51
51
  body: body,
52
- args: args,
52
+ positional_args: args,
53
+ rest_p_args: splat
53
54
  }
54
55
  end
55
56
 
@@ -3,8 +3,18 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- module Gloss
6
+ module Gloss
7
+ module Utils
8
+ module_function
9
+ def with_file_header(str)
10
+ "#{Builder::FILE_HEADER}\n\n#{str}"
11
+ end
12
+ end
7
13
  class Builder
14
+ FILE_HEADER = " #{(if Config.frozen_string_literals
15
+ "# frozen_string_literal: true\n"
16
+ end)}\n ##### This file was generated by Gloss; any changes made here will be overwritten.\n ##### See #{Config.src_dir}/ to make changes"
17
+ include Utils
8
18
  attr_reader(:"tree")
9
19
  def initialize(tree_hash, type_checker = nil)
10
20
  @indent_level = 0
@@ -16,9 +26,7 @@
16
26
  end
17
27
  def run()
18
28
  rb_output = visit_node(@tree)
19
- " #{(if Config.frozen_string_literals
20
- "# frozen_string_literal: true\n"
21
- end)}\n ##### This file was generated by Gloss; any changes made here will be overwritten.\n ##### See #{Config.src_dir}/ to make changes\n\n #{rb_output}"
29
+ with_file_header(rb_output)
22
30
  end
23
31
  def visit_node(node, scope = Scope.new)
24
32
  src = Source.new(@indent_level)
@@ -99,7 +107,7 @@ case node.[](:"type")
99
107
  src.write_ln("end")
100
108
  when "DefNode"
101
109
  args = render_args(node)
102
- src.write_ln("def #{node.[](:"name")}#{args}")
110
+ src.write_ln("def #{node.[](:"name")}#{args.[](:"representation")}")
103
111
  return_type = (if node.[](:"return_type")
104
112
  RBS::Types::ClassInstance.new(name: RBS::TypeName.new(name: eval(visit_node(node.[](:"return_type")))
105
113
  .to_s
@@ -107,34 +115,7 @@ case node.[](:"type")
107
115
  else
108
116
  RBS::Types::Bases::Any.new(location: node.[](:"location"))
109
117
  end)
110
- # @type var rp: Array[Hash[Symbol, Any]]
111
- rp = node.fetch(:"positional_args") { ||
112
- EMPTY_ARRAY }
113
- .filter() { |a|
114
- !a.[](:"value") }
115
- # @type var op: Array[Hash[Symbol, Any]]
116
- op = node.fetch(:"positional_args") { ||
117
- EMPTY_ARRAY }
118
- .filter() { |a|
119
- a.[](:"value")
120
- }
121
- method_types = [RBS::MethodType.new(type_params: EMPTY_ARRAY, type: RBS::Types::Function.new(required_positionals: rp.map() { |a|
122
- RBS::Types::Function::Param.new(name: visit_node(a)
123
- .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
124
- }, optional_positionals: op.map() { |a|
125
- RBS::Types::Function::Param.new(name: visit_node(a)
126
- .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
127
- }, rest_positionals: (if rpa = node.[](:"rest_p_args")
128
- RBS::Types::Function::Param.new(name: visit_node(rpa)
129
- .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
130
- else
131
- nil
132
- end), trailing_positionals: EMPTY_ARRAY, required_keywords: node.[](:"req_kw_args") || EMPTY_HASH, optional_keywords: node.[](:"opt_kw_args") || EMPTY_HASH, rest_keywords: (if node.[](:"rest_kw_args")
133
- RBS::Types::Function::Param.new(name: visit_node(node.[](:"rest_kw_args"))
134
- .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
135
- else
136
- nil
137
- end), return_type: return_type), block: (if node.[](:"yield_arg_count")
118
+ method_types = [RBS::MethodType.new(type_params: EMPTY_ARRAY, type: RBS::Types::Function.new(required_positionals: args.dig(:"types", :"required_positionals"), optional_positionals: args.dig(:"types", :"optional_positionals"), rest_positionals: args.dig(:"types", :"rest_positionals"), trailing_positionals: args.dig(:"types", :"trailing_positionals"), required_keywords: args.dig(:"types", :"required_keywords"), optional_keywords: args.dig(:"types", :"optional_keywords"), rest_keywords: args.dig(:"types", :"rest_keywords"), return_type: return_type), block: (if node.[](:"yield_arg_count")
138
119
  RBS::Types::Block.new(type: RBS::Types::Function.new(required_positionals: Array.new, optional_positionals: Array.new, rest_positionals: nil, trailing_positionals: Array.new, required_keywords: Hash.new, optional_keywords: Hash.new, rest_keywords: nil, return_type: RBS::Types::Bases::Any.new(location: node.[](:"location"))), required: !!node.[](:"block_arg") || node.[](:"yield_arg_count"))
139
120
  else
140
121
  nil
@@ -203,11 +184,9 @@ EMPTY_ARRAY }
203
184
  end)}#{block}"
204
185
  src.write_ln(call)
205
186
  when "Block"
206
- src.write("{ |#{node.[](:"args")
207
- .map() { |a|
208
- visit_node(a)
209
- }
210
- .join(", ")}|\n")
187
+ args = render_args(node)
188
+ src.write("{ #{args.[](:"representation")
189
+ .gsub(/(\A\(|\)\z)/, "|")}\n")
211
190
  indented(src) { ||
212
191
  src.write(visit_node(node.[](:"body")))
213
192
  }
@@ -391,7 +370,7 @@ EMPTY_ARRAY }
391
370
  end
392
371
  # @type var expanded: Array[String]
393
372
  expanded = eval(visit_node(expr))
394
- .map() { |a|
373
+ .map() { |*a|
395
374
  locals = [var_names.join("\", \"")].zip(a)
396
375
  .to_h
397
376
  (if @inside_macro
@@ -538,6 +517,11 @@ EMPTY_ARRAY }
538
517
  .join(" | ")
539
518
  end)
540
519
  src.write(output)
520
+ when "Next"
521
+ (if node.[](:"value")
522
+ val = " #{node.[](:"value")}"
523
+ end)
524
+ src.write("next#{val}")
541
525
  when "EmptyNode"
542
526
  # no op
543
527
  else
@@ -610,7 +594,35 @@ a && a.empty? }
610
594
  end)].reject(&:"empty?")
611
595
  .flatten
612
596
  .join(", ")
613
- "(#{contents})"
597
+ representation = "(#{contents})"
598
+ rp.map!() { |a|
599
+ RBS::Types::Function::Param.new(name: visit_node(a)
600
+ .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
601
+ }
602
+ op.map!() { |a|
603
+ RBS::Types::Function::Param.new(name: visit_node(a)
604
+ .to_sym, type: RBS::Types::Bases::Any.new(location: a.[](:"location")))
605
+ }
606
+ rest_p = (if rpa = node.[](:"rest_p_args")
607
+ RBS::Types::Function::Param.new(name: visit_node(rpa)
608
+ .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
609
+ else
610
+ nil
611
+ end)
612
+ {:representation => representation,
613
+ :types => {:required_positionals => rp,
614
+ :optional_positionals => op,
615
+ :rest_positionals => rest_p,
616
+ :trailing_positionals => EMPTY_ARRAY,
617
+ :required_keywords => node.[](:"req_kw_args") || EMPTY_HASH,
618
+ :optional_keywords => node.[](:"opt_kw_args") || EMPTY_HASH,
619
+ :rest_keywords => (if node.[](:"rest_kw_args")
620
+ RBS::Types::Function::Param.new(name: visit_node(node.[](:"rest_kw_args"))
621
+ .to_sym, type: RBS::Types::Bases::Any.new(location: node.[](:"location")))
622
+ else
623
+ nil
624
+ end)
625
+ }.freeze}.freeze
614
626
  end
615
627
  end
616
628
  end
@@ -3,7 +3,7 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- require "optparse"
6
+ require "optparse"
7
7
  module Gloss
8
8
  class CLI
9
9
  def initialize(argv)
@@ -12,42 +12,47 @@ module Gloss
12
12
  def run()
13
13
  command = @argv.first
14
14
  files = @argv.[]((1..-1))
15
+ err_msg = catch(:"error") { ||
15
16
  case command
16
- when "watch"
17
- Watcher.new
17
+ when "watch"
18
+ Watcher.new(files)
18
19
  .watch
19
- when "build"
20
- (if files.empty?
21
- Dir.glob("#{Config.src_dir}/**/*.gl")
22
- else
23
- files
24
- end)
20
+ when "build"
21
+ (if files.empty?
22
+ Dir.glob("#{Config.src_dir}/**/*.gl")
23
+ else
24
+ files
25
+ end)
25
26
  .each() { |fp|
26
- puts("=====> Building #{fp}")
27
- content = File.read(fp)
28
- tree_hash = Parser.new(content)
27
+ puts("=====> Building #{fp}")
28
+ content = File.read(fp)
29
+ tree_hash = Parser.new(content)
29
30
  .run
30
- type_checker = TypeChecker.new
31
- rb_output = Builder.new(tree_hash, type_checker)
31
+ type_checker = TypeChecker.new
32
+ rb_output = Builder.new(tree_hash, type_checker)
32
33
  .run
33
- type_checker.run(rb_output)
34
- puts("=====> Writing #{fp}")
35
- Writer.new(rb_output, fp)
34
+ type_checker.run(rb_output)
35
+ puts("=====> Writing #{fp}")
36
+ Writer.new(rb_output, fp)
36
37
  .run
37
- }
38
- when "init"
39
- force = false
40
- OptionParser.new() { |opt|
41
- opt.on("--force", "-f") { ||
42
- force = true
43
38
  }
44
- }
39
+ when "init"
40
+ force = false
41
+ OptionParser.new() { |opt|
42
+ opt.on("--force", "-f") { ||
43
+ force = true
44
+ }
45
+ }
45
46
  .parse(@argv)
46
- Initializer.new(force)
47
+ Initializer.new(force)
47
48
  .run
48
- else
49
- abort("Gloss doesn't know how to #{command}")
50
- end
49
+ else
50
+ throw(:"error", "Gloss doesn't know how to #{command}")
51
+ end
52
+ nil }
53
+ (if err_msg
54
+ abort(err_msg)
55
+ end)
51
56
  end
52
57
  end
53
58
  end
@@ -3,12 +3,17 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- require "ostruct"
6
+ require "ostruct"
7
7
  require "yaml"
8
8
  module Gloss
9
- user_config = YAML.safe_load(File.read(".gloss.yml"))
9
+ CONFIG_PATH = ".gloss.yml"
10
10
  Config = OpenStruct.new(default_config: {:frozen_string_literals => true,
11
11
  :src_dir => "src"}.freeze)
12
+ user_config = (if File.exist?(CONFIG_PATH)
13
+ YAML.safe_load(File.read(CONFIG_PATH))
14
+ else
15
+ Config.default_config
16
+ end)
12
17
  Config.default_config
13
18
  .each() { |k, v|
14
19
  Config.send(:"#{k}=", user_config.[](k.to_s) || v)
@@ -3,22 +3,22 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- require "yaml"
6
+ require "yaml"
7
7
  module Gloss
8
8
  class Initializer
9
9
  def initialize(force)
10
10
  @force = force
11
11
  end
12
12
  def run()
13
- (if File.exist?(".gloss.yml") && !@force
14
- abort(".gloss.yml file already exists - aborting. Use --force to override.")
13
+ (if File.exist?(CONFIG_PATH) && !@force
14
+ throw(:"error", "#{CONFIG_PATH} file already exists - aborting. Use --force to override.")
15
15
  end)
16
- File.open(".gloss.yml", "wb") { |file|
16
+ File.open(CONFIG_PATH, "wb") { |file|
17
17
  file.puts(Config.default_config
18
18
  .transform_keys(&:"to_s")
19
19
  .to_yaml)
20
20
  }
21
- puts("Created .gloss.yml with default preferences")
21
+ puts("Created #{CONFIG_PATH} with default preferences")
22
22
  end
23
23
  end
24
24
  end
@@ -3,6 +3,6 @@
3
3
  ##### This file was generated by Gloss; any changes made here will be overwritten.
4
4
  ##### See src/ to make changes
5
5
 
6
- module Gloss
7
- VERSION = "0.0.4"
6
+ module Gloss
7
+ VERSION = "0.0.5"
8
8
  end
@@ -6,24 +6,38 @@
6
6
  require "listen"
7
7
  module Gloss
8
8
  class Watcher
9
- def initialize()
10
- @paths = ["src/"]
9
+ def initialize(paths)
10
+ @paths = paths
11
+ (if @paths.empty?
12
+ @paths = [File.join(Dir.pwd, Config.src_dir)]
13
+ end)
11
14
  end
12
15
  def watch()
13
16
  puts("=====> Now listening for changes in #{@paths.join(", ")}")
14
17
  listener = Listen.to(*@paths, latency: 2) { |modified, added, removed|
15
18
  modified.+(added)
16
19
  .each() { |f|
20
+ unless f.end_with?(".gl")
21
+ next
22
+ end
23
+ puts("====> Rewriting #{f}")
17
24
  content = File.read(f)
18
- Writer.new(Builder.new(content)
25
+ Writer.new(Builder.new(Parser.new(content)
26
+ .run)
19
27
  .run, f)
20
28
  .run
29
+ puts("====> Done")
21
30
  }
22
31
  removed.each() { |f|
32
+ unless f.end_with?(".gl")
33
+ next
34
+ end
23
35
  out_path = Utils.src_path_to_output_path(f)
36
+ puts("====> Removing #{out_path}")
24
37
  (if File.exist?(out_path)
25
38
  File.delete(out_path)
26
39
  end)
40
+ puts("====> Done")
27
41
  }
28
42
  }
29
43
  listener.start
@@ -9,7 +9,7 @@ module Gloss
9
9
  module Utils
10
10
  module_function
11
11
  def src_path_to_output_path(src_path)
12
- src_path.sub(/\A(?:\.\/)?#{Config.src_dir}\/?/, "")
12
+ src_path.sub("#{Config.src_dir}/", "")
13
13
  .sub(/\.gl$/, ".rb")
14
14
  end
15
15
  end
@@ -25,7 +25,7 @@ module Gloss
25
25
  FileUtils.mkdir_p(@output_path.parent)
26
26
  end
27
27
  File.open(@output_path, "wb") { |file|
28
- file.<<(@content)
28
+ file.puts(@content)
29
29
  }
30
30
  end
31
31
  end
@@ -1,7 +1,21 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Gloss
2
+ module Utils
3
+ module_function
4
+
5
+ def with_file_header(str)
6
+ "#{Builder::FILE_HEADER}\n\n#{str}"
7
+ end
8
+ end
9
+
4
10
  class Builder
11
+ FILE_HEADER = <<~RUBY
12
+ #{"# frozen_string_literal: true\n" if Config.frozen_string_literals}
13
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
14
+ ##### See #{Config.src_dir}/ to make changes
15
+ RUBY
16
+
17
+ include Utils
18
+
5
19
  attr_reader :tree
6
20
 
7
21
  def initialize(tree_hash, type_checker = nil)
@@ -15,13 +29,7 @@ module Gloss
15
29
 
16
30
  def run
17
31
  rb_output = visit_node(@tree)
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
32
+ with_file_header(rb_output)
25
33
  end
26
34
 
27
35
  # type node = Hash[Symbol, String | Array[String | node] | Hash[Symbol, node]] | true | false
@@ -105,7 +113,7 @@ module Gloss
105
113
  src.write_ln "end"
106
114
  when "DefNode"
107
115
  args = render_args(node)
108
- src.write_ln "def #{node[:name]}#{args}"
116
+ src.write_ln "def #{node[:name]}#{args[:representation]}"
109
117
 
110
118
  return_type = if node[:return_type]
111
119
  RBS::Types::ClassInstance.new(
@@ -122,36 +130,17 @@ module Gloss
122
130
  )
123
131
  end
124
132
 
125
- rp : Array[Hash[Symbol, Any]] = node.fetch(:positional_args) { EMPTY_ARRAY }.filter { |a| !a[:value] }
126
- op : Array[Hash[Symbol, Any]] = node.fetch(:positional_args) { EMPTY_ARRAY }.filter { |a| a[:value] }
127
-
128
133
  method_types = [
129
134
  RBS::MethodType.new(
130
135
  type_params: EMPTY_ARRAY, # TODO
131
136
  type: RBS::Types::Function.new(
132
- required_positionals: rp.map do |a|
133
- RBS::Types::Function::Param.new(
134
- name: visit_node(a).to_sym,
135
- type: RBS::Types::Bases::Any.new(
136
- location: a[:location]
137
- )
138
- )
139
- end,
140
- optional_positionals: op.map do |a|
141
- RBS::Types::Function::Param.new(
142
- name: visit_node(a).to_sym,
143
- type: RBS::Types::Bases::Any.new(location: a[:location])
144
- )
145
- end,
146
- rest_positionals: (rpa = node[:rest_p_args]) ? RBS::Types::Function::Param.new(name: visit_node(rpa).to_sym, type: RBS::Types::Bases::Any.new(location: node[:location])) : nil,
147
- trailing_positionals: EMPTY_ARRAY, # TODO
148
- required_keywords: node[:req_kw_args] || EMPTY_HASH,
149
- optional_keywords: node[:opt_kw_args] || EMPTY_HASH,
150
- rest_keywords: node[:rest_kw_args] ?
151
- RBS::Types::Function::Param.new(
152
- name: visit_node(node[:rest_kw_args]).to_sym,
153
- type: RBS::Types::Bases::Any.new(location: node[:location])
154
- ) : nil,
137
+ required_positionals: args.dig(:types, :required_positionals),
138
+ optional_positionals: args.dig(:types, :optional_positionals),
139
+ rest_positionals: args.dig(:types, :rest_positionals),
140
+ trailing_positionals: args.dig(:types, :trailing_positionals),
141
+ required_keywords: args.dig(:types, :required_keywords),
142
+ optional_keywords: args.dig(:types, :optional_keywords),
143
+ rest_keywords: args.dig(:types, :rest_keywords),
155
144
  return_type: return_type
156
145
  ),
157
146
  block: node[:yield_arg_count] ?
@@ -216,8 +205,8 @@ module Gloss
216
205
  src.write_ln(call)
217
206
 
218
207
  when "Block"
219
-
220
- src.write "{ |#{node[:args].map { |a| visit_node a }.join(", ")}|\n"
208
+ args = render_args node
209
+ src.write "{ #{args[:representation].gsub(/(\A\(|\)\z)/,'|')}\n"
221
210
 
222
211
  indented(src) { src.write visit_node(node[:body]) }
223
212
 
@@ -466,6 +455,9 @@ module Gloss
466
455
  types.map { |t| visit_node(t) }.join(" | ")
467
456
  end
468
457
  src.write output
458
+ when "Next"
459
+ val = " #{node[:value]}" if node[:value]
460
+ src.write "next#{val}"
469
461
  when "EmptyNode"
470
462
  # pass
471
463
  else
@@ -497,7 +489,8 @@ module Gloss
497
489
  src.decrement_indent
498
490
  end
499
491
 
500
- private def render_args(node)
492
+ # TODO: allow NamedTuple as return type
493
+ private def render_args(node)# : { representation: String, types: Hash[Symbol, Any] }
501
494
  rp : Array[Hash[Symbol, Any]] = node.fetch(:positional_args) { EMPTY_ARRAY }.filter { |a| !a[:value] }
502
495
  op : Array[Hash[Symbol, Any]] = node.fetch(:positional_args) { EMPTY_ARRAY }.filter { |a| a[:value] }
503
496
  rkw : Hash[Symbol, Any] = node.fetch(:req_kw_args) { EMPTY_HASH }
@@ -514,7 +507,38 @@ module Gloss
514
507
  rest_p ? "*#{rest_p}" : "",
515
508
  rest_kw ? "**#{visit_node(rest_kw)}" : ""
516
509
  ].reject(&:empty?).flatten.join(", ")
517
- "(#{contents})"
510
+ representation = "(#{contents})"
511
+ rp.map! do |a|
512
+ RBS::Types::Function::Param.new(
513
+ name: visit_node(a).to_sym,
514
+ type: RBS::Types::Bases::Any.new(
515
+ location: a[:location]
516
+ )
517
+ )
518
+ end
519
+ op.map! do |a|
520
+ RBS::Types::Function::Param.new(
521
+ name: visit_node(a).to_sym,
522
+ type: RBS::Types::Bases::Any.new(location: a[:location])
523
+ )
524
+ end
525
+ rest_p = (rpa = node[:rest_p_args]) ? RBS::Types::Function::Param.new(name: visit_node(rpa).to_sym, type: RBS::Types::Bases::Any.new(location: node[:location])) : nil
526
+ {
527
+ representation: representation,
528
+ types: {
529
+ required_positionals: rp,
530
+ optional_positionals: op,
531
+ rest_positionals: rest_p,
532
+ trailing_positionals: EMPTY_ARRAY, # TODO
533
+ required_keywords: node[:req_kw_args] || EMPTY_HASH,
534
+ optional_keywords: node[:opt_kw_args] || EMPTY_HASH,
535
+ rest_keywords: node[:rest_kw_args] ?
536
+ RBS::Types::Function::Param.new(
537
+ name: visit_node(node[:rest_kw_args]).to_sym,
538
+ type: RBS::Types::Bases::Any.new(location: node[:location])
539
+ ) : nil
540
+ }
541
+ }
518
542
  end
519
543
  end
520
544
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "optparse"
4
2
 
5
3
  module Gloss
@@ -12,30 +10,35 @@ module Gloss
12
10
  # TODO: allow destructuring: command, *files = @argv
13
11
  command = @argv.first
14
12
  files = @argv[1..-1]
15
- case command
16
- when "watch"
17
- Watcher.new.watch
18
- when "build"
19
- (files.empty? ? Dir.glob("#{Config.src_dir}/**/*.gl") : files).each do |fp|
20
- puts "=====> Building #{fp}"
21
- content = File.read(fp)
22
- tree_hash = Parser.new(content).run
23
- type_checker = TypeChecker.new
24
- rb_output = Builder.new(tree_hash, type_checker).run
25
- type_checker.run(rb_output)
13
+ err_msg = catch :error do
14
+ case command
15
+ when "watch"
16
+ Watcher.new(files).watch
17
+ when "build"
18
+ (files.empty? ? Dir.glob("#{Config.src_dir}/**/*.gl") : files).each do |fp|
19
+ puts "=====> Building #{fp}"
20
+ content = File.read(fp)
21
+ tree_hash = Parser.new(content).run
22
+ type_checker = TypeChecker.new
23
+ rb_output = Builder.new(tree_hash, type_checker).run
24
+ type_checker.run(rb_output)
26
25
 
27
- puts "=====> Writing #{fp}"
28
- Writer.new(rb_output, fp).run
26
+ puts "=====> Writing #{fp}"
27
+ Writer.new(rb_output, fp).run
28
+ end
29
+ when "init"
30
+ force = false
31
+ OptionParser.new do |opt|
32
+ opt.on("--force", "-f") { force = true }
33
+ end.parse(@argv)
34
+ Initializer.new(force).run
35
+ else
36
+ throw :error, "Gloss doesn't know how to #{command}"
29
37
  end
30
- when "init"
31
- force = false
32
- OptionParser.new do |opt|
33
- opt.on("--force", "-f") { force = true }
34
- end.parse(@argv)
35
- Initializer.new(force).run
36
- else
37
- abort "Gloss doesn't know how to #{command}"
38
+ nil
38
39
  end
40
+
41
+ abort err_msg if err_msg
39
42
  end
40
43
  end
41
44
  end
@@ -4,12 +4,18 @@ require "ostruct"
4
4
  require "yaml"
5
5
 
6
6
  module Gloss
7
- user_config = YAML.safe_load(File.read(".gloss.yml"))
7
+ CONFIG_PATH = ".gloss.yml"
8
8
  Config = OpenStruct.new(
9
9
  default_config: {
10
10
  frozen_string_literals: true,
11
11
  src_dir: "src",
12
12
  }
13
13
  )
14
+
15
+ user_config = if File.exist?(CONFIG_PATH)
16
+ YAML.safe_load(File.read(CONFIG_PATH))
17
+ else
18
+ Config.default_config
19
+ end
14
20
  Config.default_config.each { |k, v| Config.send(:"#{k}=", user_config[k.to_s] || v) }
15
21
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "yaml"
4
2
 
5
3
  module Gloss
@@ -8,15 +6,15 @@ module Gloss
8
6
  end
9
7
 
10
8
  def run
11
- if File.exist?(".gloss.yml") && !@force
12
- abort ".gloss.yml file already exists - aborting. Use --force to override."
9
+ if File.exist?(CONFIG_PATH) && !@force
10
+ throw :error, "#{CONFIG_PATH} file already exists - aborting. Use --force to override."
13
11
  end
14
12
 
15
- File.open(".gloss.yml", "wb") do |file|
13
+ File.open(CONFIG_PATH, "wb") do |file|
16
14
  file.puts Config.default_config.transform_keys(&:to_s).to_yaml
17
15
  end
18
16
 
19
- puts "Created .gloss.yml with default preferences"
17
+ puts "Created #{CONFIG_PATH} with default preferences"
20
18
  end
21
19
  end
22
20
  end
@@ -1,3 +1,3 @@
1
1
  module Gloss
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -4,20 +4,36 @@ require "listen"
4
4
 
5
5
  module Gloss
6
6
  class Watcher
7
- def initialize
8
- @paths = %w[src/]
7
+ def initialize(@paths : Array[String])
8
+ @paths = [File.join(Dir.pwd, Config.src_dir)] if @paths.empty?
9
9
  end
10
10
 
11
11
  def watch
12
12
  puts "=====> Now listening for changes in #{@paths.join(', ')}"
13
13
  listener = Listen.to(*@paths, latency: 2) do |modified, added, removed|
14
14
  (modified + added).each do |f|
15
+ next unless f.end_with? ".gl"
16
+
17
+ puts "====> Rewriting #{f}"
15
18
  content = File.read(f)
16
- Writer.new(Builder.new(content).run, f).run
19
+ Writer.new(
20
+ Builder.new(
21
+ Parser.new(
22
+ content
23
+ ).run
24
+ ).run, f
25
+ ).run
26
+
27
+ puts "====> Done"
17
28
  end
18
29
  removed.each do |f|
30
+ next unless f.end_with? ".gl"
31
+
19
32
  out_path = Utils.src_path_to_output_path(f)
33
+ puts "====> Removing #{out_path}"
20
34
  File.delete out_path if File.exist? out_path
35
+
36
+ puts "====> Done"
21
37
  end
22
38
  end
23
39
  listener.start
@@ -8,7 +8,7 @@ module Gloss
8
8
  module_function
9
9
 
10
10
  def src_path_to_output_path(src_path : String) : String
11
- src_path.sub(%r{\A(?:\./)?#{Config.src_dir}/?}, "")
11
+ src_path.sub("#{Config.src_dir}/", "")
12
12
  .sub(/\.gl$/, ".rb")
13
13
  end
14
14
  end
@@ -26,7 +26,7 @@ module Gloss
26
26
  def run
27
27
  FileUtils.mkdir_p(@output_path.parent) unless @output_path.parent.exist?
28
28
  File.open(@output_path, "wb") do |file|
29
- file << @content
29
+ file.puts @content
30
30
  end
31
31
  end
32
32
  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.4
4
+ version: 0.0.5
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-30 00:00:00.000000000 Z
11
+ date: 2021-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_blank
@@ -135,6 +135,7 @@ files:
135
135
  - ".github/workflows/ruby.yml"
136
136
  - ".gitignore"
137
137
  - ".gloss.yml"
138
+ - ".rspec"
138
139
  - ".rubocop.yml"
139
140
  - Gemfile
140
141
  - Gemfile.lock
@@ -202,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
203
  - !ruby/object:Gem::Version
203
204
  version: '0'
204
205
  requirements: []
205
- rubygems_version: 3.2.3
206
+ rubygems_version: 3.1.2
206
207
  signing_key:
207
208
  specification_version: 4
208
209
  summary: A superset of ruby