graphcal_compiler/syntax/ast/decl.rs
1use crate::syntax::ast::common::{
2 Attribute, BindableVisibility, ImportKind, ModulePath, Visibility,
3};
4use crate::syntax::ast::value::{
5 DimExpr, Expr, MapEntryKey, MultiDeclSharedAxes, ParamBinding, TypeExpr, UnitExpr,
6};
7use crate::syntax::names::{
8 ConstructorName, DeclName, DimName, FieldName, GenericParamName, IndexName, IndexVariantName,
9 PlotPropertyName, ScopedName, StructTypeName, UnitName,
10};
11use crate::syntax::phase::{Phase, Raw};
12use crate::syntax::span::{Span, Spanned};
13
14// ---------------------------------------------------------------------------
15// Raw-only sugar variants
16// ---------------------------------------------------------------------------
17
18/// Declaration-level sugar — only legal in [`Raw`].
19///
20/// Each variant corresponds to a surface declaration form that is rewritten
21/// into ordinary `DeclKind` variants by [`crate::desugar`]. After desugaring,
22/// `DeclKind::Sugar(_)` carries [`core::convert::Infallible`] and these variants vanish from
23/// the type system entirely.
24#[derive(Debug, Clone)]
25pub enum RawDeclSugar {
26 /// Multi-declaration (issue #481): N parallel slots sharing one
27 /// `table[…] {…}` initializer. Desugared into N separate
28 /// `DeclKind::{Param, Node, ConstNode}` declarations.
29 ///
30 /// Pinned to `MultiDecl<Raw>` because multi-decl is by definition a
31 /// raw-only construct — the desugar pass eliminates it.
32 Multi(MultiDecl<Raw>),
33}
34
35impl RawDeclSugar {
36 /// Returns the surface span of the sugar form.
37 #[must_use]
38 pub const fn span(&self) -> Span {
39 match self {
40 Self::Multi(m) => m.span,
41 }
42 }
43}
44/// A complete source file.
45///
46/// Generic over a [`Phase`] parameter that distinguishes the parser's raw
47/// AST (carrying surface sugar) from the desugared AST consumed by name
48/// resolution and below. Defaults to [`Raw`] so existing call sites — which
49/// always handle the parser output — keep compiling unchanged.
50#[derive(Debug, Clone)]
51pub struct File<P: Phase = Raw> {
52 pub declarations: Vec<Declaration<P>>,
53}
54/// A top-level declaration.
55#[derive(Debug, Clone)]
56pub struct Declaration<P: Phase = Raw> {
57 pub attributes: Vec<Attribute>,
58 pub kind: DeclKind<P>,
59 pub span: Span,
60}
61
62#[derive(Debug, Clone)]
63pub enum DeclKind<P: Phase = Raw> {
64 Param(ParamDecl<P>),
65 Node(NodeDecl<P>),
66 ConstNode(ConstNodeDecl<P>),
67 BaseDimension(BaseDimDecl),
68 Dimension(DimDecl),
69 Unit(UnitDecl<P>),
70 Type(TypeDecl<P>),
71 Index(IndexDecl<P>),
72 Import(ImportDecl),
73 Include(IncludeDecl<P>),
74 Dag(DagDecl<P>),
75 Assert(AssertDecl<P>),
76 Plot(PlotDecl<P>),
77 Figure(FigureDecl<P>),
78 Layer(LayerDecl<P>),
79 /// Phase-specific declaration sugar.
80 ///
81 /// In [`Raw`], this is [`crate::syntax::ast::RawDeclSugar`] and carries
82 /// surface forms like multi-decl (issue #481) that are eliminated by the
83 /// desugar pass. In [`Desugared`](Raw), the
84 /// payload is [`core::convert::Infallible`] — the variant is statically
85 /// unreachable, so post-desugar consumers handle it with
86 /// [`crate::syntax::phase::never`].
87 Sugar(P::DeclSugar),
88}
89
90impl<P: Phase> DeclKind<P> {
91 /// Returns the declaration name as a string slice and its span, if the
92 /// variant carries a name. `Import` and `Include` have no name and return
93 /// `None`.
94 #[must_use]
95 pub fn name_and_span(&self) -> Option<(&str, Span)> {
96 match self {
97 Self::Param(p) => Some((p.name.value.as_str(), p.name.span)),
98 Self::Node(n) => Some((n.name.value.as_str(), n.name.span)),
99 Self::ConstNode(c) => Some((c.name.value.as_str(), c.name.span)),
100 Self::BaseDimension(d) => Some((d.name.value.as_str(), d.name.span)),
101 Self::Dimension(d) => Some((d.name.value.as_str(), d.name.span)),
102 Self::Unit(u) => Some((u.name.value.as_str(), u.name.span)),
103 Self::Type(t) => Some((t.name.value.as_str(), t.name.span)),
104 Self::Index(i) => Some((i.name.value.as_str(), i.name.span)),
105 Self::Dag(d) => Some((d.name.value.as_str(), d.name.span)),
106 Self::Assert(a) => Some((a.name.value.as_str(), a.name.span)),
107 Self::Plot(p) => Some((p.name.value.as_str(), p.name.span)),
108 Self::Figure(f) => Some((f.name.value.as_str(), f.name.span)),
109 Self::Layer(l) => Some((l.name.value.as_str(), l.name.span)),
110 Self::Import(_) | Self::Include(_) | Self::Sugar(_) => None,
111 }
112 }
113}
114
115/// Assert declaration: `assert name = <expr>;`
116///
117/// The body must evaluate to `Bool`. No type annotation (it's always Bool).
118/// Assert declarations are leaf nodes — they are evaluated after the entire graph.
119#[derive(Debug, Clone)]
120pub struct AssertDecl<P: Phase = Raw> {
121 pub visibility: Visibility,
122 pub name: Spanned<DeclName>,
123 pub body: AssertBody<P>,
124}
125
126/// The body of an assert declaration.
127#[derive(Debug, Clone)]
128pub enum AssertBody<P: Phase = Raw> {
129 /// Plain boolean expression: `assert name = expr;`
130 Expr(Expr<P>),
131 /// Tolerance: `assert name = actual ~= expected +/- tolerance;`
132 Tolerance {
133 /// The actual value expression (left of `~=`).
134 actual: Box<Expr<P>>,
135 /// The expected value expression (right of `~=`).
136 expected: Box<Expr<P>>,
137 /// The tolerance expression (right of `+/-`).
138 tolerance: Box<Expr<P>>,
139 /// Whether the tolerance is relative (`%`).
140 is_relative: bool,
141 },
142}
143
144/// The mark type in a plot declaration (Vega-Lite grammar).
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum MarkType {
147 Point,
148 Line,
149 Bar,
150 Area,
151 Rect,
152 Tick,
153}
154
155impl std::fmt::Display for MarkType {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 match self {
158 Self::Point => write!(f, "point"),
159 Self::Line => write!(f, "line"),
160 Self::Bar => write!(f, "bar"),
161 Self::Area => write!(f, "area"),
162 Self::Rect => write!(f, "rect"),
163 Self::Tick => write!(f, "tick"),
164 }
165 }
166}
167
168/// An encoding channel in a plot declaration (Vega-Lite grammar).
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum EncodingChannel {
171 X,
172 Y,
173 Color,
174 Size,
175 Shape,
176 Opacity,
177 Detail,
178 Text,
179 Tooltip,
180}
181
182impl std::fmt::Display for EncodingChannel {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 match self {
185 Self::X => write!(f, "x"),
186 Self::Y => write!(f, "y"),
187 Self::Color => write!(f, "color"),
188 Self::Size => write!(f, "size"),
189 Self::Shape => write!(f, "shape"),
190 Self::Opacity => write!(f, "opacity"),
191 Self::Detail => write!(f, "detail"),
192 Self::Text => write!(f, "text"),
193 Self::Tooltip => write!(f, "tooltip"),
194 }
195 }
196}
197
198/// The mark specification in a plot declaration: `mark: point` or `mark: line { stroke_width: 2.0 }`.
199#[derive(Debug, Clone)]
200pub struct MarkSpec<P: Phase = Raw> {
201 pub mark_type: MarkType,
202 pub mark_type_span: Span,
203 pub properties: Vec<PlotField<P>>,
204 pub span: Span,
205}
206
207/// An encoding channel mapping in a plot declaration.
208///
209/// Example: `x: for m: OpMode { @total_power[m] }`
210#[derive(Debug, Clone)]
211pub struct Encoding<P: Phase = Raw> {
212 pub channel: EncodingChannel,
213 pub channel_span: Span,
214 pub value: Expr<P>,
215 pub span: Span,
216}
217
218/// A named field in a plot or figure declaration body.
219///
220/// Example: `title: "My Chart"`
221#[derive(Debug, Clone)]
222pub struct PlotField<P: Phase = Raw> {
223 /// The field name (e.g., "title", "width", "height").
224 pub name: Spanned<PlotPropertyName>,
225 /// The field value expression.
226 pub value: Expr<P>,
227 pub span: Span,
228}
229
230/// Plot declaration: `plot name = { mark: point, encode: { x: ..., y: ... }, title: "..." };`
231///
232/// Plots are leaf declarations that depend on params/nodes via `@`-references.
233/// They produce a plot specification, not a runtime `Value`.
234#[derive(Debug, Clone)]
235pub struct PlotDecl<P: Phase = Raw> {
236 pub visibility: Visibility,
237 pub name: Spanned<DeclName>,
238 pub mark: MarkSpec<P>,
239 pub encodings: Vec<Encoding<P>>,
240 pub properties: Vec<PlotField<P>>,
241}
242
243/// Figure declaration: `figure name = { plots: [a, b], title: "..." };`
244///
245/// Figures group multiple plot declarations into a single combined chart
246/// with subplots. Like plots, they are leaf declarations.
247#[derive(Debug, Clone)]
248pub struct FigureDecl<P: Phase = Raw> {
249 pub visibility: Visibility,
250 pub name: Spanned<DeclName>,
251 /// The plot names referenced by this figure (from the `plots: [...]` field).
252 pub plot_names: Vec<Spanned<ScopedName>>,
253 /// Additional fields (e.g., `title`).
254 pub fields: Vec<PlotField<P>>,
255}
256
257/// A layer declaration: overlays multiple plots on shared axes.
258///
259/// Syntax: `layer name = { plots: [a, b], title: "..." };`
260///
261/// Unlike `figure` (which tiles plots side-by-side), `layer` overlays
262/// them on the same coordinate space. In Vega-Lite this maps to the
263/// `"layer"` composition operator.
264#[derive(Debug, Clone)]
265pub struct LayerDecl<P: Phase = Raw> {
266 pub visibility: Visibility,
267 pub name: Spanned<DeclName>,
268 /// The plot names to overlay (from the `plots: [...]` field).
269 pub plot_names: Vec<Spanned<ScopedName>>,
270 /// Additional fields (e.g., `title`).
271 pub fields: Vec<PlotField<P>>,
272}
273
274/// Import declaration (compile-time name import).
275///
276/// `import nasa.rocket;` — brings the leaf module into scope.
277/// `import nasa.rocket as nr;` — brings the leaf module under an alias.
278/// `import nasa.rocket.{Orbit, compute_thrust};` — brings only the listed names.
279///
280/// No param bindings — for DAG instantiation with param bindings, use `include`.
281#[derive(Debug, Clone)]
282pub struct ImportDecl {
283 pub visibility: Visibility,
284 pub path: ModulePath,
285 pub kind: ImportKind,
286}
287
288/// Include declaration (DAG embedding / instantiation).
289///
290/// `include nasa.rocket.compute_thrust(args);` — bare form; instance alias is
291/// the DAG's leaf name.
292/// `include nasa.rocket.compute_thrust(args) as ct;` — explicit instance alias.
293/// `include nasa.rocket.compute_thrust(args).{thrust};` — exposes selected
294/// outputs as nodes in the including DAG.
295#[derive(Debug, Clone)]
296pub struct IncludeDecl<P: Phase = Raw> {
297 pub visibility: Visibility,
298 pub path: ModulePath,
299 pub param_bindings: Vec<ParamBinding<P>>,
300 pub kind: ImportKind,
301}
302
303/// Inline DAG declaration: `dag name { ... }`
304///
305/// The body contains declarations (same as file-level). Semantics are not yet
306/// implemented — this phase only parses the syntax.
307#[derive(Debug, Clone)]
308pub struct DagDecl<P: Phase = Raw> {
309 pub visibility: Visibility,
310 /// The DAG name.
311 pub name: Spanned<DeclName>,
312 /// Declarations inside the DAG block.
313 pub body: Vec<Declaration<P>>,
314 /// Span covering the entire `dag name { ... }` block.
315 pub span: Span,
316}
317#[derive(Debug, Clone)]
318pub struct ParamDecl<P: Phase = Raw> {
319 pub name: Spanned<DeclName>,
320 pub type_ann: TypeExpr<P>,
321 /// The default value expression. `None` for required params (no default).
322 pub value: Option<Expr<P>>,
323}
324
325// ---------------------------------------------------------------------------
326// Multi-declaration surface info (issue #481)
327// ---------------------------------------------------------------------------
328//
329// A multi-decl is a single surface form — e.g.,
330//
331// param a: T[I], const node b: U[I, J] = table[I, (_, J)] { : _, …; … };
332//
333// — represented in the AST as `DeclKind::Multi(MultiDecl)`. A dedicated
334// desugar pass (`syntax::desugar::desugar_multi_decls_in_file`) expands
335// each `Multi` into N parallel ordinary declarations before lowering;
336// consumers that want the surface form (formatter, surface-aware LSP
337// features) read the AST variant directly.
338
339/// The surface form of a multi-decl: parallel declaration slots sharing a
340/// single `table[…] {…}` initializer.
341#[derive(Debug, Clone)]
342pub struct MultiDecl<P: Phase = Raw> {
343 /// Slot headers in declaration order. Length = number of declarations
344 /// this multi-decl expanded into.
345 pub slots: Vec<MultiDeclSlot<P>>,
346 /// Shared axes from the bracket prefix `table[A, B, …, (…)]`.
347 pub shared_axes: MultiDeclSharedAxes,
348 /// Per-slot extra-axis annotation from the slot tuple. Same length
349 /// as `slots`.
350 pub slot_axes: Vec<MultiSlotAxis>,
351 /// Body slices. Exactly one slice for single-shared-axis multi-decls;
352 /// multiple slices for N-D shared-axis prefixes (v3).
353 pub slices: Vec<MultiDeclSlice<P>>,
354 /// Full surface span: from the first slot's kind keyword through the
355 /// closing `;`.
356 pub span: Span,
357 /// Span of the `table[…] {…}` sub-expression.
358 pub table_expr_span: Span,
359}
360
361/// One slot in a multi-decl: kind keyword, name, type annotation, visibility.
362#[derive(Debug, Clone)]
363pub struct MultiDeclSlot<P: Phase = Raw> {
364 /// Visibility for this slot. The first slot inherits the leading
365 /// `pub`/`pub(bind)` prefix consumed before the multi-decl was
366 /// recognized; subsequent slots accept their own optional prefix
367 /// before the kind keyword.
368 pub visibility: Visibility,
369 pub kind: MultiSlotKind,
370 /// Span covering the kind keyword(s) (`param`, `node`, or `const node`).
371 pub kind_span: Span,
372 pub name: Spanned<DeclName>,
373 pub type_ann: TypeExpr<P>,
374 /// Span from kind keyword through end of the type annotation.
375 pub header_span: Span,
376}
377
378/// Value-decl kinds that a multi-decl slot can have.
379#[derive(Debug, Clone, Copy, PartialEq, Eq)]
380pub enum MultiSlotKind {
381 Param,
382 Node,
383 ConstNode,
384}
385
386/// Per-slot entry in the slot tuple `(…)`.
387#[derive(Debug, Clone)]
388pub enum MultiSlotAxis {
389 /// `_` — 1-D slot, typed `T[SharedAxis]`.
390 Underscore,
391 /// Named axis — 2-D slot, typed `T[SharedAxis, ExtraAxis]`.
392 Axis(Spanned<IndexName>),
393}
394
395/// Where a slot's columns live within each slice's header row.
396#[derive(Debug, Clone)]
397pub enum MultiSlotColumnSpan {
398 /// 1-D slot: one column at `col_idx`.
399 Single(usize),
400 /// 2-D slot: columns `start..end`, one per variant of `extra_axis`.
401 Range {
402 start: usize,
403 end: usize,
404 extra_axis: Spanned<IndexName>,
405 },
406}
407
408/// One slice of a multi-decl body: optional slice-label prefix + header + rows.
409#[derive(Debug, Clone)]
410pub struct MultiDeclSlice<P: Phase = Raw> {
411 /// Slice labels covering the shared-axis prefix except the row axis.
412 /// Empty for single-shared-axis bodies.
413 pub prefix_keys: Vec<MapEntryKey>,
414 /// Header row cells, in left-to-right order.
415 pub header_cells: Vec<MultiHeaderCell>,
416 /// Span of the entire header row (`:` through `;`).
417 pub header_span: Span,
418 /// Per-slot column span into this slice's `header_cells` and `rows`
419 /// values. Same length as `MultiDecl::slots`. May differ between
420 /// slices if their header rows list variants in different orders.
421 pub column_layout: Vec<MultiSlotColumnSpan>,
422 /// Data rows for this slice.
423 pub rows: Vec<MultiDataRow<P>>,
424}
425
426/// One cell of a multi-decl header row.
427#[derive(Debug, Clone)]
428pub enum MultiHeaderCell {
429 Underscore {
430 span: Span,
431 },
432 Variant {
433 /// Axis qualifier, if the author wrote `Axis.Variant`.
434 axis: Option<Spanned<IndexName>>,
435 variant: Spanned<IndexVariantName>,
436 span: Span,
437 },
438}
439
440impl MultiHeaderCell {
441 /// Returns the span of this cell.
442 #[must_use]
443 pub const fn span(&self) -> Span {
444 match self {
445 Self::Underscore { span } | Self::Variant { span, .. } => *span,
446 }
447 }
448}
449
450/// One data row of a multi-decl body: label + value per column.
451#[derive(Debug, Clone)]
452pub struct MultiDataRow<P: Phase = Raw> {
453 pub label: Spanned<IndexVariantName>,
454 pub values: Vec<Expr<P>>,
455 pub span: Span,
456}
457
458/// Runtime node declaration: `node name: Type = expr;`
459#[derive(Debug, Clone)]
460pub struct NodeDecl<P: Phase = Raw> {
461 pub visibility: Visibility,
462 pub name: Spanned<DeclName>,
463 pub type_ann: TypeExpr<P>,
464 pub value: Expr<P>,
465}
466
467/// Const node declaration: `const node name: Type = expr;`
468#[derive(Debug, Clone)]
469pub struct ConstNodeDecl<P: Phase = Raw> {
470 pub visibility: Visibility,
471 pub name: Spanned<DeclName>,
472 pub type_ann: TypeExpr<P>,
473 pub value: Expr<P>,
474}
475
476/// Base dimension declaration: `base dim Length;`
477#[derive(Debug, Clone)]
478pub struct BaseDimDecl {
479 pub visibility: Visibility,
480 pub name: Spanned<DimName>,
481}
482
483/// Dimension declaration with a body or required.
484///
485/// Two forms:
486/// - Derived: `dim Velocity = Length / Time;` — `definition: Some(...)`
487/// - Required: `dim D;` — `definition: None`. The library requires a
488/// dimension to be bound here from outside (via an include with
489/// dim bindings). Treated like an opaque base dimension when the
490/// library is compiled standalone.
491#[derive(Debug, Clone)]
492pub struct DimDecl {
493 pub visibility: BindableVisibility,
494 pub name: Spanned<DimName>,
495 pub definition: Option<DimExpr>,
496}
497
498/// Whether a unit is allowed in compile-time (`const`) contexts.
499#[derive(Debug, Clone, Copy, PartialEq, Eq)]
500pub enum UnitConstness {
501 /// A compile-time unit: prelude units, `base unit`, or `const unit`.
502 Const,
503 /// A runtime unit declared with plain `unit`; its scale may depend on params or nodes.
504 Dynamic,
505}
506
507impl UnitConstness {
508 /// Returns `true` for units that may appear in `const node` bodies.
509 #[must_use]
510 pub const fn is_const(self) -> bool {
511 matches!(self, Self::Const)
512 }
513}
514
515/// Unit declaration: `const unit km: Length = 1000 m;`, `unit EUR: Money = (@rate) USD;`,
516/// or `base unit m: Length;`.
517#[derive(Debug, Clone)]
518pub struct UnitDecl<P: Phase = Raw> {
519 pub visibility: Visibility,
520 pub constness: UnitConstness,
521 pub name: Spanned<UnitName>,
522 /// The dimension this unit measures.
523 pub dim_type: DimExpr,
524 /// Scale definition: `(scale_value, base_unit_expr)`.
525 /// `None` iff this is a base unit (`base unit m: Length;`).
526 pub definition: Option<UnitDef<P>>,
527}
528
529/// The scale definition part of a unit declaration: `1000 m` or `1 kg * m / s^2`.
530#[derive(Debug, Clone)]
531pub struct UnitDef<P: Phase = Raw> {
532 pub scale_expr: Expr<P>,
533 pub unit_expr: UnitExpr,
534 pub span: Span,
535}
536
537/// Type declaration: required type stubs and tagged-union bodies.
538///
539/// Forms:
540/// - Required type: `type T;` — the library requires a type bound from
541/// outside; no body at declaration.
542/// - Tagged union: `type Maneuver { Impulsive(delta_v: Velocity), Coast }`
543/// - Record-shaped type: `type Position { Position(x: Length, y: Length) }`,
544/// a single-variant union whose constructor name matches the type name.
545#[derive(Debug, Clone)]
546pub struct TypeDecl<P: Phase = Raw> {
547 pub visibility: BindableVisibility,
548 pub name: Spanned<StructTypeName>,
549 pub generic_params: Vec<GenericParam<P>>,
550 pub body: TypeDeclBody<P>,
551}
552
553/// Body of a `type` declaration.
554#[derive(Debug, Clone)]
555pub enum TypeDeclBody<P: Phase = Raw> {
556 /// Required type with no body: `type T;`.
557 Required,
558 /// Tagged-union constructor list: `type T { Ctor, Other(x: U) }`.
559 Constructors(Vec<UnionMember<P>>),
560}
561
562/// A member of a type declaration body: a constructor with an optional payload.
563///
564/// Forms:
565/// - Unit: `Coast` — `payload` is `None`.
566/// - Record-payload (parens): `Impulsive(delta_v: Velocity)` —
567/// `payload` is `Some(vec![…])`.
568/// - Record-payload (braces): `LowThrust { thrust: Force, duration: Time }`
569/// — `payload` is `Some(vec![…])`. The brace/paren choice is purely
570/// surface syntax; both produce the same AST.
571#[derive(Debug, Clone)]
572pub struct UnionMember<P: Phase = Raw> {
573 /// The constructor's name. Lives in the constructor namespace —
574 /// distinct from the type namespace.
575 pub name: Spanned<ConstructorName>,
576 /// Inline payload fields, or `None` for unit constructors.
577 pub payload: Option<Vec<FieldDecl<P>>>,
578 pub span: Span,
579}
580
581/// A field in a variant or struct type declaration.
582#[derive(Debug, Clone)]
583pub struct FieldDecl<P: Phase = Raw> {
584 pub name: Spanned<FieldName>,
585 pub type_ann: TypeExpr<P>,
586}
587
588///// The kind of an index declaration.
589#[derive(Debug, Clone)]
590pub enum IndexDeclKind<P: Phase = Raw> {
591 /// Named variants: `{ Departure, Correction, Insertion }`
592 Named {
593 variants: Vec<Spanned<IndexVariantName>>,
594 },
595 /// Numeric range: `linspace(start, end, step: step)`
596 Range {
597 start: Box<Expr<P>>,
598 end: Box<Expr<P>>,
599 step: Box<Expr<P>>,
600 },
601 /// Required named index (no variants): `index Foo;`
602 ///
603 /// Must be bound via parameterized import.
604 RequiredNamed,
605 /// Required range index with dimension constraint: `index Foo: Time;`
606 ///
607 /// Must be bound via parameterized import.
608 RequiredRange { dimension: DimExpr },
609}
610
611impl<P: Phase> IndexDeclKind<P> {
612 /// Returns `true` for required index declarations that must be bound via import.
613 #[must_use]
614 pub const fn is_required(&self) -> bool {
615 matches!(self, Self::RequiredNamed | Self::RequiredRange { .. })
616 }
617}
618
619/// Index declaration: `index Maneuver = { Departure, Correction, Insertion };`
620/// or `index TimeStep = linspace(0.0 s, 100.0 s, step: 0.1 s);`
621#[derive(Debug, Clone)]
622pub struct IndexDecl<P: Phase = Raw> {
623 pub visibility: BindableVisibility,
624 pub name: Spanned<IndexName>,
625 pub kind: IndexDeclKind<P>,
626}
627
628/// A generic parameter: `D: Dim`
629#[derive(Debug, Clone)]
630pub struct GenericParam<P: Phase = Raw> {
631 pub name: Spanned<GenericParamName>,
632 pub constraint: GenericConstraint,
633 /// Optional default type, e.g. `F: Type = Unframed`.
634 pub default: Option<TypeExpr<P>>,
635}
636
637/// Constraint on a generic parameter.
638#[derive(Debug, Clone, Copy, PartialEq, Eq)]
639pub enum GenericConstraint {
640 /// `D: Dim` -- the generic stands for a dimension.
641 Dim,
642 /// `I: Index` -- the generic stands for an index.
643 Index,
644 /// `N: Nat` -- the generic stands for a natural number (type-level).
645 Nat,
646 /// `F: Type` -- the generic stands for any type (unconstrained phantom parameter).
647 Type,
648}