vexil-lang 0.5.0

Compiler library for the Vexil schema definition language — lexer, parser, IR, and type checker
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
//! # Stability: Tier 2
//!
//! Abstract syntax tree types produced by the parser.
//!
//! The AST is source-faithful: it preserves all syntactic structure
//! from the original `.vexil` file. It is consumed by the validator
//! and the lowering pass, and can also serve as the basis for LSP
//! features and source-level tooling.

pub mod visit;

use crate::span::{Span, Spanned};
use smol_str::SmolStr;

// ---------------------------------------------------------------------------
// Top-level
// ---------------------------------------------------------------------------

/// Top-level schema document.
#[derive(Debug, Clone, PartialEq)]
pub struct Schema {
    pub span: Span,
    pub annotations: Vec<Annotation>,
    pub namespace: Option<Spanned<NamespaceDecl>>,
    pub imports: Vec<Spanned<ImportDecl>>,
    pub declarations: Vec<Spanned<Decl>>,
}

/// Namespace declaration (e.g. `namespace net.example.types`).
#[derive(Debug, Clone, PartialEq)]
pub struct NamespaceDecl {
    /// Dot-separated path segments.
    pub path: Vec<Spanned<SmolStr>>,
}

// ---------------------------------------------------------------------------
// Imports
// ---------------------------------------------------------------------------

/// An import declaration (e.g. `import net.example.types { Foo }`).
#[derive(Debug, Clone, PartialEq)]
pub struct ImportDecl {
    /// The import style: wildcard, named, or aliased.
    pub kind: ImportKind,
    /// Dot-separated namespace path being imported from.
    pub path: Vec<Spanned<SmolStr>>,
    /// Optional version constraint (e.g. `@ "1.2.0"`).
    pub version: Option<Spanned<String>>,
}

/// Import style: wildcard brings in all types, named imports specific types,
/// aliased imports the whole namespace under a qualified alias.
#[derive(Debug, Clone, PartialEq)]
pub enum ImportKind {
    /// `import ns.*` — imports all exported types.
    Wildcard,
    /// `import ns { Foo, Bar }` — imports specific named types.
    Named { names: Vec<Spanned<SmolStr>> },
    /// `import ns as Alias` — imports all types under a qualified alias prefix.
    Aliased { alias: Spanned<SmolStr> },
}

// ---------------------------------------------------------------------------
// Declarations
// ---------------------------------------------------------------------------

/// A top-level declaration in a Vexil schema.
#[derive(Debug, Clone, PartialEq)]
pub enum Decl {
    /// A message type with ordered, typed fields.
    Message(MessageDecl),
    /// A closed or open enumeration.
    Enum(EnumDecl),
    /// A bitmask / flag set.
    Flags(FlagsDecl),
    /// A tagged union (sum type).
    Union(UnionDecl),
    /// A newtype wrapper around another type.
    Newtype(NewtypeDecl),
    /// A compile-time configuration record (not wire-encoded).
    Config(ConfigDecl),
    /// A type alias declaration.
    Alias(AliasDecl),
    /// A named constant declaration.
    Const(ConstDecl),
    /// A trait declaration defining an interface.
    Trait(TraitDecl),
    /// An implementation declaration for a trait on a type.
    Impl(ImplDecl),
}

// ---------------------------------------------------------------------------
// Message
// ---------------------------------------------------------------------------

/// A message declaration with ordered, typed fields and optional tombstones.
#[derive(Debug, Clone, PartialEq)]
pub struct MessageDecl {
    /// Annotations applied to the message (e.g. `@doc`, `@deprecated`).
    pub annotations: Vec<Annotation>,
    /// The message name.
    pub name: Spanned<SmolStr>,
    /// Body items: fields and tombstones.
    pub body: Vec<MessageBodyItem>,
}

/// A single item in a message body: either a field or a tombstone.
#[derive(Debug, Clone, PartialEq)]
pub enum MessageBodyItem {
    /// A typed field with an ordinal.
    Field(Spanned<MessageField>),
    /// A tombstoned (removed) field ordinal.
    Tombstone(Spanned<Tombstone>),
    /// A named invariant condition (cross-field constraint).
    Invariant(Spanned<MessageInvariant>),
}

/// A named invariant within a message body.
/// Example: `invariant AmountNonNegative { amount >= 0 }`
#[derive(Debug, Clone, PartialEq)]
pub struct MessageInvariant {
    /// Optional name for the invariant.
    pub name: Option<Spanned<SmolStr>>,
    /// The condition expression.
    pub condition: Spanned<WhereExpr>,
}

/// A single field within a message or union variant.
#[derive(Debug, Clone, PartialEq)]
pub struct MessageField {
    /// Annotations before the field name.
    pub pre_annotations: Vec<Annotation>,
    /// The field name.
    pub name: Spanned<SmolStr>,
    /// The field ordinal (`@N`).
    pub ordinal: Spanned<u32>,
    /// Annotations between the ordinal and the type.
    pub post_ordinal_annotations: Vec<Annotation>,
    /// The field's type expression.
    pub ty: Spanned<TypeExpr>,
    /// Annotations after the type expression.
    pub post_type_annotations: Vec<Annotation>,
    /// Optional `where` constraint on the field value.
    pub where_clause: Option<Spanned<WhereExpr>>,
}

// ---------------------------------------------------------------------------
// Enum
// ---------------------------------------------------------------------------

/// An enum declaration with named variants mapped to integer ordinals.
#[derive(Debug, Clone, PartialEq)]
pub struct EnumDecl {
    /// Annotations applied to the enum.
    pub annotations: Vec<Annotation>,
    /// The enum name.
    pub name: Spanned<SmolStr>,
    /// Optional explicit backing type (`: u8`, `: u16`, etc.).
    pub backing: Option<Spanned<EnumBacking>>,
    /// Body items: variants and tombstones.
    pub body: Vec<EnumBodyItem>,
}

/// A single item in an enum body: either a variant or a tombstone.
#[derive(Debug, Clone, PartialEq)]
pub enum EnumBodyItem {
    /// A named variant with an ordinal value.
    Variant(Spanned<EnumVariant>),
    /// A tombstoned (removed) variant ordinal.
    Tombstone(Spanned<Tombstone>),
}

/// A single variant within an enum type.
#[derive(Debug, Clone, PartialEq)]
pub struct EnumVariant {
    /// Annotations on this variant.
    pub annotations: Vec<Annotation>,
    /// The variant name.
    pub name: Spanned<SmolStr>,
    /// The variant's integer ordinal.
    pub ordinal: Spanned<u32>,
}

/// Explicit backing integer type for an enum.
#[derive(Debug, Clone, PartialEq)]
pub enum EnumBacking {
    /// 8-bit unsigned backing.
    U8,
    /// 16-bit unsigned backing.
    U16,
    /// 32-bit unsigned backing.
    U32,
    /// 64-bit unsigned backing.
    U64,
}

// ---------------------------------------------------------------------------
// Flags
// ---------------------------------------------------------------------------

/// A flags (bitmask) declaration with named bit positions.
#[derive(Debug, Clone, PartialEq)]
pub struct FlagsDecl {
    /// Annotations applied to the flags type.
    pub annotations: Vec<Annotation>,
    /// The flags type name.
    pub name: Spanned<SmolStr>,
    /// Body items: bit definitions and tombstones.
    pub body: Vec<FlagsBodyItem>,
}

/// A single item in a flags body: either a bit definition or a tombstone.
#[derive(Debug, Clone, PartialEq)]
pub enum FlagsBodyItem {
    /// A named bit at a specific position.
    Bit(Spanned<FlagsBit>),
    /// A tombstoned (removed) bit position.
    Tombstone(Spanned<Tombstone>),
}

/// A single bit definition within a flags type.
#[derive(Debug, Clone, PartialEq)]
pub struct FlagsBit {
    /// Annotations on this bit.
    pub annotations: Vec<Annotation>,
    /// The bit name.
    pub name: Spanned<SmolStr>,
    /// The bit position (0-based).
    pub ordinal: Spanned<u32>,
}

// ---------------------------------------------------------------------------
// Union
// ---------------------------------------------------------------------------

/// A tagged union (sum type) with named variants, each carrying optional fields.
#[derive(Debug, Clone, PartialEq)]
pub struct UnionDecl {
    /// Annotations applied to the union.
    pub annotations: Vec<Annotation>,
    /// The union name.
    pub name: Spanned<SmolStr>,
    /// Body items: variants and tombstones.
    pub body: Vec<UnionBodyItem>,
}

/// A single item in a union body: either a variant or a tombstone.
#[derive(Debug, Clone, PartialEq)]
pub enum UnionBodyItem {
    /// A named variant with an ordinal and optional fields.
    Variant(Spanned<UnionVariant>),
    /// A tombstoned (removed) variant ordinal.
    Tombstone(Spanned<Tombstone>),
}

/// A single variant within a union type.
#[derive(Debug, Clone, PartialEq)]
pub struct UnionVariant {
    /// Annotations on this variant.
    pub annotations: Vec<Annotation>,
    /// The variant name.
    pub name: Spanned<SmolStr>,
    /// The variant's tag ordinal.
    pub ordinal: Spanned<u32>,
    /// Fields carried by this variant (uses message field structure).
    pub fields: Vec<MessageBodyItem>,
}

// ---------------------------------------------------------------------------
// Newtype
// ---------------------------------------------------------------------------

/// A newtype wrapper that creates a distinct type around an existing type.
#[derive(Debug, Clone, PartialEq)]
pub struct NewtypeDecl {
    /// Annotations on the newtype.
    pub annotations: Vec<Annotation>,
    /// The newtype name.
    pub name: Spanned<SmolStr>,
    /// The inner type being wrapped.
    pub inner_type: Spanned<TypeExpr>,
}

// ---------------------------------------------------------------------------
// Config
// ---------------------------------------------------------------------------

/// A compile-time configuration record (not encoded on the wire).
#[derive(Debug, Clone, PartialEq)]
pub struct ConfigDecl {
    /// Annotations on the config.
    pub annotations: Vec<Annotation>,
    /// The config name.
    pub name: Spanned<SmolStr>,
    /// The config fields with types and default values.
    pub fields: Vec<Spanned<ConfigField>>,
}

/// A single field within a config record.
#[derive(Debug, Clone, PartialEq)]
pub struct ConfigField {
    /// Annotations on this field.
    pub annotations: Vec<Annotation>,
    /// The field name.
    pub name: Spanned<SmolStr>,
    /// The field's type expression.
    pub ty: Spanned<TypeExpr>,
    /// The default value for this field.
    pub default_value: Spanned<DefaultValue>,
}

// ---------------------------------------------------------------------------
// Type Alias
// ---------------------------------------------------------------------------

/// A type parameter for generic type aliases (e.g., `T` in `type Vec3<T> = ...`).
#[derive(Debug, Clone, PartialEq)]
pub struct TypeParam {
    pub name: Spanned<SmolStr>,
    /// Optional bounds on the type parameter (e.g., `T: Numeric`).
    /// Currently unused but reserved for future use.
    pub bounds: Vec<Spanned<SmolStr>>,
}

/// A type alias declaration (e.g. `type Vec3<T> = array<T, 3>`).
#[derive(Debug, Clone, PartialEq)]
pub struct AliasDecl {
    /// Annotations on the alias.
    pub annotations: Vec<Annotation>,
    /// The alias name.
    pub name: Spanned<SmolStr>,
    /// Type parameters for generic aliases (e.g., `<T>` in `type Vec3<T> = ...`).
    /// Empty for non-generic aliases.
    pub type_params: Vec<TypeParam>,
    /// The target type expression this alias resolves to.
    pub target: Spanned<TypeExpr>,
}

// ---------------------------------------------------------------------------
// Const Declaration
// ---------------------------------------------------------------------------

/// A named constant declaration (e.g. `const MAX_SIZE : u32 = 1024`).
#[derive(Debug, Clone, PartialEq)]
pub struct ConstDecl {
    /// Annotations on the constant.
    pub annotations: Vec<Annotation>,
    /// The constant name.
    pub name: Spanned<SmolStr>,
    /// The constant's type expression.
    pub ty: Spanned<TypeExpr>,
    /// The constant's value expression.
    pub value: Spanned<ConstExpr>,
}

/// A compile-time constant expression (supports arithmetic and references to other constants).
#[derive(Debug, Clone, PartialEq)]
pub enum ConstExpr {
    /// Signed integer literal.
    Int(i64),
    /// Unsigned integer literal.
    UInt(u64),
    /// Floating-point literal.
    Float(f64),
    /// Hexadecimal literal.
    Hex(u64),
    /// Boolean literal.
    Bool(bool),
    /// Reference to another constant by name.
    ConstRef(SmolStr),
    /// Binary arithmetic operation on two sub-expressions.
    BinOp {
        /// The arithmetic operator.
        op: BinOpKind,
        /// Left operand.
        left: Box<ConstExpr>,
        /// Right operand.
        right: Box<ConstExpr>,
    },
}

/// Arithmetic and comparison operators for expressions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOpKind {
    /// Addition.
    Add,
    /// Subtraction.
    Sub,
    /// Multiplication.
    Mul,
    /// Division.
    Div,
    /// Equality.
    Eq,
    /// Inequality.
    Ne,
    /// Less than.
    Lt,
    /// Less than or equal.
    Le,
    /// Greater than.
    Gt,
    /// Greater than or equal.
    Ge,
}

// ---------------------------------------------------------------------------
// Trait Declaration
// ---------------------------------------------------------------------------

/// A trait declaration defining an interface with fields and functions.
#[derive(Debug, Clone, PartialEq)]
pub struct TraitDecl {
    /// Annotations applied to the trait.
    pub annotations: Vec<Annotation>,
    /// The trait name.
    pub name: Spanned<SmolStr>,
    /// Type parameters for generic traits (e.g., `<T>` in `trait Foo<T>`).
    pub type_params: Vec<TypeParam>,
    /// Required fields for types implementing this trait.
    pub fields: Vec<MessageField>,
    /// Required function signatures for types implementing this trait.
    pub functions: Vec<TraitFnDecl>,
}

/// A function declaration within a trait.
#[derive(Debug, Clone, PartialEq)]
pub struct TraitFnDecl {
    /// The function name.
    pub name: Spanned<SmolStr>,
    /// Function parameters.
    pub params: Vec<FnParam>,
    /// Optional return type (None for functions returning void).
    pub return_type: Option<Spanned<TypeExpr>>,
}

/// A function parameter.
#[derive(Debug, Clone, PartialEq)]
pub struct FnParam {
    /// The parameter name.
    pub name: Spanned<SmolStr>,
    /// The parameter type.
    pub ty: Spanned<TypeExpr>,
}

// ---------------------------------------------------------------------------
// Impl Declaration
// ---------------------------------------------------------------------------

/// A function implementation within an impl block.
#[derive(Debug, Clone, PartialEq)]
pub struct ImplFnDecl {
    /// Annotations applied to the function.
    pub annotations: Vec<Annotation>,
    /// The function name.
    pub name: Spanned<SmolStr>,
    /// Function parameters.
    pub params: Vec<FnParam>,
    /// Optional return type.
    pub return_type: Option<Spanned<TypeExpr>>,
    /// Function body (if present) — currently just a semicolon for external fns.
    pub body: ImplFnBody,
}

/// Function body in an impl block.
#[derive(Debug, Clone, PartialEq)]
pub enum ImplFnBody {
    /// External function (no body, just semicolon).
    External,
    /// Block body with statements.
    Block(Vec<Statement>),
}

// ---------------------------------------------------------------------------
// Expressions
// ---------------------------------------------------------------------------

/// A runtime expression in the Vexil AST.
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
    /// Integer literal.
    Int(i64),
    /// Unsigned integer literal.
    UInt(u64),
    /// Float literal.
    Float(f64),
    /// Boolean literal.
    Bool(bool),
    /// String literal.
    String(String),
    /// Identifier reference.
    Ident(SmolStr),
    /// Field access: `obj.field`.
    FieldAccess(Box<Expr>, Spanned<SmolStr>),
    /// Function call: `fn(args)`.
    Call(Box<Expr>, Vec<Expr>),
    /// Method call: `obj.method(args)` - crucial for trait dispatch.
    MethodCall(Box<Expr>, Spanned<SmolStr>, Vec<Expr>),
    /// Binary operation: `lhs op rhs`.
    Binary(BinOpKind, Box<Expr>, Box<Expr>),
    /// Unary operation: `op expr`.
    Unary(UnaryOpKind, Box<Expr>),
    /// Self reference within impl block.
    SelfRef,
}

/// Unary operators.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOpKind {
    /// Negation: `-expr`.
    Neg,
    /// Logical NOT: `!expr`.
    Not,
}

// ---------------------------------------------------------------------------
// Statements
// ---------------------------------------------------------------------------

/// A statement in a function body.
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
    /// Expression evaluated for side effects or return value.
    Expr(Expr),
    /// Variable binding: `let name: Type = value;`.
    Let {
        name: Spanned<SmolStr>,
        ty: Option<Spanned<TypeExpr>>,
        value: Expr,
    },
    /// Return statement: `return expr;`.
    Return(Option<Expr>),
    /// Assignment: `target = value;`.
    Assign { target: Expr, value: Expr },
}

/// An implementation declaration for a trait on a type.
#[derive(Debug, Clone, PartialEq)]
pub struct ImplDecl {
    /// Annotations applied to the impl.
    pub annotations: Vec<Annotation>,
    /// The trait being implemented.
    pub trait_name: Spanned<SmolStr>,
    /// Optional type arguments for generic traits (e.g., `<u64>` in `impl Tagged<u64>`).
    pub type_args: Vec<Spanned<TypeExpr>>,
    /// The target type receiving the implementation.
    pub target_type: Spanned<SmolStr>,
    /// Function implementations.
    pub functions: Vec<ImplFnDecl>,
}

// ---------------------------------------------------------------------------
// Type expressions
// ---------------------------------------------------------------------------

/// A type expression in the Vexil AST (source-faithful, unresolved).
#[derive(Debug, Clone, PartialEq)]
pub enum TypeExpr {
    /// A built-in primitive type (bool, integers, floats, void).
    Primitive(PrimitiveType),
    /// A sub-byte integer type with a specific bit width.
    SubByte(SubByteType),
    /// A semantic type with special wire encoding (string, bytes, uuid, etc.).
    Semantic(SemanticType),
    /// A named type reference (local declaration or import).
    Named(SmolStr),
    /// A qualified type reference: `namespace.Type`.
    Qualified(SmolStr, SmolStr),
    /// Generic type instantiation: `Name<TypeArg>`.
    Generic(SmolStr, Box<Spanned<TypeExpr>>),
    /// Optional wrapper: `optional<T>`.
    Optional(Box<Spanned<TypeExpr>>),
    /// Variable-length array: `array<T>`.
    Array(Box<Spanned<TypeExpr>>),
    /// Fixed-size array with compile-time known length: `array<T, N>`.
    FixedArray(Box<Spanned<TypeExpr>>, u64),
    /// A set of unique values: `set<T>`.
    Set(Box<Spanned<TypeExpr>>),
    /// An associative map: `map<K, V>`.
    Map(Box<Spanned<TypeExpr>>, Box<Spanned<TypeExpr>>),
    /// A result type (success or error): `result<Ok, Err>`.
    Result(Box<Spanned<TypeExpr>>, Box<Spanned<TypeExpr>>),
    /// 2D vector parameterized by element type.
    Vec2(Box<Spanned<TypeExpr>>),
    /// 3D vector parameterized by element type.
    Vec3(Box<Spanned<TypeExpr>>),
    /// 4D vector parameterized by element type.
    Vec4(Box<Spanned<TypeExpr>>),
    /// Quaternion parameterized by element type.
    Quat(Box<Spanned<TypeExpr>>),
    /// 3x3 matrix parameterized by element type.
    Mat3(Box<Spanned<TypeExpr>>),
    /// 4x4 matrix parameterized by element type.
    Mat4(Box<Spanned<TypeExpr>>),
    /// Inline bitfield: `bits { name1, name2, ... }`.
    BitsInline(Vec<SmolStr>),
}

/// Built-in primitive scalar types.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PrimitiveType {
    /// Boolean (1 bit on wire).
    Bool,
    /// 8-bit unsigned integer.
    U8,
    /// 16-bit unsigned integer.
    U16,
    /// 32-bit unsigned integer.
    U32,
    /// 64-bit unsigned integer.
    U64,
    /// 8-bit signed integer.
    I8,
    /// 16-bit signed integer.
    I16,
    /// 32-bit signed integer.
    I32,
    /// 64-bit signed integer.
    I64,
    /// 32-bit IEEE 754 float.
    F32,
    /// 64-bit IEEE 754 float.
    F64,
    /// 32-bit fixed-point (16.16).
    Fixed32,
    /// 64-bit fixed-point (32.32).
    Fixed64,
    /// Zero-width void type.
    Void,
}

/// A sub-byte integer type with a specific bit width (e.g. `u3`, `i5`).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SubByteType {
    /// Whether the type is signed (`true`) or unsigned (`false`).
    pub signed: bool,
    /// Number of bits (1..=64).
    pub bits: u8,
}

/// Semantic types with special wire encoding and validation rules.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SemanticType {
    /// UTF-8 string (variable-length on wire).
    String,
    /// Opaque byte sequence (variable-length on wire).
    Bytes,
    /// 24-bit RGB color (fixed 24 bits).
    Rgb,
    /// 128-bit UUID (fixed 128 bits).
    Uuid,
    /// 64-bit timestamp (fixed 64 bits).
    Timestamp,
    /// 256-bit cryptographic hash (fixed 256 bits).
    Hash,
}

// ---------------------------------------------------------------------------
// Annotations
// ---------------------------------------------------------------------------

/// A user or built-in annotation attached to a declaration or field (e.g. `@doc("...")`).
#[derive(Debug, Clone, PartialEq)]
pub struct Annotation {
    /// Source span of the entire annotation including `@` and arguments.
    pub span: Span,
    /// The annotation name (without the `@` prefix).
    pub name: Spanned<SmolStr>,
    /// Optional arguments (parenthesized key-value pairs).
    pub args: Option<Vec<AnnotationArg>>,
}

/// A single argument to an annotation (positional or keyed).
#[derive(Debug, Clone, PartialEq)]
pub struct AnnotationArg {
    /// Source span of this argument.
    pub span: Span,
    /// Optional key name (e.g. `reason:` in `@deprecated(reason: "...")`).
    pub key: Option<Spanned<SmolStr>>,
    /// The argument value.
    pub value: Spanned<AnnotationValue>,
}

/// A literal value within an annotation argument.
#[derive(Debug, Clone, PartialEq)]
pub enum AnnotationValue {
    /// Unsigned integer literal.
    Int(u64),
    /// Hexadecimal integer literal.
    Hex(u64),
    /// String literal.
    Str(String),
    /// Boolean literal.
    Bool(bool),
    /// Lowercase identifier.
    Ident(SmolStr),
    /// Uppercase identifier (PascalCase).
    UpperIdent(SmolStr),
}

// ---------------------------------------------------------------------------
// Where Clause Constraints
// ---------------------------------------------------------------------------

/// A constraint expression attached to a field via `where`.
#[derive(Debug, Clone, PartialEq)]
pub enum WhereExpr {
    /// Binary logical AND: `a && b`
    And(Box<Spanned<WhereExpr>>, Box<Spanned<WhereExpr>>),
    /// Binary logical OR: `a || b`
    Or(Box<Spanned<WhereExpr>>, Box<Spanned<WhereExpr>>),
    /// Logical NOT: `!a`
    Not(Box<Spanned<WhereExpr>>),
    /// Comparison: `value == expr`, `value != expr`, etc.
    Cmp {
        op: CmpOp,
        operand: Box<Spanned<WhereOperand>>,
    },
    /// Range check: `value in low..high` or `value in low..<high`
    Range {
        low: Box<Spanned<WhereOperand>>,
        high: Box<Spanned<WhereOperand>>,
        exclusive_high: bool, // true for `..<`, false for `..`
    },
    /// Length check: `len(value)` compared to something
    LenCmp {
        op: CmpOp,
        operand: Box<Spanned<WhereOperand>>,
    },
    /// Length in range: `len(value) in low..high`
    LenRange {
        low: Box<Spanned<WhereOperand>>,
        high: Box<Spanned<WhereOperand>>,
        exclusive_high: bool,
    },
}

/// Comparison operators used in `where` constraint expressions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CmpOp {
    /// `==` — equality.
    Eq,
    /// `!=` — inequality.
    Ne,
    /// `<` — less than.
    Lt,
    /// `>` — greater than.
    Gt,
    /// `<=` — less than or equal.
    Le,
    /// `>=` — greater than or equal.
    Ge,
}

/// Operands in where expressions (the `value` keyword is implicit).
#[derive(Debug, Clone, PartialEq)]
pub enum WhereOperand {
    /// Integer literal
    Int(i64),
    /// Float literal
    Float(f64),
    /// String literal
    String(String),
    /// Boolean literal
    Bool(bool),
    /// The `value` keyword - refers to the field's value
    Value,
    /// Reference to a const: `MAX_SIZE`, etc.
    ConstRef(SmolStr),
}

// ---------------------------------------------------------------------------
// Default values (for config fields)
// ---------------------------------------------------------------------------

/// A default value literal for config fields.
#[derive(Debug, Clone, PartialEq)]
pub enum DefaultValue {
    /// No default (explicitly unset).
    None,
    /// Boolean default.
    Bool(bool),
    /// Signed integer default.
    Int(i64),
    /// Unsigned integer default.
    UInt(u64),
    /// Floating-point default.
    Float(f64),
    /// String default.
    Str(String),
    /// Identifier reference (e.g. an enum variant name).
    Ident(SmolStr),
    /// Uppercase identifier reference.
    UpperIdent(SmolStr),
    /// Array default (list of values).
    Array(Vec<Spanned<DefaultValue>>),
}

// ---------------------------------------------------------------------------
// Tombstone
// ---------------------------------------------------------------------------

/// A tombstoned (removed) field or variant, used for schema evolution.
#[derive(Debug, Clone, PartialEq)]
pub struct Tombstone {
    /// The ordinal that was removed.
    pub ordinal: Spanned<u32>,
    /// Arguments to the `@removed` annotation (reason, since, etc.).
    pub args: Vec<TombstoneArg>,
    /// The original type of the removed field, retained as wire-inert history metadata.
    pub original_type: Option<Spanned<TypeExpr>>,
}

/// A key-value argument within a tombstone's `@removed` annotation.
#[derive(Debug, Clone, PartialEq)]
pub struct TombstoneArg {
    /// Source span of this argument.
    pub span: Span,
    /// The argument key (e.g. `reason`, `since`).
    pub key: Spanned<SmolStr>,
    /// The argument value.
    pub value: Spanned<AnnotationValue>,
}