This repository has been archived on 2024-06-20. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
coffee.pygments/tests/examplefiles/cr/test.cr
Oleh Prypin 6f43092173
Also add auto-updatable output-based tests to examplefiles (#1689)
Co-authored-by: Georg Brandl <georg@python.org>
2021-01-20 10:48:45 +01:00

2551 lines
35 KiB
Crystal

# 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"
%(hello ("world")) # => "hello (\"world\")"
%[hello ["world"]] # => "hello [\"world\"]"
%{hello {"world"}} # => "hello {\"world\"}"
%<hello <"world">> # => "hello <\"world\">"
%|hello "world"| # => "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