What are you cooking?

I was able to spend a few more hours steering some sessions and managed to get a few things done.

$ cloc src
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Crystal                         34            799           1626           7828
-------------------------------------------------------------------------------

  1. Naive ARC (Automatic Reference Counting, ala Swift) with --check-leaks option to instrument reference counting and validate things on program exit:
lib LibC
  fun printf(format : Pointer(UInt8), ...) : Int32
end

class Point
  def initialize(@x : Int32, @y : Int32)
  end
end

i = 0
while i < 3
  p = Point.new(i, i)
  i = i + 1
end
LibC.printf("done\n")
$ bin/jasper run --check-leaks examples/leaky.jasper
jasper: 3 live allocation(s) at exit
done                                    # exit 1
  1. Per-method incremental compilation: change a method, only that code gets recompiled. Relocate a method to another file, if no change to the method itself, no recompilation necessary:
# examples/wordcount.jasper
require "./tokenizer"

lib LibC
  fun printf(format : Pointer(UInt8), ...) : Int32
end

text = "the quick brown fox jumps over the lazy dog"
scanner = Tokenizer.new(text)
words = 0
longest = 0
length = scanner.next_word_size
while length >= 0
  words += 1
  longest = length if length > longest
  length = scanner.next_word_size
end
LibC.printf("%d words, longest %d\n", words, longest)
# examples/tokenizer.jasper
class Tokenizer
  def initialize(@text : String)
    @pos = 0
  end

  def space?(b : UInt8) : Bool
    b == 32_u8 || b == 10_u8 || b == 9_u8
  end

  # Length of the next word, or -1 when the text is exhausted.
  def next_word_size : Int32
    size = @text.size
    while @pos < size && space?(@text.byte_at(@pos))
      @pos = @pos + 1
    end
    return -1 if @pos >= size
    length = 0
    while @pos < size && !space?(@text.byte_at(@pos))
      length = length + 1
      @pos = @pos + 1
    end
    length
  end
end
$ bin/jasper run --stats examples/wordcount.jasper
Parse                        00:00:00.0008 (0.50MB)
Semantic: register classes   00:00:00.0000 (0.50MB)
Semantic: declare signatures 00:00:00.0001 (0.50MB)
Semantic: type check         00:00:00.0002 (0.50MB)
Semantic: flow analysis      00:00:00.0000 (0.50MB)
Semantic: hierarchy layout   00:00:00.0000 (0.50MB)
Semantic: checks             00:00:00.0000 (0.50MB)
Semantic: ARC synthesis      00:00:00.0000 (0.50MB)
Codegen (myc IR)             00:00:00.0019 (0.75MB)
Backend (myc)                00:00:00.0761 (1.44MB)
Cache                        2 hits, 13 misses (myc-qbe 24ce939d128c)
Link (cc)                    00:00:00.0274 (1.44MB)
9 words, longest 5
  1. Abstract classes, and basic inheritance, even with ivar coverage (to catch corner cases):
lib LibC
  fun printf(format : Pointer(UInt8), ...) : Int32
end

abstract class Shape
  def initialize(@name : String)
  end

  def name : String
    @name
  end
end

class Circle < Shape
  def initialize(value : Bool)
    return if value
    @name = "Circle"
  end
end

c = Circle.new(true)

# the next line is invalid memory in Crystal
LibC.printf("name=%s\n", c.name)

In Jasper does not even compile:

$ bin/jasper run 1.jasper
1.jasper:16:5: error: `Circle#initialize` must call `super` to initialize `@name`
      return if value
      ^^^^^^

But of course, nothing else for the time being, just playing with some theories, there is no cross-platform, stdlib, channels, fibers, not even unions :sweat_smile:

Just having some fun, learning a bit about those things that always was interested to see them at work and never had the opportunity to get to this point (only did 1 refactoring so far).

Cheers!