Darklang Documentation
repository·main·Indexed 24 days ago
https://github.com/darklang/darkAn integrated language, editor, and infrastructure platform for creating backends and CLIs. Documentation covers database schema management via schema.sql and incremental migrations, the LibParser recursive-descent pipeline, LibDB SQLite-backed persistence, LibSerialization hashing and binary formats, and the WASM-based browser REPL.
What's inside Darklang
- The Darklang implementation of the Model Context Protocol (MCP) enables communication between AI models and external tools or resources. Much like the Language Server Protocol (LSP), MCP utilizes JSON-RPC for communication between clients and servers. This allows AI models to access external data, execute code, and interact with various services through a standardized interface.
Overview of LibConfig
mainLibConfig is a system for managing adjustable configuration values used across the Darklang CLI and other entry points. These configuration values are backed by environment variables and are designed to always provide sensible defaults if no environment variable is set.Overview of LibDB persistence
mainLibDB provides SQLite-backed persistence for core Darklang services, including the package manager, branches, SCM operations, user databases, and execution traces. It serves as the persistent companion to the in-memory
PT.PackageManagerfound inLibExecution.Key characteristics:
- Content-Addressing: Items are content-addressed using SHA256 keys.
- Branch Scoping: Locations (bindings of names to hashes) are scoped to specific branches.
What is Darklang?
mainDarklang is a combined language, editor, and infrastructure designed to simplify the development of backends and Command Line Interfaces (CLIs).Understand the Language Server Protocol (LSP) implementation in Darklang
mainThe
@Darklang.LanguageServerProtocolmodule provides types and functions to support the Language Server Protocol (LSP) version 3.17.0. It is designed primarily as an LSP Server implementation.Key architectural components include:
common.dark: Contains the base types used across the protocol.- Spec-organized files: Most files are organized according to the sections of the official LSP specification.
io.dark: The intended entry point for handling incoming messages and dispatching responses and notifications to the client (currently reserved for future abstraction).
Types of Dark test files
mainTest files in the
testfilesdirectory are categorized by their purpose:executionfiles: Used to test the Dark language and most of the standard library.httpclientfiles: Used to test the HTTP Client in the standard library.http-serverfiles: Test the HTTP handlers hosted bydarklang serve(driven throughStdlib.HttpServer.serve).datadirectory: Houses static assets used as accessory data for tests.
Use the Local Execution host
mainThe Local Execution host allows you to run local scripts with both the Backend and special F#/dotnet libraries enabled. This provides a way to execute code within the Darklang environment while leveraging local system capabilities and specific language libraries.Overview of LibSerialization storage and hashing
mainLibSerialization is responsible for how code is stored on disk and how content-addressable hashes are produced. It consists of two primary components:
- Binary format: Used for the package store and on-disk caches. This format is subject to schema-stability rules to ensure data remains readable as the Project Type (PT) and Runtime (RT) evolve.
- Deterministic serialization (
Hashing/Canonical.fs): Used specifically for hashing. This process skips identity-irrelevant fields—such as AST node IDs and descriptions—to ensure that the same logical code always produces the same hash.
Overview of the LibParser pipeline
mainLibParser is a hand-written recursive-descent parser for Darklang source code. It transforms raw source text into a syntax tree through a multi-stage pipeline. The pipeline consists of:
- Lexing:
Lexer.tokenizeconverts source into tokens and trivia (comments and positions), preserving whitespace text only for position tracking. - Syntax Parsing:
Parser.parseSyntaxperforms a shared private syntax pass. - Lowering/Branching:
- Tooling Path:
Parser.parseproduces aWrittenTypestree along with syntax and structural diagnostics used for IDE/tooling support. - Execution Path:
Parser.parseFor Modeperforms structural and file-purpose validation, resulting in aValidatedSourceFile. This is further lowered viaWrittenTypesToProgramTypesinto executableProgramTypes.
- Tooling Path:
To ensure consistency, the two lowering paths are verified via differential testing to ensure their
ProgramTypesoutput is identical.source │ Lexer.tokenize tokens + trivia ▼ │ Parser.parseSyntax shared private syntax pass ▼ ├─ Parser.parse WrittenTypes tree + diagnostics │ └─ Builtins.Language.WrittenTypesToDarkTypes └─ Parser.parseFor Mode ValidatedSourceFile └─ WrittenTypesToProgramTypes → executable ProgramTypes- Lexing:
Understand Darklang REPL state and builtins
mainThe WASM REPL has specific behaviors regarding state and available builtins:
State Management
- Package Items: Declarations of
fn,type, orvalbecome in-memory package items under theReplowner. These are callable without qualification in subsequent entries, and redefinitions will take precedence. - Session Variables: A bare
let x = ...declaration persists its bindings as session variables. These are injected into later entries as pre-loaded VM registers. - Scope Limitation: Declarations cannot close over session variables; package items are considered static.
Builtins and Environment
- Standard Library:
Stdlib.*is provided via the package snapshot. - Core Builtins: Includes
Builtins.PureandBuiltins.Http.Client(which usesfetchand is subject to CORS). - Browser I/O: Supports
printLineandprint(buffered into results) andpmGetLocationsBy*lookups used by the pretty-printer. - Output: The REPL output matches the behavior of
dark eval.
- Package Items: Declarations of
Understand the Darklang CLI data directory structure
mainThe
.darklangdirectory is the central data store for the Darklang CLI. It manages package definitions, settings, and logs.Directory Contents:
data.db: A SQLite database containing package definitions (including the standard library) and other CLI data.logs/: A subdirectory containing CLI operation logs for debugging.README.md: Documentation explaining the directory contents.
Important Note on Portability: If you move the Darklang CLI executable, you must move the
.darklangdirectory with it. If the directory is separated from the executable, you will lose your local package cache and settings, and updates to your local package store will not be preserved.How Vanilla JSON serialization works in Dark
mainDark uses a "Vanilla" JSON serializer to handle arbitrary types. To prevent accidental breaking changes to serialization formats, Dark enforces strict allow-listing and testing requirements for any type intended for serialization.
Requirements for using Vanilla serialization:
Explicit Allow-listing: You must explicitly allow a type using
Json.Vanilla.allow<type>(). Attempting to serialize or deserialize a type that has not been explicitly allowed will result in an exception.- Placement: The
allowcall should be placed in aninitfunction within the same file that uses the serializer, rather than in the file where the type is defined. - Nesting: You only need to call
allowfor the top-level type being serialized; you do not need to explicitly allow all nested types within that structure.
- Placement: The
Mandatory Test Cases: Every top-level type that is serialized must have a corresponding test case. This ensures that any changes to the type's structure are caught via git diffs in the serialization artifacts.
- Tests will fail if an allowed type lacks a test case.
- Tests will fail if a test case exists for a type that hasn't been explicitly allowed.