Skip to content

Comments

Accepted

Accepted for V1 comment and documentation-comment source semantics.

Catalyst has regular comments, declaration documentation comments, and module documentation comments. Regular comments are semantic whitespace. Documentation comments are source tokens that attach to documentable source items and lower into normalized documentation metadata for tooling and reflection.

Comment delimiters are ASCII byte sequences. Normalized documentation text is valid UTF-8 []const u8.

Comment Forms

Form Kind Meaning
// text regular line comment ignored by language semantics
/* text */ regular block comment ignored by language semantics
/// text declaration doc line documents the next documentable item
/** text */ declaration doc block documents the next documentable item
//! text module doc line documents the current source-file namespace
/*! text */ module doc block documents the current source-file namespace

Line comments run to the physical line ending. Block comments may span lines and nest.

// Regular note.

/*
  Regular block.

  /*
    Nested block.
  */
*/

/// Public docs for `read`.
pub fn read(dst: []f32) usize {
}

Comment delimiters inside string and code point literals are literal text, not comments:

const url = "module://std/main.ct" // comment starts here
const text = "not /* a comment */"
const slash = '/'

Lexical Disambiguation

Doc-comment openers are intentionally narrow so visual separator comments do not accidentally become public documentation.

Source Kind
// regular line comment
/// declaration doc line
//! module doc line
//// regular line comment
/* regular block comment
/** declaration doc block
/*! module doc block
/*** regular block comment
/**/ regular block comment

Tie-breaking is longest-form except for visual separators and empty block comments: //// and /*** are regular comments, and /**/ is an empty regular block comment.

All block-comment forms use the same nesting rule. After the lexer enters any block comment, nested /* increments the nesting depth and */ decrements it. The outer comment token ends when depth returns to zero.

/**
  Documents the parser.

  /*
    Nested regular block inside documentation text.
  */
*/
pub fn parse(source: []const u8) Ast!ParseError {
}

Unterminated block comments are lexer errors.

Regular Comments

Regular comments are whitespace-equivalent for language semantics. They do not appear in SIR or IR and do not affect name resolution, type checking, runtime behavior, or documentation text.

Lexer and parser tooling should still preserve regular comments as trivia so formatters, IDEs, source navigation, and diagnostics can use them.

Comments are token separators. They may appear between grammar tokens where separation is legal, but cannot split one lexical token or appear inside adjacency-sensitive syntax that requires immediate spelling.

var x /* note */ = 1
std /* note */ .math.min(a, b)

These forms are invalid or tokenize as separate tokens:

ident/* note */ifier
1/* note */.0
@/* note */export(.c)

Statement Separation

Newlines inside or after comments count normally for newline-sensitive statement separation. Comments themselves do not synthesize statement separators.

const a = 1 // first value
const b = 2

A block comment on one physical line does not separate two statements. The semicolon is the separator here:

const a = 1 /* note */; const b = 2

Without the semicolon, the same-line form is invalid:

const a = 1 /* note */ const b = 2

If the block comment contains a newline, that newline participates in statement separation:

const a = 1 /*
note
*/
const b = 2

Declaration Documentation

Declaration doc comments attach to the next documentable item in the same leading metadata block. Only whitespace, attributes, regular comments, and other declaration doc comments may appear between the doc comments and the documented item. A blank source line terminates a declaration metadata block.

/// Reads samples into `dst`.
@deprecated("use read_into")
pub fn read(dst: []f32) usize {
}

Attributes and doc comments share the leading metadata block. Canonical style places doc comments first, then attributes, then visibility and the declaration header:

/// Exported C entry point.
@export(.c)
pub fn scale(x: f32) f32 {
  return x
}

Doc comments attach to the whole declaration, including visibility and any trailing conditional declaration guard:

/// Compares values when `T` supports equality.
fn contains(self: *const Self, value: *const T) bool
  if T.implements(PartialEq(T))
{
}

Doc comments may not appear inside a declaration header or before a trailing guard clause:

pub /// invalid
fn scale(x: f32) f32 {
  return x
}

fn contains(self: *const Self, value: *const T) bool
  /// invalid: guards are not documentable targets
  if T.implements(PartialEq(T))
{
}

Regular comments inside a declaration metadata block are allowed, but should warn because they are easy to confuse with public documentation:

/// Reads samples.
// Internal note kept out of generated docs.
pub fn read(dst: []f32) usize {
}

Regular comments between a declaration header and its trailing guard are also allowed, but should warn:

fn contains(self: *const Self, value: *const T) bool
  // Available only when T supports equality.
  if T.implements(PartialEq(T))
{
}

Documentable Items

V1 documentable items are:

  • const declarations, including namespace bindings such as const std = module("std")
  • fn declarations, with or without bodies
  • struct fields
  • contract operations
  • implementation declarations
  • implementation operations
  • struct and contract type constructors where the grammar permits a leading metadata block
/// Local alias for the standard library.
const std = module("std")

/// A reusable audio buffer.
pub const AudioBuffer = struct {
  /// Number of valid samples.
  len: usize

  /// Backing sample storage.
  data: []f32
}

/// `AudioBuffer` iteration yields sample pointers in index order.
impl AudioBuffer as Sequence(*const f32) {
  /// Returns an iterator over sample pointers.
  fn iter(self: *const Self) comptime Iterator(*const f32) {
  }
}

Visibility does not affect doc-comment legality. Both public and private documentable items may have docs, and reflection preserves docs for private items. Visibility affects lint policy: public API items without docs are lintable, while missing private docs should not warn by default.

Invalid doc targets

Doc comments on non-documentable constructs are hard compile errors.

Local declarations are not documentable in V1:

fn parse(source: []const u8) Ast!ParseError {
  /// invalid
  var index: usize = 0
}

Imports do not create reflected API docs:

/// invalid
import std.math

Use regular comments for local notes, imports, statement explanations, parameter notes, and attribute arguments.

Parameters do not have attached doc comments in V1. Document parameters through the function's canonical documentation markup instead:

/// Writes bytes into `dst`.
///
/// @param dst Destination buffer.
/// @param src Source bytes.
pub fn write(dst: []u8, src: []const u8) void {
}

Module Documentation

Module doc comments document the current source-file namespace. They do not attach to the next declaration.

//! Audio DSP utilities.
//!
//! Exposes filters, buffers, and sample conversion helpers.

const std = module("std")

Block form:

/*!
Audio DSP utilities.

Exposes filters, buffers, and sample conversion helpers.
*/

const std = module("std")

Module doc comments may appear only in the initial file metadata block, before the first non-metadata item. After a real declaration or import appears, later module doc comments are compile errors.

Module doc comments and declaration doc comments may not be interleaved in the same metadata block. Put module docs first, end that block with a blank source line, then start declaration docs for the first documentable item.

//! Audio DSP utilities.

/// Reads samples.
pub fn read(dst: []f32) usize {
}

This interleaved form is invalid:

//! Audio DSP utilities.
/// invalid: declaration docs must start after the module docs block ends
pub fn read(dst: []f32) usize {
}

Documentation for an imported module belongs in that module's own source file. A doc comment on a local namespace binding documents only the binding:

/// Local alias for the standard library namespace.
const std = module("std")

Doc Comment Grouping

Each /// line is one doc-line token. Adjacent declaration doc-line tokens in the same leading metadata block merge directly with \n; they do not get an extra blank line unless a bare doc line is present.

/// Reads samples.
/// Returns the number of samples read.
pub fn read(dst: []f32) usize {
}

A bare /// line creates a blank line in the normalized docs:

/// Reads samples.
///
/// Returns the number of samples read.
pub fn read(dst: []f32) usize {
}

When a metadata block mixes a run of doc-line tokens with a doc-block token, or contains multiple doc-block tokens, the normalized pieces concatenate in source order with one blank line between pieces:

/// Reads samples.
/**
  Returns the number of samples read.
*/
pub fn read(dst: []f32) usize {
}

A blank source line breaks declaration doc attachment. A doc comment left unattached at end of file, before a blank line, or before a non-documentable target is a compile error.

Normalization

The compiler normalizes doc comments before exposing them through reflection metadata such as docs: ?[]const u8. Rendering to HTML or another presentation format is a tooling concern.

For /// and //! lines:

  • strip the opening marker
  • strip one following space if present
  • preserve the rest of the line exactly
  • merge adjacent lines with \n

For /** ... */ and /*! ... */ blocks:

  • strip the outer delimiters
  • normalize line endings to \n
  • drop an initial empty content line
  • drop a final empty or whitespace-only content line
  • remove the minimum indentation shared by non-empty content lines
  • strip one leading * and one following space from each line when present
/**
 * Reads samples.
 *
 * Returns the number of samples read.
 */
pub fn read(dst: []f32) usize {
}

Normalizes to:

Reads samples.

Returns the number of samples read.

Documentation Markup

Catalyst documentation tooling should treat normalized doc text as Catalyst Doc Markup. Catalyst Doc Markup is Markdown-compatible at its base layer and may define semantic API-doc extensions such as parameter, return, error, law, safety, allocation, realtime, or capability sections.

The compiler stores normalized text only. It does not validate, render, or semantically interpret doc markup in V1. Markup diagnostics belong to documentation tools or lints.

/// Reads samples into `dst`.
///
/// @param dst Destination sample buffer.
/// @returns Number of samples read.
/// @errors ReadError when the source cannot provide samples.
///
/// ```ct
/// const count = try reader.read(dst)
/// ```
pub fn read(self: *Reader, dst: []f32) usize!ReadError {
}

Phase Ownership

Lexer
Recognizes regular comments, doc comments, module doc comments, invalid UTF-8, and unterminated block comments.
Parser/AST
Preserves regular comments as trivia, groups doc-comment tokens into metadata blocks, and attaches declaration docs to documentable AST nodes.
Sema/reflection
Lowers attached docs into normalized docs: ?[]const u8 metadata and reports invalid or unattached doc comments.
SIR/IR
Do not carry regular comments or documentation comments except through explicit semantic metadata that has already been accepted.