Implementing CCL
Core Requirements
Section titled “Core Requirements”Every CCL implementation needs two operations:
- Parse - Convert text to flat key-value entries
- Build Hierarchy - Convert entries to nested structure via recursive parsing
Everything else is optional library convenience.
Language Patterns
Section titled “Language Patterns”Functional (Gleam, OCaml)
Section titled “Functional (Gleam, OCaml)”pub fn parse(text: String) -> Result(List(Entry), ParseError)pub fn build_hierarchy(entries: List(Entry)) -> Result(CCL, ObjectError)Use Result types, pattern matching, immutable data.
Imperative (Go, Java)
Section titled “Imperative (Go, Java)”func Parse(text string) ([]Entry, error)func BuildHierarchy(entries []Entry) (CCL, error)Use error returns, interfaces, builder patterns.
Dynamic (Python, JavaScript)
Section titled “Dynamic (Python, JavaScript)”def parse(text: str) -> list[Entry]def build_hierarchy(entries: list[Entry]) -> CCLUse exceptions, native collections, optional type hints.
Recursive Parsing Algorithm
Section titled “Recursive Parsing Algorithm”function build_hierarchy(entries): result = {} for (key, value) in entries: if key == "": add_to_list(result, value) else if value_looks_like_ccl(value): nested = parse(value) # Uses nested context detection result[key] = build_hierarchy(nested) # Recurse else: result[key] = value return resultFixed-point termination: Recurse until no more CCL syntax found.
See Parsing Algorithm for details.
Optional Features
Section titled “Optional Features”Type-Safe Access (library convenience):
get_string(ccl, path...): stringget_int(ccl, path...): intEntry Processing (composition utilities):
filter(entries, predicate): entriescompose(entries1, entries2): entriesSee Library Features for details.
Testing
Section titled “Testing”Use CCL Test Suite (447 assertions, 205 tests):
- Core Parsing: Filter tests by
functions: ["parse"] - Object Construction: Filter by
functionscontainingbuild_hierarchy - Typed Access: Filter by
validationstarting withget_ - Optional Features: Filter by
featuresarrays (comments,experimental_dotted_keys, etc.)
See Test Suite Guide for complete filtering examples.
Internal Representation Choices
Section titled “Internal Representation Choices”How you represent CCL data internally affects which features are easy to implement.
The OCaml Approach
Section titled “The OCaml Approach”The reference OCaml implementation represents all data as nested KeyMap structures:
type ccl = KeyMap of ccl StringMap.tAdvantages:
- Uniform data structure (everything is a nested map)
- Elegant recursion with
fixfunction - Clean pattern matching and algebraic properties
Trade-off: This model cannot distinguish between a string value:
name = Aliceand a nested key with empty value:
name = Alice =Both produce identical models: { "name": { "Alice": {} } }
Alternative: Tagged Union
Section titled “Alternative: Tagged Union”For implementations that need structure-preserving print, use a tagged union:
type Value = | String(string) | Object(map<string, Value>) | List(list<Value>)Advantages:
- Easy to implement
print(structure recovery is straightforward) - Can distinguish string values from nested structures
- Natural representation for most languages
Lazy Hierarchy Building
Section titled “Lazy Hierarchy Building”Another approach: preserve original entries and build hierarchy on-demand:
type CCL = { entries: List(Entry), hierarchy: Lazy(Object)}Advantages:
- Perfect structure preservation for
print - Can support both
printandcanonical_format - Deferred parsing cost
See Library Features for details on print vs canonical_format.
Common Challenges
Section titled “Common Challenges”Infinite Recursion: Fixed-point algorithm terminates naturally
Duplicate Keys: Merge into object or list based on value types
Empty Keys: Treat = value as list item
Unicode/CRLF: Break only on LF; preserve CR, preserve unicode
See Syntax Reference for edge cases.