864 lines
22 KiB
Markdown
864 lines
22 KiB
Markdown
---
|
|
gitea: none
|
|
include_toc: true
|
|
---
|
|
|
|
# Lyng tutorial
|
|
|
|
Lyng is a very simple language, where we take only most important and popular features from
|
|
other scripts and languages. In particular, we adopt _principle of minimal confusion_[^1].
|
|
In other word, the code usually works as expected when you see it. So, nothing unusual.
|
|
|
|
__Other documents to read__ maybe after this one:
|
|
|
|
- [Advanced topics](advanced_topics.md)
|
|
- [OOP notes](OOP.md)
|
|
- [math in Lyng](math.md)
|
|
- Some class references: [List], [Real], [Range], [Iterable], [Iterator]
|
|
- Some samples: [combinatorics](samples/combinatorics.lyng.md)
|
|
See [samples folder](samples)
|
|
|
|
# Expressions
|
|
|
|
Everything is an expression in Lyng. Even an empty block:
|
|
|
|
// empty block
|
|
>>> void
|
|
|
|
any block also returns it's last expression:
|
|
|
|
if( true ) {
|
|
2 + 2
|
|
3 + 3
|
|
}
|
|
>>> 6
|
|
|
|
If you don't want block to return anything, use `void`:
|
|
|
|
fn voidFunction() {
|
|
3 + 4 // this will be ignored
|
|
void
|
|
}
|
|
voidFunction()
|
|
>>> void
|
|
|
|
otherwise, last expression will be returned:
|
|
|
|
fn normalize(value, minValue, maxValue) {
|
|
(value - minValue) / (maxValue-minValue)
|
|
}
|
|
normalize( 4, 0.0, 10.0)
|
|
>>> 0.4
|
|
|
|
Every construction is an expression that returns something (or `void`):
|
|
|
|
val x = 111 // or autotest will fail!
|
|
val limited = if( x > 100 ) 100 else x
|
|
limited
|
|
>>> 100
|
|
|
|
You can use blocks in if statement, as expected:
|
|
|
|
val x = 200
|
|
val limited = if( x > 100 ) {
|
|
100 + x * 0.1
|
|
}
|
|
else
|
|
x
|
|
limited
|
|
>>> 120.0
|
|
|
|
When putting multiple statments in the same line it is convenient and recommended to use `;`:
|
|
|
|
var from; var to
|
|
from = 0; to = 100
|
|
>>> 100
|
|
|
|
Notice: returned value is `100` as assignment operator returns its assigned value.
|
|
Most often you can omit `;`, but improves readability and prevent some hardly seen bugs.
|
|
|
|
## Assignments
|
|
|
|
Assignemnt is an expression that changes its lvalue and return assigned value:
|
|
|
|
var x = 100
|
|
x = 20
|
|
println(5 + (x=6)) // 11: x changes its value!
|
|
x
|
|
>>> 11
|
|
>>> 6
|
|
|
|
As the assignment itself is an expression, you can use it in strange ways. Just remember
|
|
to use parentheses as assignment operation insofar is left-associated and will not
|
|
allow chained assignments (we might fix it later). Use parentheses insofar:
|
|
|
|
var x = 0
|
|
var y = 0
|
|
x = (y = 5)
|
|
assert(x==5)
|
|
assert(y==5)
|
|
>>> void
|
|
|
|
Note that assignment operator returns rvalue, it can't be assigned.
|
|
|
|
## Modifying arithmetics
|
|
|
|
There is a set of assigning operations: `+=`, `-=`, `*=`, `/=` and even `%=`.
|
|
|
|
var x = 5
|
|
assert( 25 == (x*=5) )
|
|
assert( 25 == x)
|
|
assert( 24 == (x-=1) )
|
|
assert( 12 == (x/=2) )
|
|
x
|
|
>>> 12
|
|
|
|
Notice the parentheses here: the assignment has low priority!
|
|
|
|
These operators return rvalue, unmodifiable.
|
|
|
|
## Assignment return r-value!
|
|
|
|
## Math
|
|
|
|
It is rather simple, like everywhere else:
|
|
|
|
val x = 2.0
|
|
sin(x * π/4) / 2.0
|
|
>>> 0.5
|
|
|
|
See [math](math.md) for more on it. Notice using Greek as identifier, all languages are allowed.
|
|
|
|
Logical operation could be used the same
|
|
|
|
var x = 10
|
|
++x >= 11
|
|
>>> true
|
|
|
|
## Supported operators
|
|
|
|
| op | ass | args | comments |
|
|
|:--------:|-----|-------------------|----------|
|
|
| + | += | Int or Real | |
|
|
| - | -= | Int or Real | infix |
|
|
| * | *= | Int or Real | |
|
|
| / | /= | Int or Real | |
|
|
| % | %= | Int or Real | |
|
|
| && | | Bool | |
|
|
| \|\| | | Bool | |
|
|
| !x | | Bool | |
|
|
| < | | String, Int, Real | (1) |
|
|
| <= | | String, Int, Real | (1) |
|
|
| >= | | String, Int, Real | (1) |
|
|
| > | | String, Int, Real | (1) |
|
|
| == | | Any | (1) |
|
|
| === | | Any | (2) |
|
|
| !== | | Any | (2) |
|
|
| != | | Any | (1) |
|
|
| ++a, a++ | | Int | |
|
|
| --a, a-- | | Int | |
|
|
|
|
(1)
|
|
: comparison are based on comparison operator which can be overloaded
|
|
|
|
(2)
|
|
: referential equality means left and right operands references exactly same instance of some object. Note that all
|
|
singleton object, like `null`, are referentially equal too, while string different literals even being equal are most
|
|
likely referentially not equal
|
|
|
|
Reference quality and object equality example:
|
|
|
|
assert( null == null) // singletons
|
|
assert( null === null)
|
|
// but, for non-singletons:
|
|
assert( 5 == 5)
|
|
assert( 5 !== 5)
|
|
assert( "foo" !== "foo" )
|
|
>>> void
|
|
|
|
# Variables
|
|
|
|
Much like in kotlin, there are _variables_:
|
|
|
|
var name = "Sergey"
|
|
|
|
Variables can be not initialized at declaration, in which case they must be assigned before use, or an exception
|
|
will be thrown:
|
|
|
|
var foo
|
|
// WRONG! Exception will be thrown at next line:
|
|
foo + "bar"
|
|
|
|
Correct pattern is:
|
|
|
|
foo = "foo"
|
|
// now is OK:
|
|
foo + bar
|
|
|
|
This is though a rare case when you need uninitialized variables, most often you can use conditional operators
|
|
and even loops to assign results (see below).
|
|
|
|
# Constants
|
|
|
|
Almost the same, using `val`:
|
|
|
|
val foo = 1
|
|
foo += 1 // this will throw exception
|
|
|
|
# Constants
|
|
|
|
Same as in kotlin:
|
|
|
|
val HalfPi = π / 2
|
|
|
|
Note using greek characters in identifiers! All letters allowed, but remember who might try to read your script, most
|
|
likely will know some English, the rest is the pure uncertainty.
|
|
|
|
# Defining functions
|
|
|
|
fun check(amount) {
|
|
if( amount > 100 )
|
|
"enough"
|
|
else
|
|
"more"
|
|
}
|
|
>>> Callable@...
|
|
|
|
Notice how function definition return a value, instance of `Callable`.
|
|
|
|
You can use both `fn` and `fun`. Note that function declaration _is an expression returning callable_,
|
|
but Lyng syntax requires using the __lambda syntax__ to create such.
|
|
|
|
val check = {
|
|
it > 0 && it < 100
|
|
}
|
|
assert( check(1) )
|
|
assert( !check(101) )
|
|
>>> void
|
|
|
|
See lambdas section below.
|
|
|
|
There are default parameters in Lyng:
|
|
|
|
fn check(amount, prefix = "answer: ") {
|
|
prefix + if( amount > 100 )
|
|
"enough"
|
|
else
|
|
"more"
|
|
}
|
|
assert( "do: more" == check(10, "do: ") )
|
|
check(120)
|
|
>>> "answer: enough"
|
|
|
|
## Closures
|
|
|
|
Each __block has an isolated context that can be accessed from closures__. For example:
|
|
|
|
var counter = 1
|
|
|
|
// this is ok: counter is incremented
|
|
fun increment(amount=1) {
|
|
// use counter from a closure:
|
|
counter = counter + amount
|
|
}
|
|
|
|
increment(10)
|
|
assert( counter == 11 )
|
|
|
|
val callable = {
|
|
// this obscures global outer var with a local one
|
|
var counter = 0
|
|
// ...
|
|
counter = 1
|
|
// ...
|
|
counter
|
|
}
|
|
|
|
assert(callable() == 1)
|
|
// but the global counter is not changed:
|
|
assert(counter == 11)
|
|
>>> void
|
|
|
|
## Lambda functions
|
|
|
|
Lambda expression is a block with optional argument list ending with `->`. If argument list is omitted,
|
|
the call arguments will be assigned to `it`:
|
|
|
|
lambda = {
|
|
it + "!"
|
|
}
|
|
assert( lambda is Callable)
|
|
assert( lambda("hello") == "hello!" )
|
|
void
|
|
|
|
### `it` assignment rules
|
|
|
|
When lambda is called with:
|
|
|
|
- no arguments: `it == void`
|
|
- exactly one argument: `it` will be assigned to it
|
|
- more than 1 argument: `it` will be a `List` with these arguments:
|
|
|
|
Here is an example:
|
|
|
|
val lambda = { it }
|
|
assert( lambda() == void )
|
|
assert( lambda("one") == "one")
|
|
assert( lambda("one", "two") == ["one", "two"])
|
|
>>> void
|
|
|
|
If you need to create _unnamed_ function, use alternative syntax (TBD, like { -> } ?)
|
|
|
|
### Declaring parameters
|
|
|
|
Parameter is a list of comma-separated names, with optional default value; last
|
|
one could be with ellipsis that means "the rest pf arguments as List":
|
|
|
|
assert( { a -> a }(10) == 10 )
|
|
assert( { a, b -> [a,b] }(1,2) == [1,2])
|
|
assert( { a, b=-1 -> [a,b] }(1) == [1,-1])
|
|
assert( { a, b...-> [a,...b] }(100) == [100])
|
|
// notice that splat syntax in array literal unrills
|
|
// ellipsis-caught arguments back:
|
|
assert( { a, b...-> [a,...b] }(100, 1, 2, 3) == [100, 1, 2, 3])
|
|
void
|
|
|
|
### Using lambda as the parameter
|
|
|
|
// note that fun returns its last calculated value,
|
|
// in our case, result after in-place addition:
|
|
fun mapValues(iterable, transform) {
|
|
var result = []
|
|
for( x in iterable ) result += transform(x)
|
|
}
|
|
assert( [11, 21, 31] == mapValues( [1,2,3], { it*10+1 }))
|
|
>>> void
|
|
|
|
### Auto last parameter
|
|
|
|
When the function call is follower by the `{` in the same line, e.g. lambda immediately
|
|
after function call, it is treated as a last argument to the call, e.g.:
|
|
|
|
fun mapValues(iterable, transform) {
|
|
var result = []
|
|
for( x in iterable ) result += transform(x)
|
|
}
|
|
val mapped = mapValues( [1,2,3]) {
|
|
it*10+1
|
|
}
|
|
assert( [11, 21, 31] == mapped)
|
|
>>> void
|
|
|
|
|
|
# Lists (aka arrays)
|
|
|
|
Lyng has built-in mutable array class `List` with simple literals:
|
|
|
|
[1, "two", 3.33].size
|
|
>>> 3
|
|
|
|
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable].
|
|
|
|
Lists can contain any type of objects, lists too:
|
|
|
|
val list = [1, [2, 3], 4]
|
|
assert( list is List ) // concrete implementatino
|
|
assert( list is Array ) // general interface
|
|
assert(list.size == 3)
|
|
// second element is a list too:
|
|
assert(list[1].size == 2)
|
|
>>> void
|
|
|
|
Notice usage of indexing. You can use negative indexes to offset from the end of the list; see more in [Lists](List.md).
|
|
|
|
When you want to "flatten" it to single array, you can use splat syntax:
|
|
|
|
[1, ...[2,3], 4]
|
|
>>> [1, 2, 3, 4]
|
|
|
|
Of course, you can splat from anything that is List (or list-like, but it will be defined later):
|
|
|
|
val a = ["one", "two"]
|
|
val b = [10.1, 20.2]
|
|
["start", ...b, ...a, "end"]
|
|
>>> ["start", 10.1, 20.2, "one", "two", "end"]
|
|
|
|
Of course, you can set any list element:
|
|
|
|
val a = [1, 2, 3]
|
|
a[1] = 200
|
|
a
|
|
>>> [1, 200, 3]
|
|
|
|
Lists are comparable, and it works well as long as their respective elements are:
|
|
|
|
assert( [1,2,3] == [1,2,3])
|
|
|
|
// but they are _different_ objects:
|
|
assert( [1,2,3] !== [1,2,3])
|
|
|
|
// when sizes are different, but common part is equal,
|
|
// longer is greater
|
|
assert( [1,2,3] > [1,2] )
|
|
|
|
// otherwise, where the common part is greater, the list is greater:
|
|
assert( [1,2,3] < [1,3] )
|
|
>>> void
|
|
|
|
The simplest way to concatenate lists is using `+` and `+=`:
|
|
|
|
// + works to concatenate iterables:
|
|
assert( [5, 4] + ["foo", 2] == [5, 4, "foo", 2])
|
|
var list = [1, 2]
|
|
|
|
// append allow adding iterables: all elements of it:
|
|
list += [2, 1]
|
|
// or you can append a single element:
|
|
list += "end"
|
|
assert( list == [1, 2, 2, 1, "end"])
|
|
>>> void
|
|
|
|
***Important note***: the pitfall of using `+=` is that you can't append in [Iterable] instance as an object: it will always add all its contents. Use `list.add` to add a single iterable instance:
|
|
|
|
var list = [1, 2]
|
|
val other = [3, 4]
|
|
|
|
// appending lists is clear:
|
|
list += other
|
|
assert( list == [1, 2, 3, 4] )
|
|
|
|
// but appending other Iterables could be confusing:
|
|
list += (10..12)
|
|
assert( list == [1, 2, 3, 4, 10, 11, 12])
|
|
>>> void
|
|
|
|
Use `list.add` to avoid confusion:
|
|
|
|
var list = [1, 2]
|
|
val other = [3, 4]
|
|
|
|
// appending lists is clear:
|
|
list.add(other)
|
|
assert( list == [1, 2, [3, 4]] )
|
|
|
|
// but appending other Iterables could be confusing:
|
|
list.add(10..12)
|
|
assert( list == [1, 2, [3, 4], (10..12)])
|
|
>>> void
|
|
|
|
|
|
To add elements to the list:
|
|
|
|
val x = [1,2]
|
|
x.add(3)
|
|
assert( x == [1,2,3])
|
|
// same as x += ["the", "end"] but faster:
|
|
x.add("the", "end")
|
|
assert( x == [1, 2, 3, "the", "end"])
|
|
>>> void
|
|
|
|
Self-modifying concatenation by `+=` also works:
|
|
|
|
val x = [1, 2]
|
|
x += [3, 4]
|
|
assert( x == [1, 2, 3, 4])
|
|
>>> void
|
|
|
|
You can insert elements at any position using `addAt`:
|
|
|
|
val x = [1,2,3]
|
|
x.addAt(1, "foo", "bar")
|
|
assert( x == [1, "foo", "bar", 2, 3])
|
|
>>> void
|
|
|
|
Using splat arguments can simplify inserting list in list:
|
|
|
|
val x = [1, 2, 3]
|
|
x.addAt( 1, ...[0,100,0])
|
|
x
|
|
>>> [1, 0, 100, 0, 2, 3]
|
|
|
|
Using negative indexes can insert elements as offset from the end, for example:
|
|
|
|
val x = [1,2,3]
|
|
x.addAt(-1, 10)
|
|
x
|
|
>>> [1, 2, 10, 3]
|
|
|
|
Note that to add to the end you still need to use `add` or positive index of the after-last element:
|
|
|
|
val x = [1,2,3]
|
|
x.addAt(3, 10)
|
|
x
|
|
>>> [1, 2, 3, 10]
|
|
|
|
## Removing list items
|
|
|
|
val x = [1, 2, 3, 4, 5]
|
|
x.removeAt(2)
|
|
assert( x == [1, 2, 4, 5])
|
|
// or remove range (start inclusive, end exclusive):
|
|
x.removeRangeInclusive(1,2)
|
|
assert( x == [1, 5])
|
|
>>> void
|
|
|
|
Again, you can use negative indexes. For example, removing last elements like:
|
|
|
|
val x = [1, 2, 3, 4, 5]
|
|
|
|
// remove last:
|
|
x.removeAt(-1)
|
|
assert( x == [1, 2, 3, 4])
|
|
|
|
// remove 2 last:
|
|
x.removeRangeInclusive(-2,-1)
|
|
assert( x == [1, 2])
|
|
>>> void
|
|
|
|
# Flow control operators
|
|
|
|
## if-then-else
|
|
|
|
As everywhere else, and as expression:
|
|
|
|
val count = 11
|
|
if( count > 10 )
|
|
println("too much")
|
|
else {
|
|
// do something else
|
|
println("just "+count)
|
|
}
|
|
>>> too much
|
|
>>> void
|
|
|
|
Notice returned value `void`: it is because of `println` have no return value, e.g., `void`.
|
|
|
|
Or, more neat:
|
|
|
|
var count = 3
|
|
println( if( count > 10 ) "too much" else "just " + count )
|
|
>>> just 3
|
|
>>> void
|
|
|
|
## while
|
|
|
|
Regular pre-condition while loop, as expression, loop returns it's last line result:
|
|
|
|
var count = 0
|
|
while( count < 5 ) {
|
|
count++
|
|
count * 10
|
|
}
|
|
>>> 50
|
|
|
|
We can break as usual:
|
|
|
|
var count = 0
|
|
while( count < 5 ) {
|
|
if( count < 5 ) break
|
|
count = ++count * 10
|
|
}
|
|
>>> void
|
|
|
|
Why `void`? Because `break` drops out without the chute, not providing anything to return. Indeed, we should provide
|
|
exit value in the case:
|
|
|
|
var count = 0
|
|
while( count < 50 ) {
|
|
if( count > 3 ) break "too much"
|
|
count = ++count * 10
|
|
"wrong "+count
|
|
}
|
|
>>> "too much"
|
|
|
|
### Breaking nested loops
|
|
|
|
If you have several loops and want to exit not the inner one, use labels:
|
|
|
|
var count = 0
|
|
// notice the label:
|
|
outerLoop@ while( count < 5 ) {
|
|
var innerCount = 0
|
|
while( innerCount < 100 ) {
|
|
innerCount = innerCount + 1
|
|
|
|
if( innerCount == 5 && count == 2 )
|
|
// and here we break the labelled loop:
|
|
break@outerLoop "5/2 situation"
|
|
}
|
|
count = count + 1
|
|
count * 10
|
|
}
|
|
>>> "5/2 situation"
|
|
|
|
### and continue
|
|
|
|
We can skip the rest of the loop and restart it, as usual, with `continue` operator.
|
|
|
|
var count = 0
|
|
var countEven = 0
|
|
while( count < 10 ) {
|
|
count = count + 1
|
|
if( count % 2 == 1) continue
|
|
countEven = countEven + 1
|
|
}
|
|
"found even numbers: " + countEven
|
|
>>> "found even numbers: 5"
|
|
|
|
`continue` can't "return" anything: it just restarts the loop. It can use labeled loops to restart outer ones (we intentionally avoid using for loops here):
|
|
|
|
var count = 0
|
|
var total = 0
|
|
// notice the label:
|
|
outerLoop@ while( count++ < 5 ) {
|
|
var innerCount = 0
|
|
while( innerCount < 10 ) {
|
|
if( ++innerCount == 10 )
|
|
continue@outerLoop
|
|
}
|
|
// we don't reach it because continue above restarts our loop
|
|
total = total + 1
|
|
}
|
|
total
|
|
>>> 0
|
|
|
|
Notice that `total` remains 0 as the end of the outerLoop@ is not reachable: `continue` is always called and always make
|
|
Lyng to skip it.
|
|
|
|
## else statement
|
|
|
|
The while and for loops can be followed by the else block, which is executed when the loop
|
|
ends normally, without breaks. It allows override loop result value, for example,
|
|
to not calculate it in every iteration. For example, consider this naive prime number
|
|
test function (remember function return it's last expression result):
|
|
|
|
fun naive_is_prime(candidate) {
|
|
val x = if( candidate !is Int) candidate.toInt() else candidate
|
|
var divisor = 1
|
|
while( ++divisor < x/2 || divisor == 2 ) {
|
|
if( x % divisor == 0 ) break false
|
|
}
|
|
else true
|
|
}
|
|
assert( !naive_is_prime(16) )
|
|
assert( naive_is_prime(17) )
|
|
assert( naive_is_prime(3) )
|
|
assert( !naive_is_prime(4) )
|
|
>>> void
|
|
|
|
## Loop return value diagram
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
S((start)) --> Cond{check}
|
|
Cond--false, no else--->V((void))
|
|
Cond--true-->E(["last = loop_body()" ])
|
|
E--break value---->BV((value))
|
|
E--> Check2{check}
|
|
E--break---->V
|
|
Check2 --false-->E
|
|
Check2 --true, no else--->L((last))
|
|
Check2 --true, else-->Else(["else_clause()"])
|
|
Cond--false, else--->Else
|
|
Else --> Ele4$nr((else))
|
|
```
|
|
|
|
So the returned value, as seen from diagram could be one of:
|
|
|
|
- `void`, if the loop was not executed, e.g. `condition` was initially false, and there was no `else` clause, or if the empty break was executed.
|
|
- value returned from `break value' statement
|
|
- value returned from the `else` clause, of the loop was not broken
|
|
- value returned from the last execution of loop body, if there was no `break` and no `else` clause.
|
|
|
|
|
|
## For loops
|
|
|
|
For loop are intended to traverse collections, and all other objects that supports
|
|
size and index access, like lists:
|
|
|
|
var letters = 0
|
|
for( w in ["hello", "wolrd"]) {
|
|
letters += w.length
|
|
}
|
|
"total letters: "+letters
|
|
>>> "total letters: 10"
|
|
|
|
For loop support breaks the same as while loops above:
|
|
|
|
fun search(haystack, needle) {
|
|
for(ch in haystack) {
|
|
if( ch == needle)
|
|
break "found"
|
|
}
|
|
else null
|
|
}
|
|
assert( search("hello", 'l') == "found")
|
|
assert( search("hello", 'z') == null)
|
|
>>> void
|
|
|
|
We can use labels too:
|
|
|
|
fun search(haystacks, needle) {
|
|
exit@ for( hs in haystacks ) {
|
|
for(ch in hs ) {
|
|
if( ch == needle)
|
|
break@exit "found"
|
|
}
|
|
}
|
|
else null
|
|
}
|
|
assert( search(["hello", "world"], 'l') == "found")
|
|
assert( search(["hello", "world"], 'z') == null)
|
|
>>> void
|
|
|
|
# Self-assignments in expression
|
|
|
|
There are auto-increments and auto-decrements:
|
|
|
|
var counter = 0
|
|
assert(counter++ * 100 == 0)
|
|
assert(counter == 1)
|
|
>>> void
|
|
|
|
but:
|
|
|
|
var counter = 0
|
|
assert( ++counter * 100 == 100)
|
|
assert(counter == 1)
|
|
>>> void
|
|
|
|
The same with `--`:
|
|
|
|
var count = 100
|
|
var sum = 0
|
|
while( count > 0 ) sum = sum + count--
|
|
sum
|
|
>>> 5050
|
|
|
|
There are self-assigning version for operators too:
|
|
|
|
var count = 100
|
|
var sum = 0
|
|
while( count > 0 ) sum += count--
|
|
sum
|
|
>>> 5050
|
|
|
|
# Ranges
|
|
|
|
Ranges are convenient to represent the interval between two values:
|
|
|
|
5 in (0..100)
|
|
>>> true
|
|
|
|
It could be open and closed:
|
|
|
|
assert( 5 in (1..5) )
|
|
assert( 5 !in (1..<5) )
|
|
>>> void
|
|
|
|
Ranges could be inside other ranges:
|
|
|
|
assert( (2..3) in (1..10) )
|
|
>>> void
|
|
|
|
There are character ranges too:
|
|
|
|
'd' in 'a'..'e'
|
|
>>> true
|
|
|
|
and you can use ranges in for-loops:
|
|
|
|
for( x in 'a' ..< 'c' ) println(x)
|
|
>>> a
|
|
>>> b
|
|
>>> void
|
|
|
|
See [Ranges](Range.md) for detailed documentation on it.
|
|
|
|
# Comments
|
|
|
|
// single line comment
|
|
var result = null // here we will store the result
|
|
>>> void
|
|
|
|
# Integral data types
|
|
|
|
| type | description | literal samples |
|
|
|--------|---------------------------------|---------------------|
|
|
| Int | 64 bit signed | `1` `-22` `0x1FF` |
|
|
| Real | 64 bit double | `1.0`, `2e-11` |
|
|
| Bool | boolean | `true` `false` |
|
|
| Char | single unicode character | `'S'`, `'\n'` |
|
|
| String | unicode string, no limits | "hello" (see below) |
|
|
| List | mutable list | [1, "two", 3] |
|
|
| Void | no value could exist, singleton | void |
|
|
| Null | missing value, singleton | null |
|
|
| Fn | callable type | |
|
|
|
|
See also [math operations](math.md)
|
|
|
|
## Character details
|
|
|
|
The type for the character objects is `Char`.
|
|
|
|
### Char literal escapes
|
|
|
|
Are the same as in string literals with little difference:
|
|
|
|
| escape | ASCII value |
|
|
|--------|-------------------|
|
|
| \n | 0x10, newline |
|
|
| \t | 0x07, tabulation |
|
|
| \\ | \ slash character |
|
|
| \' | ' apostrophe |
|
|
|
|
### Char instance members
|
|
|
|
assert( 'a'.code == 0x61 )
|
|
>>> void
|
|
|
|
| member | type | meaning |
|
|
|--------|------|--------------------------------|
|
|
| code | Int | Unicode code for the character |
|
|
| | | |
|
|
|
|
|
|
## String details
|
|
|
|
### String operations
|
|
|
|
Concatenation is a `+`: `"hello " + name` works as expected. No confusion.
|
|
|
|
### Literals
|
|
|
|
String literal could be multiline:
|
|
|
|
"Hello
|
|
World"
|
|
|
|
though multiline literals is yet work in progress.
|
|
|
|
# Built-in functions
|
|
|
|
See [math functions](math.md). Other general purpose functions are:
|
|
|
|
| name | description |
|
|
|----------------------------------------------|----------------------------------------------------------|
|
|
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
|
|
| println(args...) | Open for overriding, it prints to stdout. |
|
|
|
|
# Built-in constants
|
|
|
|
| name | description |
|
|
|-------------------------------------|------------------------------|
|
|
| Real, Int, List, String, List, Bool | Class types for real numbers |
|
|
| π | See [math](math.md) |
|
|
|
|
[List]: List.md
|
|
[Iterable]: Iterable.md
|
|
[Iterator]: Iterator.md
|
|
[Real]: Real.md
|
|
[Range]: Range.md
|
|
|
|
|