gloss 0.0.1 → 0.0.6

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