Programs
A Sere program is a module. The compiler typechecks the module, expands macros, then lowers a typed AST to LLVM. Linking produces a native binary only when the entry module defines main.
Entry
main may return i32 or void. An i32 becomes the process exit code.
def main() -> i32:
return 0Arguments arrive as list[str] when you ask for them. The generated C main converts argv into that list.
def main(argv: list[str]) -> i32:
return len(argv)A module without main is still a valid compilation unit. --emit-llvm works. The linker will not invent a C main for you.
Initialization order
- Linked native
sere_mod_init(if you provided one with--link). - Top-level statements in imported modules (typechecked, then run as module init).
- Top-level statements in the entry file.
main.
Top-level work is module initialization, not a script body that replaces main. Keep it for constants, tables, and static counters.
Bindings at the top level
A typed binding may omit an initializer. The slot is default-initialized. = always requires an expression.
ptr: Unique[i32] # ok
n: i32 = 0 # ok
# n: i32 = # errorprelude.sere is loaded into the user module and marked fromPrelude(), so it is not re-emitted as your code. Names from the prelude are in scope without an import.
Indentation
Indent with spaces only. Tabs are IndentationError. The lexer emits Indent / Dedent / Newline tokens; the parser treats indentation as structure, not style.
What the compiler actually emits
Reachable functions become LLVM functions. User main is wrapped as C main. Unique pointers are dropped at end of scope. The runtime library sere_rt is always linked.
See The compiler for the pipeline and Memory for drop vs free.