#! home/.arturo/bin

; this is a comment
; this is another comment

;---------------------------------
; VARIABLES & VALUES
;---------------------------------

; numbers
a1: 2
a2: 3.14
a3: to :complex [1 2.0]     ; 1.0+2.0i

; strings
c1: "this is a string"
c2: {
    this is a multiline string
    that is indentation-agnostic
}

c3: {:
    this is
        a verbatim
            multiline string
                which will remain exactly
                    as the original
:}

c4: {
    this is a multiline string
    with |a1| lines.
}

annotatedStrings: {!md
    # This is a markdown example

    I'm a paragraph

    > I'm a blockquote

    ```c
    #include <stdio.h>
    int main() {
        char * name = "World";
        printf("Hello, %s!", name);
        printf( "I'm a highlighted C code"
                "inside Markdown highlighted code,"
                "inside Arturo. Awesome!"
            );
        return 0;
    }
    ```
}

smartString: »I'm a string until the end of line    ; I'm not a comment
safeString: «««I'm a safe string. 🙂»»»             ; I'm a comment

; characters
ch: `c`

; blocks/arrays
d: [1 2 3]

; dictionaries
e: #[
    name: "John"
    surname: "Doe"
    age: 34
    likes: [pizza spaghetti]
]

; yes, functions are values too
f: function [x][
    2 * x
]

; colors - right, you can directly define them as well!
g1: #red
g2: #0077BF

; dates
h: now              ; 2021-05-03T17:10:48+02:00

; logical values
i1: true
i2: false
i3: maybe

;---------------------------------
; BASIC OPERATORS
;---------------------------------

; simple arithmetic
1 + 1       ; => 2
8 - 1       ; => 7
4.2 - 1.1   ; => 3.1
10 * 2      ; => 20
35 / 4      ; => 8
35 // 4     ; => 8.75
2 ^ 5       ; => 32
5 % 3       ; => 2

; bitwise operators
and 3 5     ; => 1
or 3 5      ; => 7
xor 3 5     ; => 6

; pre-defined constants
pi          ; => 3.141592653589793
epsilon     ; => 2.718281828459045
null        ; => null
true        ; => true
false       ; => false

;---------------------------------
; COMPARISON OPERATORS
;---------------------------------

; equality
1 = 1       ; => true
2 = 1       ; => false

; inequality
1 <> 1      ; => false
2 <> 1      ; => true

; more comparisons
1 < 10      ; => true
1 =< 10     ; => true
10 =< 10    ; => true
1 > 10      ; => false
1 >= 10     ; => false
11 >= 10    ; => true

;---------------------------------
; CONDITIONALS
;---------------------------------

; logical operators
and? true true      ; => true
and? true false     ; => false
or? true false      ; => true
or? false false     ; => false

and? [1=2][2<3]     ; => false
                    ; (the second block will not be evaluated)

; simple if statements
if 2 > 1 [ print "yes!"]    ; yes!
if 3 <> 2 -> print "true!"  ; true!

; if/else statements
if? 2 > 3 -> print "2 is greater than 3"
else -> print "2 is not greater than 3"         ; 2 is not greater than 3

; switch statements
switch 2 > 3 -> print "2 is greater than 3"
             -> print "2 is not greater than 3" ; 2 is not greater than 3

a: (2 > 3)["yes"]["no"]         ; a: "no"
a: (2 > 3)? -> "yes" -> "no"    ; a: "no" (exactly the same as above)

; case/when statements
case [1]
    when? [>2] -> print "1 is greater than 2. what?!"
    when? [<0] -> print "1 is less than 0. nope..."
    else -> print "here we are!"                ; here we are!

;---------------------------------
; LOOPS
;---------------------------------

; with `loop`
arr: [1 4 5 3]
loop arr 'x [
    print ["x =" x]
]
; x = 1
; x = 4
; x = 5
; x = 3

; with loop and custom index
loop.with:'i arr 'x [
    print ["item at position" i "=>" x]
]
; item at position 0 => 1
; item at position 1 => 4
; item at position 2 => 5
; item at position 3 => 3

; using ranges
loop 1..3 'x ->         ; since it's a single statement
    print x             ; there's no need for [block] notation
                        ; we can wrap it up using the `->` syntactic sugar

loop `a`..`c` 'ch ->
    print ch
; a
; b
; c

; picking multiple items
loop 1..10 [x y] ->
    print ["x =" x ", y =" y]
; x = 1 , y = 2
; x = 3 , y = 4
; x = 5 , y = 6
; x = 7 , y = 8
; x = 9 , y = 10

; looping through a dictionary
dict: #[name: "John", surname: "Doe", age: 34]
loop dict [key value][
    print [key "->" value]
]
; name -> John
; surname -> Doe
; age -> 34

; while loops
i: new 0
while [i<3][
    print ["i =" i]
    inc 'i
]
; i = 0
; i = 1
; i = 2

;---------------------------------
; STRINGS
;---------------------------------

; case
a: "tHis Is a stRinG"
print upper a               ; THIS IS A STRING
print lower a               ; this is a string
print capitalize a          ; THis Is a stRinG

; concatenation
a: "Hello " ++ "World!"     ; a: "Hello World!"

; strings as an array
split "hello"               ; => [h e l l o]
split.words "hello world"   ; => [hello world]

print first "hello"         ; h
print last "hello"          ; o

; conversion
to :string 123              ; => "123"
to :integer "123"           ; => 123

; joining strings together
join ["hello" "world"]              ; => "helloworld"
join.with:"-" ["hello" "world"]     ; => "hello-world"

; string interpolation
x: 2
print ~"x = |x|"            ; x = 2

; interpolation with `print`
print ["x =" x]             ; x = 2
                            ; (`print` works by calculating the given block
                            ;  and joining the different values as strings
                            ;  with a single space between them)

; templates
print render.template {
    <||= switch x=2 [ ||>
        Yes, x = 2
    <||][||>
        No, x is not 2
    <||]||>
} ; Yes, x = 2

; matching
prefix? "hello" "he"        ; => true
suffix? "hello" "he"        ; => false

contains? "hello" "ll"      ; => true
contains? "hello" "he"      ; => true
contains? "hello" "x"       ; => false

in? "ll" "hello"            ; => true
in? "x" "hello"             ; => false

;---------------------------------
; BLOCKS
;---------------------------------

; calculate a block
arr: [1 1+1 1+1+1]
@arr                        ; => [1 2 3]

; execute a block
sth: [print "Hello world"]  ; this is perfectly valid,
                            ; could contain *anything*
                            ; and will not be executed...

do sth                      ; Hello world
                            ; (...until we tell it to)

; array indexing
arr: ["zero" "one" "two" "three"]
print first arr             ; zero
print arr\0                 ; zero
print last arr              ; three
print arr\3                 ; three

x: 2
print get arr x             ; two
print arr\[x]               ; two

; setting an array element
arr\0: "nada"
set arr 2 "dos"
print arr                   ; nada one dos three

; adding elements to an array
arr: new []
'arr ++ "one"
'arr ++ "two"
print arr                   ; one two

; remove elements from an array
arr: new ["one" "two" "three" "four"]
'arr -- "two"               ; arr: ["one" "three" "four"]
remove 'arr .index 0        ; arr: ["three" "four"]

; getting the size of an array
arr: ["one" 2 "three" 4]
print size arr              ; 4

; getting a slice of an array
print slice ["one" "two" "three" "four"] 0 1        ; one two

; check if array contains a specific element
print contains? arr "one"   ; true
print contains? arr "five"  ; false

; sorting array
arr: [1 5 3 2 4]
sort arr                    ; => [1 2 3 4 5]
sort.descending arr         ; => [5 4 3 2 1]

; mapping values
map 1..10 [x][2*x]          ; => [2 4 6 8 10 12 14 16 18 20]
map 1..10 'x -> 2*x         ; same as above
map 1..10 => [2*&]          ; same as above
map 1..10 => [2*]           ; same as above

; selecting/filtering array values
select 1..10 [x][odd? x]    ; => [1 3 5 7 9]
select 1..10 => odd?        ; same as above

filter 1..10 => odd?        ; => [2 4 6 8 10]
                            ; (now, we leave out all odd numbers -
                            ;  while select keeps them)

; misc operations
arr: ["one" 2 "three" 4]
reverse arr                 ; => [4 "three" 2 "one"]
shuffle arr                 ; => [2 4 "three" "one"]
unique [1 2 3 2 3 1]        ; => [1 2 3]
permutate [1 2 3]           ; => [[1 2 3] [1 3 2] [3 1 2] [2 1 3] [2 3 1] [3 2 1]]
take 1..10 3                ; => [1 2 3]
repeat [1 2] 3              ; => [1 2 1 2 1 2]

;---------------------------------
; FUNCTIONS
;---------------------------------

; declaring a function
f: function [x][ 2*x ]
f: function [x]-> 2*x       ; same as above
f: $[x]->2*x                ; same as above (only using the `$` alias
                            ;  for the `function`... function)

; calling a function
f 10                        ; => 20

; returning a value
g: function [x][
    if x < 2 -> return 0

    res: 0
    loop 0..x 'z [
        res: res + z
    ]
    return res
]

;---------------------------------
; CUSTOM TYPES
;---------------------------------

; defining a custom type
define :person [                            ; define a new custom type "Person"
    name                                    ; with fields: name, surname, age
    surname
    age
][
    ; with custom post-construction initializer
    init: [
        this\name: capitalize this\name
    ]

    ; custom print function
    print: [
        render "NAME: |this\name|, SURNAME: |this\surname|, AGE: |this\age|"
    ]

    ; custom comparison operator
    compare: 'age
]

; create a method for our custom type
sayHello: function [this][
    ensure -> is? :person this

    print ["Hello" this\name]
]

; create new objects of our custom type
a: to :person ["John" "Doe" 34]                 ; let's create 2 "Person"s
b: to :person ["jane" "Doe" 33]                 ; and another one

; call pseudo-inner method
sayHello a                                      ; Hello John
sayHello b                                      ; Hello Jane

; access object fields
print ["The first person's name is:" a\name]    ; The first person's name is: John
print ["The second person's name is:" b\name]   ; The second person's name is: Jane

; changing object fields
a\name: "Bob"
sayHello a                                      ; Hello Bob

; verifying object type
print type a                                    ; :person
print is? :person a                             ; true

; printing objects
print a                                         ; NAME: John, SURNAME: Doe, AGE: 34

; sorting user objects (using custom comparator)
sort @[a b]                                     ; Jane..., John...
sort.descending @[a b]                          ; John..., Jane...

eofString:
---
I'm an end of file
Multiline string.