Basic syntax
How a JARU program is written: the order of its parts, how statements are separated, how blocks are closed, how values are written and in which order operators are calculated. The pitfalls people fall into most often are at the end.
Each part of the language then has its own page: Variables and constants, Operators, Control structures, Functions...
A program
A JARU program is one .aru file, or several. There is no main function: the VM runs the main file from top to bottom, and the program ends when it reaches the end.
This is the usual order:
use Math // 1. The modules the program uses
const SIDE = 4 // 2. The constants
func area(side) // 3. The functions
return side * side
end
// 4. What the program does
var a = area(SIDE)
println("Area: ", a) // Area: 16
println("The largest: ", Math.max(a, 20)) // The largest: 20
useopens the VM modules you are going to use; several fit on one line:use Display, Sprite, Input. Without it, the program compiles, but it stops at the first line that uses the module, with the error “Module 'Math' not found or not imported.”. The modules are in The API.import "File.aru"brings in the code of another file of the project. That code runs before the first line of the main file, even if theimportis further down. Programs in several files explains it.- A function exists from the line where it is declared. If you call it from higher up, the program stops with the error “Undefined variable”. More in Functions.
- The program can also end earlier, with
exit: see Errors and exceptions.
Statements and semicolons
The normal thing is to write one statement per line. The semicolon at the end is optional:
var lives = 3
var score = 0;
To put several on the same line, separate them with ;. The compiler also separates them without it, but they are harder to read:
var x = 10; var y = 20; println(x + y) // 30
A line break does not end a statement: the compiler considers it finished when what comes next can no longer be part of it. That is why a long statement can carry on in the line below:
var total = 10 +
20 +
30
println(total) // 60
++ or --Because a line break does not end a statement, a ++ or a -- at the start of a line sticks to the line above if that line ends in a variable:
var games = 0
var score = 10
var best = score
++games // read as: var best = score++
println(best, " ", score, " ", games) // 10 11 0
Write games++ or games += 1, or end the line above with ;.
Comments
// One-line comment: it runs to the end of the line
var lives = 3 // also after a statement
/* Comment over
several lines */
println("10 // 2") // inside a string, // is text: prints 10 // 2
- Comments can contain accented letters.
- A
/* … */cannot go inside another one: the first*/closes the comment, and whatever is left behind it is an error. - A
/*without its*/gives the error “Detected unterminated comment, expecting '*/'”.
/* A comment /* inside another one */ is not allowed */
println("hello")
Blocks: end, parentheses and then
Every block is closed with end. Indentation does not matter to the compiler: it is only there to make the code easier to read.
| Block | Opens with | Page |
|---|---|---|
| Condition | if (…) then, and inside it elsif (…) then and else | Control structures |
| Selection | switch (…), and inside it case …: and default: | Control structures |
| Loops | for (…; …; …), foreach (var x in …), while (…) and until (…) | Control structures |
| Functions | func name(…) and fn(…): | Functions |
| Classes | class Name, and inside it def method(…) | Classes and objects |
| Structs and enumerations | struct Name and enum Name | Structs and Enumerations |
| Errors | try, and inside it catch (var e) | Errors and exceptions |
The rules that are forgotten most often:
- The whole condition goes inside parentheses in
if,elsif,while,untilandswitch. ifandelsifalways takethen.whileanduntiltake neitherthennordo.- It is
elsif, notelifnorelse if. Anelse ifopens a newifinside theelse, which needs its ownend. If it is missing, the compiler says so: “missing 'end' at the end of the 'else' block (check the 'else if' on line 4: in JARU it is 'elsif')”. - A short block fits on one line:
if (lives == 0) then println("Game over") end.
var lives = 2
var seconds = 30
if (lives > 0 and seconds > 0) then
println("Keep playing")
elsif (lives > 0) then
println("Time is up")
else
println("Game over")
end
These three do not compile:
var lives = 2
if lives > 0 then // the parentheses are missing
println("Keep playing")
end
var lives = 2
var seconds = 30
if (lives > 0) and (seconds > 0) then // the condition ends at the first )
println("Keep playing")
end
var level = 2
if (level == 1) then
println("Easy")
elif (level == 2) then // it is elsif
println("Normal")
end
Literals
This is how each value is written directly in the code:
| Value | Examples | How it is written |
|---|---|---|
| Integer | 42, -7, 0x1F, 0xff | In decimal, or in hexadecimal with 0x in front |
| Decimal | 3.14, .5, 1e3, 2.5e-2 | With a point or with an exponent |
| String | "hello", 'hello' | Between double or single quotes |
| Boolean | true, false, on, off | on is true, and off is false |
| No value | nil | |
| List, map and array | [1, 2], {"x": 1}, {1, 2} | See Lists, maps and arrays |
println(0x1F) // 31
println(0xff) // 255
println(1e3) // 1000
println(on == true) // true
In hexadecimal, the letters work in upper and lower case. A number with an exponent is always a decimal, even though println writes 1000. The range of integers, what happens with a number that doesn't fit in 32 bits and hexadecimals above 0x7FFFFFFF are in Data types.
Strings
A string ends with the same quote it starts with, so the other kind can go inside: 'She said "hi"', "It's". The backslash \ starts an escape: \n (line break), \r, \t and \\ (a backslash).
\"is not an escape. The string does not end there, but the backslash stays inside:"she said \"hi\""printsshe said \"hi\". To put double quotes in a string, write the string between single quotes.- Careful with Windows paths:
"C:\new"contains a line break. Write"C:\\new". - A string does not end at the end of the line. If you forget to close it, it carries on in the lines below, and the error, “String without terminating quotation mark”, appears further down than where the quote is missing.
- Accented letters are fine inside strings, but not in names.
What each escape puts in the string and how to write a string over several lines is in Writing strings.
Operator precedence
When an expression has several operators, the ones in the upper rows are calculated first. Those in the same row are calculated from left to right, except ? : and the assignments, which go from right to left.
| Operators | What they are | |
|---|---|---|
| 1 | a.b, a[i], f(x) | Property or method, index and call |
| 2 | -x, +x, not x, !x, ~x, ++x, --x, x++, x-- | Single-value operators |
| 3 | *, /, %, div, mod | Multiplication and division |
| 4 | +, - | Addition and subtraction |
| 5 | <<, >> | Bit shifts |
| 6 | &, |, ^ | Bitwise |
| 7 | <, <=, >, >= | Comparison |
| 8 | ==, != | Equality |
| 9 | and, or | Logical |
| 10 | ? : | Conditional |
| 11 | =, +=, -=, *=, /=, %= | Assignment |
Three things are different from other languages:
println(not 1 == 2) // false: it is (not 1) == 2
println(not (1 == 2)) // true
println(6 & 3 == 2) // true: it is (6 & 3) == 2
println(4 | 1 & 2) // 0: it is (4 | 1) & 2
println(true or false and false) // false: it is (true or false) and false
println(true or (false and false)) // true
notand!are calculated before comparisons. To negate a comparison, put it in parentheses.- The bitwise operators go before comparisons, the other way round from C, and
&,|and^have the same precedence. andandoralso have the same precedence:
and and or have the same precedenceIn most languages, and is calculated before or. Not in JARU: they are calculated from left to right, so a or b and c is (a or b) and c. When you mix and and or, use parentheses.
Pitfalls
All the known issues of JARU, together, are in Known issues.
String + number
+ joins two strings, but not a string and a number. "Lives: " + lives compiles, but the program stops when it reaches that line, with the error “Unsupported operand types for operator '+'.”. Convert the number first with toString(), as in "Lives: " + lives.toString(), or pass the parts to println separately: println("Lives: ", lives). Joining strings explains it.
.. is not a JARU operator: the compiler gives the error “Operator '..' doesn't exist in JARU. Use '+' to join strings.”.
var lives = 3
println("Lives: " .. lives)
Methods on a literal or on parentheses
A method goes after a variable, an element or a property: name.size(), list[0].toString(), player.lives.toString(). After a literal or after parentheses it does not compile, with the error “Expected identifier in method name.”:
println("hello".size())
var a = 2
var b = 3
println((a + b).toString())
Store the value in a variable first:
var greeting = "hello"
println(greeting.size()) // 5
var a = 2
var b = 3
var sum = a + b
println(sum.toString()) // 5
With a number it is worse: in 5.toString(), the compiler reads 5. as the decimal 5.0, and toString() as another statement. A line like var s = 5.toString() compiles, and the program stops with “Undefined variable 'toString'.”.
Indexing what a call returns
f(x)[0] compiles, but it does not index what f returns: the program stops with the error “This object type is not indexable.”. With a method it is worse: map.keys()[0] makes the compiler close without explaining the error, and list.copy()[0] stops the program with an error that makes no sense (“List has no property ''.”). Store the result in a variable first:
func ends(list)
return [list[0], list[list.size() - 1]]
end
var numbers = [4, 8, 15]
// println(ends(numbers)[0]) <- stops when it runs
var e = ends(numbers)
println(e[0], " ", e[1]) // 4 15