gloss 0.0.1 → 0.0.6

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +3 -0
  3. data/.github/workflows/crystal_specs.yml +26 -0
  4. data/.github/workflows/ruby_specs.yml +33 -0
  5. data/.gitignore +2 -1
  6. data/.rspec +1 -0
  7. data/Gemfile +0 -1
  8. data/Gemfile.lock +26 -34
  9. data/LICENSE +21 -0
  10. data/README.md +59 -7
  11. data/Rakefile +4 -0
  12. data/exe/gloss +15 -1
  13. data/ext/gloss/Makefile +27 -5
  14. data/ext/gloss/extconf.rb +3 -25
  15. data/ext/gloss/{src/lib → lib}/cr_ruby.cr +0 -0
  16. data/ext/gloss/lib/rbs_types.cr +3 -0
  17. data/ext/gloss/spec/parser_spec.cr +124 -0
  18. data/ext/gloss/spec/spec_helper.cr +2 -0
  19. data/ext/gloss/src/cr_ast.cr +129 -31
  20. data/ext/gloss/src/gloss.cr +12 -18
  21. data/ext/gloss/src/lexer.cr +59 -1
  22. data/ext/gloss/src/parser.cr +548 -254
  23. data/ext/gloss/src/rb_ast.cr +153 -27
  24. data/gloss.gemspec +1 -0
  25. data/lib/gloss.rb +4 -1
  26. data/lib/gloss/builder.rb +607 -409
  27. data/lib/gloss/cli.rb +64 -24
  28. data/lib/gloss/config.rb +16 -10
  29. data/lib/gloss/errors.rb +13 -7
  30. data/lib/gloss/initializer.rb +11 -6
  31. data/lib/gloss/logger.rb +34 -0
  32. data/lib/gloss/parser.rb +35 -0
  33. data/lib/gloss/scope.rb +8 -3
  34. data/lib/gloss/source.rb +18 -15
  35. data/lib/gloss/type_checker.rb +96 -0
  36. data/lib/gloss/version.rb +6 -1
  37. data/lib/gloss/watcher.rb +63 -19
  38. data/lib/gloss/writer.rb +18 -12
  39. data/sig/gloss.rbs +3 -0
  40. data/sig/listen.rbs +1 -0
  41. data/src/lib/gloss/builder.gl +546 -0
  42. data/src/lib/gloss/cli.gl +55 -0
  43. data/src/lib/gloss/config.gl +21 -0
  44. data/src/lib/gloss/errors.gl +11 -0
  45. data/src/lib/gloss/initializer.gl +20 -0
  46. data/src/lib/gloss/logger.gl +26 -0
  47. data/src/lib/gloss/parser.gl +31 -0
  48. data/src/lib/gloss/scope.gl +9 -0
  49. data/src/lib/gloss/source.gl +32 -0
  50. data/src/lib/gloss/type_checker.gl +101 -0
  51. data/src/lib/gloss/version.gl +3 -0
  52. data/src/lib/gloss/watcher.gl +67 -0
  53. data/src/lib/gloss/writer.gl +33 -0
  54. metadata +42 -6
  55. data/lib/gloss.bundle.dwarf +0 -0
  56. data/src/lib/hrb/initializer.gl +0 -22
  57. data/src/lib/hrb/watcher.gl +0 -32
File without changes
@@ -0,0 +1,3 @@
1
+ module Gloss
2
+ class Any; end
3
+ end
@@ -0,0 +1,124 @@
1
+ require "./spec_helper"
2
+
3
+ module Gloss
4
+ describe Parser do
5
+ it "allows single quoted strings" do
6
+ Gloss.parse_string("puts 'hello world'").should be_truthy
7
+ end
8
+
9
+ it "doesn't require 'of' after empty arrays" do
10
+ Gloss.parse_string("arr = []").should eq(
11
+ %q|{"type":"Assign","op":null,"target":{"type":"Var","name":"arr"},"value":{"type":"ArrayLiteral","elements":[],"frozen":false}}|
12
+ )
13
+ end
14
+
15
+ it "doesn't require 'of' after empty hashes" do
16
+ Gloss.parse_string("hsh = {}").should be_truthy
17
+ end
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
26
+
27
+ it "parses rescue with ruby syntax" do
28
+ Gloss.parse_string(<<-GLOSS).should be_truthy
29
+ begin
30
+ raise "Abc"
31
+ rescue RuntimeError => e
32
+ p e.message
33
+ rescue NoMethodError
34
+ rescue => e
35
+ end
36
+ GLOSS
37
+ end
38
+
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
44
+
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
50
+
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
56
+
57
+ # it "parses the and operator" do
58
+ # Gloss.parse_string("puts 'hello world' if 1 and 2").should be_truthy
59
+ # end
60
+
61
+ # it "parses the or operator" do
62
+ # Gloss.parse_string("puts 'hello world' if true or false").should be_truthy
63
+ # end
64
+
65
+ # it "parses the not operator" do
66
+ # Gloss.parse_string("puts 'hello world' if true and not false").should be_truthy
67
+ # end
68
+
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
74
+
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
90
+
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
+ end
124
+ end
@@ -0,0 +1,2 @@
1
+ require "spec"
2
+ require "../src/parser"
@@ -1,5 +1,6 @@
1
1
  require "compiler/crystal/syntax/*"
2
2
  require "json"
3
+ require "./rb_ast"
3
4
 
4
5
  module Crystal
5
6
  abstract class ASTNode
@@ -61,7 +62,7 @@ module Crystal
61
62
 
62
63
  class SymbolLiteral < ASTNode
63
64
  def to_rb
64
- Rb::AST::LiteralNode.new(":#{@value.to_s}", Rb::AST::RbLiteral::Symbol)
65
+ Rb::AST::LiteralNode.new(%{:"#{@value.to_s}"}, Rb::AST::RbLiteral::Symbol)
65
66
  end
66
67
  end
67
68
 
@@ -73,13 +74,13 @@ module Crystal
73
74
 
74
75
  class HashLiteral < ASTNode
75
76
  def to_rb
76
- 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} })
77
78
  end
78
79
  end
79
80
 
80
81
  class NamedTupleLiteral < ASTNode
81
82
  def to_rb
82
- 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)
83
84
  end
84
85
  end
85
86
 
@@ -91,7 +92,7 @@ module Crystal
91
92
 
92
93
  class RegexLiteral < ASTNode
93
94
  def to_rb
94
- Rb::AST::RegexLiteral.new("//")
95
+ Rb::AST::RegexLiteral.new(@value.to_rb)
95
96
  end
96
97
  end
97
98
 
@@ -103,8 +104,19 @@ module Crystal
103
104
 
104
105
  class Def < ASTNode
105
106
  def to_rb
106
- Rb::AST::DefNode.new(@name, @args.map(&.to_rb), @body.to_rb, receiver.try(&.to_rb),
107
- return_type.try(&.to_rb), @double_splat.try(&.to_rb))
107
+ positional_args = args.dup
108
+ splat = @splat_index ? positional_args.delete_at(@splat_index.as(Int32)) : nil
109
+ Rb::AST::DefNode.new(
110
+ receiver.try(&.to_rb),
111
+ @name,
112
+ positional_args.map(&.to_rb),
113
+ splat.try(&.to_rb),
114
+ @double_splat.try(&.to_rb),
115
+ @body.to_rb,
116
+ return_type.try(&.to_rb),
117
+ @yields,
118
+ @block_arg.try &.to_rb
119
+ )
108
120
  end
109
121
  end
110
122
 
@@ -128,28 +140,38 @@ module Crystal
128
140
 
129
141
  class Block < ASTNode
130
142
  def to_rb
131
- 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)
132
146
  end
133
147
  end
134
148
 
135
149
  class Call < ASTNode
136
150
  def to_rb
137
- Rb::AST::Call.new(@obj.try(&.to_rb), @name, @args.map(&.to_rb), @block.try(&.to_rb),
138
- @block_arg.try(&.to_rb))
151
+ Rb::AST::Call.new(
152
+ @obj.try(&.to_rb),
153
+ @name,
154
+ @args.map(&.to_rb),
155
+ @named_args.try(&.map(&.to_rb.as(Rb::AST::Arg))),
156
+ @block.try(&.to_rb),
157
+ @block_arg.try(&.to_rb),
158
+ @has_parentheses
159
+ )
139
160
  end
140
161
  end
141
162
 
142
- class NamedArgument < ASTNode
163
+ class Arg < ASTNode
164
+ property keyword_arg : Bool = false
165
+
143
166
  def to_rb
144
- Rb::AST::EmptyNode.new(self.class.name)
167
+ Rb::AST::Arg.new(@name, @external_name, @restriction.try(&.to_rb),
168
+ @default_value.try(&.to_rb), @keyword_arg)
145
169
  end
146
170
  end
147
171
 
148
- class Arg < ASTNode
149
- property keyword_arg : Bool = false
150
-
172
+ class NamedArgument < ASTNode
151
173
  def to_rb
152
- Rb::AST::Arg.new(@name, @external_name, @restriction.try(&.to_rb), @default_value.try(&.to_rb))
174
+ Rb::AST::Arg.new(@name, @name, nil, @value.to_rb, true)
153
175
  end
154
176
  end
155
177
 
@@ -159,6 +181,12 @@ module Crystal
159
181
  end
160
182
  end
161
183
 
184
+ class Unless < ASTNode
185
+ def to_rb
186
+ Rb::AST::Unless.new(@cond.to_rb, @then.to_rb, @else.to_rb)
187
+ end
188
+ end
189
+
162
190
  class Assign < ASTNode
163
191
  def to_rb
164
192
  Rb::AST::Assign.new(@target.to_rb, @value.to_rb)
@@ -173,7 +201,7 @@ module Crystal
173
201
 
174
202
  class MultiAssign < ASTNode
175
203
  def to_rb
176
- Rb::AST::EmptyNode.new(self.class.name)
204
+ Rb::AST::MultiAssign.new(@targets.map(&.to_rb), @values.map(&.to_rb))
177
205
  end
178
206
  end
179
207
 
@@ -197,7 +225,7 @@ module Crystal
197
225
 
198
226
  class Global < ASTNode
199
227
  def to_rb
200
- Rb::AST::EmptyNode.new(self.class.name)
228
+ Rb::AST::GlobalVar.new(@name)
201
229
  end
202
230
  end
203
231
 
@@ -207,12 +235,6 @@ module Crystal
207
235
  end
208
236
  end
209
237
 
210
- class MacroExpression < ASTNode
211
- def to_rb
212
- Rb::AST::EmptyNode.new(self.class.name)
213
- end
214
- end
215
-
216
238
  class MacroIf < ASTNode
217
239
  def to_rb
218
240
  Rb::AST::MacroIf.new(@cond.to_rb, @then.to_rb, @else.to_rb)
@@ -293,7 +315,20 @@ module Crystal
293
315
  class When < ASTNode
294
316
  def to_rb
295
317
  Rb::AST::When.new(
296
- @conds.map(&.to_rb),
318
+ @conds.map do |c|
319
+ if c.is_a? Call
320
+ arg_name = "x"
321
+ ProcLiteral.new(
322
+ Def.new(
323
+ "->",
324
+ [Arg.new(arg_name)],
325
+ c.tap { |call| call.obj = Var.new(arg_name) }
326
+ )
327
+ ).to_rb
328
+ else
329
+ c.to_rb
330
+ end
331
+ end,
297
332
  @body.to_rb,
298
333
  @exhaustive
299
334
  )
@@ -349,7 +384,7 @@ module Crystal
349
384
  class ExceptionHandler < ASTNode
350
385
  def to_rb
351
386
  Rb::AST::ExceptionHandler.new(@body.to_rb, @rescues.try(&.map(&.to_rb)), @else.try(&.to_rb),
352
- @ensure.try(&.to_rb))
387
+ @ensure.try(&.to_rb))
353
388
  end
354
389
  end
355
390
 
@@ -359,11 +394,74 @@ module Crystal
359
394
  end
360
395
  end
361
396
 
362
- {% for class_name in %w[ProcNotation Macro OffsetOf VisibilityModifier IsA RespondsTo
363
- Select ImplicitObj AnnotationDef While Until Generic UninitializedVar
364
- ProcLiteral ProcPointer Union Self Yield Include
365
- Extend LibDef FunDef TypeDef CStructOrUnionDef ExternalVar Alias
366
- Metaclass Cast NilableCast TypeOf Annotation
397
+ class Union < ASTNode
398
+ def to_rb
399
+ Rb::AST::Union.new(@types.map(&.to_rb))
400
+ end
401
+ end
402
+
403
+ class Generic < ASTNode
404
+ def to_rb
405
+ Rb::AST::Generic.new(@name.to_rb, @type_vars.map(&.to_rb))
406
+ end
407
+ end
408
+
409
+ class ProcLiteral < ASTNode
410
+ def to_rb
411
+ Rb::AST::Proc.new(@def.to_rb)
412
+ end
413
+ end
414
+
415
+ class Include < ASTNode
416
+ def to_rb
417
+ Rb::AST::Include.new(@name.to_rb)
418
+ end
419
+ end
420
+
421
+ class Extend < ASTNode
422
+ def to_rb
423
+ Rb::AST::Extend.new(@name.to_rb)
424
+ end
425
+ end
426
+
427
+ class IsA < ASTNode
428
+ def to_rb
429
+ Rb::AST::Call.new(
430
+ @obj.to_rb,
431
+ "is_a?",
432
+ [@const.to_rb],
433
+ nil,
434
+ nil,
435
+ nil,
436
+ false
437
+ )
438
+ end
439
+ end
440
+
441
+ class VisibilityModifier < ASTNode
442
+ def to_rb
443
+ Rb::AST::VisibilityModifier.new(@modifier, @exp.to_rb)
444
+ end
445
+ end
446
+
447
+ class Yield < ASTNode
448
+ def to_rb
449
+ Rb::AST::Call.new(
450
+ nil,
451
+ "yield",
452
+ @exps.map(&.to_rb),
453
+ nil,
454
+ nil,
455
+ nil,
456
+ !@exps.empty?
457
+ )
458
+ end
459
+ end
460
+
461
+ {% for class_name in %w[ProcNotation Macro OffsetOf RespondsTo
462
+ Select ImplicitObj AnnotationDef While Until UninitializedVar
463
+ ProcPointer Self LibDef FunDef TypeDef CStructOrUnionDef
464
+ ExternalVar Alias Metaclass Cast NilableCast TypeOf Annotation
367
465
  Underscore MagicConstant Asm AsmOperand] %}
368
466
  class {{class_name.id}} < ASTNode
369
467
  def to_rb
@@ -372,7 +470,7 @@ module Crystal
372
470
  end
373
471
  {% end %}
374
472
 
375
- {% for class_name in %w[PointerOf SizeOf InstanceSizeOf Out MacroVerbatim DoubleSplat] %}
473
+ {% for class_name in %w[PointerOf SizeOf InstanceSizeOf Out MacroVerbatim] %}
376
474
  class {{class_name.id}} < UnaryExpression
377
475
  def to_rb
378
476
  Rb::AST::EmptyNode.new(self.class.name)
@@ -1,31 +1,25 @@
1
- require "./lib/cr_ruby"
1
+ require "../lib/cr_ruby"
2
+ require "../lib/rbs_types"
2
3
  require "./cr_ast"
3
4
  require "./rb_ast"
4
5
  require "./parser"
5
6
 
6
- def parse_buffer(self : CrRuby::VALUE, buffer : CrRuby::VALUE)
7
- plain_buffer = CrRuby.rb_str_to_str(buffer)
8
- c_buffer = CrRuby.rb_string_value_cstr(pointerof(plain_buffer))
9
- crystal_buffer = String.new(c_buffer)
7
+ def parse_string(self : CrRuby::VALUE, str : CrRuby::VALUE)
8
+ st = CrRuby.rb_str_to_str(str)
9
+ string = String.new(CrRuby.rb_string_value_cstr(pointerof(st)))
10
10
 
11
- output = parse_buffer(crystal_buffer)
11
+ output = begin
12
+ Gloss.parse_string(string)
13
+ rescue e : Crystal::SyntaxException
14
+ e.to_s
15
+ end
12
16
 
13
17
  CrRuby.rb_str_new_cstr(output)
14
18
  end
15
19
 
16
- def parse_buffer(buffer : String)
17
- begin
18
- tree = Gloss::Parser.parse buffer
19
- tree.to_rb.to_json
20
- rescue e : Crystal::SyntaxException
21
- pp e.backtrace
22
- e.to_s
23
- end
24
- end
25
-
26
- fun init = Init_gloss
20
+ fun init = Init_gls
27
21
  GC.init
28
22
  LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
29
23
  gloss = CrRuby.rb_define_module("Gloss");
30
- CrRuby.rb_define_singleton_method(gloss, "parse_buffer", ->parse_buffer(CrRuby::VALUE, CrRuby::VALUE), 1);
24
+ CrRuby.rb_define_singleton_method(gloss, "parse_buffer", ->parse_string(CrRuby::VALUE, CrRuby::VALUE), 1);
31
25
  end