Skip to content

Lexemes

A keyword is a word reserved by the language for its own syntax.

KeywordPurpose
letBinds a value to a name inside a function
mutMarks a binding or a parameter as mutable
constDefines a compile-time constant of a primitive type
fnDefines a function or a method
structDefines a record type with fields
enumDefines a type with variants
interfaceDefines a set of methods a type can satisfy
implContains methods implemented by a type
typeDefines a named type or a type alias
KeywordPurpose
ifTakes a branch when a condition holds
elseTakes the remaining branch
matchSelects a branch by pattern
forIterates over values
inNames what a for loop iterates
whileRepeats while a condition holds
loopRepeats until a break
breakExits the enclosing loop
continueStarts the next iteration of the loop
returnExits the enclosing function
KeywordPurpose
tryScopes ? to a block
deferRuns an expression on function exit
recoverCatches a panic
assertStates an expected result in a test
KeywordPurpose
taskStarts a concurrent task (goroutine)
selectWaits on several channel operations
KeywordPurpose
importBrings a package into scope
pubMakes an item visible outside its package
asConverts between types, or names a pattern

An identifier is a name given to a declaration, such as a variable, a function, or a type. It starts with a letter or underscore, followed by any number of letters, digits, or underscores. Identifiers are case-sensitive.

foovariables, functions, parameters
Pointtypes, type parameters
MAX_SIZEconstants
_countdiscarded

The bare underscore _ is a wildcard pattern, not a usable identifier.

Integer literals have type int.

let decimal = 42
let with_separators = 1_000_000
let hex = 0xFF
let octal = 0o755
let binary = 0b1010_0001

Underscore separators improve readability. They cannot be leading, trailing, or consecutive.

_1000leading, parses as identifier
1000_error: trailing
1__000error: consecutive
1_000ok

Hex, octal, and binary literals use prefixes 0x, 0o, and 0b (case-insensitive). Legacy leading-zero octal (0755) is not allowed. Use the 0o prefix (0o755) instead.

Float literals have type float64. A decimal point requires digits on both sides. Exponent notation uses e with an optional sign.

let pi = 3.14159
let half = 0.5
let sci = 1.5e-3

An i suffix on a decimal numeric literal creates an imaginary value, for use with complex64 and complex128. Only decimal literals support the i suffix.

let im = 4i
let im_float = 3.14i
let yes = true
let no = false

String literals are enclosed in double quotes and may span multiple lines. Type: string.

let greeting = "Hello, world!"
let escaped = "line one\nline two"
let quoted = "She said \"hi\""
let multiline = "This is
a very long
multiline string."

A newline between the opening and closing " is preserved in the value as a \n byte. Source-code indentation inside a multi-line string is part of the value.

Escape sequences:

SequenceMeaning
\\Backslash
\"Double quote
\nNewline
\rCarriage return
\tTab

A raw string literal begins with r" and ends with ". Inside, every character is literal, i.e. backslashes are not escapes. A raw string may span multiple lines.

let pattern = r"([a-zA-Z])(\d)"
let path = r"C:\Users\me"
let block = r"line one
line two"

Raw strings cannot contain a double quote. Escape it as \" in a regular string instead.

A format string begins with f" and can contain interpolated expressions in {}. The text portions follow the same multi-line rules as regular strings. Interpolation expressions inside {} must remain on a single line.

let name = "Alice"
let age = 30
let msg = f"Hello, {name}! You are {age} years old."
let multiline = f"name: {name}
age: {age}"

Use {{ and }} to escape braces.

Rune literals are enclosed in single quotes.

let c = 'a'
let newline = '\n'
let null = '\0'

Escape sequences: \\, \', \n, \r, \t, \0.

In a bracketed sequence of values, all elements must have the same type. This sequence is a Slice by default, or an Array when an array type is expected.

let nums = [1, 2, 3]inferred as Slice<int>
let address: Array<byte, 4> = [127, 0, 0, 1]inferred type overridden
let empty: Slice<int> = []empty requires annotation

Line comments start with // and extend to the end of the line.

let x = 42 // a comment

Doc comments start with /// and document the item that follows.

/// Returns the sum of two integers.
fn add(a: int, b: int) -> int {
a + b
}

File comments start with //! and document the file itself. They form one contiguous block at the top of the file. Nothing but a shebang may come before it, and its content is emitted at the top of the generated Go file.

//! Copyright 2026 Acme Corp.
//! SPDX-License-Identifier: Apache-2.0
import "strings"

At the very start of a file, a #! plus an interpreter is a shebang line, which makes a script directly executable on Unix.

#!/usr/bin/env -S lis run
import "go:fmt"
fn main() {
fmt.Println("hi")
}
Terminal window
chmod +x greet.lis && ./greet.lis

Semicolons are never required. lis format removes any you write.