Skip to main content

microcad_syntax/ast/
literal.rs

1// Copyright © 2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::Span;
5use crate::ast::{ItemExtras, SingleType};
6use std::num::{ParseFloatError, ParseIntError};
7use std::ops::Neg;
8use thiserror::Error;
9
10/// A literal value
11#[derive(Debug, PartialEq)]
12#[allow(missing_docs)]
13pub struct Literal {
14    pub span: Span,
15    pub extras: ItemExtras,
16    pub literal: LiteralKind,
17}
18
19impl Literal {
20    /// Get the span for the literal
21    pub fn span(&self) -> Span {
22        self.literal.span()
23    }
24}
25
26/// The various types of literal values a [`Literal`] can contain
27#[derive(Debug, PartialEq)]
28#[allow(missing_docs)]
29pub enum LiteralKind {
30    Error(LiteralError),
31    String(StringLiteral),
32    Bool(BoolLiteral),
33    Integer(IntegerLiteral),
34    Float(FloatLiteral),
35    Quantity(QuantityLiteral),
36}
37
38impl LiteralKind {
39    /// Get the span for the literal
40    pub fn span(&self) -> Span {
41        match self {
42            LiteralKind::Error(lit) => lit.span.clone(),
43            LiteralKind::String(lit) => lit.span.clone(),
44            LiteralKind::Bool(lit) => lit.span.clone(),
45            LiteralKind::Integer(lit) => lit.span.clone(),
46            LiteralKind::Float(lit) => lit.span.clone(),
47            LiteralKind::Quantity(lit) => lit.span.clone(),
48        }
49    }
50}
51
52/// A string literal, without format expressions
53#[derive(Debug, PartialEq)]
54#[allow(missing_docs)]
55pub struct StringLiteral {
56    pub span: Span,
57    pub content: String,
58}
59
60/// A boolean literal, either `true` or `false`
61#[derive(Debug, PartialEq)]
62#[allow(missing_docs)]
63pub struct BoolLiteral {
64    pub span: Span,
65    pub value: bool,
66}
67
68/// An integer literal without type
69#[derive(Debug, PartialEq)]
70#[allow(missing_docs)]
71pub struct IntegerLiteral {
72    pub span: Span,
73    pub value: i64,
74}
75
76impl Neg for IntegerLiteral {
77    type Output = IntegerLiteral;
78
79    fn neg(self) -> Self::Output {
80        IntegerLiteral {
81            span: (self.span.start - 1)..self.span.end,
82            value: -self.value,
83        }
84    }
85}
86
87/// An float literal without type
88#[derive(Debug, PartialEq)]
89#[allow(missing_docs)]
90pub struct FloatLiteral {
91    pub span: Span,
92    pub value: f64,
93}
94
95impl Neg for FloatLiteral {
96    type Output = FloatLiteral;
97
98    fn neg(self) -> Self::Output {
99        FloatLiteral {
100            span: (self.span.start - 1)..self.span.end,
101            value: -self.value,
102        }
103    }
104}
105
106// A float literal with type
107#[derive(Debug, PartialEq)]
108#[allow(missing_docs)]
109pub struct QuantityLiteral {
110    pub span: Span,
111    pub value: f64,
112    pub ty: SingleType,
113}
114
115impl Neg for QuantityLiteral {
116    type Output = QuantityLiteral;
117
118    fn neg(self) -> Self::Output {
119        QuantityLiteral {
120            span: (self.span.start - 1)..self.span.end,
121            value: -self.value,
122            ty: self.ty,
123        }
124    }
125}
126
127/// An error that can be encountered while parsing literal tokens
128#[derive(Debug, PartialEq, Clone)]
129#[allow(missing_docs)]
130pub struct LiteralError {
131    pub span: Span,
132    pub kind: LiteralErrorKind,
133}
134
135#[derive(Debug, Error, PartialEq, Clone)]
136#[allow(missing_docs)]
137pub enum LiteralErrorKind {
138    #[error(transparent)]
139    Float(#[from] ParseFloatError),
140    #[error(transparent)]
141    Int(#[from] ParseIntError),
142    #[error("unclosed string literal")]
143    UnclosedString,
144    #[error("only numeric literals can be typed")]
145    Untypable,
146}