# Examples taken from http://crystal-lang.org/docs/
# Copyright 2012-2018 Manas Technology Solutions.
# Updated on May 17th, 2018 by @faustinoaq.

require "http/server"

server = HTTP::Server.new(8080) do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello world! The time is #{Time.now}"
end

puts "Listening on http://0.0.0.0:8080"
server.listen

module HTTP
  class RequestHandler
  end
end

alias NumericValue = Float32 | Float64 | Int32 | Int64

enum Time::DayOfWeek
end

class Greeting
  class_property global_greeting = "Hello world"

  @@default_greeting = "Hello world"

  def initialize(@custom_greeting = nil)
  end

  def print_greeting
    greeting = @custom_greeting || @@default_greeting
    puts greeting
  end
end

LUCKY_NUMBERS     = [3, 7, 11]
DOCUMENTATION_URL = "http://crystal-lang.org/docs"

module Scorecard
  class Parser
    def parse(score_text)
      begin
        score_text.scan(SCORE_PATTERN) do |match|
          handle_match(match)
        end
      rescue err : ParseError
        # handle error ...
      end
    end
  end
end

module Money
  CURRENCIES = {
    "EUR" => 1.0,
    "ARS" => 10.55,
    "USD" => 1.12,
    "JPY" => 134.15,
  }

  class Amount
    getter :currency, :value

    def initialize(@currency, @value)
    end
  end

  class CurrencyConversion
    def initialize(@amount, @target_currency)
    end

    def amount
      # implement conversion ...
    end
  end
end

i = 0
while i < 10
  proc = ->(x : Int32) do
    spawn do
      puts(x)
    end
  end
  proc.call(i)
  i += 1
end

Fiber.yield

# A buffered channel of capacity 2
channel = Channel(Int32).new(2)

spawn do
  channel.send(1)
  channel.send(2)
  channel.send(3)
end

3.times do |i|
  puts channel.receive
end

class MyDictionary(K, V)
end

MyBox.new(1)       # :: MyBox(Int32)
MyBox.new("hello") # :: MyBox(String)

module Moo(T)
  def t
    T
  end
end

class Foo(U)
  include Moo(U)

  def initialize(@value : U)
  end
end

foo = Foo.new(1)
foo.t # Int32

class Parent(T)
end

class Int32Child < Parent(Int32)
end

class GenericChild(T) < Parent(T)
end

class Person
end

a = 1
ptr = pointerof(a)
ptr[100_000] = 2 # undefined behaviour, probably a segmentation fault

alias Int32OrString = Int32 | String

alias Int32OrNil = Int32?

alias Int32OrNil_ = Int32 | ::Nil

alias Int32Ptr = Int32*

alias Int32Ptr_ = Pointer(Int32)

alias Int32_8 = Int32[8]

alias Int32_8_ = StaticArray(Int32, 8)

alias Int32StringTuple = {Int32, String}

alias Int32StringTuple_ = Tuple(Int32, String)

alias Int32ToString = Int32 -> String

alias Int32ToString_ = Proc(Int32, String)

alias ProcThatReturnsInt32 = -> Int32

alias Int32AndCharToString = Int32, Char -> String

alias ComplexProc = (Int32 -> Int32) -> String

def foo(x : Int32)
  "instance"
end

def foo(x : Int32.class)
  "class"
end

foo 1     # "instance"
foo Int32 # "class"

class Parent
end

class Child1 < Parent
end

class Child2 < Parent
end

ary = [] of Parent.class
ary << Child1
ary << Child2

# Same as not specifying a restriction, not very useful
def foo(x : _)
end

# A bit more useful: any two arguments Proc that returns an Int32:
def foo(x : _, _ -> Int32)
end

# alias SameAsInt32 = typeof(2)
# alias Int32OrString_ = typeof(1, "a")

class Person
  def initialize(name)
    @name = name
    @age = 0
  end

  def name
    @name
  end

  def age
    @age
  end
end

john = Person.new "John"
peter = Person.new "Peter"

john.name # => "John"
john.age  # => 0

peter.name # => "Peter"

class Person
  def self.new(name)
    instance = Person.allocate
    instance.initialize(name)
    instance
  end
end

if a.is_a?(String)
  # here a is a String
end

if b.is_a?(Number)
  # here b is a Number
end

a = some_condition ? 1 : "hello"
# a : Int32 | String

if a.is_a?(Number)
  # a : Int32
else
  # a : String
end

if a.is_a?(String) && b.is_a?(Number)
  # here a is a String and b is a Number
end

a.+(b)

struct Vector2
  getter x, y

  def initialize(@x, @y)
  end

  def +(other)
    Vector2.new(x + other.x, y + other.y)
  end
end

v1 = Vector2.new(1, 2)
v2 = Vector2.new(3, 4)
v1 + v2 # => Vector2(@x=4, @y=6)

struct Vector2
  def -
    Vector2.new(-x, -y)
  end
end

v1 = Vector2.new(1, 2)
-v1 # => Vector2(@x=-1, @y=-2)

class MyArray
  def [](index)
    # ...
  end

  def [](index1, index2, index3)
    # ...
  end

  def []=(index, value)
    # ...
  end
end

array = MyArray.new

array[1]       # invokes the first method
array[1, 2, 3] # invokes the second method
array[1] = 2   # invokes the third method

array.[](1)       # invokes the first method
array.[](1, 2, 3) # invokes the second method
array.[]=(1, 2)   # invokes the third method

raise "OH NO!"
raise Exception.new("Some error")

class MyException < Exception
end

begin
  raise MyException.new("OH NO!")
rescue ex : MyException
  puts "Rescued MyException: #{ex.message}"
end

begin
  # ...
rescue ex : MyException | MyOtherException
  # only MyException or MyOtherException
rescue
  # any other kind of exception
ensure
  puts "Cleanup..."
end

def some_method
  something_dangerous
rescue
  # execute if an exception is raised
end

array = [1, 2, 3]
array[4]  # raises because of IndexError
array[4]? # returns nil because of index out of bounds

def some_proc(&block : Int32 -> Int32)
  block
end

x = 0
proc = ->(i : Int32) { x += i }
proc = some_proc(&proc)
proc.call(1)  # => 1
proc.call(10) # => 11
x             # => 11

def add(x, y)
  x + y
end

adder = ->add(Int32, Int32)
adder.call(1, 2) # => 3

module Curses
  class Window
  end
end

Curses::Window.new

module ItemsSize
  def size
    items.size
  end
end

class Items
  include ItemsSize

  def items
    [1, 2, 3]
  end
end

items = Items.new
items.size # => 3

module Base64
  extend self

  def encode64(string)
    # ...
  end

  def decode64(string)
    # ...
  end
end

Base64.encode64 "hello" # => "aGVsbG8="

if some_condition
  a = 1
else
  a = "hello"
end

a_as_int = a.as(Int32)
a_as_int.abs # works, compiler knows that a_as_int is Int32

ptr = Pointer(Int32).malloc(1)
ptr.as(Int8*) # :: Pointer(Int8)

array = [1, 2, 3]

# object_id returns the address of an object in memory,
# so we create a pointer with that address
ptr = Pointer(Void).new(array.object_id)

# Now we cast that pointer to the same type, and
# we should get the same value
array2 = ptr.as(Array(Int32))
array2.same?(array) # => true

a = 1
b = a.as(Int32 | Float64)
b # :: Int32 | Float64

ary = [1, 2, 3]

# We want to create an array 1, 2, 3 of Int32 | Float64
ary2 = ary.map { |x| x.as(Int32 | Float64) }

ary2        # :: Array(Int32 | Float64)
ary2 << 1.5 # OK

class Person
  def initialize(@name)
  end

  def name
    @name
  end
end

a = [] of Person
x = a.map { |f| f.name } # Error: can't infer block return type

a = [] of Person
x = a.map { |f| f.name.as(String) } # OK

Person.new "John"

a = [] of Person
x = a.map { |f| f.name } # OK

loop do
  do_something
  break if some_condition
end

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

Point.new 1, 2

# 2 x Int32 = 2 x 4 = 8
instance_sizeof(Point) # => 12

a = 1
while a < 5
  a += 1
  if a == 3
    next
  end
  puts a
end

# The above prints the numbers 2, 4 and 5

lib C
  # In C: double cos(double x)
  fun cos(value : Float64) : Float64

  fun getch : Int32

  fun srand(seed : UInt32)

  fun exit(status : Int32) : NoReturn

  fun printf(format : UInt8*, ...) : Int32
end

C.cos(1.5) # => 0.0707372
C.srand(1_u32)

a = 1
b = 2
C.printf "%d + %d = %d\n", a, b, a + b

lib LibSDL
  fun init = SDL_Init(flags : UInt32) : Int32
end

lib LLVMIntrinsics
  fun ceil_f32 = "llvm.ceil.f32"(value : Float32) : Float32
end

lib MyLib
  fun my_fun(some_size : LibC::SizeT)
end

@[Link("pcre")]
lib LibPCRE
end

lib C
  {% if flag?(:x86_64) %}
    alias SizeT = UInt64
  {% else %}
    alias SizeT = UInt32
  {% end %}

  fun memcmp(p1 : Void*, p2 : Void*, size : C::SizeT) : Int32
end

lib X
  enum SomeEnum
    Ten       = 10
    Twenty    = 10 * 2
    ThirtyTwo = 1 << 5
  end
end

lib X
  enum SomeEnum
    A = 1_u32
  end
end

X::SomeEnum::Zero # => 0_i8
X::SomeEnum::Two  # => 2_i8

lib X
  fun callback(f : Int32 -> Int32)
end

f = ->(x : Int32) { x + 1 }
X.callback(f)

X.callback ->(x) { x + 1 }

X.callback nil

lib LibFoo
  fun store_callback(callback : ->)
  fun execute_callback
end

LibFoo.store_callback ->{ raise "OH NO!" }
LibFoo.execute_callback

lib LibFoo
  fun store_callback(callback : ->)

  @[Raises]
  fun execute_callback
end

@[Link("pcre")]
lib PCRE
  INFO_CAPTURECOUNT = 2
end

PCRE::INFO_CAPTURECOUNT # => 2

lib U
  # In C:
  #
  #  union IntOrFloat {
  #    int some_int;
  #    double some_float;
  #  };
  union IntOrFloat
    some_int : Int32
    some_float : Float64
  end
end

value = U::IntOrFloat.new

value = uninitialized U::IntOrFlaot
value.some_int # => some garbage value

value = U::IntOrFloat.new
value.some_int = 1
value.some_int   # => 1
value.some_float # => 4.94066e-324

def change_it(value)
  value.some_int = 1
end

value = U::IntOrFloat.new
change_it value
value.some_int # => 0

lib C
  # In C:
  #
  #  struct TimeZone {
  #    int minutes_west;
  #    int dst_time;
  #  };
  struct TimeZone
    minutes_west : Int32
    dst_time : Int32
  end
end

lib C
  # This is a forward declaration
  struct Node
  end

  struct Node
    node : Node*
  end
end

tz = C::TimeZone.new

tz = uninitialized C::TimeZone
tz.minutes_west # => some garbage value

tz = C::TimeZone.new
tz.minutes_west = 1
tz.minutes_west # => 1

tz = C::TimeZone.new minutes_west: 1, dst_time: 2
tz.minutes_west # => 1
tz.dst_time     # => 2

def change_it(tz)
  tz.minutes_west = 1
end

tz = C::TimeZone.new
change_it tz
tz.minutes_west # => 0

lib C
  $errno : Int32
end

C.errno # => some value
C.errno = 0
C.errno # => 0

lib C
  @[ThreadLocal]
  $errno : Int32
end

lib C
  fun waitpid(pid : Int32, status_ptr : Int32*, options : Int32) : Int32
end

status_ptr = uninitialized Int32

C.waitpid(pid, pointerof(status_ptr), options)

C.waitpid(pid, out status_ptr, options)

lib X
  type CInt = Int32
end

{% if flag?(:x86_64) %}
  # some specific code for 64 bits platforms
{% else %}
  # some specific code for non-64 bits platforms
{% end %}

{% if flag?(:linux) && flag?(:x86_64) %}
  # some specific code for linux 64 bits
{% end %}

lib C
  {% if flag?(:linux) && flag?(:x86_64) %}
    struct SomeStruct
      some_field : Int32
    end
  {% else %}
    struct SomeStruct
      some_field : Int64
    end
  {% end %}
end

# Assigns to a local variable
local = 1

# Assigns to a global property
class Global
  class_property global1 = 1
  class_getter global2 = 2
  class_setter global3 = 3

  # Fails on nil
  class_property! global4 = 4
  class_getter! global5 = 5
  class_setter! global6 = 6
end

class Testing
  # Assigns to an instance variable
  @instance = 2

  # Assigns to a class variable
  @@class = 3
end

local += 1 # same as: local = local + 1

# The above is valid with these operators:
# +, -, *, /, %, |, &, ^, **, <<, >>

local ||= 1 # same as: local || (local = 1)
local &&= 1 # same as: local && (local = 1)

# A setter
person.name=("John")

# The above can be written as:
person.name = "John"

# An indexed assignment
objects.[]=(2, 3)

# The above can be written as:
objects[2] = 3

# Not assignment-related, but also syntax sugar:
objects.[](2, 3)

# The above can be written as:
objects[2, 3]

person.age += 1 # same as: person.age = person.age + 1

person.name ||= "John" # same as: person.name || (person.name = "John")
person.name &&= "John" # same as: person.name && (person.name = "John")

objects[1] += 2 # same as: objects[1] = objects[1] + 2

objects[1] ||= 2 # same as: objects[1]? || (objects[1] = 2)
objects[1] &&= 2 # same as: objects[1]? && (objects[1] = 2)

alias PInt32 = Pointer(Int32)

ptr = PInt32.malloc(1) # : Pointer(Int32)

alias RecArray = Array(Int32) | Array(RecArray)

ary = [] of RecArray
ary.push [1, 2, 3]
ary.push ary
ary # => [[1, 2, 3], [...]]

module Json
  alias Type = Nil |
               Bool |
               Int64 |
               Float64 |
               String |
               Array(Type) |
               Hash(String, Type)
end

a = 1
if a > 0
  a = 10
end
a # => 10

b = 1
if b > 2
  b = 10
else
  b = 20
end
b # => 20

if some_condition
  do_something
elsif some_other_condition
  do_something_else
else
  do_that
end

a = 1
if some_condition
  a = "hello"
else
  a = true
end
# a : String | Bool

b = 1
if some_condition
  b = "hello"
end
# b : Int32 | String

if some_condition
  c = 1
else
  c = "hello"
end
# c : Int32 | String

if some_condition
  d = 1
end
# d : Int32 | Nil

a = 1
if some_condition
  a = "hello"
  # a : String
  a.size
end
# a : String | Int32

if some_condition
  e = 1
else
  e = "hello"
  # e : String
  return
end
# e : Int32

enum Color : UInt8
  Red        # 0
  Green      # 1
  Blue   = 5 # overwritten to 5
  Yellow     # 6 (5 + 1)

  def red?
    self == Color::Red
  end
end

Color::Red.value # :: UInt8

@[Flags]
enum IOMode
  Read  # 1
  Write # 2
  Async # 4
end

IOMode::None.value # => 0
IOMode::All.value  # => 7

puts(Color::Red)                    # prints "Red"
puts(IOMode::Write | IOMode::Async) # prints "Write, Async"

puts Color.new(1) # => prints "Green"

puts Color.new(10) # => prints "10"

Color::Red.red?  # => true
Color::Blue.red? # => false

def paint(color : Color)
  case color
  when Color::Red
    # ...
  else
    # Unusual, but still can happen
    raise "unknown color: #{color}"
  end
end

paint Color::Red

def paint(color : Symbol)
  case color
  when :red
    # ...
  else
    raise "unknown color: #{color}"
  end
end

paint :red

name = "Crystal"
age = 1

flower = "Tulip"
# At this point 'flower' is a String

flower = 1

# At this point 'flower' is an Int32

class Foo
  def finalize
    # Invoked when Foo is garbage-collected
    puts "Bye bye from #{self}!"
  end
end

# Prints "Bye bye ...!" for ever
loop do
  Foo.new
end

# Defines a method in the program
def add(x, y)
  x + y
end

# Invokes the add method in the program
add(1, 2) # => 3

def even?(num)
  if num % 2 == 0
    return true
  end

  return false
end

def add(x, y)
  x + y
end

class Foo
  def bar
    # invokes the program's add method
    add(1, 2)

    # invokes Foo's baz method
    baz(1, 2)
  end

  def baz(x, y)
    x * y
  end
end

def baz(x, y)
  x + y
end

class Foo
  def bar
    baz(4, 2)   # => 2
    ::baz(4, 2) # => 6
  end

  def baz(x, y)
    x - y
  end
end

x = 1

def add(y)
  x + y # error: undefined local variable or method 'x'
end

add(2)

add 1, 2 # same as add(1, 2)

class Counter
  @@instances = 0

  def initialize
    @@instances += 1
  end

  def self.instances
    @@instances
  end
end

Counter.instances # => 0
Counter.new
Counter.new
Counter.new
Counter.instances # => 3

class Counter
  def self.increment
    @@instances += 1
  end
end

Counter.increment # Error: undefined method '+' for Nil

class Parent
  @@counter = 0
end

class Child < Parent
  def self.counter
    @@counter
  end
end

Child.counter # => nil

unless some_condition
  then_expression
else
  else_expression
end

# Can also be written as a suffix
close_door unless door_closed?

a = 1
b = typeof(a) # => Int32

typeof(1, "a", 'a') # => (Int32 | String | Char)

hash = {} of Int32 => String
another_hash = typeof(hash).new # :: Hash(Int32, String)

class Array
  def self.elem_type(typ)
    if typ.is_a?(Array)
      elem_type(typ.first)
    else
      typ
    end
  end
end

nest = [1, ["b", [:c, ['d']]]]
flat = Array(typeof(Array.elem_type(nest))).new
typeof(nest) # => Array(Int32 | Array(String | Array(Symbol | Array(Char))))
typeof(flat) # => Array(String | Int32 | Symbol | Char)

a = 2 if some_condition

x = 0
proc = ->{ x += 1; x }
proc.call # => 1
proc.call # => 2
x         # => 2

def counter
  x = 0
  ->{ x += 1; x }
end

proc = counter
proc.call # => 1
proc.call # => 2

def foo
  yield
end

x = 1
foo do
  x = "hello"
end
x # : Int32 | String

x = 1
foo do
  x = "hello"
end
x # : Int32 | String

x = 'a'
x # : Char

def capture(&block)
  block
end

x = 1
capture { x = "hello" }

x = 'a'
x # : Int32 | String | Char

def capture(&block)
  block
end

x = 1
->{ x = "hello" }

x = 'a'
x # : Int32 | String | Char

abstract class Animal
  # Makes this animal talk
  abstract def talk
end

class Dog < Animal
  def talk
    "Woof!"
  end
end

class Cat < Animal
  def talk
    "Miau"
  end
end

class Person
  getter pet

  def initialize(@name, @pet)
  end
end

john = Person.new "John", Dog.new
peter = Person.new "Peter", Cat.new

john.pet.talk # => "Woof!"

a = 1 > 2 ? 3 : 4

# The above is the same as:
a = if 1 > 2
      3
    else
      4
    end

def some_method : String
  "hello"
end

PI = 3.14

module Earth
  RADIUS = 6_371_000
end

PI            # => 3.14
Earth::RADIUS # => 6_371_000

TEN = begin
  a = 0
  while a < 10
    a += 1
  end
  a
end

TEN # => 10

class Person
  getter name

  def initialize(@name)
    @age = 0
  end
end

john = Person.new "John"
john.name      # => "John"
john.name.size # => 4

one = Person.new 1
one.name     # => 1
one.name + 2 # => 3

john = Person.new "John"
one = Person.new 1

john = Person.new "John"
one = Person.new 1

# Error: undefined method 'size' for Int32
john.name.size

# Error: no overload matches 'String#+' with types Int32
john.name + 3

john = Person.new "John"
john.name.size
one = Person.new 1

class Person
  getter name

  def initialize(@name)
    @age = 0
  end

  def address
    @address
  end

  def address=(@address)
  end
end

john = Person.new "John"
john.address = "Argentina"

# Error: undefined method 'size' for Nil
john.address.size

class Person
  @age = 0

  def initialize(@name)
  end
end

class Person
  @age : Int32

  def initialize(@name)
    @age = 0
  end
end

a = if 2 > 1
      3
    else
      4
    end
a # => 3

if 1 > 2
else
  3
end

def twice(&block)
  yield
  yield
end

twice() do
  puts "Hello!"
end

twice do
  puts "Hello!"
end

twice { puts "Hello!" }

def twice
  yield 1
  yield 2
end

twice do |i|
  puts "Got #{i}"
end

twice { |i| puts "Got #{i}" }

def many
  yield 1, 2, 3
end

many do |x, y, z|
  puts x + y + z
end

# Output: 6

def many
  yield 1, 2, 3
end

many do |x, y|
  puts x + y
end

# Output: 3

def twice
  yield
  yield
end

twice do |i|
  puts i.inspect
end

def some
  yield 1, 'a'
  yield true, "hello"
  yield 2
end

some do |first, second|
  # first is Int32 | Bool
  # second is Char | String | Nil
end

method do |argument|
  argument.some_method
end

method(&.some_method)

method &.some_method(arg1, arg2)

method &.+(2)
method &.[index]

def twice
  v1 = yield 1
  puts v1

  v2 = yield 2
  puts v2
end

twice do |i|
  i + 1
end

ary = [1, 2, 3]
ary.map { |x| x + 1 }         # => [2, 3, 4]
ary.select { |x| x % 2 == 1 } # => [1, 3]

def transform(value)
  yield value
end

transform(1) { |x| x + 1 } # => 2

def thrice
  puts "Before 1"
  yield 1
  puts "Before 2"
  yield 2
  puts "Before 3"
  yield 3
  puts "After 3"
end

thrice do |i|
  if i == 2
    break
  end
end

def twice
  yield 1
  yield 2
end

twice { |i| i + 1 }         # => 3
twice { |i| break "hello" } # => "hello"

value = twice do |i|
  if i == 1
    break "hello"
  end
  i + 1
end
value # :: Int32 | String

values = twice { break 1, 2 }
values # => {1, 2}

value = twice { break }
value # => nil

def twice
  yield 1
  yield 2
end

twice do |i|
  if i == 1
    puts "Skipping 1"
    next
  end

  puts "Got #{i}"
end

def twice
  v1 = yield 1
  puts v1

  v2 = yield 2
  puts v2
end

twice do |i|
  if i == 1
    next 10
  end

  i + 1
end

# Output
# 10
# 3

class Foo
  def one
    1
  end

  def yield_with_self
    with self yield
  end

  def yield_normally
    yield
  end
end

def one
  "one"
end

Foo.new.yield_with_self { one } # => 1
Foo.new.yield_normally { one }  # => "one"

def twice
  yield 1
  yield 2
end

twice do |i|
  puts "Got: #{i}"
end

i = 1
puts "Got: #{i}"
i = 2
puts "Got: #{i}"

3.times do |i|
  puts i
end

struct Int
  def times
    i = 0
    while i < self
      yield i
      i += 1
    end
  end
end

i = 0
while i < 3
  puts i
  i += 1
end

class Person
  def initialize(@name)
  end

  def greet
    puts "Hi, I'm #{@name}"
  end
end

class Employee < Person
end

employee = Employee.new "John"
employee.greet # "Hi, I'm John"

class Person
  def initialize(@name)
  end
end

class Employee < Person
  def initialize(@name, @company_name)
  end
end

Employee.new "John", "Acme" # OK
Employee.new "Peter"        # Error: wrong number of arguments
# for 'Employee:Class#new' (1 for 2)

class Person
  def greet(msg)
    puts "Hi, #{msg}"
  end
end

class Employee < Person
  def greet(msg)
    puts "Hello, #{msg}"
  end
end

p = Person.new
p.greet "everyone" # "Hi, everyone"

e = Employee.new
e.greet "everyone" # "Hello, everyone"

class Person
  def greet(msg)
    puts "Hi, #{msg}"
  end
end

class Employee < Person
  def greet(msg : Int32)
    puts "Hi, this is a number: #{msg}"
  end
end

e = Employee.new
e.greet "everyone" # "Hi, everyone"

e.greet 1 # "Hi, this is a number: 1"

class Person
  def greet(msg)
    puts "Hello, " # {msg}"
  end
end

class Employee < Person
  def greet(msg)
    super # Same as: super(msg)
    super("another message")
  end
end

def int_to_int(&block : Int32 -> Int32)
  block
end

proc = int_to_int { |x| x + 1 }
proc.call(1) # => 2

class Model
  def on_save(&block)
    @on_save_callback = block
  end

  def save
    if callback = @on_save_callback
      callback.call
    end
  end
end

model = Model.new
model.on_save { puts "Saved!" }
model.save # prints "Saved!"

def some_proc(&block : Int32 ->)
  block
end

proc = some_proc { |x| x + 1 }
proc.call(1) # void

def some_proc(&block : Int32 -> _)
  block
end

proc = some_proc { |x| x + 1 }
proc.call(1) # 2

proc = some_proc { |x| x.to_s }
proc.call(1) # "1"

macro update_x
  x = 1
end

x = 0
update_x
x # => 1

macro dont_update_x
  %x = 1
  puts %x
end

x = 0
dont_update_x # outputs 1
x             # => 0

macro fresh_vars_sample(*names)
  # First declare vars
  {% for name, index in names %}
    print "Declaring: ", "%name{index}", '\n'
    %name{index} = {{index}}
  {% end %}

  # Then print them
  {% for name, index in names %}
    print "%name{index}: ", %name{index}, '\n'
  {% end %}
end

fresh_vars_sample a, b, c

# Sample output:
# Declaring: __temp_255
# Declaring: __temp_256
# Declaring: __temp_257
# __temp_255: 0
# __temp_256: 1
# __temp_257: 2

class Object
  macro instance_vars_names
    def instance_vars_names : Array(String)
      {{ @type.instance_vars.map &.name.stringify }}
    end
  end
end

class Person
  def initialize(@name, @age)
  end
end

person = Person.new "John", 30
person.instance_vars_names # => ["name", "age"]

class Object
  macro has_instance_var?(name)
    def has_instance_var?(name) : Bool
      # We cannot access name inside the macro expansion here,
      # instead we need to use the macro language to construct an array
      # and do the inclusion check at runtime.
      {{ @type.instance_vars.map &.name.stringify }}.includes? name
    end
  end
end

person = Person.new "John", 30
person.has_instance_var?("name")     # => true
person.has_instance_var?("birthday") # => false

class Parent
  macro inherited
    def {{@type.name.downcase.id}}
      1
    end
  end
end

class Child < Parent
end

Child.new.child # => 1

macro method_missing(name, args, block)
  print "Got ", {{name.id.stringify}}, " with ", {{args.size}}, " arguments", '\n'
end

foo          # Prints: Got foo with 0 arguments
bar 'a', 'b' # Prints: Got bar with 2 arguments

sizeof(Int32) # => 4
sizeof(Int64) # => 8

# On a 64 bits machine
sizeof(Pointer(Int32)) # => 8
sizeof(String)         # => 8

a = 1
sizeof(typeof(a)) # => 4

class Foo
  macro emphasize(value)
    "***#{ {{value}} }***"
  end

  def yield_with_self
    with self yield
  end
end

Foo.new.yield_with_self { emphasize(10) } # => "***10***"

# This generates:
#
#     def :foo
#       1
#     end
define_method :foo, 1

macro define_method(name, content)
  def {{name.id}}
    {{content}}
  end
end

# This correctly generates:
#
#     def foo
#       1
#     end
define_method :foo, 1

macro define_method(name, content)
  def {{name}}
    {% if content == 1 %}
      "one"
    {% else %}
      {{content}}
    {% end %}
  end
end

define_method foo, 1
define_method bar, 2

foo # => one
bar # => 2

{% if env("TEST") %}
  puts "We are in test mode"
{% end %}

macro define_dummy_methods(names)
  {% for name, index in names %}
    def {{name.id}}
      {{index}}
    end
  {% end %}
end

define_dummy_methods [foo, bar, baz]

foo # => 0
bar # => 1
baz # => 2

macro define_dummy_methods(hash)
  {% for key, value in hash %}
    def {{key.id}}
      {{value}}
    end
  {% end %}
end

define_dummy_methods({foo: 10, bar: 20})
foo # => 10
bar # => 20

{% for name, index in ["foo", "bar", "baz"] %}
  def {{name.id}}
    {{index}}
  end
{% end %}

foo # => 0
bar # => 1
baz # => 2

macro define_dummy_methods(*names)
  {% for name, index in names %}
    def {{name.id}}
      {{index}}
    end
  {% end %}
end

define_dummy_methods foo, bar, baz

foo # => 0
bar # => 1
baz # => 2

macro println(*values)
   print {{*values}}, '\n'
end

println 1, 2, 3 # outputs 123\n

VALUES = [1, 2, 3]

{% for value in VALUES %}
  puts {{value}}
{% end %}

until some_condition
  do_this
end

# The above is the same as:
while !some_condition
  do_this
end

a = some_condition ? nil : 3
# a is Int32 or Nil

if a
  # Since the only way to get here is if a is truthy,
  # a can't be nil. So here a is Int32.
  a.abs
end

if a = some_expression
  # here a is not nil
end

if a && b
  # here both a and b are guaranteed not to be Nil
end

if @a
  # here @a can be nil
end

# First option: assign it to a variable
if a = @a
  # here a can't be nil
end

# Second option: use `Object#try` found in the standard library
@a.try do |a|
  # here a can't be nil
end

if method # first call to a method that can return Int32 or Nil
  # here we know that the first call did not return Nil
  method # second call can still return Int32 or Nil
end

class Person
  def become_older(by = 1)
    @age += by
  end
end

john = Person.new "John"
john.age # => 0

john.become_older
john.age # => 1

john.become_older 2
john.age # => 3

john.become_older by: 5

def some_method(x, y = 1, z = 2, w = 3)
  # do something...
end

some_method 10                   # x = 10, y = 1, z = 2, w = 3
some_method 10, z: 10            # x = 10, y = 1, z = 10, w = 3
some_method 10, w: 1, y: 2, z: 3 # x = 10, y = 2, z = 3, w = 1

case exp
when value1, value2
  do_something
when value3
  do_something_else
else
  do_another_thing
end

case var
when String
  # var : String
  do_something
when Int32
  # var : Int32
  do_something_else
else
  # here var is neither a String nor an Int32
  do_another_thing
end

case num
when .even?
  do_something
when .odd?
  do_something_else
end

case
when cond1, cond2
  do_something
when cond3
  do_something_else
end

a = 1
a.responds_to?(:abs)  # => true
a.responds_to?(:size) # => false

foo_or_bar = /foo|bar/
heeello = /h(e+)llo/
integer = /\d+/

r = /foo/imx

slash = /\//

r = %r(regex with slash: /)

"hello world"

"\"" # double quote
"\\" # backslash
"\e" # escape
"\f" # form feed
"\n" # newline
"\r" # carriage return
"\t" # tab
"\v" # vertical tab

"\101" # == "A"
"\123" # == "S"
"\12"  # == "\n"
"\1"   # string with one character with code point 1

"\u0041" # == "A"

"\u{41}"    # == "A"
"\u{1F52E}" # == "🔮"

"hello
      world" # same as "hello\n      world"

"hello " \
"world, " \
"no newlines" # same as "hello world, no newlines"

"hello \
     world, \
     no newlines" # same as "hello world, no newlines"

# Supports double quotes and nested parenthesis
%(hello ("world")) # same as "hello (\"world\")"

# Supports double quotes and nested brackets
%[hello ["world"]] # same as "hello [\"world\"]"

# Supports double quotes and nested curlies
%{hello {"world"}} # same as "hello {\"world\"}"

# Supports double quotes and nested angles
%<hello <"world">> # same as "hello <\"world\">"

<<-XML
<parent>
  <child />
</parent>
XML

# Same as "Hello\n  world"
<<-STRING
  Hello
    world
  STRING

# Same as "  Hello\n    world"
<<-STRING
    Hello
      world
  STRING

a = 1
b = 2
"sum = #{a + b}" # "sum = 3"

1.0     # Float64
1.0_f32 # Float32
1_f32   # Float32

1e10   # Float64
1.5e10 # Float64
1.5e-7 # Float64

+1.3 # Float64
-0.5 # Float64

1_000_000.111_111 # better than 1000000.111111

'a'
'z'
'0'
'_'
"ã‚"

'\'' # single quote
'\\' # backslash
'\e' # escape
'\f' # form feed
'\n' # newline
'\r' # carriage return
'\t' # tab
'\v' # vertical tab

"\101" # == 'A'
"\123" # == 'S'
"\12"  # == '\n'
"\1"   # code point 1

'\u0041' # == 'A'

'\u{41}'    # == 'A'
'\u{1F52E}' # == '🔮'

{1 => 2, 3 => 4}   # Hash(Int32, Int32)
{1 => 2, 'a' => 3} # Hash(Int32 | Char, Int32)

{} of Int32 => Int32 # same as Hash(Int32, Int32).new

{key1: 'a', key2: 'b'} # Hash(Symbol, Char)

{"key1": 'a', "key2": 'b'} # Hash(String, Char)

MyType{"foo" => "bar"}

tmp = MyType.new
tmp["foo"] = "bar"
tmp

tmp = MyType(typeof("foo"), typeof("bar")).new
tmp["foo"] = "bar"
tmp

MyType(String, String){"foo" => "bar"}

:hello
:good_bye

# With spaces and symbols
:"symbol with spaces"

# Ending with question and exclamation marks
:question?
:exclamation!

# For the operators
:+
:-
:*
:/
:==
:<
:<=
:>
:>=
:!
:!=
:=~
:!~
:&
:|
:^
:~
:**
:>>
:<<
:%
:[]
:[]?
:[]=
:<=>
:===

x..y  # an inclusive range, in mathematics: [x, y]
x...y # an exclusive range, in mathematics: [x, y)

# A proc without arguments
->{ 1 } # Proc(Int32)

# A proc with one argument
->(x : Int32) { x.to_s } # Proc(Int32, String)

# A proc with two arguments:
->(x : Int32, y : Int32) { x + y } # Proc(Int32, Int32, Int32)

Proc(Int32, String).new { |x| x.to_s } # Proc(Int32, String)

proc = ->(x : Int32, y : Int32) { x + y }
proc.call(1, 2) # => 3

def one
  1
end

proc = ->one
proc.call # => 1

def plus_one(x)
  x + 1
end

proc = ->plus_one(Int32)
proc.call(41) # => 42

str = "hello"
proc = ->str.count(Char)
proc.call('e') # => 1
proc.call('l') # => 2

tuple = {1, "hello", 'x'} # Tuple(Int32, String, Char)
tuple[0]                  # => 1       (Int32)
tuple[1]                  # => "hello" (String)
tuple[2]                  # => 'x'     (Char)

[1, 2, 3]         # Array(Int32)
[1, "hello", 'x'] # Array(Int32 | String | Char)

[] of Int32 # same as Array(Int32).new

%w(one two three) # ["one", "two", "three"]

%i(one two three) # [:one, :two, :three]

MyType{1, 2, 3}

tmp = MyType.new
tmp << 1
tmp << 2
tmp << 3
tmp

tmp = MyType(typeof(1, 2, 3)).new
tmp << 1
tmp << 2
tmp << 3
tmp

MyType(Int32 | String){1, 2, "foo"}

nil

1 # Int32

1_i8  # Int8
1_i16 # Int16
1_i32 # Int32
1_i64 # Int64

1_u8  # UInt8
1_u16 # UInt16
1_u32 # UInt32
1_u64 # UInt64

+10 # Int32
-20 # Int32

2147483648          # Int64
9223372036854775808 # UInt64

1_000_000 # better than 1000000

0b1101 # == 13

0o123 # == 83

0xFE012D # == 16646445
0xfe012d # == 16646445

true  # A Bool that is true
false # A Bool that is false

a = 1

ptr = pointerof(a)
ptr.value = 2

a # => 2

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

  def x
    @x
  end

  def x_ptr
    pointerof(@x)
  end
end

point = Point.new 1, 2

ptr = point.x_ptr
ptr.value = 10

point.x # => 10

def add(x : Number, y : Number)
  x + y
end

# Ok
add 1, 2 # Ok

# Error: no overload matches 'add' with types Bool, Bool
add true, false

def add(x, y)
  x + y
end

add true, false

# A class that has a + method but isn't a Number
class Six
  def +(other)
    6 + other
  end
end

# add method without type restrictions
def add(x, y)
  x + y
end

# OK
add Six.new, 10

# add method with type restrictions
def restricted_add(x : Number, y : Number)
  x + y
end

# Error: no overload matches 'restricted_add' with types Six, Int32
restricted_add Six.new, 10

class Person
  def ==(other : self)
    other.name == name
  end

  def ==(other)
    false
  end
end

john = Person.new "John"
another_john = Person.new "John"
peter = Person.new "Peter"

john == another_john # => true
john == peter        # => false (names differ)
john == 1            # => false (because 1 is not a Person)

class Person
  def self.compare(p1 : self, p2 : self)
    p1.name == p2.name
  end
end

john = Person.new "John"
peter = Person.new "Peter"

Person.compare(john, peter) # OK

def foo(x : Int32)
end

foo 1       # OK
foo "hello" # Error

def foo(x : Int32.class)
end

foo Int32  # OK
foo String # Error

def foo(x : Int32.class)
  puts "Got Int32"
end

def foo(x : String.class)
  puts "Got String"
end

foo Int32  # prints "Got Int32"
foo String # prints "Got String"

def foo(*args : Int32)
end

def foo(*args : String)
end

foo 1, 2, 3       # OK, invokes first overload
foo "a", "b", "c" # OK, invokes second overload
foo 1, 2, "hello" # Error
foo()             # Error

def foo
  # This is the empty-tuple case
end

def foo(x : T)
  T
end

foo(1)       # => Int32
foo("hello") # => String

def foo(x : Array(T))
  T
end

foo([1, 2])   # => Int32
foo([1, "a"]) # => (Int32 | String)

def foo(x : T.class)
  Array(T)
end

foo(Int32)  # => Array(Int32)
foo(String) # => Array(String)

class Person
  # Increases age by one
  def become_older
    @age += 1
  end

  # Increases age by the given number of years
  def become_older(years : Int32)
    @age += years
  end

  # Increases age by the given number of years, as a String
  def become_older(years : String)
    @age += years.to_i
  end

  # Yields the current age of this person and increases
  # its age by the value returned by the block
  def become_older
    @age += yield @age
  end
end

person = Person.new "John"

person.become_older
person.age # => 1

person.become_older 5
person.age # => 6

person.become_older "12"
person.age # => 18

person.become_older do |current_age|
  current_age < 20 ? 10 : 30
end
person.age # => 28

a = 1
a.is_a?(Int32)          # => true
a.is_a?(String)         # => false
a.is_a?(Number)         # => true
a.is_a?(Int32 | String) # => true

# One for each thread
@[ThreadLocal]
values = [] of Int32

@[AlwaysInline]
def foo
  1
end

@[NoInline]
def foo
  1
end

lib LibFoo
  @[CallConvention("X86_StdCall")]
  fun foo : Int32
end

def sum(*elements)
  total = 0
  elements.each do |value|
    total += value
  end
  total
end

# elements is Tuple(Int32, Int32, Int32, Float64)
sum 1, 2, 3, 4.5

if a.responds_to?(:abs)
  # here a's type will be reduced to those responding to the 'abs' method
end

a = some_condition ? 1 : "hello"
# a : Int32 | String

if a.responds_to?(:abs)
  # here a will be Int32, since Int32#abs exists but String#abs doesn't
else
  # here a will be String
end

if (a = @a).responds_to?(:abs)
  # here a is guaranteed to respond to `abs`
end

def capture(&block)
  block
end

def invoke(&block)
  block.call
end

proc = capture { puts "Hello" }
invoke(&proc) # prints "Hello"

def capture(&block)
  block
end

def twice
  yield
  yield
end

proc = capture { puts "Hello" }
twice &proc

twice &->{ puts "Hello" }

def say_hello
  puts "Hello"
end

twice &->say_hello

def foo
  yield 1
end

def wrap_foo
  puts "Before foo"
  foo do |x|
    yield x
  end
  puts "After foo"
end

wrap_foo do |i|
  puts i
end

def foo
  yield 1
end

def wrap_foo(&block : Int32 -> _)
  puts "Before foo"
  foo(&block)
  puts "After foo"
end

wrap_foo do |i|
  puts i
end

foo_forward do |i|
  break # error
end

a = 2
while (a += 1) < 20
  if a == 10
    # goes to 'puts a'
    break
  end
end
puts a # => 10

class Person
  private def say(message)
    puts message
  end

  def say_hello
    say "hello"      # OK, no receiver
    self.say "hello" # Error, self is a receiver

    other = Person.new "Other"
    other.say "hello" # Error, other is a receiver
  end
end

class Employee < Person
  def say_bye
    say "bye" # OK
  end
end

module Namespace
  class Foo
    protected def foo
      puts "Hello"
    end
  end

  class Bar
    def bar
      # Works, because Foo and Bar are under Namespace
      Foo.new.foo
    end
  end
end

Namespace::Bar.new.bar

class Person
  protected def self.say(message)
    puts message
  end

  def say_hello
    Person.say "hello"
  end
end

buffer = uninitialized UInt8[256]

foo = rand(5) > 1 ? 1 : nil

foo.not_nil!.to_i
