Reference / Lexical structure
Lexical structure
The lexical structure of Sisu is how source text becomes tokens: names, keywords, literals, operators, comments and line breaks.
// A comment runs to the end of its line.
fn main() {
let greeting = "hello"
print(greeting)
}
hello
On this page, the output of an example is in an output panel, so that every // in the code is an ordinary comment.
Source text
A source file is text in UTF-8 (E0072). A line ends with a line feed, or with a carriage return and a line feed. The last line does not need a line break.
Outside comments and literals, the source uses only these characters:
- The ASCII letters, the digits and
_. - The characters of the operators and punctuation, such as
+,(and". - Spaces, tabs and carriage returns. They separate tokens and have no other meaning.
A carriage return without a line feed does not end a line. So two statements with only a carriage return between them are an error (E0011).
Any other character is an error (E0010). This includes a letter that is not ASCII, such as é, and a lone &.
A no-break space and a byte order mark are errors too, also at the start of the file. Comments, character literals and string literals can hold any character.
fn main() {
let total = 2 # 3 // error[E0010]
}
E0010: what this error means
Comments
A comment starts with // and runs to the end of the line. It can stand on a line of its own, or after code.
fn main() {
// A comment on a line of its own.
let n = 2 // A comment after code.
/// Three slashes start an ordinary comment too.
print(n)
}
2
Sisu has no block comment: /* is the operator /, then the operator *. A // in a string literal is part of the text, not a comment. A comment cannot be in an interpolation (E0097).
fn main() {
print("a // b") // Only this part is a comment.
}
a // b
Names
A name starts with an ASCII letter or _. After that come ASCII letters, digits and _. Names are case-sensitive, so total and Total are two names.
fn main() {
let total = 1
let Total = 2
let _count = 3
let x2 = 4
print(total + Total + _count + x2)
}
10
The rules:
- A name cannot be a keyword or a reserved word.
_alone is not a name. It is a keyword thatlet,forandmatchuse to bind nothing.- The built-in functions and types, such as
print,i64andstring, are names, not keywords.
Keywords
These words are keywords. A keyword cannot be a name.
| Keywords | |
|---|---|
| Declarations | fn, class, enum, const, let, mut |
| Control flow | if, else, while, for, in, loop, match, break, continue, return |
| Values | true, false, None, self |
| Other | is, todo, _ |
var is a keyword that Sisu does not use. Write let mut (E0013).
These words are reserved for a later version of Sisu. A reserved word cannot be a name or start an expression (E0083).
as, echo, extern, import, interface, pub, record, test, type, weak
fn main() {
let record = 1 // error[E0083]
}
E0083: what this error means
Line breaks
A line break ends a statement. No ; is necessary.
fn main() {
let a = 20
let b = 22
print(a + b)
}
42
A line break ends a statement only after a token that can end one. These tokens can end a statement:
- A name, a literal,
true,false,None,selfor_. return,break,continueortodo.?,),]or}.
After any other token, such as an operator or ,, the statement continues on the next line. So a long expression breaks after an operator.
fn main() {
let total = 1 +
2 +
3
print(total)
}
6
Inside ( and [, a line break never ends a statement. A { inside them opens a block, and in that block, line breaks end statements again.
fn main() {
let xs = [
10,
20,
]
print(xs.len())
}
2
A line break inside the type-parameter list of a fn, class or enum header does not end a statement either. This applies when the header starts its statement. A header that comes after a } on the same line must keep its list on one line.
class Pair<
A,
B,
> {
first: A
second: B
}
fn main() {
let p = Pair(first: 1, second: "two")
print(p.first + p.second.len())
}
4
Layout rules
Because a line break can end a statement, some forms must stay on one line.
Outside ( and [, a binary operator ends a line, and cannot start the next line. A - at the start of a line, followed by a space or a tab, is an error (E0002). The - could be a subtraction or a negation.
fn main() {
let n = 10
- 2 // error[E0002]
}
E0002: what this error means
This rule applies only when the line break before the - ends a statement. After an operator, the statement continues, so a - on the next line is a negation.
fn main() {
let n = 10 +
- 2
print(n)
}
8
When the line break before it ends a statement, a - with no space or tab after it is a negation. The negation starts a new statement. So in this example, n stays 10, and -2 is an unused value (W0004).
fn main() {
let n = 10
-2 // warning[W0004]
print(n)
}
10
W0004: what this warning means
The other layout rules:
- The
{of afn,class,enum,if,else,match,while,fororloopis on the line of its header (E0004). - An
elseis on the line of the}before it:} else {(E0006). - Inside
(or[, a[cannot start a line after an expression (E0005). Outside brackets, a[at the start of a line starts a new statement. It never indexes the line above.
fn main()
{ // error[E0004]
print(1)
}
E0004: what this error means
Semicolons
A ; separates two statements on one line, as a line break does. It also separates two fields, two methods, two variants or two match arms on one line.
fn main() {
let x = 2; print(x * 21)
}
42
A ; that separates nothing is unnecessary, and the program builds with a warning (W0007). These are the unnecessary places:
- the end or the start of a line
- after
{, or before}orelse - each extra
;in;; - after a top-level
fn,classorenum
fn main() {
print(1); // warning[W0007]
}
1
W0007: what this warning means
The rules:
- After a top-level
const, a;separates it from an item after it on the same line:const X = 1; fn main() {}. - Inside
(and[, a;is an error (E0011). The;in[value; length]is part of the array syntax. - When removing a
;would join two lines into a different statement, the;is an error, not a warning (E0011).
Line continuation
A line that starts with . or ?. continues the line above it. So a chain of method calls can put each call on its own line. Blank lines and comment lines can come between.
fn main() {
let n = "one two three"
.words()
// A comment line does not stop the chain.
.len()
print(n)
}
3
A line that starts with .. does not continue the line above it, because .. starts a range. A ; at the end of the line above also stops the continuation.
Sisu