Syntax reference
The precise rules, for when you want to look something up. If you're just starting, read the Guide first — this is the reference behind it.
Source form
- Plain UTF-8 text,
.lipextension. A#!/usr/bin/env lippyline is allowed at the top. #starts a comment; it runs to the end of the line.- Indentation isn't significant (but four spaces inside a block reads well).
- No semicolons. One statement per line — you can't put two statements on one line, so the question of a separator never comes up.
Where a line ends
A statement ends at the end of the line, unless the line is obviously unfinished — and it's unfinished when either:
- A bracket is open — a
(,[or{hasn't been closed. This is how list and map literals span lines. - The line ends on something that needs a right-hand side — a
+ - * /, a word operator (and,or,mod,equals,greater than, …), a comma,is, or a preposition (to,at,of,in).
There's no line-continuation character. To wrap a long line, break it after an operator or comma, where the sentence is audibly unfinished:
total is price + shipping +
handling
Rule of thumb: end the line where you'd pause reading it aloud.
Names and keywords
Names are letters, digits and _, starting with a letter. They're case-sensitive;
lowercase is the convention.
These words are reserved and can't be used as names:
is and or not mod
equals greater less than at least most
contains starts ends with matches
of to by from in when
if then else end
for give loop stop skip
fun return
true false nothing
Notice what isn't reserved: say, warn, ask, run, capture, read, write,
move, push, split, fetch, connect, query and the rest are ordinary functions,
not keywords. say "hi" is just a function call.
Values and literals
| Kind | Example |
|---|---|
| number (one numeric type) | 42, 3.14, -7 |
| string | "hello" — double quotes |
| bool | true, false |
| nothing | nothing |
| list | [1, 2, 3] — trailing comma allowed |
| map | {"gitea": "the git server"} — trailing comma allowed |
| range | 1 to 100 (numbers only) |
| raw string | `\d+` — backticks, taken literally |
Strings
Double-quoted strings interpolate — "{expr}" evaluates any expression, including
at/of chains: "exit code {code of result}". Escapes are \n, \t, \", \\, \{.
A raw string in backticks is taken exactly as written — no escapes, no interpolation — and may span lines. Reach for it when the text has backslashes, braces or quotes:
name matches `^IMG_\d+`
config is from_json(`{"port": 3000}`)
Operators and precedence
Loosest to tightest binding:
| Level | Operators |
|---|---|
| 1 | or |
| 2 | and |
| 3 | not |
| 4 | comparisons: equals, not equals, greater than, less than, at least, at most, contains, starts with, ends with, matches |
| 5 | range to |
| 6 | + - |
| 7 | * / mod |
| 8 | unary - |
| 9 | access: at, of |
| 10 | calls, ( ) |
Comparisons don't chain — a equals b equals c is an error with a hint. matches is the
pattern comparison, the same question contains asks but with a pattern:
if name matches `^IMG_\d+` then …
at and of
There's no dot notation and no [ ] indexing. Two words do all access:
collection at key— indexed access. Lists are 1-based; maps use the same word:args at 1,services at "gitea". It's also an assignment target:scores at "lee" is 10.name of expr— property access:ok of result,code of result,lines of text,first of args,last of parts,files of folder. The built-in properties (len,first,last,lines,keys,copy,files) can also be called —len(args)is exactlylen of args.
Calls
Calls are paren-free, Ruby-style:
say "hello"
greet name
move old to new
run "rsync -a {src}/ {dst}/"
The rules that keep this unambiguous:
-
Arguments run to the end of the statement.
say a + bmeanssay(a + b). -
So a paren-free call is always the last thing in its statement — the whole statement, or the whole right side of
is,when,return, orfor … in. -
Paren-free calls don't nest. An inner call needs parentheses:
number(ask "your guess? "),split trim(line). Writingsplit trim lineis an error that says exactly that. -
Multiple arguments are separated by commas —
greet name, greeting— or by a preposition (below). -
A lone name that names a function calls it, but only when it stands alone as a whole statement. In expression position a name is the value itself:
beep # a statement: calls beep() b is beep # an expression: b is now the function b() # …called later, through the name routes is {"/": home} # functions live in lists and maps like any value -
Parenthesised calls —
trim(line, "%")— are always available and behave identically. Use parens only when nesting forces you to.
Prepositional parameters
Some functions read as sentences because their last parameter is declared with a preposition, which replaces the comma at the call site:
fun move(what, to where) # called: move a to b
fun push(item, to list) # called: push item to keep
fun write(text, to path) # called: write report to "out.txt"
This is ordinary syntax — your own functions can declare prepositional parameters too. The
prepositions in use are to and by (as in sort xs by len).
Statements
name is expr # assignment (also: target at key is expr)
some_call arg, arg # a call
if expr then … else if expr then … else … end
for name in expr … end # lists, ranges, and (with two names) maps
for line in stdin … end # lines piped in — one at a time
for key, value in expr … end
names is for s in services give name of s # a for read as one value
big is for f in files give f when len(f) greater than 100
loop … end # forever, until stop
stop # break | stop when expr
skip # continue | skip when expr
fun name(params) … end
return expr # or a bare return
isis the only way to assign, and assignment is never an expression —if x is 5 thenis an error (it means: did you meanequals?).thencloses a condition, so a multi-line condition needs no special rule — it simply isn't finished untilthen.else ifchains; there's noelif/elsif.stopandskipare only legal inside a loop.- Function definitions declare parameters in parens; only calls go paren-free.
Deliberately not in lippy
Semicolons, ++/--, the ternary ?:, switch, while (use loop + stop when), dot
notation, [ ] indexing, single-quoted strings, and more than one statement per line.