gloss 0.0.4 → 0.0.5

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: 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