# `Color.CSS.Calc`

A tiny `calc()` expression evaluator for CSS color components.

Supports:

* Numeric literals (`255`, `0.5`, `-1.2`).

* Percentages (`50%`) — interpreted as the raw percent value
  (`50.0`), *not* as a fraction. Channel parsers in
  `Color.CSS` decide whether to scale.

* The four arithmetic operators `+ - * /` with the conventional
  precedence and associativity.

* Parenthesised sub-expressions.

* Identifier references (`r`, `g`, `b`, `h`, `s`, `l`, `alpha`,
  …) that resolve against a `bindings` map. This is what makes
  relative color syntax (`oklch(from teal calc(l + 0.1) c h)`)
  work.

* The `none` keyword — evaluated as `0.0`.

Whitespace is ignored.

## Example

    iex> {:ok, ast} = Color.CSS.Calc.parse("255 / 2")
    iex> {:ok, value} = Color.CSS.Calc.evaluate(ast, %{})
    iex> value
    127.5

    iex> {:ok, ast} = Color.CSS.Calc.parse("l + 0.1")
    iex> {:ok, value} = Color.CSS.Calc.evaluate(ast, %{"l" => 0.5})
    iex> Float.round(value, 4)
    0.6

    iex> {:ok, ast} = Color.CSS.Calc.parse("(r + g + b) / 3")
    iex> {:ok, value} = Color.CSS.Calc.evaluate(ast, %{"r" => 200, "g" => 100, "b" => 50})
    iex> Float.round(value, 4)
    116.6667

# `evaluate`

Evaluates a parsed `calc()` AST against a binding map.

### Arguments

* `ast` is a value returned by `parse/1`.

* `bindings` is a map from identifier strings to numeric values.

### Returns

* `{:ok, float}` on success.

* `{:error, reason}` on division by zero or unbound identifier.

# `parse`

Parses a calc() body into an AST.

### Arguments

* `string` is the body of a `calc(...)` expression — everything
  between the outermost parens.

### Returns

* `{:ok, ast}` on success.

* `{:error, reason}` on a parse error.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
