Grammar
The whole shape of a construct: sigils, the head, the three brackets, and when a line is prose.
#Prose and constructs
A Chalk document is plain text, and everything in it is a construct — a node, a micronode, or a directive. A bare line of writing is already one: it compiles to a paragraph node. A named node is recognised by the bracket after its name, so a line beginning Note: this is important compiles as a paragraph even though note is a node. Micronodes and directives carry their own sigil, so they need no bracket: !cite:#fs1969() and :overview.part.chalk stand bare.
A few constructs have marker spellings instead of a written name: # opens a heading node, - a list, --- a divider and === a step, and **…**, *…*, `…` and $…$ are micronodes with their own delimiters. A marker is an alternate spelling, not a fourth kind — what it opens is an ordinary node or micronode.
scene{
# Falling objects
Galileo's insight was that mass does not matter.
}[
canvas.graph#drop{
curve#h(expression: 100 - 4.9x^2)
}(
x-axis-label: t
y-axis-label: height
)
]That is a whole valid document: one scene whose body holds the narrative and whose detail declares a graph environment with a single curve.
#The head
Before its brackets, every construct is written the same way:
name[#id][:value]#id assigns an identifier — available on every node, micronode and directive. :value supplies the construct's implicit attribute, which is whichever attribute the element declares first. Both are optional and independent, and the order is fixed: # before :.
curve#flight #flight names this curve
image:photo.jpg :photo.jpg fills source, the first attribute image declaresAn id and one attribute is all the head carries. Everything else is an ordinary attribute written in the group, and the two forms mean the same thing:
image#hero:photo.jpg(caption: Demand curve)
image#hero(source: photo.jpg; caption: Demand curve)The : value is a simple token: it starts after the colon and ends at the first bracket, whitespace, or end of line. Anything more complex — spaces, brackets, a list — is written as a named attribute instead. Because the head is read in that fixed order, a # inside the value is an ordinary character, which is what lets !cite:#fs1969() parse as a reference.
#The three brackets
{ }— the body[ ]— the detail, which for a scene is its canvas( )— the attribute group
What is legal inside the body and the detail is the element's containment policy. Every reference page prints its policy under "Allowed content", and the reverse ("Allowed in") says where the element may be written. Body & detail covers the policies in full.
() is an attribute group with nothing in it. Since a bare node head compiles as paragraph text until a bracket follows, image:photo.jpg() is how a node with no other attributes is written. Micronodes need no such trick — their sigil already marks them.#Attributes
Attributes live in a parenthesised group, separated by semicolons: point(x: 1; y: 2; colour: orange), or one per line when the group is written across several. The group comes last, after the body and detail. The control-flow directives are the exception — (audience: teacher){…} and (n: 0..4){…} put theirs first.
Every value in Chalk is text until the attribute it lands in parses it, and a value that attribute's type cannot parse is a compile error — nothing is coerced or quietly defaulted. This is why a dataset column needs no type of its own: a cell reaches an attribute as text and is parsed there, failing exactly as the same text typed by hand would.
A type may be narrower than its name suggests — a number restricted to whole values, or to a range, or a list held to a fixed length — and when a value is refused the error names the restriction it broke rather than just the type. The type reference lists what each one accepts.
#Sigils
.— namespace and member access:canvas.graph,#islands.area#— assigns or refers to an identifier:curve#flight,path: #flight:— supplies the implicit attribute:image:photo.jpg!— a micronode:!teal{…},!upper{…}@— a directive:(…),#islands*— the document header, at the top of the file{{ }}— an expression:{{z-expected}},{{2 * #f.slope}}