Add bilingual manual and localized docs routes
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# Arithmetic Expression
|
||||
|
||||
Decodal supports arithmetic over concrete numeric values.
|
||||
|
||||
```dcdl
|
||||
{
|
||||
workers = 2 + 2;
|
||||
timeout = 30.0 / 2;
|
||||
port = 8000 + 80;
|
||||
negative = -1;
|
||||
}
|
||||
```
|
||||
|
||||
## Operators
|
||||
|
||||
- `+` addition
|
||||
- `-` subtraction
|
||||
- `*` multiplication
|
||||
- `/` division
|
||||
- unary `-` negation
|
||||
|
||||
`*` and `/` bind tighter than `+` and `-`.
|
||||
Parentheses can be used to make grouping explicit.
|
||||
|
||||
```dcdl
|
||||
2 + 3 * 4 # 14
|
||||
(2 + 3) * 4 # 20
|
||||
```
|
||||
|
||||
## Numeric behavior
|
||||
|
||||
Arithmetic requires concrete `Int` or `Float` operands.
|
||||
`Int + Int`, `Int - Int`, and `Int * Int` produce `Int` when no overflow occurs.
|
||||
Mixed `Int` / `Float` arithmetic produces `Float`.
|
||||
Division always produces `Float`.
|
||||
|
||||
Division by zero and integer overflow are evaluation errors.
|
||||
|
||||
Arithmetic expressions can be used anywhere a concrete numeric expression is expected, including defaults and numeric constraints.
|
||||
|
||||
```dcdl
|
||||
port = Int & > 4000 + 42 default 8080;
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Array Expression
|
||||
|
||||
An array expression represents an ordered sequence of values.
|
||||
|
||||
```dcdl
|
||||
[1, 2, 3]
|
||||
["a", "b", "c"]
|
||||
```
|
||||
|
||||
An array literal is a concrete value and does not require all elements to have the same type.
|
||||
|
||||
```dcdl
|
||||
["api", 8080, true]
|
||||
```
|
||||
|
||||
## Array constraint
|
||||
|
||||
Write an array constraint by placing an ellipsis immediately after `[`, followed by an element constraint.
|
||||
|
||||
```dcdl
|
||||
Names = [...String];
|
||||
Ports = [...(Int & >= 1 & <= 65535)];
|
||||
```
|
||||
|
||||
`[...T]` represents an array of zero or more elements, all of which satisfy `T`.
|
||||
An empty array satisfies every array constraint.
|
||||
|
||||
```dcdl
|
||||
[...String] & []
|
||||
[...String] & ["api", "worker"]
|
||||
```
|
||||
|
||||
The element constraint can be an object range.
|
||||
A field with a default that exists only on the right remains abstract in every element when the array constraint is composed.
|
||||
Its default is selected only if the array is later materialized.
|
||||
|
||||
```dcdl
|
||||
Services = [...{
|
||||
name = String;
|
||||
enabled = Bool default true;
|
||||
}];
|
||||
|
||||
Services & [{ name = "api"; }]
|
||||
```
|
||||
|
||||
Applying an array constraint to a concrete array refines each element against the element range using the same rules as `as`.
|
||||
For an object element range, a field present only on the left produces an error, while a field present only on the right remains abstract.
|
||||
|
||||
Decodal does not provide an abstract array type without an element constraint.
|
||||
The former `Array` primitive type is unavailable, and `T` is required in `[...T]`.
|
||||
`[String]` is not an array constraint; it is a concrete one-element array containing the unresolved `String` constraint.
|
||||
An array constraint describes only the element range. It has no length, positional tuple, or uniqueness constraints.
|
||||
|
||||
## Array concatenation
|
||||
|
||||
`++` concatenates two concrete arrays.
|
||||
|
||||
```dcdl
|
||||
base = ["read", "write"];
|
||||
extra = ["admin"];
|
||||
roles = base ++ extra;
|
||||
```
|
||||
|
||||
`roles` has the same value as:
|
||||
|
||||
```dcdl
|
||||
["read", "write", "admin"]
|
||||
```
|
||||
|
||||
`++` does not transform array elements.
|
||||
Elements from the right side follow those from the left.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Range Refinement
|
||||
|
||||
`narrower as wider` verifies that the left side is more concrete and narrower than the right side, then composes them asymmetrically.
|
||||
|
||||
```dcdl
|
||||
Int & > 10 as Int & > 0
|
||||
```
|
||||
|
||||
This succeeds and returns the narrower left-hand range, `Int & > 10`.
|
||||
The reverse, `Int as Int & > 0`, fails because the left side is not guaranteed to satisfy the right side.
|
||||
|
||||
## Object
|
||||
|
||||
For objects, the right side defines the field domain used to check the left:
|
||||
|
||||
- A field found only on the left lies outside the right-hand domain and produces an error.
|
||||
- A field found on both sides is checked recursively, and the narrower left-hand result is used.
|
||||
- A field found only on the right remains unevaluated in the result, whether or not it has a default.
|
||||
- Field order in the result follows the right-hand side.
|
||||
|
||||
```dcdl
|
||||
partial = {
|
||||
port = 8080;
|
||||
} as {
|
||||
host = String;
|
||||
port = Int;
|
||||
enabled = Bool default true;
|
||||
};
|
||||
```
|
||||
|
||||
`partial.port` becomes the concrete value `8080`.
|
||||
`partial.host` and `partial.enabled` remain abstract fields from the right; `as` neither selects nor forces the default for `enabled`.
|
||||
|
||||
If `partial` is later materialized, the normal materialization rules apply.
|
||||
In this example, unresolved `host` produces an error. If `host` is also made concrete, the default for `enabled` is selected during materialization.
|
||||
|
||||
## Default
|
||||
|
||||
A default is not evidence of range containment.
|
||||
When the same field exists on both sides, the result uses the left side and does not inject the right-hand default.
|
||||
|
||||
```dcdl
|
||||
Int & > 0 as (Int default 1)
|
||||
```
|
||||
|
||||
The result is `Int & > 0` without a default.
|
||||
A right-only object field, however, is retained as a whole and therefore retains any default its abstract value already had.
|
||||
|
||||
## Map and array ranges
|
||||
|
||||
`{...T}` allows arbitrary object keys and verifies that each value is narrower than `T`.
|
||||
|
||||
```dcdl
|
||||
ports = {
|
||||
http = 80;
|
||||
https = 443;
|
||||
} as {...(Int & >= 1 & <= 65535)};
|
||||
```
|
||||
|
||||
When `[...T]` is used on the right, every array element is refined against `T` in the same way.
|
||||
|
||||
## Concrete right-hand ranges
|
||||
|
||||
Primitive and composed constraints validate values in the usual way.
|
||||
If the right side is a concrete scalar or array literal, the left must have the same value or the same length and element structure.
|
||||
A function cannot be used as the right-hand range.
|
||||
|
||||
The left side does not have to be concrete.
|
||||
Abstract values can be used when their containment relation can be determined.
|
||||
Primitive types, numeric bounds, identical regexes or predicates, and element ranges of arrays and maps support containment checks.
|
||||
|
||||
A function parameter written as `name: range` uses the same refinement rules as `as` when its argument is forced.
|
||||
|
||||
## Precedence
|
||||
|
||||
`as` has the lowest precedence, below `default`, and is left-associative.
|
||||
|
||||
```dcdl
|
||||
narrow & overrides as Wider
|
||||
```
|
||||
|
||||
This is interpreted as `(narrow & overrides) as Wider`.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Composition Expression
|
||||
|
||||
A composition expression combines multiple values, constraints, or structures.
|
||||
|
||||
## `&`
|
||||
|
||||
`&` performs constraint-preserving composition.
|
||||
|
||||
```dcdl
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
Config = MyConfig & { port = 8000; };
|
||||
```
|
||||
|
||||
Because object fields present on only one side are retained, `&` has no direction.
|
||||
Use [`as`](./ascription.md) to verify that the left side is narrower than the right and treat the right side as the field domain.
|
||||
|
||||
## `//`
|
||||
|
||||
`//` performs a right-biased structural patch.
|
||||
|
||||
```dcdl
|
||||
Patched = Base // {
|
||||
feature_hoge.enable = false;
|
||||
};
|
||||
```
|
||||
|
||||
See [Operators](../operators.md) for details.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Default Expression
|
||||
|
||||
A `default` expression specifies a fallback selected during materialization when no explicit value exists.
|
||||
|
||||
```dcdl
|
||||
port = Int default 8080;
|
||||
```
|
||||
|
||||
`default` is not a constraint.
|
||||
See [Constraints and Defaults](../constraints-and-defaults.md) for details.
|
||||
|
||||
## Evaluation
|
||||
|
||||
The fallback expression is evaluated only if materialization requires it.
|
||||
When an explicit value exists, the `default` is neither evaluated nor selected.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Function Call Expression
|
||||
|
||||
A function call expression applies a function value to arguments.
|
||||
|
||||
```dcdl
|
||||
increment(41)
|
||||
```
|
||||
|
||||
## Evaluation
|
||||
|
||||
An argument is evaluated when the function body references it.
|
||||
If the argument is referenced several times, its evaluation result is reused.
|
||||
When a parameter has a range, the argument is refined using the same rules as `narrower as wider`.
|
||||
Fields present only in the parameter range remain abstract, and defaults are not selected at this point.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Function Expression
|
||||
|
||||
A function expression is a value that accepts arguments and returns an expression.
|
||||
|
||||
```dcdl
|
||||
(value: Int) => value + 1
|
||||
```
|
||||
|
||||
See [Functions](../functions.md) for the complete function specification.
|
||||
|
||||
## Evaluation
|
||||
|
||||
A function can reference bindings from the lexical scope where it was defined.
|
||||
Its body is evaluated when the function is called, not when the function value is created.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Identifier Expression
|
||||
|
||||
An identifier expression references a name bound in the lexical scope.
|
||||
|
||||
```dcdl
|
||||
Port
|
||||
MyConfig
|
||||
mkConfig
|
||||
```
|
||||
|
||||
## Evaluation
|
||||
|
||||
An identifier evaluates the corresponding binding when its value is needed.
|
||||
If no such binding exists, evaluation produces an unresolved-identifier error.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Import Expression
|
||||
|
||||
An import expression returns a DCDL module or structured value resolved by the host.
|
||||
|
||||
```dcdl
|
||||
import "./config.dcdl"
|
||||
```
|
||||
|
||||
The specifier is a string literal. The host defines its interpretation as a path, resource name, or another identifier.
|
||||
See [Modules and Imports](../modules-and-imports.md) for details.
|
||||
|
||||
## Evaluation
|
||||
|
||||
Imported values are evaluated lazily, and unreferenced fields are not evaluated.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Expression
|
||||
|
||||
This chapter classifies the expressions evaluated by the language.
|
||||
|
||||
An expression is the basic unit for representing a value, constraint, structure, or computation.
|
||||
|
||||
```text
|
||||
Expr
|
||||
├─ literal
|
||||
├─ identifier
|
||||
├─ path reference
|
||||
├─ object
|
||||
├─ map constraint
|
||||
├─ array
|
||||
├─ array constraint
|
||||
├─ function
|
||||
├─ function call
|
||||
├─ let
|
||||
├─ match
|
||||
├─ import
|
||||
├─ composition
|
||||
├─ range refinement (`as`)
|
||||
└─ default
|
||||
```
|
||||
|
||||
Links to each expression specification are collected in the [manual contents](../../index.md).
|
||||
@@ -0,0 +1,16 @@
|
||||
# Let Expression
|
||||
|
||||
A `let` expression creates local bindings.
|
||||
|
||||
```dcdl
|
||||
let
|
||||
base = 8000;
|
||||
offset = 80;
|
||||
in
|
||||
base + offset
|
||||
```
|
||||
|
||||
## Evaluation
|
||||
|
||||
A binding is evaluated when it is referenced.
|
||||
An unreferenced binding is not evaluated, and the result is reused if the same binding is referenced several times.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Literal Expression
|
||||
|
||||
A literal expression is a concrete value written directly in source.
|
||||
|
||||
## Kinds
|
||||
|
||||
```dcdl
|
||||
"hello"
|
||||
123
|
||||
3.14
|
||||
true
|
||||
false
|
||||
```
|
||||
|
||||
## Corresponding primitive types
|
||||
|
||||
- A string literal satisfies `String`.
|
||||
- An integer literal satisfies `Int`.
|
||||
- A floating-point literal satisfies `Float`.
|
||||
- `true` and `false` satisfy `Bool`.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Logical and Comparison Expressions
|
||||
|
||||
Decodal supports boolean logic over concrete `Bool` values and comparison over concrete scalar values.
|
||||
|
||||
```dcdl
|
||||
{
|
||||
is_prod = env == "prod";
|
||||
high_port = port > 9000;
|
||||
enabled = is_prod && high_port;
|
||||
disabled = !enabled;
|
||||
}
|
||||
```
|
||||
|
||||
## Logical operators
|
||||
|
||||
- `!expr` negates a concrete `Bool`.
|
||||
- `lhs && rhs` returns boolean AND.
|
||||
- `lhs || rhs` returns boolean OR.
|
||||
|
||||
`&&` and `||` short-circuit: the right-hand side is evaluated only when needed.
|
||||
Logical operands must evaluate to concrete `Bool` values.
|
||||
|
||||
## Comparison operators
|
||||
|
||||
- `==`
|
||||
- `!=`
|
||||
- `<`
|
||||
- `<=`
|
||||
- `>`
|
||||
- `>=`
|
||||
|
||||
`==` and `!=` compare concrete scalar values: `String`, `Bool`, `Int`, and `Float`.
|
||||
`Int` and `Float` can be compared to each other numerically.
|
||||
|
||||
Ordering operators `<`, `<=`, `>`, and `>=` compare concrete numeric values only.
|
||||
They are separate from prefix comparison constraints such as `> 443`.
|
||||
|
||||
```dcdl
|
||||
port = Int & > 443 default 9443;
|
||||
is_high = port > 9000;
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
# Match Expression
|
||||
|
||||
A `match` expression compares a target value against patterns from top to bottom and selects the first matching arm.
|
||||
|
||||
```dcdl
|
||||
foo = match inputs.a.hoge {
|
||||
>= 20: {
|
||||
value = 200;
|
||||
};
|
||||
>= 10: {
|
||||
value = 100;
|
||||
};
|
||||
_: {
|
||||
value = 300;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
`_` is the fallback pattern.
|
||||
|
||||
## Ordering
|
||||
|
||||
Arms are ordered; Decodal does not automatically select the most specific pattern.
|
||||
If a broad condition appears first, narrower conditions after it are unreachable.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Object Expression
|
||||
|
||||
An object expression is a collection of named fields.
|
||||
|
||||
```dcdl
|
||||
{
|
||||
host = "127.0.0.1";
|
||||
port = 8000;
|
||||
}
|
||||
```
|
||||
|
||||
Objects can represent both configuration values and schemas.
|
||||
|
||||
```dcdl
|
||||
MyConfig = {
|
||||
host = String;
|
||||
port = Int default 8080;
|
||||
};
|
||||
```
|
||||
|
||||
## Dot-path fields
|
||||
|
||||
A nested field can be defined with a dot path.
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge.enable = false;
|
||||
}
|
||||
```
|
||||
|
||||
This represents the same structure as:
|
||||
|
||||
```dcdl
|
||||
{
|
||||
feature_hoge = {
|
||||
enable = false;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Map constraint
|
||||
|
||||
An associative-array constraint applies the same schema to every field value in an object.
|
||||
|
||||
```dcdl
|
||||
Services = {...{
|
||||
port = Int;
|
||||
enabled = Bool default true;
|
||||
}};
|
||||
```
|
||||
|
||||
`{...T}` represents an object whose keys are not fixed and whose values all satisfy `T`.
|
||||
An empty object is allowed. After materialization, it becomes the same object data as an object with named fields.
|
||||
|
||||
```dcdl
|
||||
services = {
|
||||
api = { port = 8080; };
|
||||
worker = { port = 8081; enabled = false; };
|
||||
} as Services;
|
||||
```
|
||||
|
||||
Each entry is refined by `as` into the right-hand field domain, so a field other than `port` or `enabled` on the left produces an error.
|
||||
A field found only on the right remains abstract whether or not it has a default.
|
||||
An object supplied by the host can preserve string keys that are not valid identifiers, although field names written in DCDL source are limited to ordinary identifiers.
|
||||
|
||||
## Object rest constraint
|
||||
|
||||
Fixed fields and a value range for arbitrary keys can coexist in one object.
|
||||
|
||||
```dcdl
|
||||
OpenConfig = {
|
||||
enabled = Bool default true;
|
||||
...Unknown
|
||||
};
|
||||
```
|
||||
|
||||
`...T` applies only to remaining fields that were not named explicitly.
|
||||
Each named field uses its own range; the rest range is not additionally applied to it.
|
||||
A rest constraint does not generate fields. Materialization outputs only additional fields that actually exist.
|
||||
|
||||
```dcdl
|
||||
{
|
||||
enabled = false;
|
||||
plugin = { name = "cache"; };
|
||||
} as OpenConfig
|
||||
```
|
||||
|
||||
An object can contain one `...T`, at its end.
|
||||
An object without it is closed, and an undeclared left-hand field under `as` produces an error.
|
||||
`{...T}` with no named fields is an abstract map constraint; materializing it by itself requires a default or a concrete value.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Path Reference Expression
|
||||
|
||||
A path reference expression accesses a field of an object.
|
||||
|
||||
```dcdl
|
||||
config.host
|
||||
config.feature_hoge.enable
|
||||
```
|
||||
|
||||
## Evaluation
|
||||
|
||||
The expression on the left is evaluated as an object, then the named field is referenced.
|
||||
The referenced field is not evaluated until needed.
|
||||
|
||||
Referencing a field that does not exist produces a diagnostic.
|
||||
An explicit `Unknown` range does not represent a missing field, so it does not make field presence ambiguous.
|
||||
Reference in New Issue
Block a user