Skip to content

Documentation Generator — Lexer Changes & Doc Test Engine

Status: Refined with implementation details. INVEST: 5/5/4/4/5/4 after refinement.

Lexer Changes for /// Doc Comments

Token Type

cpp
// In include/token.hpp
enum class TokenType : uint32_t {
    // ... existing ...
    DOC_COMMENT = 0x0C00,  // /// doc comment
};

Lexer Implementation

cpp
// In src/parsing/Lexer.cpp

if (state.source[state.pos] == '/' && state.source[state.pos + 1] == '/') {
    if (state.source[state.pos + 2] == '/') {
        // Doc comment: ///
        return lexDocComment(state);
    } else {
        // Regular comment: // → skip (existing behavior)
        return skipLineComment(state);
    }
}

Token lexDocComment(LexState &state) {
    state.pos += 3;  // skip ///
    
    // Optional space after ///
    if (state.source[state.pos] == ' ') {
        state.pos++;
    }
    
    // Lex until end of line
    auto start = state.pos;
    while (state.pos < state.source.size() && state.source[state.pos] != '\n') {
        state.pos++;
    }
    
    auto text = state.source.substr(start, state.pos - start);
    return Token(TokenType::DOC_COMMENT, text, state.line, state.col);
}

AST Association

Doc comments are associated with the AST node that follows them:

cpp
// In ParserImpl.cpp, when parsing top-level definitions:
if (peek().type == TokenType::DOC_COMMENT) {
    currentDocComment = consume().repr;
    // The next definition (fun/type/trait/val) gets this doc comment
}

// In each definition's AST node:
struct FunctionDef : Definition {
    std::string docComment;  // Associated doc comment (empty if none)
    // ... existing fields ...
};

Cross-Reference Resolution

Algorithm

Input: Doc comment text with [TypeName] or `functionName` references
Output: Resolved HTML links

1. Collect all exported symbols from the module
2. For each `[Ident]` in the doc comment:
   a. Look up Ident in the module's exported symbols
   b. If found → emit <a href="#symbol-ident">Ident</a>
   c. If not found → check imported modules
   d. If found in imported module → emit <a href="module.html#symbol-ident">Ident</a>
   e. If not found → emit Ident as plain text (no warning, silent fallback)

Search Index

The doc generator builds a search index as JSON:

json
{
  "entries": [
    {"name": "readFile", "type": "function", "module": "std.io", "url": "std/io.html#readFile"},
    {"name": "HashMap", "type": "type", "module": "std.collections", "url": "std/collections.html#HashMap"},
    ...
  ]
}

Doc Test Execution

Doc tests are code blocks in doc comments that are compiled and executed:

ng
/// # Examples
/// ```
/// val x = add(2, 3);
/// assert(x == 5);
/// ```
fun add(a: i32, b: i32) -> i32 => a + b;

Execution Mechanism

cpp
class DocTestRunner {
public:
    auto runTests(const std::string &moduleName, const std::vector<DocTest> &tests) -> TestResults;
    
private:
    // For each doc code block:
    // 1. Extract the code (between ``` and ```)
    // 2. Compile it in a sandbox that imports the documented module
    // 3. Execute and check for errors
    // 4. Record pass/fail
    
    struct DocTest {
        std::string code;
        std::string moduleName;
        uint32_t line;
    };
};

Sandboxing

Doc tests execute in a restricted environment:

  • No file system access (readFile/writeFile disabled)
  • No module import outside the documented module
  • Configurable timeout (default: 5 seconds)
  • No mutable global state beyond the test scope

Output Format

HTML Template (simplified)

html
<!DOCTYPE html>
<html>
<head>
    <title>Module: {{module_name}} — NG Documentation</title>
    <link rel="stylesheet" href="ng-doc.css">
</head>
<body>
    <nav class="sidebar">
        <h2>Modules</h2>
        <ul>
        {{#each modules}}
            <li><a href="{{name}}.html">{{name}}</a></li>
        {{/each}}
        </ul>
    </nav>
    <main>
        <h1>Module: {{module_name}}</h1>
        <section class="functions">
            <h2>Functions</h2>
            {{#each functions}}
            <article id="{{name}}">
                <h3><code>{{signature}}</code></h3>
                <div class="doc">{{{docHtml}}}</div>
            </article>
            {{/each}}
        </section>
    </main>
    <script src="search.js"></script>
</body>
</html>

CSS Styling (ng-doc.css)

Dark/light theme via CSS variables. Responsive layout with collapsible sidebar.

CLI Interface: ng doc

bash
ng doc                                # Generate HTML docs for current project
ng doc --output docs/                 # Specify output directory
ng doc --format markdown              # Output Markdown instead of HTML
ng doc --serve                        # Start HTTP server at localhost:8080
ng doc --check                        # Check that all public items have docs
ng doc --test                         # Run doc tests
ng doc --no-deps                      # Only document the current project, not deps

Effort Estimate

ComponentEffort
Lexer changes for ///0.5 day
AST association (store doc comments)0.5 day
HTML template rendering2 weeks
Markdown->HTML rendering library integration1 week
Cross-reference resolution1 week
Search index0.5 week
Doc test extraction + execution2 weeks
ng doc --serve (HTTP server)0.5 week
CSS + theme1 week
Tests1 week
Total~9 weeks

Made with ❤️ by the NG community.