3. Programs and Source Files
3.1 Phase Distinction
Scope: source-level and compile-time organization only.
- Parse/compile/validate/typecheck/plan operate on source-level program structure.
- Frontend resolution may assemble bundled stdlib and imported libraries before compile.
- Runtime execution semantics are defined in Chapter 9 and policy switches in Chapter 10.
- File/program structure is independent from runtime strategy details.
3.2 Source File
A Culsma source file:
MUSTfollow lexical rules in Chapter 2.SHOULDuse the.culsextension in tooling and examples.MAYcontain zero or more source-level include declarations.MAYcontain zero or more library import declarations.MAYcontain zero or more top-level executable statements.MAYcontain zero or moreprotocoldeclarations.
Baseline:
includeis used for source-level file loading.importis used for library-level module loading through frontend resolution.- Cross-protocol reference uses
include ProtocolName;or qualified call syntaxModule.Protocol(...).
Run boundary:
- A single run has exactly one entry source.
- Top-level executable statements in the entry source form the script entry.
- Files loaded through source include or library import are definition dependencies for the current run.
- CLI tooling
MAYaccept multiple input files as a batch; each batch item is a separate run with its own entry source, runtime state, and output.
3.3 Program
A Culsma program is the parsed result of one entry source plus definition dependencies loaded for that run.
Program-level rules:
- A program contains top-level source include metadata, top-level library import metadata, top-level script statements, and protocol declarations.
- Protocol order in a loaded program is deterministic and follows loader traversal order.
- A program
MAYretain source include declarations as source-loading metadata. - A program
MAYretain library import declarations as frontend-resolution metadata. - Protocol names are case-sensitive.
- Source spans are preserved on protocol and statement nodes for diagnostics.
- The parser AST shape is not itself the final frontend bundle; frontend resolution may prepare a derived program for compile.
Top-level syntax summary:
Program := { SourceIncludeDecl | LibraryImportDecl } { TopLevelStatement | ProtocolDecl }
SourceIncludeDecl := "include" STRING ";"
LibraryImportDecl := "import" IDENTIFIER ";"
ProtocolDecl := "protocol" IDENTIFIER [ "(" [ ParamDeclList ] ")" ] [ ReturnsDecl ] "{" { Statement } "}"
TopLevelStatement := Statement
ParamDeclList := ParamDecl { "," ParamDecl }
ParamDecl := IDENTIFIER [ "=" Expression ]
ReturnsDecl := "returns" "(" [ ReturnNameList ] ")"
ReturnNameList := IDENTIFIER { "," IDENTIFIER }3.3.1 Abstract Program Model
Parser output is organized in this abstract shape:
Program
-> source_includes: SourceIncludeDecl*
-> library_imports: LibraryImportDecl*
-> statements: Statement*
-> protocols: ProtocolDecl*
-> params: ParamDecl*
-> returns: ReturnName*
-> statements: Statement*The abstract program model describes parser output shape, not later semantic/runtime behavior.
3.4 Protocol Declarations
A protocol declaration has the form:
protocol <IDENTIFIER>( [ <ParamDeclList> ] ) [ returns ( <ReturnNameList> ) ] { <statement>* }
protocol <IDENTIFIER> { <statement>* } // compatible formRules:
- A program
MAYcontain multiple protocols. - A protocol name
MUSTbe a validIDENTIFIERandMUST NOTbe a keyword. - Protocol declarations
MUST NOTbe nested. - File loading rejects duplicate protocol names across loaded source files.
- Frontend resolution rejects imported protocol names that conflict with protocols already loaded from the entry source and its include dependencies.
- Frontend resolution also rejects conflicts against bundled stdlib protocol names.
- A protocol
MAYdeclare named parameters with optional defaults. - A protocol
MAYdeclare explicit return names withreturns (...). - If
returns (...)is declared, the tail return statementSHOULDbind those names explicitly. - A protocol declaration is a callable definition in the loaded program namespace.
3.5 Include, Import, and Execution Entry
3.5.1 include
include "<relative_or_absolute_path>.culs"; loads another source file.
Rules:
- File include declarations are source-level declarations and appear before top-level executable statements and
protocoldeclarations. - Relative paths are resolved against the including file's directory.
- Included files are loaded recursively.
- Missing include targets are load-stage hard errors.
- Include cycles are load-stage hard errors.
- Repeated include of the same canonical path SHOULD be deduplicated by loader.
- After one file is parsed successfully, all protocols loaded from that file inherit the source module name derived from the file stem.
3.5.2 import
import <LibraryModuleName>; loads a library module through frontend resolution.
Rules:
- Library import declarations appear before top-level executable statements and
protocoldeclarations. - Import resolution is a frontend-resolver concern, not a parser file-include concern.
- User library protocols join the active program namespace during frontend resolution.
- Bundled standard-library protocols remain separately available as callable definitions and do not join the active program namespace.
- Missing import targets are frontend-resolution hard errors.
- Imported protocols join the active program namespace before compile.
3.5.3 Qualified Protocol Reference
<ModuleName>.<ProtocolName>(); references another protocol.
Rules:
ModuleNameresolves from loaded source module names or imported library module names.- Referenced protocol targets
MUSTexist within the active program namespace. - Resolution is exact and case-sensitive.
- Missing targets are plan-stage hard errors reported as
PLAN_UNKNOWN_REFERENCE; this diagnostic code is defined in Chapter 8. - Reference cycles are plan-stage hard errors reported as
PLAN_REFERENCE_CYCLE; this diagnostic code is defined in Chapter 8. - Protocol calls SHOULD use named arguments.
- The parser also accepts positional surface arguments, but plan-time parameter binding is name-based.
Source-surface notes:
include ProtocolName;remains part of the parser surface as a protocol-include statement.Module.Protocol(...)is also valid and is the clearer cross-file call form.- Both forms resolve against the active loaded program namespace during plan lowering.
3.5.4 Execution Entry
Source files may contain top-level executable statements and multiple protocol definitions. Execution entry selection belongs to the execution/planning layer.
Planning behavior is summarized in Chapter 9.
Rules:
- Top-level executable statements in the entry source form the script entry.
- Protocol declarations are callable definitions available to script statements and protocol references.
- The version-scoped compatibility rule summarized in Chapter 9 covers entry sources with exactly one unreferenced root protocol.
- Top-level executable statements in include/import dependency files are parsed but are not added to the current run's script entry and are not executed.
3.6 Scope Boundary
The active program namespace boundary is the visibility boundary:
- File include resolution loads targets from the source-level include graph rooted at the entry source.
- Library import resolution loads targets from frontend-configured library roots and bundled stdlib.
- The loaded program namespace consists of protocols from the entry source, included local files, and resolved imported library protocols.
- Bundled standard-library protocols remain separately available as callable definitions without joining the loaded program namespace.
- Dependency-file script statements do not cross this namespace boundary.
