This chapter only describes syntax. For semantics go to Chapter 8.
expr - stands for expression
In this chapter <...>
is a placeholder for necessary value and <[...]>
for unnecessary.
Things in []
are usually not required
<name:type>
is just a named group like <...>
stmt
stands for Statement.
{
Code
}
is a single statement
When you run Quail program, interpreter follows the following execution flow:
Preprocessing #Chapter 3
Lexical analysis #Chapter 4
Parsing #Part 2.3 #Chapter 5
Running #Chapter 6 #Chapter 7 #Chapter 8
#
denotes a comment
#:
denotes a preprocessor directive
Examples: (see Chapter 3)
#:include "file.q"
#:alias "HELLO" print("Hello")
#?
denotes a doc
Examples: (see Chapter 13)
#? This is a description
#?author Tapeline
Operator | Meaning | Example |
---|---|---|
+ |
Addition | 1 + 2 |
- |
Subtraction | 1 - 2 |
* |
Multiplication | 1 * 2 |
/ |
Division | 1 / 2 |
// |
Integer division (without remainder and floating point) | 1 // 2 |
% |
Remainder of division | 1 % 2 |
^ |
Power | 3 ^ 2 |
= |
Assignment | a = 2 |
== |
Equals | 1 == 2 |
!= |
Not equals | 1 != 2 |
! , not |
Logical bool | !true , not true |
&& |
Locigal and | true && true |
\|| |
Logical or | true \|| false |
< |
Is less than | 1 < 2 |
> |
Is greater than | 1 > 2 |
<= |
Is less or equal | 1 <= 2 |
>= |
Is greater or equal | 1 >= 2 |
: |
Range operator | 1:5 , 1:10:2 |
:+ |
Range-include operator | 1:+5 , 1+:10:2 |
<< |
Shift left | [1, 2, 3] << 1 |
>> |
Shift right | [1, 2, 3] >> 1 |
in |
Is element in | 1 in [1, 2, 3] |
not in |
is element absent | 1 not in [1, 2, 3] |
instanceof |
Check if object is instace of class | a instanceof MyClass |
Operators +
, -
, *
, /
, //
, %
and ^
support shortens like this:
a += b
With a general form of
<expr1> operator= <expr2>
which is basically a syntax sugar for
<expr1> = <expr1> operator <expr2>
if-else if-else
Runs code basing on conditions
if <condition> <stmt>
if <condition> <stmt>
else <stmt>
if <condition> <stmt>
else if <condition> <stmt> ...
if <condition> <stmt>
else if <condition> <stmt>
else <stmt>
Examples:
if a == b f()
if a == b {
f()
}
if a == b f()
else f1()
if a == b {
f()
} else {
f1()
}
if a == b {
f()
} else if a == c {
f1()
}
if a == b {
f()
} else if a == c {
f1()
} else {
f2()
}
through ... as ...
Iterates over a number range
through <range> as <variable> <stmt>
Supports all range variations.
Examples:
through 1:10 as i print(i)
through 1:+10 as i print(i)
through 1:10:2 as i print(i)
through 10:1 as i print(i)
while
Iterates while condition is true
while <condition> <stmt>
Examples:
while i > 2 {
i /= 3
}
loop-stop when
Iterates while condition is false.
Checks condition after iteration (like do-while)
loop <stmt> stop when <condition>
Examples:
loop {
cmd = input()
} stop when cmd == "exit"
for ... in ...
Iterates over a collection (list, string, dict…)
for <variable> in <collection> <stmt>
every <variable> in <collection> <stmt>
Example:
for student in students {
print(student.name)
}
try-catch
Tries to execute code. If an exception is thrown, suppresses it and proceeds to corresponding catch block. If there is no applicable catch block, rethrows the exception. If there are no catch blocks at all, the exception is always suppressed
try <stmt>
try <stmt>
catch as <variable> <stmt>
(Catches all)
try <stmt>
catch <expr: exception class> as <variable> <stmt>
(Catches exceptions that are instanceof given class)
Examples:
try {
f()
}
try {
a[i] = b
} catch as e {
print(e)
}
try {
a[i] = b
} catch IndexOutOfBoundsException as e {
print(e)
}
Instructions are one-word control words.
break
Quits current loop
break
Example
while true {
cmd = input()
if cmd == "quit"
break
}
Effects are one-line constructions with general form: <effect> <value>
assert
Throws assertion exception when value is not true
assert <expr>
Example
assert value != null
strike
Quits multiple nested loops (like break)
strike <expr>
Example
through 0:+10 as x {
through 0:+10 as y {
through 0:+10 as z {
strike 2
}
}
}
import
Get code from specified file and execute it in this runtime
import <expr>
Example
import "common.q"
use
Import a library and place it in some variable
use <expr> = <variable>
Example
use "lang/reflect" = reflect
return
Return value from function or a file
return
return <expr>
Examples:
function f(x) {
if x == null return
return x.a
}
throw
Throw an exception
throw <expr>
Examples:
throw "Error!"
throw Exception() % {"message"="Error!"}
async
Run node in parallel
async <anything>
Examples:
async while true {
print("Hi!")
}
Available modifiers are:
func
string
bool
num
dict
list
class
void
required
local
final
static
First group is called type modifiers, the second - behaviour modifiers
Types string
, num
, bool
support conversion.
<modifier>(<expr>)
Examples:
a = num(input())
b = string([1, 2, 3])
c = bool(1)
You can modify variable on assignment using following syntax instead of just <variable>
:
<modifier group> <variable>
<modifier group 1> | <modifier group 2> | ... <variable>
Modifier group is one or more modifiers split by space.
Notice! Behaviour modifiers should always go only into first modifier group!
Examples:
string name = ""
num | void result = null
local final num MY_CONSTANT = 3.1415
Same rules as in 2.3.6.2 apply
Functions can be defined the same way as written in 2.3.7.1, but instead of function
and method
keywords you can write a type moidifier:
num sqrt(num x) {
return x ^ 0.5
}
This syntax will not affect anything, but it is helpful for developing.
function <variable: name>([<variable: argument>, ...]) <stmt>
method <variable: name>([<variable: argument>, ...]) <stmt>
Examples:
function f() {}
constructor ([<variable: argument>, ...]) <stmt>
Syntax sugar for function _constructor ...
gets <variable>(this) <stmt>
Syntax sugar for function _get_<variable> ...
sets <variable>(this, value) <stmt>
Syntax sugar for function _set_<variable> ...
override <operator>([<variable: argument>, ...]) <stmt>
Syntax sugar for function operator ...
, where operator is defined
using following table:
Operator char | Function name |
---|---|
+ |
_add |
- |
_sub |
* |
_mul |
/ |
_div |
// |
_intdiv |
% |
_mod |
^ |
_pow |
== |
_eq |
!= |
_neq |
< |
_cmpl |
> |
_cmpg |
<= |
_cmple |
>= |
_cmpge |
! |
_not |
<< |
_shl |
>> |
_shr |
override <type>(this) <stmt>
Syntax sugar for function type ...
, where type is defined
using following table:
Type modifier | Function name |
---|---|
string |
_tostring |
bool |
_tobool |
num |
_tonumber |
Examples:
constructor (this, name) {
this.name = name
}
override +(this, other) {
return this.val + other.val
}
override string(this) {
return string(this.val)
}
gets myProperty(this) {
return this.calculateProperty()
}
class <variable: name> [like <expr>] <stmt>
Examples:
class Student {
string name
constructor (this, name) {
this.name = name
}
}
class MyException like Exception {
constructor (this, message) {
this.message = message
}
}
Values are considered expressions
<digits>[.<digits>]
Examples:
132
123.3231
true
false
"<content>"
Escape sequences supported: \\
, \n
, \r
, \t
null
[<expr>, <expr>...]
Examples:
[]
[234]
[231, 3123, 4343]
{<expr>=<expr>, ...}
Examples:
{}
{"a" = 1}
{"a" = 1, "b" = 2}
function (<variable: argument>, ...) <stmt>
(<variable: argument>, ...) -> <stmt>
Examples:
f = function (a, b) return a + b
f = (a, b) -> return a + b
Generators are also considered expressions
[<expr> for <variable> in <expr>]
[<expr> for <variable> in <expr> if <condition>]
[<expr> every <variable> in <expr>]
[<expr> every <variable> in <expr> if <condition>]
[<expr> through <range> as <variable>]
[<expr> through <range> as <variable> if <condition>]
Examples:
[x^2 for x in 1:+10]
[x^2 for x in 1:+10 if x % 2 == 0]
[x^2 through 1:+10 as x if x % 2 == 0]
{<key: expr> = <value: expr> for <variable> in <expr>}
{<key: expr> = <value: expr> for <variable> in <expr> if <condition>}
{<key: expr> = <value: expr> every <variable> in <expr>}
{<key: expr> = <value: expr> every <variable> in <expr> if <condition>}
{<key: expr> = <value: expr> through <range> as <variable>}
{<key: expr> = <value: expr> through <range> as <variable> if <condition>}
Examples:
{x = x^2 for x in 1:+10}
{x = x^2 for x in 1:+10 if x % 2 == 0}
{x = x^2 through 1:+10 as x if x % 2 == 0}
<expr>()
<expr>(<arg1>, <arg2>, ...)
<expr>(<arg1>, <arg2>, <kwarg name: variable>=<expr>, ...)
Example
print()
print("Hello!")
input(prompt="input a message> ")
<expr>.<name: variable>()
<expr>.<name: variable>(<arg1>, <arg2>, ...)
<expr>.<name: variable>(<arg1>, <arg2>, <kwarg name: variable>=<expr>, ...)
Examples:
thread.result()
<expr>.<name: variable>
Example:
math.pi
<expr>.<name: variable>
Example:
myObj.field = value
<expr>[<index: expr>]
Example:
a[0]
<expr>[<index: expr>] = <expr>
Example:
a[0] = 4
<start:expr> : <end:expr>
<start:expr> :+ <end:expr>
<start:expr> : <end:expr> : <step:expr>
<start:expr> :+ <end:expr> : <step:expr>
Examples:
1:10
1:+10
1:+10:2
<expr>[<range*>]
range* - is a usual range, but start and end can be omitted like this:
a[:10]
, a[::-1]
, a[1:]
, a[1::4]
Examples:
a[1:10]
a[::-1]
Section (or code block) is a statement that contains multiple statements that are executed one-by-one.
Section can be defined in 3 different ways:
{
- }
:
if a == b {
foo()
bar()
}
do|does|has|with|then
- end
:
if a == b then
foo()
bar()
end
function foo() do
...
end
function bar() does
...
end
class MyClass has
string field
override string(this) with
return this.field
end
end
do|does|has|with|then
are interchangeable
:
:
if a == b:
foo()
bar()
In this case indentation matters!
All 3 different styles can be combined (but this is not recommended due to bad readability):
while true {
for i in 1:10 do
if i % 2 == 0:
print(i)
end
}