gloss 0.0.5 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a26f64e872c2884f2fa5969d762e66ed6f1346732933f08ae7b92eb07aea5ab3
4
- data.tar.gz: 9cf39520e8dc43e6a74f9ea606a719815e2bd503d19744c7979478f746b8345f
3
+ metadata.gz: 9315ca2d8d487ca5729f7665abcba9e922ec4e0ee85ac54787248d07b54b7ed5
4
+ data.tar.gz: 78928faa1a7e77e0184594a31786c0befdc796a4a9b46c5f509adb95c39d37c3
5
5
  SHA512:
6
- metadata.gz: bdb98f72e57945e9de8bbd03296d95b2cfc221f915e7e8e8fd511e8b4794ef3bb65f1830d9769486c30a004595c3a7f84e3e785bb1b1297d778cff984a0a3234
7
- data.tar.gz: 9758d730e858adf27c60f89ec9e88ffb07d3950d2a4f493b1d0ad2b00b7a10999fd76c9ae802753428fdfb0cab005adfe773456c415b504809217507ac30fbc5
6
+ metadata.gz: c3178c5cf74e3946e327cbe97b5c5f421090e7b678d331c34ce5c8a45aea137cdc9628f91481425e18f66cf3245983d1e5bf965feaae211d619d8d9f5ed55647
7
+ data.tar.gz: 9d9b5ef709cffe8efb6b0124762bbe025948caa51772cf33a38143862003048f15ee83198c2fd0f6a05128f14da65a9feb22b45205563b686bacb70d51dd5741
data/.gitattributes ADDED
@@ -0,0 +1,3 @@
1
+ lib/gloss/**/*.rb linguist-generated
2
+ # this will basically cover 99% of the syntax anyway
3
+ **/*.gl linguist-language=Crystal
@@ -1,4 +1,4 @@
1
- name: Crystal CI
1
+ name: Crystal Specs
2
2
 
3
3
  on:
4
4
  push:
@@ -5,7 +5,7 @@
5
5
  # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
6
  # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
7
 
8
- name: Ruby
8
+ name: Ruby Specs
9
9
 
10
10
  on:
11
11
  push:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gloss (0.0.5)
4
+ gloss (0.0.6)
5
5
  fast_blank
6
6
  listen
7
7
  rbs
data/README.md CHANGED
@@ -1,7 +1,15 @@
1
1
  # Gloss
2
+ [![Gem Version](https://badge.fury.io/rb/gloss.svg)](https://rubygems.org/gems/gloss)
3
+ [![Ruby Specs](https://github.com/johansenja/gloss/workflows/Ruby%20Specs/badge.svg)](https://github.com/johansenja/gloss/actions?query=workflow%3A%22Ruby+Specs%22)
4
+ [![Crystal Specs](https://github.com/johansenja/gloss/workflows/Crystal%20Specs/badge.svg)](https://github.com/johansenja/gloss/actions?query=workflow%3A%22Crystal+Specs%22)
5
+ [![Total Downloads](http://ruby-gem-downloads-badge.herokuapp.com/gloss?type=total&color=green&metric=true&label=downloads%20(total)&total_label=)](https://rubygems.org/gems/gloss)
6
+ [![Current Version](http://ruby-gem-downloads-badge.herokuapp.com/gloss?color=green&label=downloads%20(current%20version)&metric=true)](https://rubygems.org/gems/gloss)
7
+ [![Total Views](https://counter.gofiber.io/badge/johansenja/gloss)](https://rubygems.org/gems/gloss)
2
8
 
3
9
  [Gloss](https://en.wikipedia.org/wiki/Gloss_(annotation)) is a high-level programming language based on [Ruby](https://github.com/ruby/ruby) and [Crystal](https://github.com/crystal-lang/crystal), which compiles to ruby; its aims are on transparency,
4
- efficiency, and to enhance ruby's goal of developer happiness and productivity. Some of the features include:
10
+ efficiency, and to enhance ruby's goal of developer happiness and productivity.
11
+
12
+ ### Current features
5
13
 
6
14
  - Type checking, via optional type annotations
7
15
  - Compile-time macros
@@ -10,11 +18,26 @@ efficiency, and to enhance ruby's goal of developer happiness and productivity.
10
18
  - All ruby files are valid gloss files (a small exceptions for now; workarounds are mostly available)
11
19
  - Other syntactic sugar
12
20
 
13
- Coming soon:
14
- - abstract classes
21
+ ### Current Status
22
+
23
+ This project is at a stage where the core non-crystal parts are written in Gloss and compile to ruby (essentially self-hosting), albeit with the type checking being fairly loose. However the project is still in the very early stages; with (as of yet) no Linux support nor error handling (see roadmap below). Use at your own discretion!
24
+
25
+ ### Approx. roadmap:
26
+
27
+ - Improve error handling and logging (currently almost non-existant)
28
+ - Address Linux compatibility (currently more or less non-existant)
29
+ - Implement different strictnesses of type checking
30
+ - Metaprogramming helpers/safety:*
31
+ - Abstract classes and methods
32
+ - Method lookup/existence checking at compile time
33
+ - Method overloading
34
+
35
+ #### Related items:
36
+
37
+ - Rails helpers; probably some time away*
38
+ - Editor plugins/syntax highlighting/langserver; probably some time away*
15
39
 
16
- Maybe on the roadmap:
17
- - Method overloading
40
+ *__Dependent on popularity__
18
41
 
19
42
  ## Examples:
20
43
 
@@ -223,4 +246,12 @@ then
223
246
 
224
247
  then
225
248
 
249
+ `mkdir src && echo "puts 'hello world'" > src/hello_world.gl`
250
+
251
+ then
252
+
226
253
  `gloss build`
254
+
255
+ then
256
+
257
+ `ruby ./hello_world.rb`
data/exe/gloss CHANGED
@@ -3,4 +3,18 @@
3
3
  require "bundler/setup"
4
4
  require "gloss"
5
5
 
6
- Gloss::CLI.new(ARGV).run
6
+ begin
7
+ Gloss::CLI.new(ARGV).run
8
+ rescue SystemExit
9
+ # raised by `abort` or `exit`; no op
10
+ rescue => e
11
+ Gloss.logger.fatal <<~MSG
12
+ Unexpected error: #{e.class.name}
13
+ Message: #{e.message}
14
+ Trace:
15
+ #{e.backtrace.join("\n")}
16
+
17
+ This is probably a bug and may warrant a bug report at https://github.com/johansenja/gloss/issues
18
+ MSG
19
+ exit 1
20
+ end
@@ -235,12 +235,6 @@ module Crystal
235
235
  end
236
236
  end
237
237
 
238
- class MacroExpression < ASTNode
239
- def to_rb
240
- Rb::AST::EmptyNode.new(self.class.name)
241
- end
242
- end
243
-
244
238
  class MacroIf < ASTNode
245
239
  def to_rb
246
240
  Rb::AST::MacroIf.new(@cond.to_rb, @then.to_rb, @else.to_rb)
@@ -11,7 +11,6 @@ def parse_string(self : CrRuby::VALUE, str : CrRuby::VALUE)
11
11
  output = begin
12
12
  Gloss.parse_string(string)
13
13
  rescue e : Crystal::SyntaxException
14
- pp e.backtrace
15
14
  e.to_s
16
15
  end
17
16
 
data/lib/gloss.rb CHANGED
@@ -17,6 +17,7 @@ require "gloss/source"
17
17
  require "gloss/scope"
18
18
  require "gloss/builder"
19
19
  require "gloss/errors"
20
+ require "gloss/logger"
20
21
 
21
22
  require "gls" unless ENV["CI"] # a bit of a hack for now
22
23
 
data/lib/gloss/builder.rb CHANGED
@@ -107,7 +107,14 @@ case node.[](:"type")
107
107
  src.write_ln("end")
108
108
  when "DefNode"
109
109
  args = render_args(node)
110
- src.write_ln("def #{node.[](:"name")}#{args.[](:"representation")}")
110
+ receiver = (if node.[](:"receiver")
111
+ visit_node(node.[](:"receiver"))
112
+ else
113
+ nil
114
+ end)
115
+ src.write_ln("def #{(if receiver
116
+ "#{receiver}."
117
+ end)}#{node.[](:"name")}#{args.[](:"representation")}")
111
118
  return_type = (if node.[](:"return_type")
112
119
  RBS::Types::ClassInstance.new(name: RBS::TypeName.new(name: eval(visit_node(node.[](:"return_type")))
113
120
  .to_s
@@ -121,7 +128,11 @@ case node.[](:"type")
121
128
  nil
122
129
  end), location: node.[](:"location"))]
123
130
  method_definition = RBS::AST::Members::MethodDefinition.new(name: node.[](:"name")
124
- .to_sym, kind: :"instance", types: method_types, annotations: EMPTY_ARRAY, location: node.[](:"location"), comment: node.[](:"comment"), overload: false)
131
+ .to_sym, kind: (if receiver
132
+ :"class"
133
+ else
134
+ :"instance"
135
+ end), types: method_types, annotations: EMPTY_ARRAY, location: node.[](:"location"), comment: node.[](:"comment"), overload: false)
125
136
  (if @current_scope
126
137
  @current_scope.members
127
138
  .<<(method_definition)
@@ -197,7 +208,7 @@ EMPTY_ARRAY }
197
208
  else
198
209
  ".."
199
210
  end)
200
- src.write("(", visit_node(node.[](:"from")), dots, visit_node(node.[](:"to")), ")")
211
+ src.write("(", "(", visit_node(node.[](:"from")), ")", dots, "(", visit_node(node.[](:"to")), ")", ")")
201
212
  when "LiteralNode"
202
213
  src.write(node.[](:"value"))
203
214
  when "ArrayLiteral"
@@ -216,7 +227,7 @@ EMPTY_ARRAY }
216
227
  str.<<(case c.[](:"type")
217
228
  when "LiteralNode"
218
229
  c.[](:"value")
219
- .[]((1...-1))
230
+ .[](((1)...(-1)))
220
231
  else
221
232
  ["\#{", visit_node(c)
222
233
  .strip, "}"].join
@@ -278,11 +289,12 @@ EMPTY_ARRAY }
278
289
  key = case k
279
290
  when String
280
291
  k.to_sym
292
+ .inspect
281
293
  else
282
294
  visit_node(k)
283
295
  end
284
296
  value = visit_node(v)
285
- "#{key.inspect} => #{value}" }
297
+ "#{key} => #{value}" }
286
298
  src.write("{#{contents.join(",\n")}}")
287
299
  (if node.[](:"frozen")
288
300
  src.write(".freeze")
data/lib/gloss/cli.rb CHANGED
@@ -11,10 +11,24 @@ module Gloss
11
11
  end
12
12
  def run()
13
13
  command = @argv.first
14
- files = @argv.[]((1..-1))
14
+ files = @argv.[](((1)..(-1)))
15
15
  err_msg = catch(:"error") { ||
16
16
  case command
17
17
  when "watch"
18
+ files = files.map() { |f|
19
+ path = (if Pathname.new(f)
20
+ .absolute?
21
+ f
22
+ else
23
+ File.join(Dir.pwd, f)
24
+ end)
25
+ (if Pathname.new(path)
26
+ .exist?
27
+ path
28
+ else
29
+ throw(:"error", "Pathname #{f} does not exist")
30
+ end)
31
+ }
18
32
  Watcher.new(files)
19
33
  .watch
20
34
  when "build"
@@ -24,7 +38,8 @@ case command
24
38
  files
25
39
  end)
26
40
  .each() { |fp|
27
- puts("=====> Building #{fp}")
41
+ Gloss.logger
42
+ .info("Building #{fp}")
28
43
  content = File.read(fp)
29
44
  tree_hash = Parser.new(content)
30
45
  .run
@@ -32,7 +47,8 @@ case command
32
47
  rb_output = Builder.new(tree_hash, type_checker)
33
48
  .run
34
49
  type_checker.run(rb_output)
35
- puts("=====> Writing #{fp}")
50
+ Gloss.logger
51
+ .info("Writing #{fp}")
36
52
  Writer.new(rb_output, fp)
37
53
  .run
38
54
  }
@@ -51,7 +67,8 @@ case command
51
67
  end
52
68
  nil }
53
69
  (if err_msg
54
- abort(err_msg)
70
+ Gloss.logger.fatal(err_msg)
71
+ exit(1)
55
72
  end)
56
73
  end
57
74
  end
@@ -18,7 +18,8 @@ module Gloss
18
18
  .transform_keys(&:"to_s")
19
19
  .to_yaml)
20
20
  }
21
- puts("Created #{CONFIG_PATH} with default preferences")
21
+ Gloss.logger
22
+ .info("Created #{CONFIG_PATH} with default preferences")
22
23
  end
23
24
  end
24
25
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##### This file was generated by Gloss; any changes made here will be overwritten.
4
+ ##### See src/ to make changes
5
+
6
+ module Gloss
7
+ def self.logger()
8
+ (if @logger
9
+ @logger
10
+ else
11
+ env_log_level = ENV.fetch("LOG_LEVEL") { ||
12
+ "INFO" }
13
+ real_log_level = {"UNKNOWN" => Logger::UNKNOWN,
14
+ "FATAL" => Logger::FATAL,
15
+ "ERROR" => Logger::ERROR,
16
+ "WARN" => Logger::WARN,
17
+ "INFO" => Logger::INFO,
18
+ "DEBUG" => Logger::DEBUG,
19
+ "NIL" => nil,
20
+ nil => nil,
21
+ "" => nil}.fetch(env_log_level)
22
+ @logger = Logger.new((if real_log_level
23
+ STDOUT
24
+ else
25
+ nil
26
+ end))
27
+ formatter = Logger::Formatter.new
28
+ @logger.formatter=(proc() { |severity, datetime, progname, msg|
29
+ formatter.call(severity, datetime, progname, msg)
30
+ })
31
+ @logger
32
+ end)
33
+ end
34
+ end
data/lib/gloss/parser.rb CHANGED
@@ -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
- module Gloss
6
+ module Gloss
7
7
  class Parser
8
8
  def initialize(str)
9
9
  @str = str
@@ -13,7 +13,22 @@
13
13
  begin
14
14
  JSON.parse(tree_json, symbolize_names: true)
15
15
  rescue JSON::ParserError
16
- raise(Errors::ParserError, tree_json)
16
+ error_message = tree_json
17
+ error_message.match(/.+\s:(\d+)$/)
18
+ (if $~.[](1)
19
+ line_number = $~.[](1)
20
+ .to_i
21
+ context = @str.lines
22
+ .[]((( line_number.-(2)
23
+ )..(line_number)))
24
+ .map
25
+ .with_index() { |line, index|
26
+ "#{index.-(1)
27
+ .+(line_number)}| #{line}" }
28
+ .join
29
+ error_message = "#{context.rstrip}\n\n#{error_message}\n"
30
+ end)
31
+ throw(:"error", error_message)
17
32
  end
18
33
  end
19
34
  end
@@ -3,7 +3,8 @@
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
+ require "pry-byebug"
7
+ module Gloss
7
8
  class TypeChecker
8
9
  Project = Struct.new(:"targets")
9
10
  attr_reader(:"steep_target", :"top_level_decls")
@@ -15,30 +16,40 @@
15
16
  @top_level_decls = {}
16
17
  end
17
18
  def run(rb_str)
18
- unless check_types(rb_str)
19
- raise(Errors::TypeError, @steep_target.errors
19
+ begin
20
+ valid_types = check_types(rb_str)
21
+ rescue ParseError => e
22
+ throw(:"error", "")
23
+ rescue => e
24
+ throw(:"error", "Type checking Error: #{e.message} (#{e.class})")
25
+ end
26
+ unless valid_types
27
+ errors = @steep_target.errors
20
28
  .map() { |e|
21
29
  case e
22
- when Steep::Errors::NoMethod
30
+ when Steep::Diagnostic::Ruby::NoMethod
23
31
  "Unknown method :#{e.method}, location: #{e.type
24
32
  .location
25
33
  .inspect}"
26
- when Steep::Errors::MethodBodyTypeMismatch
34
+ when Steep::Diagnostic::Ruby::MethodBodyTypeMismatch
27
35
  "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
28
- when Steep::Errors::IncompatibleArguments
29
- " Invalid argmuents - method type: #{e.method_type}\n method name: #{e.method_type
36
+ when Steep::Diagnostic::Ruby::IncompatibleArguments
37
+ "Invalid argmuents - method type: #{e.method_type}\nmethod name: #{e.method_type
30
38
  .method_decls
31
39
  .first
32
40
  .method_name}"
33
- when Steep::Errors::ReturnTypeMismatch
41
+ when Steep::Diagnostic::Ruby::ReturnTypeMismatch
34
42
  "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
35
- when Steep::Errors::IncompatibleAssignment
43
+ when Steep::Diagnostic::Ruby::IncompatibleAssignment
36
44
  "Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
45
+ when Steep::Diagnostic::Ruby::UnexpectedBlockGiven
46
+ "Unexpected block given"
37
47
  else
38
48
  e.inspect
39
49
  end
40
50
  }
41
- .join("\n"))
51
+ .join("\n")
52
+ throw(:"error", errors)
42
53
  end
43
54
  true
44
55
  end
@@ -58,6 +69,25 @@ true
58
69
  env = env.resolve_type_names
59
70
  @steep_target.instance_variable_set("@environment", env)
60
71
  @steep_target.type_check
72
+ (if @steep_target.status
73
+ .is_a?(Steep::Project::Target::SignatureErrorStatus)
74
+ throw(:"error", @steep_target.status
75
+ .errors
76
+ .map() { |e|
77
+ " SignatureSyntaxError:\n Location: #{e.location}\n Message: \"#{e.exception
78
+ .error_value
79
+ .value}\"" }
80
+ .join("\n"))
81
+ end)
82
+ @steep_target.source_files
83
+ .each() { |path, f|
84
+ (if f.status
85
+ .is_a?(Steep::Project::SourceFile::ParseErrorStatus)
86
+ e = f.status
87
+ .error
88
+ throw(:"error", "#{e.class}: #{e.message}")
89
+ end)
90
+ }
61
91
  @steep_target.status
62
92
  .is_a?(Steep::Project::Target::TypeCheckStatus) && @steep_target.no_error? && @steep_target.errors
63
93
  .empty?
data/lib/gloss/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  ##### See src/ to make changes
5
5
 
6
6
  module Gloss
7
- VERSION = "0.0.5"
7
+ VERSION = "0.0.6"
8
8
  end
data/lib/gloss/watcher.rb CHANGED
@@ -3,50 +3,72 @@
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 "listen"
6
+ require "listen"
7
7
  module Gloss
8
8
  class Watcher
9
9
  def initialize(paths)
10
10
  @paths = paths
11
11
  (if @paths.empty?
12
12
  @paths = [File.join(Dir.pwd, Config.src_dir)]
13
+ @only = /\.gl$/
14
+ else
15
+ file_names = Array.new
16
+ paths = Array.new
17
+ @paths.each() { |pa|
18
+ pn = Pathname.new(pa)
19
+ paths.<<(pn.parent
20
+ .to_s)
21
+ file_names.<<((if pn.file?
22
+ pn.basename
23
+ .to_s
24
+ else
25
+ pa
26
+ end))
27
+ }
28
+ @paths = paths.uniq
29
+ @only = /#{Regexp.union(file_names)}/
13
30
  end)
14
31
  end
15
32
  def watch()
16
- puts("=====> Now listening for changes in #{@paths.join(", ")}")
17
- listener = Listen.to(*@paths, latency: 2) { |modified, added, removed|
33
+ Gloss.logger
34
+ .info("Now listening for changes in #{@paths.join(", ")}")
35
+ listener = Listen.to(*@paths, latency: 2, only: @only) { |modified, added, removed|
18
36
  modified.+(added)
19
37
  .each() { |f|
20
- unless f.end_with?(".gl")
21
- next
22
- end
23
- puts("====> Rewriting #{f}")
38
+ Gloss.logger
39
+ .info("Rewriting #{f}")
24
40
  content = File.read(f)
25
- Writer.new(Builder.new(Parser.new(content)
41
+ err = catch(:"error") { ||
42
+ Writer.new(Builder.new(Parser.new(content)
26
43
  .run)
27
44
  .run, f)
28
45
  .run
29
- puts("====> Done")
46
+ nil }
47
+ (if err
48
+ Gloss.logger
49
+ .error(err)
50
+ else
51
+ Gloss.logger
52
+ .info("Done")
53
+ end)
30
54
  }
31
55
  removed.each() { |f|
32
- unless f.end_with?(".gl")
33
- next
34
- end
35
56
  out_path = Utils.src_path_to_output_path(f)
36
- puts("====> Removing #{out_path}")
57
+ Gloss.logger
58
+ .info("Removing #{out_path}")
37
59
  (if File.exist?(out_path)
38
60
  File.delete(out_path)
39
61
  end)
40
- puts("====> Done")
62
+ Gloss.logger
63
+ .info("Done")
41
64
  }
42
65
  }
43
- listener.start
44
66
  begin
45
- loop() { ||
46
- sleep(10)
47
- }
67
+ listener.start
68
+ sleep
48
69
  rescue Interrupt
49
- puts("=====> Interrupt signal received, shutting down")
70
+ Gloss.logger
71
+ .info("Interrupt signal received, shutting down")
50
72
  exit(0)
51
73
  end
52
74
  end
@@ -113,7 +113,8 @@ module Gloss
113
113
  src.write_ln "end"
114
114
  when "DefNode"
115
115
  args = render_args(node)
116
- src.write_ln "def #{node[:name]}#{args[:representation]}"
116
+ receiver = node[:receiver] ? visit_node(node[:receiver]) : nil
117
+ src.write_ln "def #{"#{receiver}." if receiver}#{node[:name]}#{args[:representation]}"
117
118
 
118
119
  return_type = if node[:return_type]
119
120
  RBS::Types::ClassInstance.new(
@@ -162,7 +163,7 @@ module Gloss
162
163
  ]
163
164
  method_definition = RBS::AST::Members::MethodDefinition.new(
164
165
  name: node[:name].to_sym,
165
- kind: :instance,
166
+ kind: receiver ? :class : :instance,
166
167
  types: method_types,
167
168
  annotations: EMPTY_ARRAY, # TODO
168
169
  location: node[:location],
@@ -215,9 +216,10 @@ module Gloss
215
216
  when "RangeLiteral"
216
217
  dots = node[:exclusive] ? "..." : ".."
217
218
 
218
- # parentheses help the compatibility with precendence of operators in some situations
219
+ # parentheses around the whole thing help the compatibility with precendence of operators in some situations
219
220
  # eg. (1..3).cover? 2 vs. 1..3.cover? 2
220
- src.write "(", visit_node(node[:from]), dots, visit_node(node[:to]), ")"
221
+ # parentheses around either number help with things like arithemtic ((x-1)..(y+5))
222
+ src.write "(", "(", visit_node(node[:from]), ")", dots, "(", visit_node(node[:to]), ")", ")"
221
223
 
222
224
  when "LiteralNode"
223
225
 
@@ -296,12 +298,12 @@ module Gloss
296
298
  contents = node[:elements].map do |k, v|
297
299
  key = case k
298
300
  when String
299
- k.to_sym
301
+ k.to_sym.inspect
300
302
  else
301
303
  visit_node k
302
304
  end
303
305
  value = visit_node v
304
- "#{key.inspect} => #{value}"
306
+ "#{key} => #{value}"
305
307
  end
306
308
 
307
309
  src.write "{#{contents.join(",\n")}}"
data/src/lib/gloss/cli.gl CHANGED
@@ -13,17 +13,25 @@ module Gloss
13
13
  err_msg = catch :error do
14
14
  case command
15
15
  when "watch"
16
+ files = files.map do |f|
17
+ path = Pathname.new(f).absolute? ? f : File.join(Dir.pwd, f)
18
+ if Pathname.new(path).exist?
19
+ path
20
+ else
21
+ throw :error, "Pathname #{f} does not exist"
22
+ end
23
+ end
16
24
  Watcher.new(files).watch
17
25
  when "build"
18
26
  (files.empty? ? Dir.glob("#{Config.src_dir}/**/*.gl") : files).each do |fp|
19
- puts "=====> Building #{fp}"
27
+ Gloss.logger.info "Building #{fp}"
20
28
  content = File.read(fp)
21
29
  tree_hash = Parser.new(content).run
22
30
  type_checker = TypeChecker.new
23
31
  rb_output = Builder.new(tree_hash, type_checker).run
24
32
  type_checker.run(rb_output)
25
33
 
26
- puts "=====> Writing #{fp}"
34
+ Gloss.logger.info "Writing #{fp}"
27
35
  Writer.new(rb_output, fp).run
28
36
  end
29
37
  when "init"
@@ -38,7 +46,10 @@ module Gloss
38
46
  nil
39
47
  end
40
48
 
41
- abort err_msg if err_msg
49
+ if err_msg
50
+ Gloss.logger.fatal err_msg
51
+ exit 1
52
+ end
42
53
  end
43
54
  end
44
55
  end
@@ -14,7 +14,7 @@ module Gloss
14
14
  file.puts Config.default_config.transform_keys(&:to_s).to_yaml
15
15
  end
16
16
 
17
- puts "Created #{CONFIG_PATH} with default preferences"
17
+ Gloss.logger.info "Created #{CONFIG_PATH} with default preferences"
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,26 @@
1
+ module Gloss
2
+ def self.logger
3
+ if @logger
4
+ @logger
5
+ else
6
+ env_log_level = ENV.fetch("LOG_LEVEL") { "INFO" }
7
+ real_log_level = {
8
+ "UNKNOWN" => Logger::UNKNOWN,
9
+ "FATAL" => Logger::FATAL,
10
+ "ERROR" => Logger::ERROR,
11
+ "WARN" => Logger::WARN,
12
+ "INFO" => Logger::INFO,
13
+ "DEBUG" => Logger::DEBUG,
14
+ "NIL" => nil,
15
+ nil => nil,
16
+ "" => nil
17
+ }.fetch env_log_level
18
+ @logger = Logger.new(real_log_level ? STDOUT : nil)
19
+ formatter = Logger::Formatter.new
20
+ @logger.formatter = proc do |severity, datetime, progname, msg|
21
+ formatter.call(severity, datetime, progname, msg)
22
+ end
23
+ @logger
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Gloss
4
2
  class Parser
5
3
  def initialize(@str : String)
@@ -10,9 +8,23 @@ module Gloss
10
8
  begin
11
9
  JSON.parse tree_json, symbolize_names: true
12
10
  rescue JSON::ParserError
13
- # if parsing fails then tree is invalid and most likely an error message from the parser in
14
- # crystal
15
- raise Errors::ParserError, tree_json
11
+ error_message = tree_json
12
+ error_message.match /.+\s:(\d+)$/
13
+ if $1
14
+ line_number = $1.to_i
15
+ # line numbers start at 1, but array index starts at 0; so this still gives one line
16
+ # either side of the offending line
17
+ context = @str.lines[(line_number - 2)..(line_number)].map.with_index { |line, index|
18
+ "#{index - 1 + line_number}| #{line}"
19
+ }.join
20
+ error_message = <<~MSG
21
+ #{context.rstrip}
22
+
23
+ #{error_message}
24
+
25
+ MSG
26
+ end
27
+ throw :error, error_message
16
28
  end
17
29
  end
18
30
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "pry-byebug"
2
3
 
3
4
  module Gloss
4
5
  class TypeChecker
@@ -20,27 +21,37 @@ module Gloss
20
21
  end
21
22
 
22
23
  def run(rb_str)
23
- unless check_types(rb_str)
24
- raise Errors::TypeError,
25
- @steep_target.errors.map { |e|
26
- case e
27
- when Steep::Errors::NoMethod
28
- "Unknown method :#{e.method}, location: #{e.type.location.inspect}"
29
- when Steep::Errors::MethodBodyTypeMismatch
30
- "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
31
- when Steep::Errors::IncompatibleArguments
32
- <<-ERR
33
- Invalid argmuents - method type: #{e.method_type}
34
- method name: #{e.method_type.method_decls.first.method_name}
35
- ERR
36
- when Steep::Errors::ReturnTypeMismatch
37
- "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
38
- when Steep::Errors::IncompatibleAssignment
39
- "Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
40
- else
41
- e.inspect
42
- end
43
- }.join("\n")
24
+ begin
25
+ valid_types = check_types rb_str
26
+ rescue ParseError => e
27
+ throw :error, ""
28
+ rescue => e
29
+ throw :error, "Type checking Error: #{e.message} (#{e.class})"
30
+ end
31
+
32
+ unless valid_types
33
+ errors = @steep_target.errors.map { |e|
34
+ case e
35
+ when Steep::Diagnostic::Ruby::NoMethod
36
+ "Unknown method :#{e.method}, location: #{e.type.location.inspect}"
37
+ when Steep::Diagnostic::Ruby::MethodBodyTypeMismatch
38
+ "Invalid method body type - expected: #{e.expected}, actual: #{e.actual}"
39
+ when Steep::Diagnostic::Ruby::IncompatibleArguments
40
+ <<-ERR
41
+ Invalid argmuents - method type: #{e.method_type}
42
+ method name: #{e.method_type.method_decls.first.method_name}
43
+ ERR
44
+ when Steep::Diagnostic::Ruby::ReturnTypeMismatch
45
+ "Invalid return type - expected: #{e.expected}, actual: #{e.actual}"
46
+ when Steep::Diagnostic::Ruby::IncompatibleAssignment
47
+ "Invalid assignment - cannot assign #{e.rhs_type} to type #{e.lhs_type}"
48
+ when Steep::Diagnostic::Ruby::UnexpectedBlockGiven
49
+ "Unexpected block given"
50
+ else
51
+ e.inspect
52
+ end
53
+ }.join("\n")
54
+ throw :error, errors
44
55
  end
45
56
 
46
57
  true
@@ -65,6 +76,23 @@ module Gloss
65
76
 
66
77
  @steep_target.type_check
67
78
 
79
+ if @steep_target.status.is_a? Steep::Project::Target::SignatureErrorStatus
80
+ throw :error, @steep_target.status.errors.map { |e|
81
+ <<~MSG
82
+ SignatureSyntaxError:
83
+ Location: #{e.location}
84
+ Message: "#{e.exception.error_value.value}"
85
+ MSG
86
+ }.join("\n")
87
+ end
88
+
89
+ @steep_target.source_files.each do |path, f|
90
+ if f.status.is_a? Steep::Project::SourceFile::ParseErrorStatus
91
+ e = f.status.error
92
+ throw :error, "#{e.class}: #{e.message}"
93
+ end
94
+ end
95
+
68
96
  @steep_target.status.is_a?(Steep::Project::Target::TypeCheckStatus) &&
69
97
  @steep_target.no_error? &&
70
98
  @steep_target.errors.empty?
@@ -1,3 +1,3 @@
1
1
  module Gloss
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -5,42 +5,61 @@ require "listen"
5
5
  module Gloss
6
6
  class Watcher
7
7
  def initialize(@paths : Array[String])
8
- @paths = [File.join(Dir.pwd, Config.src_dir)] if @paths.empty?
8
+ if @paths.empty?
9
+ @paths = [File.join(Dir.pwd, Config.src_dir)]
10
+ @only = /\.gl$/
11
+ else
12
+ file_names = Array.new
13
+ paths = Array.new
14
+ @paths.each do |pa|
15
+ pn = Pathname.new(pa)
16
+ paths << pn.parent.to_s
17
+ file_names << (pn.file? ? pn.basename.to_s : pa)
18
+ end
19
+ @paths = paths.uniq
20
+ @only = /#{Regexp.union(file_names)}/
21
+ end
9
22
  end
10
23
 
11
24
  def watch
12
- puts "=====> Now listening for changes in #{@paths.join(', ')}"
13
- listener = Listen.to(*@paths, latency: 2) do |modified, added, removed|
25
+ Gloss.logger.info "Now listening for changes in #{@paths.join(', ')}"
26
+ listener = Listen.to(
27
+ *@paths,
28
+ latency: 2,
29
+ only: @only
30
+ ) do |modified, added, removed|
14
31
  (modified + added).each do |f|
15
- next unless f.end_with? ".gl"
16
-
17
- puts "====> Rewriting #{f}"
32
+ Gloss.logger.info "Rewriting #{f}"
18
33
  content = File.read(f)
19
- Writer.new(
20
- Builder.new(
21
- Parser.new(
22
- content
23
- ).run
24
- ).run, f
25
- ).run
26
-
27
- puts "====> Done"
34
+ err = catch :error do
35
+ Writer.new(
36
+ Builder.new(
37
+ Parser.new(
38
+ content
39
+ ).run
40
+ ).run, f
41
+ ).run
42
+ nil
43
+ end
44
+ if err
45
+ Gloss.logger.error err
46
+ else
47
+ Gloss.logger.info "Done"
48
+ end
28
49
  end
29
50
  removed.each do |f|
30
- next unless f.end_with? ".gl"
31
-
32
51
  out_path = Utils.src_path_to_output_path(f)
33
- puts "====> Removing #{out_path}"
52
+ Gloss.logger.info "Removing #{out_path}"
34
53
  File.delete out_path if File.exist? out_path
35
54
 
36
- puts "====> Done"
55
+ Gloss.logger.info "Done"
37
56
  end
38
57
  end
39
- listener.start
40
58
  begin
41
- loop { sleep 10 }
59
+ listener.start
60
+ sleep
42
61
  rescue Interrupt
43
- puts "=====> Interrupt signal received, shutting down"
62
+ Gloss.logger.info "Interrupt signal received, shutting down"
44
63
  exit 0
45
64
  end
46
65
  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.5
4
+ version: 0.0.6
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-31 00:00:00.000000000 Z
11
+ date: 2021-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fast_blank
@@ -131,8 +131,9 @@ extensions:
131
131
  - ext/gloss/extconf.rb
132
132
  extra_rdoc_files: []
133
133
  files:
134
- - ".github/workflows/crystal.yml"
135
- - ".github/workflows/ruby.yml"
134
+ - ".gitattributes"
135
+ - ".github/workflows/crystal_specs.yml"
136
+ - ".github/workflows/ruby_specs.yml"
136
137
  - ".gitignore"
137
138
  - ".gloss.yml"
138
139
  - ".rspec"
@@ -163,6 +164,7 @@ files:
163
164
  - lib/gloss/config.rb
164
165
  - lib/gloss/errors.rb
165
166
  - lib/gloss/initializer.rb
167
+ - lib/gloss/logger.rb
166
168
  - lib/gloss/parser.rb
167
169
  - lib/gloss/scope.rb
168
170
  - lib/gloss/source.rb
@@ -177,6 +179,7 @@ files:
177
179
  - src/lib/gloss/config.gl
178
180
  - src/lib/gloss/errors.gl
179
181
  - src/lib/gloss/initializer.gl
182
+ - src/lib/gloss/logger.gl
180
183
  - src/lib/gloss/parser.gl
181
184
  - src/lib/gloss/scope.gl
182
185
  - src/lib/gloss/source.gl