diff options
Diffstat (limited to 'tests/examplefiles/test.cr')
-rw-r--r-- | tests/examplefiles/test.cr | 2871 |
1 files changed, 2871 insertions, 0 deletions
diff --git a/tests/examplefiles/test.cr b/tests/examplefiles/test.cr new file mode 100644 index 00000000..028ff6f3 --- /dev/null +++ b/tests/examplefiles/test.cr @@ -0,0 +1,2871 @@ +# Examples taken from http://crystal-lang.org/docs/ +# Copyright 2012-2016 Manas Technology Solutions. + + +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 + + +$global_greeting = "Hello world" + +class Greeting + @@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 + ifdef 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 + + +ifdef x86_64 + # some specific code for 64 bits platforms +else + # some specific code for non-64 bits platforms +end + + +ifdef linux && x86_64 + # some specific code for linux 64 bits +end + + +lib C + ifdef linux + 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 variable +$global = 4 + +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 def instance_vars_names : Array(String) + {{ @type.instance_vars.map &.name.stringify }} + end +end + +class Person + def initialize(@name, @age) + end +end + +person = Person.new "John", 30 +person.instance_vars_names #=> ["name", "age"] + + +class Object + macro 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 + +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] |