Skip to content

Implementing CCL

Every CCL implementation needs two operations:

  1. Parse - Convert text to flat key-value entries
  2. Build Hierarchy - Convert entries to nested structure via recursive parsing

Everything else is optional library convenience.

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.

func Parse(text string) ([]Entry, error)
func BuildHierarchy(entries []Entry) (CCL, error)

Use error returns, interfaces, builder patterns.

def parse(text: str) -> list[Entry]
def build_hierarchy(entries: list[Entry]) -> CCL

Use exceptions, native collections, optional type hints.

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 result

Fixed-point termination: Recurse until no more CCL syntax found.

See Parsing Algorithm for details.

Type-Safe Access (library convenience):

get_string(ccl, path...): string
get_int(ccl, path...): int

Entry Processing (composition utilities):

filter(entries, predicate): entries
compose(entries1, entries2): entries

See Library Features for details.

Use CCL Test Suite (447 assertions, 205 tests):

  1. Core Parsing: Filter tests by functions: ["parse"]
  2. Object Construction: Filter by functions containing build_hierarchy
  3. Typed Access: Filter by validation starting with get_
  4. Optional Features: Filter by features arrays (comments, experimental_dotted_keys, etc.)

See Test Suite Guide for complete filtering examples.

How you represent CCL data internally affects which features are easy to implement.

The reference OCaml implementation represents all data as nested KeyMap structures:

type ccl = KeyMap of ccl StringMap.t

Advantages:

  • Uniform data structure (everything is a nested map)
  • Elegant recursion with fix function
  • Clean pattern matching and algebraic properties

Trade-off: This model cannot distinguish between a string value:

name = Alice

and a nested key with empty value:

name =
Alice =

Both produce identical models: { "name": { "Alice": {} } }

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

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 print and canonical_format
  • Deferred parsing cost

See Library Features for details on print vs canonical_format.

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.