Metadata-Version: 2.4
Name: cbt_cfg
Version: 0.1.0
Summary: Configuration with Bounded Types
License-Expression: GPL-3.0-or-later
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# CBT – Configuration with Bounded Types

A Python library for lexing, parsing, and interpreting the CBT configuration language.

CBT is an S-expression based configuration language with a type system featuring
schemas, constraints, generics, union types, lazy self-references, and cycle detection.

## Quick Start

```python
from cbt import interpret, load

# Interpret CBT source text directly
result = interpret("""
    (define-schema server
        (host Str)
        (port (constraint Int (range 1 65535)))
        (tls (default Bool false)))

    (server
        (host "example.com")
        (port 443))
""")

# result == {"server": {"host": "example.com", "port": 443, "tls": False}}

# Or load from a .cbt file
result = load("config.cbt")
```

## CLI Usage

```bash
python main.py examples/test.cbt
```

Outputs the interpreted configuration as JSON.

## Language Features

### Schemas (Structs)

Schemas define typed, named collections of fields:

```lisp
(define-schema training
    (model_path Str)
    (lr Float)
    (epochs Int)
    (batch_size Int))

(training
    (model_path "openai/gpt2")
    (lr 1e-3)
    (epochs 3)
    (batch_size 16))
```

### Schema Extensions (Inheritance)

Schemas can extend other schemas, inheriting all their fields:

```lisp
(define-schema base-config
    (lr Float)
    (epochs Int))

(define-schema training-config extends base-config
    (batch_size Int)
    (optimizer Str))

; training-config has all fields: lr, epochs, batch_size, optimizer
(training-config
    (lr 1e-3)
    (epochs 10)
    (batch_size 32)
    (optimizer "adamw"))
```

Child fields override parent fields with the same name. Deep inheritance chains
are supported. Parent `ensure` clauses are not inherited (each schema defines
its own validation):

```lisp
(define-schema base
    (port (constraint Int (gt 0))))

(define-schema server extends base
    (host Str)
    (ensure (= (self host) "production-server")))
```

### Primitive Types

| Type    | Description                  | Example        |
|---------|------------------------------|----------------|
| `Str`   | String                       | `"hello"`      |
| `Int`   | Integer (not bool)           | `42`, `-7`     |
| `Float` | Float (also accepts int)     | `3.14`, `1e-3` |
| `Bool`  | Boolean                      | `true`, `false` |
| `Nil`   | Null / none                  | `nil`          |

### Union Types

Combine multiple types with `|`:

```lisp
(define-schema config
    (value (| Str Int)))  ; accepts either a string or an integer
```

### Default Values

Fields can have default values that are computed lazily:

```lisp
(define-schema config
    (name (default Str "unnamed"))
    (lr (default Float 1e-3)))
```

### Constraints

Add validation predicates on top of a base type:

```lisp
(define-func range (min max) (all (gt min) (lt max)))

(define-schema config
    (port (constraint Int (range 1 65535)))
    (lr (constraint Float (all (gt 0.0) (lt 1.0)))))
```

### Generics (Type-Level Functions)

Define parameterized types:

```lisp
(define-generic optional (a) (default (| a Nil) nil))

(define-schema config
    (name (optional Str))   ; expands to (default (| Str Nil) nil)
    (value (optional Int))) ; expands to (default (| Int Nil) nil)
```

### Self-References

Fields can reference sibling fields lazily:

```lisp
(define-schema training
    (batch_size Int)
    (micro_batch_size Int)
    (gradient_accumulation_steps
        (default Int (// (self batch_size) (self micro_batch_size)))))
```

Cycle detection prevents infinite recursion when two defaults reference each other.

### Lists

Fields can hold lists of typed values. Single values are auto-wrapped:

```lisp
(define-schema dataset (path Str))

(define-schema config
    (datasets (List dataset)))

; Single item (auto-wrapped in a list):
(config (datasets (dataset (path "data.jsonl"))))

; Multiple items (variadic):
(config
    (datasets
        (dataset (path "train.jsonl"))
        (dataset (path "val.jsonl"))))
```

### Nested Schemas

Schema instances can be nested inline without extra wrapping:

```lisp
(define-schema address
    (street Str)
    (city Str))

(define-schema person
    (name Str)
    (address address))

(person
    (name "Alice")
    (address
        (street "123 Main St")
        (city "Springfield")))
```

### Ensure Clauses

Validate meta-properties like "was this field explicitly provided?":

```lisp
(define-schema config
    (batch_size (default Int 0))
    (gradient_accumulation_steps (default Int 0))
    (ensure (one-of (provided batch_size)
                    (provided gradient_accumulation_steps))))
```

### Conditional Fields (`when`)

Fields can be conditionally required based on other field values using `when`:

```lisp
(define-schema config
    (mode Str)
    ; momentum is only present when mode is "sgd"
    (momentum (when Float (= (self mode) "sgd"))))
```

When the predicate is **true**, the field behaves according to its inner type.
When the predicate is **false**, the field is **absent** from the output.

`when` composes with `default` and `constraint`:

```lisp
(define-schema config
    (mode Str)
    ; When mode is "train", lr is required Float; otherwise field is absent
    (lr (when Float (= (self mode) "train")))
    ; When mode is "production", port has constraint; otherwise field is absent
    (port (when (constraint Int (range 1 65535)) (= (self mode) "production")))
    ; When mode is "train", momentum defaults to 0.9; otherwise field is absent
    (momentum (when (default Float 0.9) (= (self mode) "train"))))
```

A common pattern (mutually exclusive required fields):

```lisp
(define-schema config
    (batch_size (when Int (not (provided gradient_accumulation_steps))))
    (gradient_accumulation_steps (when Int (not (provided batch_size))))
    (ensure (any (provided batch_size) (provided gradient_accumulation_steps))))
```

If a user provides a field whose `when` predicate is false, a `ValidationError` is raised.

### Boolean Predicates

Boolean expressions for use in `when`, `ensure`, and other predicate contexts:

```lisp
(= a b)           ; equality comparison
(not expr)        ; logical negation
(all expr ...)    ; logical conjunction (all must be true)
(any expr ...)    ; logical disjunction (at least one must be true)
(one-of expr ...) ; exactly one must be true
(provided field)  ; true if field was explicitly provided
```

### Imports

Import native functions from built-in modules:

```lisp
(import (core fs) is_path)

(define-type File (constraint Str (fs is_path)))
```

### Binary Operations

Arithmetic in value expressions:

```lisp
(* a b)   ; multiplication
(// a b)  ; integer division
(+ a b)   ; addition
(- a b)   ; subtraction
```

## Requirements

Python ≥ 3.10 (uses `match` patterns and modern type hints)

## License

Copyright (C) 2026 Fizz

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
