Skip to content

3. Programs and Source Files

3.1 Phase Distinction

Scope: source-level and compile-time organization only.

  1. Parse/compile/validate/typecheck/plan operate on source-level program structure.
  2. Frontend resolution may assemble bundled stdlib and imported libraries before compile.
  3. Runtime execution semantics are defined in Chapter 9 and policy switches in Chapter 10.
  4. File/program structure is independent from runtime strategy details.

3.2 Source File

A Culsma source file:

  1. MUST follow lexical rules in Chapter 2.
  2. SHOULD use the .culs extension in tooling and examples.
  3. MAY contain zero or more source-level include declarations.
  4. MAY contain zero or more library import declarations.
  5. MAY contain zero or more top-level executable statements.
  6. MAY contain zero or more protocol declarations.

Baseline:

  1. include is used for source-level file loading.
  2. import is used for library-level module loading through frontend resolution.
  3. Cross-protocol reference uses include ProtocolName; or qualified call syntax Module.Protocol(...).

Run boundary:

  1. A single run has exactly one entry source.
  2. Top-level executable statements in the entry source form the script entry.
  3. Files loaded through source include or library import are definition dependencies for the current run.
  4. CLI tooling MAY accept 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:

  1. A program contains top-level source include metadata, top-level library import metadata, top-level script statements, and protocol declarations.
  2. Protocol order in a loaded program is deterministic and follows loader traversal order.
  3. A program MAY retain source include declarations as source-loading metadata.
  4. A program MAY retain library import declarations as frontend-resolution metadata.
  5. Protocol names are case-sensitive.
  6. Source spans are preserved on protocol and statement nodes for diagnostics.
  7. The parser AST shape is not itself the final frontend bundle; frontend resolution may prepare a derived program for compile.

Top-level syntax summary:

text
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:

text
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:

text
protocol <IDENTIFIER>( [ <ParamDeclList> ] ) [ returns ( <ReturnNameList> ) ] { <statement>* }
protocol <IDENTIFIER> { <statement>* }   // compatible form

Rules:

  1. A program MAY contain multiple protocols.
  2. A protocol name MUST be a valid IDENTIFIER and MUST NOT be a keyword.
  3. Protocol declarations MUST NOT be nested.
  4. File loading rejects duplicate protocol names across loaded source files.
  5. Frontend resolution rejects imported protocol names that conflict with protocols already loaded from the entry source and its include dependencies.
  6. Frontend resolution also rejects conflicts against bundled stdlib protocol names.
  7. A protocol MAY declare named parameters with optional defaults.
  8. A protocol MAY declare explicit return names with returns (...).
  9. If returns (...) is declared, the tail return statement SHOULD bind those names explicitly.
  10. 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:

  1. File include declarations are source-level declarations and appear before top-level executable statements and protocol declarations.
  2. Relative paths are resolved against the including file's directory.
  3. Included files are loaded recursively.
  4. Missing include targets are load-stage hard errors.
  5. Include cycles are load-stage hard errors.
  6. Repeated include of the same canonical path SHOULD be deduplicated by loader.
  7. 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:

  1. Library import declarations appear before top-level executable statements and protocol declarations.
  2. Import resolution is a frontend-resolver concern, not a parser file-include concern.
  3. User library protocols join the active program namespace during frontend resolution.
  4. Bundled standard-library protocols remain separately available as callable definitions and do not join the active program namespace.
  5. Missing import targets are frontend-resolution hard errors.
  6. Imported protocols join the active program namespace before compile.

3.5.3 Qualified Protocol Reference

<ModuleName>.<ProtocolName>(); references another protocol.

Rules:

  1. ModuleName resolves from loaded source module names or imported library module names.
  2. Referenced protocol targets MUST exist within the active program namespace.
  3. Resolution is exact and case-sensitive.
  4. Missing targets are plan-stage hard errors reported as PLAN_UNKNOWN_REFERENCE; this diagnostic code is defined in Chapter 8.
  5. Reference cycles are plan-stage hard errors reported as PLAN_REFERENCE_CYCLE; this diagnostic code is defined in Chapter 8.
  6. Protocol calls SHOULD use named arguments.
  7. The parser also accepts positional surface arguments, but plan-time parameter binding is name-based.

Source-surface notes:

  1. include ProtocolName; remains part of the parser surface as a protocol-include statement.
  2. Module.Protocol(...) is also valid and is the clearer cross-file call form.
  3. 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:

  1. Top-level executable statements in the entry source form the script entry.
  2. Protocol declarations are callable definitions available to script statements and protocol references.
  3. The version-scoped compatibility rule summarized in Chapter 9 covers entry sources with exactly one unreferenced root protocol.
  4. 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:

  1. File include resolution loads targets from the source-level include graph rooted at the entry source.
  2. Library import resolution loads targets from frontend-configured library roots and bundled stdlib.
  3. The loaded program namespace consists of protocols from the entry source, included local files, and resolved imported library protocols.
  4. Bundled standard-library protocols remain separately available as callable definitions without joining the loaded program namespace.
  5. Dependency-file script statements do not cross this namespace boundary.

Public language reference for the current Culsma surface.