glsl_lang_pp/processor/
nodes.rs

1use std::{borrow::Cow, cmp::Ordering, convert::TryFrom, str::FromStr};
2
3use arrayvec::ArrayVec;
4use rowan::NodeOrToken;
5use thiserror::Error;
6
7use lang_util::{FileId, SmolStr};
8
9use crate::{
10    exts::names::ExtNameAtom,
11    parser::{SyntaxKind::*, SyntaxNode, SyntaxToken},
12    processor::expr::{EvalResult, ExprEvaluator},
13    types::{
14        path::{ParsedPath, PathType},
15        Token,
16    },
17    util::Unescaped,
18};
19
20use super::{
21    definition::{trim_ws, MacroInvocation},
22    event::{Event, OutputToken, SendEvent},
23    expand::ExpandLocation,
24    ProcessorState,
25};
26
27#[derive(Debug, Clone)]
28pub struct Directive<I> {
29    file_id: FileId,
30    node: SyntaxNode,
31    inner: I,
32}
33
34impl<I> Directive<I> {
35    pub fn new(file_id: FileId, node: SyntaxNode, inner: I) -> Self {
36        Self {
37            file_id,
38            node,
39            inner,
40        }
41    }
42
43    pub fn file_id(&self) -> FileId {
44        self.file_id
45    }
46
47    pub fn node(&self) -> &SyntaxNode {
48        &self.node
49    }
50
51    pub fn into_inner(self) -> (FileId, I, SyntaxNode) {
52        (self.file_id, self.inner, self.node)
53    }
54}
55
56impl<I: TryFrom<(FileId, SyntaxNode)> + std::fmt::Debug + Clone> TryFrom<(FileId, SyntaxNode)>
57    for Directive<I>
58{
59    type Error = (I::Error, SyntaxNode);
60
61    fn try_from((file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
62        match I::try_from((file_id, value.clone())) {
63            Ok(inner) => Ok(Self {
64                file_id,
65                inner,
66                node: value,
67            }),
68            Err(err) => Err((err, value)),
69        }
70    }
71}
72
73impl<I> std::ops::Deref for Directive<I> {
74    type Target = I;
75
76    fn deref(&self) -> &Self::Target {
77        &self.inner
78    }
79}
80
81pub type DirectiveResult<I> =
82    Result<Directive<I>, <Directive<I> as TryFrom<(FileId, SyntaxNode)>>::Error>;
83
84pub trait DirectiveExt: Sized {
85    fn into_node(self) -> SyntaxNode;
86}
87
88impl<I: TryFrom<(FileId, SyntaxNode)> + std::fmt::Debug + Clone> DirectiveExt
89    for DirectiveResult<I>
90{
91    fn into_node(self) -> SyntaxNode {
92        match self {
93            Ok(directive) => directive.into_inner().2,
94            Err(error) => error.1,
95        }
96    }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct Invalid;
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub struct Empty;
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct Version {
107    pub number: u16,
108    pub profile: VersionProfile,
109    pub parsed_profile: Option<VersionProfile>,
110}
111
112impl Default for Version {
113    fn default() -> Self {
114        // Spec 3.3: shaders that do not include a #version directive will be treated as targeting
115        // version 1.10
116        Self {
117            number: 110,
118            profile: VersionProfile::None,
119            parsed_profile: None,
120        }
121    }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Error)]
125pub enum VersionError {
126    #[error("missing version number in #version directive")]
127    MissingVersionNumber,
128    #[error("invalid version number in #version directive")]
129    InvalidVersionNumber { version_number: String },
130    #[error("unsupported version number in #version directive")]
131    UnsupportedVersionNumber,
132    #[error("invalid version profile")]
133    InvalidVersionProfile { version_number: u16 },
134    #[error("cannot specify a profile")]
135    ProfileUnsupported { version_number: u16 },
136    #[error("es profile is required")]
137    EsProfileRequired { version_number: u16 },
138}
139
140const VALID_VERSION_NUMBERS: [u16; 17] = [
141    100, 110, 120, 130, 140, 150, 300, 310, 320, 330, 400, 410, 420, 430, 440, 450, 460,
142];
143
144impl TryFrom<(FileId, SyntaxNode)> for Version {
145    type Error = VersionError;
146
147    fn try_from((_file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
148        // Parse version number
149        // The GLSL spec refers to the ISO/IEC 14882:1998 (C++) standard for preprocessor
150        // directives, and mentions that #version, due to it "following the same convention"
151        // as __VERSION__, is "a decimal integer". The cited standard, section 2.9
152        // "Preprocessing numbers", says that such numbers "lexically include all integral
153        // literal tokens". These correspond to the "integer-constant" (for the GLSL grammar)
154        // or "integer-literal" (for the C++ grammar) grammar symbols. But, because #version
155        // is a decimal integer, it is understood that the only "integer-constant" production
156        // rule we have to accept is the one for decimal constants, which are sequences of
157        // decimal digits beginning with a nonzero digit and followed by an optional
158        // "integer-suffix" (u or U character). This is a subset of what we can parse as
159        // (U)INT_CONST tokens, but other implementations are likely to observe Postel's law
160        let version_number: u16 = value
161            .children()
162            .find_map(|token| {
163                if token.kind() == PP_VERSION_NUMBER {
164                    token.first_token()
165                } else {
166                    None
167                }
168            })
169            .ok_or(Self::Error::MissingVersionNumber)
170            .and_then(|token| {
171                let token_string = Unescaped::new(token.text()).to_string();
172                match Token::parse_digits(&token_string) {
173                    Token::INT_CONST(version_number)
174                        if version_number >= 0 && version_number <= u16::MAX as i32 =>
175                    {
176                        Ok(version_number as u16)
177                    }
178                    Token::UINT_CONST(version_number) if version_number <= u16::MAX as u32 => {
179                        Ok(version_number as u16)
180                    }
181                    _ => Err(Self::Error::InvalidVersionNumber {
182                        version_number: token_string.into_owned(),
183                    }),
184                }
185            })?;
186
187        // Check the version number is supported
188        VALID_VERSION_NUMBERS
189            .binary_search(&version_number)
190            .map_err(|_| Self::Error::UnsupportedVersionNumber)?;
191
192        // Find the profile
193        let profile = if let Some(profile) = value.children().find_map(|token| {
194            if token.kind() == PP_VERSION_PROFILE {
195                token.first_token()
196            } else {
197                None
198            }
199        }) {
200            Unescaped::new(profile.text())
201                .to_string()
202                .parse()
203                .map_err(|_| Self::Error::InvalidVersionProfile { version_number })?
204        } else {
205            VersionProfile::None
206        };
207
208        // A profile argument can only be used with version 150 or greater.
209        if version_number < 150 && profile != VersionProfile::None {
210            return Err(Self::Error::ProfileUnsupported { version_number });
211        }
212
213        // If version 300 or 310 is specified, the profile argument is not optional and must be es,
214        // or a compile-time error results
215        if (version_number == 300 || version_number == 310) && profile != VersionProfile::Es {
216            return Err(Self::Error::EsProfileRequired { version_number });
217        }
218
219        if version_number == 100 || version_number == 300 || version_number == 310 {
220            // Shaders that specify #version 100 will be treated as targeting version 1.00 of the
221            // OpenGL ES Shading Language. Shaders that specify #version 300 will be treated as
222            // targeting version 3.00 of the OpenGL ES Shading Language. Shaders that specify
223            // #version 310 will be treated as targeting version 3.10 of the OpenGL ES Shading
224            // Language.
225
226            Ok(Self {
227                number: version_number,
228                profile: VersionProfile::Es,
229                parsed_profile: Some(profile),
230            })
231        } else if version_number >= 150 {
232            // If no profile argument is provided and the version is 150 or greater, the default is
233            // core.
234
235            Ok(Self {
236                number: version_number,
237                profile: if profile == VersionProfile::None {
238                    VersionProfile::Core
239                } else {
240                    profile
241                },
242                parsed_profile: Some(profile),
243            })
244        } else {
245            Ok(Self {
246                number: version_number,
247                profile,
248                parsed_profile: Some(profile),
249            })
250        }
251    }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
255pub enum VersionProfile {
256    None,
257    Core,
258    Compatibility,
259    Es,
260}
261
262impl FromStr for VersionProfile {
263    type Err = ();
264
265    fn from_str(s: &str) -> Result<Self, Self::Err> {
266        Ok(match s {
267            "core" => Self::Core,
268            "compatibility" => Self::Compatibility,
269            "es" => Self::Es,
270            _ => {
271                return Err(());
272            }
273        })
274    }
275}
276
277impl VersionProfile {
278    /// Returns an integer representing the relative size of the feature set of an OpenGL profile.
279    /// Profiles with a higher index are assumed to offer a superset of the features of profiles
280    /// with a lower index.
281    fn as_feature_set_size_index(&self) -> usize {
282        // OpenGL ES offers a feature set smaller than the core profile.
283        // Conversely, the core profile has less features than the compatibility profile.
284        // When no profile is specified, the profile defaults to core
285        match self {
286            Self::None | Self::Core => 1,
287            Self::Compatibility => 2,
288            Self::Es => 0,
289        }
290    }
291}
292
293impl PartialOrd for VersionProfile {
294    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
295        Some(self.cmp(other))
296    }
297}
298
299impl Ord for VersionProfile {
300    fn cmp(&self, other: &Self) -> Ordering {
301        self.as_feature_set_size_index()
302            .cmp(&other.as_feature_set_size_index())
303    }
304}
305
306/// Extension behavior
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
308pub enum ExtensionBehavior {
309    Require,
310    Enable,
311    Warn,
312    Disable,
313}
314
315impl ExtensionBehavior {
316    pub fn is_active(&self) -> bool {
317        matches!(
318            self,
319            ExtensionBehavior::Require | ExtensionBehavior::Enable | ExtensionBehavior::Warn
320        )
321    }
322}
323
324impl FromStr for ExtensionBehavior {
325    type Err = ();
326
327    fn from_str(s: &str) -> Result<Self, Self::Err> {
328        Ok(match s {
329            "require" => Self::Require,
330            "enable" => Self::Enable,
331            "warn" => Self::Warn,
332            "disable" => Self::Disable,
333            _ => {
334                return Err(());
335            }
336        })
337    }
338}
339
340/// Extension name
341#[derive(Debug, Clone, PartialEq, Eq)]
342pub enum ExtensionName {
343    /// All extensions
344    All,
345    /// Specific extension
346    Specific(ExtNameAtom),
347}
348
349impl ExtensionName {
350    pub fn new(name: Cow<'_, str>) -> Self {
351        Self::from(name.as_ref())
352    }
353}
354
355impl PartialEq<ExtNameAtom> for ExtensionName {
356    fn eq(&self, other: &ExtNameAtom) -> bool {
357        match self {
358            ExtensionName::All => false,
359            ExtensionName::Specific(this) => this == other,
360        }
361    }
362}
363
364impl std::fmt::Display for ExtensionName {
365    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366        match self {
367            ExtensionName::All => write!(f, "all"),
368            ExtensionName::Specific(name) => write!(f, "{}", name),
369        }
370    }
371}
372
373impl From<&str> for ExtensionName {
374    fn from(name: &str) -> Self {
375        if name == "all" {
376            Self::All
377        } else {
378            Self::Specific(ExtNameAtom::from(name))
379        }
380    }
381}
382
383impl From<ExtNameAtom> for ExtensionName {
384    fn from(name: ExtNameAtom) -> Self {
385        if name.as_ref() == "all" {
386            Self::All
387        } else {
388            Self::Specific(name)
389        }
390    }
391}
392
393#[derive(Debug, Clone, PartialEq, Eq)]
394pub struct Extension {
395    pub name: ExtensionName,
396    pub behavior: ExtensionBehavior,
397}
398
399impl Extension {
400    pub fn require(name: ExtensionName) -> Self {
401        Self {
402            name,
403            behavior: ExtensionBehavior::Require,
404        }
405    }
406
407    pub fn enable(name: ExtensionName) -> Self {
408        Self {
409            name,
410            behavior: ExtensionBehavior::Enable,
411        }
412    }
413
414    pub fn warn(name: ExtensionName) -> Self {
415        Self {
416            name,
417            behavior: ExtensionBehavior::Warn,
418        }
419    }
420
421    pub fn disable(name: ExtensionName) -> Self {
422        Self {
423            name,
424            behavior: ExtensionBehavior::Disable,
425        }
426    }
427}
428
429#[derive(Debug, Clone, PartialEq, Eq, Error)]
430pub enum ExtensionError {
431    #[error("missing extension name in #extension directive")]
432    MissingExtensionName,
433    #[error("missing extension behavior in #extension directive")]
434    MissingExtensionBehavior { name: ExtensionName },
435    #[error("invalid extension behavior in #extension directive")]
436    InvalidExtensionBehavior { name: ExtensionName },
437    #[error("invalid behavior in #extension all directive")]
438    InvalidAllBehavior { behavior: ExtensionBehavior },
439}
440
441impl TryFrom<(FileId, SyntaxNode)> for Extension {
442    type Error = ExtensionError;
443
444    fn try_from((_file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
445        // Collect identifiers
446        let idents: ArrayVec<_, 2> = value
447            .children_with_tokens()
448            .filter_map(|node_or_token| {
449                if let NodeOrToken::Token(token) = node_or_token {
450                    if token.kind() == IDENT_KW {
451                        Some(token)
452                    } else {
453                        None
454                    }
455                } else {
456                    None
457                }
458            })
459            .skip(1)
460            .take(2)
461            .collect();
462
463        let name = idents
464            .first()
465            .ok_or(Self::Error::MissingExtensionName)
466            .map(|name| ExtensionName::new(Unescaped::new(name.text()).to_string()))?;
467
468        let behavior = idents
469            .get(1)
470            .ok_or_else(|| Self::Error::MissingExtensionBehavior { name: name.clone() })
471            .and_then(|behavior| {
472                ExtensionBehavior::from_str(&Unescaped::new(behavior.text()).to_string())
473                    .map_err(|_| Self::Error::InvalidExtensionBehavior { name: name.clone() })
474            })?;
475
476        if name == ExtensionName::All
477            && behavior != ExtensionBehavior::Warn
478            && behavior != ExtensionBehavior::Disable
479        {
480            return Err(Self::Error::InvalidAllBehavior { behavior });
481        }
482
483        Ok(Self { name, behavior })
484    }
485}
486
487#[derive(Debug, Clone, PartialEq, Eq)]
488pub struct DefineObject {
489    tokens: SyntaxNode,
490}
491
492impl DefineObject {
493    pub fn new(tokens: SyntaxNode) -> Self {
494        Self { tokens }
495    }
496
497    pub fn one() -> Self {
498        Self::from_str("1").unwrap()
499    }
500
501    pub fn body(&self) -> &SyntaxNode {
502        &self.tokens
503    }
504}
505
506impl FromStr for DefineObject {
507    type Err = ();
508
509    fn from_str(s: &str) -> Result<Self, Self::Err> {
510        Ok(Self {
511            tokens: crate::parser::Parser::new(s)
512                .parse_define_body()
513                .ok_or(())?,
514        })
515    }
516}
517
518#[derive(Debug, Clone, PartialEq, Eq)]
519pub struct DefineFunction {
520    args: Vec<SmolStr>,
521    tokens: SyntaxNode,
522}
523
524impl DefineFunction {
525    pub fn new(args: Vec<SmolStr>, tokens: SyntaxNode) -> Self {
526        Self { args, tokens }
527    }
528
529    pub fn arg_names(&self) -> &[SmolStr] {
530        &self.args
531    }
532
533    pub fn body(&self) -> &SyntaxNode {
534        &self.tokens
535    }
536}
537
538#[derive(Debug, Clone, PartialEq, Eq)]
539pub enum DefineKind {
540    Object(DefineObject),
541    Function(DefineFunction),
542}
543
544/// A preprocessor definition
545#[derive(Debug, Clone, PartialEq, Eq)]
546pub struct Define {
547    /// Name of this definition
548    name: SmolStr,
549    /// Type of this definition
550    kind: DefineKind,
551    /// true if this definition can't be #undef-ined
552    protected: bool,
553}
554
555impl Define {
556    pub fn object(name: SmolStr, object: DefineObject, protected: bool) -> Self {
557        Self {
558            name,
559            kind: DefineKind::Object(object),
560            protected,
561        }
562    }
563
564    pub fn function(name: SmolStr, function: DefineFunction, protected: bool) -> Self {
565        Self {
566            name,
567            kind: DefineKind::Function(function),
568            protected,
569        }
570    }
571
572    pub fn name(&self) -> &str {
573        &self.name
574    }
575
576    pub fn kind(&self) -> &DefineKind {
577        &self.kind
578    }
579
580    pub fn protected(&self) -> bool {
581        self.protected
582    }
583}
584
585#[derive(Debug, Clone, PartialEq, Eq, Error)]
586pub enum DefineError {
587    #[error("missing name for #define")]
588    MissingName,
589    #[error("missing body for #define")]
590    MissingBody { name: SmolStr },
591}
592
593impl TryFrom<(FileId, SyntaxNode)> for Define {
594    type Error = DefineError;
595
596    fn try_from((_file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
597        // Find out define name
598        let name = value
599            .children_with_tokens()
600            .filter_map(|node_or_token| {
601                if let NodeOrToken::Token(token) = node_or_token {
602                    if token.kind() == IDENT_KW {
603                        Some(token)
604                    } else {
605                        None
606                    }
607                } else {
608                    None
609                }
610            })
611            .nth(1)
612            .ok_or(Self::Error::MissingName)?;
613
614        let name = Unescaped::new(name.text());
615
616        // Find the body
617        let body = value
618            .children()
619            .find(|node| node.kind() == PP_DEFINE_BODY)
620            .ok_or_else(|| Self::Error::MissingBody { name: name.into() })?;
621
622        // Find the arguments
623        if let Some(args) = value.children().find(|node| node.kind() == PP_DEFINE_ARGS) {
624            let args = args
625                .children()
626                .filter_map(|arg| {
627                    if arg.kind() == PP_DEFINE_ARG {
628                        arg.first_token()
629                            .map(|token| Unescaped::new(token.text()).into())
630                    } else {
631                        None
632                    }
633                })
634                .collect();
635
636            Ok(Self::function(
637                name.into(),
638                DefineFunction::new(args, body),
639                false,
640            ))
641        } else {
642            Ok(Self::object(name.into(), DefineObject::new(body), false))
643        }
644    }
645}
646
647#[derive(Debug, Clone, PartialEq, Eq)]
648pub struct IfDef {
649    pub ident: SmolStr,
650}
651
652#[derive(Debug, Clone, PartialEq, Eq, Error)]
653pub enum IfDefError {
654    #[error("identifier for #ifdef is missing")]
655    MissingIdentifier,
656}
657
658impl TryFrom<(FileId, SyntaxNode)> for IfDef {
659    type Error = IfDefError;
660
661    fn try_from((_file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
662        let pp_ident = value
663            .children()
664            .find_map(|node| {
665                if node.kind() == PP_IDENT {
666                    node.first_child_or_token()
667                        .and_then(|node_or_token| node_or_token.into_token())
668                } else {
669                    None
670                }
671            })
672            .ok_or(Self::Error::MissingIdentifier)?;
673
674        Ok(Self {
675            ident: Unescaped::new(pp_ident.text()).into(),
676        })
677    }
678}
679
680#[derive(Debug, Clone, PartialEq, Eq)]
681pub struct IfNDef {
682    pub ident: SmolStr,
683}
684
685impl TryFrom<(FileId, SyntaxNode)> for IfNDef {
686    type Error = <IfDef as TryFrom<(FileId, SyntaxNode)>>::Error;
687
688    fn try_from(value: (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
689        Ok(Self {
690            ident: IfDef::try_from(value)?.ident,
691        })
692    }
693}
694
695#[derive(Debug, Clone, PartialEq, Eq)]
696pub struct Undef {
697    pub ident: SmolStr,
698}
699
700impl TryFrom<(FileId, SyntaxNode)> for Undef {
701    type Error = <IfDef as TryFrom<(FileId, SyntaxNode)>>::Error;
702
703    fn try_from(value: (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
704        Ok(Self {
705            ident: IfDef::try_from(value)?.ident,
706        })
707    }
708}
709
710#[derive(Debug, Clone, PartialEq, Eq)]
711pub struct Error {
712    pub message: String,
713}
714
715#[derive(Debug, Clone, PartialEq, Eq, Error)]
716pub enum ErrorError {
717    #[error("missing body for #error")]
718    MissingBody,
719}
720
721impl TryFrom<(FileId, SyntaxNode)> for Error {
722    type Error = ErrorError;
723
724    fn try_from((_file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
725        let body = value
726            .children()
727            .find(|node| node.kind() == PP_ERROR_BODY)
728            .ok_or(Self::Error::MissingBody)?;
729
730        // Unescape line continuations
731        let raw_message = body.text();
732        let mut message = String::with_capacity(raw_message.len().into());
733
734        raw_message.for_each_chunk(|chunk| {
735            message.extend(Unescaped::new(chunk).chars());
736        });
737
738        Ok(Self { message })
739    }
740}
741
742#[derive(Debug, Clone, PartialEq, Eq)]
743pub struct Include {
744    file_id: FileId,
745    path: SyntaxNode,
746}
747
748impl Include {
749    pub fn path(
750        &self,
751        current_state: &ProcessorState,
752        location: &ExpandLocation,
753    ) -> Result<ParsedPath, IncludeError> {
754        // Perform macro substitution
755        let tokens = self
756            .path
757            .children_with_tokens()
758            .filter_map(NodeOrToken::into_token)
759            .map(|token| (token, self.file_id))
760            .collect();
761        let subs_events = MacroInvocation::substitute_vec(current_state, tokens, location);
762
763        // Make sure they are all tokens
764        if !subs_events.iter().all(Event::is_token) {
765            return Err(IncludeError::MalformedPath {
766                tokens: subs_events.into_iter().map(Into::into).collect(),
767            });
768        }
769
770        let subs_tokens: Vec<_> = subs_events
771            .into_iter()
772            .filter_map(Event::into_token)
773            .collect();
774
775        // Discard trivial tokens
776        let subs_tokens = trim_ws(&subs_tokens);
777
778        // By now, the include should either be a quote string or an angle string, and this should
779        // be the only token
780        if subs_tokens.is_empty() {
781            return Err(IncludeError::MissingPath);
782        } else if subs_tokens.len() > 1 {
783            return Err(IncludeError::ExtraTokens {
784                tokens: subs_tokens.to_vec(),
785            });
786        }
787
788        // unwrap: we just checked there is one
789        let first_token = subs_tokens.first().unwrap();
790        let ty = match first_token.kind() {
791            ANGLE_STRING => PathType::Angle,
792            QUOTE_STRING => PathType::Quote,
793            _ => {
794                return Err(IncludeError::InvalidPathLiteral {
795                    token: first_token.clone(),
796                });
797            }
798        };
799
800        let text = first_token.text();
801        let text = &text[1..text.len() - 1];
802
803        Ok(ParsedPath {
804            path: text.to_string(),
805            ty,
806        })
807    }
808}
809
810#[derive(Debug, Clone, PartialEq, Eq, Error)]
811pub enum IncludeError {
812    #[error("missing path for #include directive")]
813    MissingPath,
814    #[error("malformed path")]
815    MalformedPath { tokens: Vec<SendEvent> },
816    #[error("extra tokens in #include path")]
817    ExtraTokens { tokens: Vec<OutputToken> },
818    #[error("invalid path literal")]
819    InvalidPathLiteral { token: OutputToken },
820}
821
822impl TryFrom<(FileId, SyntaxNode)> for Include {
823    type Error = IncludeError;
824
825    fn try_from((file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
826        Ok(Self {
827            file_id,
828            path: value
829                .children()
830                .find(|node| node.kind() == PP_INCLUDE_PATH)
831                .ok_or(Self::Error::MissingPath)?,
832        })
833    }
834}
835
836#[derive(Debug, Clone, PartialEq, Eq)]
837pub struct Line {
838    file_id: FileId,
839    body: SyntaxNode,
840}
841
842#[derive(Debug, Clone, PartialEq, Eq)]
843pub enum ParsedLine {
844    Line(u32),
845    LineAndFileNumber(u32, u32),
846    LineAndPath(u32, String),
847}
848
849impl ParsedLine {
850    pub fn line_number(&self) -> u32 {
851        match self {
852            ParsedLine::Line(line)
853            | ParsedLine::LineAndFileNumber(line, _)
854            | ParsedLine::LineAndPath(line, _) => *line,
855        }
856    }
857}
858
859impl From<ParsedLine> for lang_util::located::FileOverride {
860    fn from(value: ParsedLine) -> Self {
861        match value {
862            ParsedLine::Line(_) => Self::None,
863            ParsedLine::LineAndFileNumber(_, file) => Self::Number(file),
864            ParsedLine::LineAndPath(_, path) => Self::Path(path),
865        }
866    }
867}
868
869impl Line {
870    pub fn parse(
871        &self,
872        current_state: &ProcessorState,
873        location: &ExpandLocation,
874    ) -> Result<ParsedLine, LineError> {
875        // Perform macro substitution
876        let tokens = self
877            .body
878            .children_with_tokens()
879            .filter_map(NodeOrToken::into_token)
880            .map(|token| (token, self.file_id))
881            .collect();
882        let subs_events = MacroInvocation::substitute_vec(current_state, tokens, location);
883
884        // Make sure they are all tokens
885        if !subs_events.iter().all(Event::is_token) {
886            return Err(LineError::MalformedLine {
887                tokens: subs_events.into_iter().map(Into::into).collect(),
888            });
889        }
890
891        // Evalute the expressions in the line directive
892        let eval_results: Vec<_> = ExprEvaluator::new(
893            subs_events.iter().filter_map(Event::as_token),
894            current_state,
895        )
896        .collect();
897
898        // By now, the include should either be a quote string or an angle string, and this should
899        // be the only token
900        if !eval_results.is_empty() && eval_results.len() <= 2 {
901            let token = &eval_results[0];
902            // Line number
903            let line_number: u32 = if let EvalResult::Constant(Ok(value)) = token {
904                if *value >= 0 {
905                    Some(*value as _)
906                } else {
907                    None
908                }
909            } else {
910                None
911            }
912            .ok_or_else(|| LineError::InvalidLineNumber {
913                token: token.clone(),
914            })?;
915
916            if eval_results.len() > 1 {
917                let token = &eval_results[1];
918                match token {
919                    EvalResult::Constant(value) => Ok(ParsedLine::LineAndFileNumber(
920                        line_number,
921                        if let Ok(value) =
922                            value.and_then(|val| if val >= 0 { Ok(val as _) } else { Err(()) })
923                        {
924                            Ok(value)
925                        } else {
926                            Err(LineError::InvalidPath {
927                                token: token.clone(),
928                            })
929                        }?,
930                    )),
931                    EvalResult::Token(token) => {
932                        if token.kind() == QUOTE_STRING {
933                            let text = token.text();
934                            Ok(ParsedLine::LineAndPath(
935                                line_number,
936                                text[1..text.len() - 1].to_string(),
937                            ))
938                        } else {
939                            Err(LineError::InvalidPath {
940                                token: EvalResult::Token(token.clone()),
941                            })
942                        }
943                    }
944                }
945            } else {
946                Ok(ParsedLine::Line(line_number))
947            }
948        } else if eval_results.is_empty() {
949            Err(LineError::MissingLineNumber)
950        } else {
951            Err(LineError::ExtraTokens {
952                tokens: eval_results,
953            })
954        }
955    }
956}
957
958#[derive(Debug, Clone, PartialEq, Eq, Error)]
959pub enum LineError {
960    #[error("missing body for #line directive")]
961    MissingBody,
962    #[error("malformed line")]
963    MalformedLine { tokens: Vec<SendEvent> },
964    #[error("missing line number")]
965    MissingLineNumber,
966    #[error("extra tokens in #line path")]
967    ExtraTokens { tokens: Vec<EvalResult> },
968    #[error("invalid line number")]
969    InvalidLineNumber { token: EvalResult },
970    #[error("invalid path")]
971    InvalidPath { token: EvalResult },
972}
973
974impl TryFrom<(FileId, SyntaxNode)> for Line {
975    type Error = LineError;
976
977    fn try_from((file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
978        Ok(Self {
979            file_id,
980            body: value
981                .children()
982                .find(|node| node.kind() == PP_LINE_BODY)
983                .ok_or(Self::Error::MissingBody)?,
984        })
985    }
986}
987
988#[derive(Debug, Clone, PartialEq, Eq, Error)]
989pub enum IfEvalError {
990    #[error("malformed expression")]
991    MalformedExpr { tokens: Vec<SendEvent> },
992    #[error("missing expression")]
993    MissingExpr,
994    #[error("extra tokens at end of expression")]
995    ExtraTokens { tokens: Vec<EvalResult> },
996    #[error("invalid constant expression")]
997    InvalidExpr { token: EvalResult },
998}
999
1000fn eval_inner(
1001    definition_file_id: FileId,
1002    body: &SyntaxNode,
1003    current_state: &ProcessorState,
1004    location: &ExpandLocation,
1005) -> (bool, Option<IfEvalError>) {
1006    // Perform macro substitution
1007    let tokens = body
1008        .children_with_tokens()
1009        .filter_map(NodeOrToken::into_token)
1010        .map(|token| (token, definition_file_id))
1011        .collect();
1012    let subs_events = MacroInvocation::substitute_vec(current_state, tokens, location);
1013
1014    // Make sure they are all tokens
1015    if !subs_events.iter().all(Event::is_token) {
1016        return (
1017            true,
1018            Some(IfEvalError::MalformedExpr {
1019                tokens: subs_events.into_iter().map(Into::into).collect(),
1020            }),
1021        );
1022    }
1023
1024    // Evalute the expressions in the line directive
1025    let eval_results: Vec<_> = ExprEvaluator::new(
1026        subs_events.iter().filter_map(Event::as_token),
1027        current_state,
1028    )
1029    .collect();
1030
1031    // Check that we have at least "something" to evaluate
1032    if eval_results.is_empty() {
1033        return (true, Some(IfEvalError::MissingExpr));
1034    }
1035
1036    // Split the result and the rest
1037    let (first, rest): (_, Vec<_>) = {
1038        let mut iter = eval_results.into_iter();
1039        let first = iter.next().unwrap();
1040        (first, iter.collect())
1041    };
1042
1043    // Do we have extra tokens?
1044    let error = if !rest.is_empty() {
1045        Some(IfEvalError::ExtraTokens { tokens: rest })
1046    } else {
1047        None
1048    };
1049
1050    // Is the result an int?
1051    match first {
1052        EvalResult::Constant(Ok(value)) => (value != 0, error),
1053        other => (true, Some(IfEvalError::InvalidExpr { token: other })),
1054    }
1055}
1056
1057fn eval_if<E: From<IfEvalError>>(
1058    definition_file_id: FileId,
1059    body: &SyntaxNode,
1060    current_state: &ProcessorState,
1061    location: &ExpandLocation,
1062) -> (bool, Option<E>) {
1063    let (result, e) = eval_inner(definition_file_id, body, current_state, location);
1064    (result, e.map(E::from))
1065}
1066
1067#[derive(Debug, Clone, PartialEq, Eq, Error)]
1068pub enum IfError {
1069    #[error("missing body for #if directive")]
1070    MissingBody,
1071    #[error(transparent)]
1072    Eval(#[from] IfEvalError),
1073}
1074
1075#[derive(Debug, Clone, PartialEq, Eq)]
1076pub struct If {
1077    file_id: FileId,
1078    body: SyntaxNode,
1079}
1080
1081impl If {
1082    pub fn eval(
1083        &self,
1084        current_state: &ProcessorState,
1085        location: &ExpandLocation,
1086    ) -> (bool, Option<IfError>) {
1087        eval_if(self.file_id, &self.body, current_state, location)
1088    }
1089}
1090
1091impl TryFrom<(FileId, SyntaxNode)> for If {
1092    type Error = IfError;
1093
1094    fn try_from((file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
1095        Ok(Self {
1096            file_id,
1097            body: value
1098                .children()
1099                .find(|node| node.kind() == PP_IF_EXPR)
1100                .ok_or(Self::Error::MissingBody)?,
1101        })
1102    }
1103}
1104
1105#[derive(Debug, Clone, PartialEq, Eq)]
1106pub struct Elif {
1107    file_id: FileId,
1108    body: SyntaxNode,
1109}
1110
1111impl Elif {
1112    pub fn eval(
1113        &self,
1114        current_state: &ProcessorState,
1115        location: &ExpandLocation,
1116    ) -> (bool, Option<ElifError>) {
1117        eval_if(self.file_id, &self.body, current_state, location)
1118    }
1119}
1120
1121#[derive(Debug, Clone, PartialEq, Eq, Error)]
1122pub enum ElifError {
1123    #[error("missing body for #elif directive")]
1124    MissingBody,
1125    #[error(transparent)]
1126    Eval(#[from] IfEvalError),
1127}
1128
1129impl TryFrom<(FileId, SyntaxNode)> for Elif {
1130    type Error = ElifError;
1131
1132    fn try_from((file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
1133        Ok(Self {
1134            file_id,
1135            body: value
1136                .children()
1137                .find(|node| node.kind() == PP_IF_EXPR)
1138                .ok_or(Self::Error::MissingBody)?,
1139        })
1140    }
1141}
1142
1143#[derive(Debug, Clone, PartialEq, Eq)]
1144pub struct Else;
1145
1146#[derive(Debug, Clone, PartialEq, Eq, Error)]
1147pub enum ElseError {}
1148
1149impl TryFrom<(FileId, SyntaxNode)> for Else {
1150    type Error = ElseError;
1151
1152    fn try_from(_: (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
1153        Ok(Self)
1154    }
1155}
1156
1157#[derive(Debug, Clone, PartialEq, Eq)]
1158pub struct EndIf;
1159
1160#[derive(Debug, Clone, PartialEq, Eq, Error)]
1161pub enum EndIfError {}
1162
1163impl TryFrom<(FileId, SyntaxNode)> for EndIf {
1164    type Error = EndIfError;
1165
1166    fn try_from(_: (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
1167        Ok(Self)
1168    }
1169}
1170
1171#[derive(Debug, Clone, PartialEq, Eq)]
1172pub struct Pragma {
1173    value: ParsedPragma,
1174    raw: String,
1175}
1176
1177impl Pragma {
1178    pub fn value(&self) -> &ParsedPragma {
1179        &self.value
1180    }
1181
1182    pub fn raw(&self) -> &str {
1183        self.raw.as_str()
1184    }
1185
1186    fn parse_function_pragma(tokens: &[SyntaxToken]) -> Option<bool> {
1187        if tokens.len() == 4 {
1188            let value = Unescaped::new(tokens[2].text()).to_string();
1189
1190            if tokens[1].kind() == LPAREN && tokens[3].kind() == RPAREN {
1191                if value == "on" {
1192                    return Some(true);
1193                } else if value == "off" {
1194                    return Some(false);
1195                }
1196            }
1197        }
1198
1199        None
1200    }
1201}
1202
1203#[derive(Debug, Clone, PartialEq, Eq)]
1204pub enum ParsedPragma {
1205    StdGl(SyntaxNode),
1206    Optimize(bool),
1207    Debug(bool),
1208    Unknown(SyntaxNode),
1209}
1210
1211#[derive(Debug, Clone, PartialEq, Eq, Error)]
1212pub enum PragmaError {
1213    #[error("missing body")]
1214    MissingBody,
1215    #[error("{name} pragma syntax is incorrect")]
1216    IncorrectSyntax { name: SmolStr },
1217}
1218
1219impl TryFrom<(FileId, SyntaxNode)> for Pragma {
1220    type Error = PragmaError;
1221
1222    fn try_from((_file_id, value): (FileId, SyntaxNode)) -> Result<Self, Self::Error> {
1223        let body = value
1224            .children()
1225            .find(|node| node.kind() == PP_PRAGMA_BODY)
1226            .ok_or(Self::Error::MissingBody)?;
1227
1228        let raw = body.text().to_string();
1229
1230        // Collect non-trivial tokens
1231        let tokens: Vec<_> = body
1232            .children_with_tokens()
1233            .filter_map(NodeOrToken::into_token)
1234            .filter(|token| !token.kind().is_whitespace())
1235            .collect();
1236
1237        if let Some(first_token) = tokens.first() {
1238            if first_token.kind() == IDENT_KW {
1239                let name = SmolStr::from(Unescaped::new(first_token.text()));
1240                match name.as_str() {
1241                    "STDGL" => {
1242                        return Ok(Self {
1243                            value: ParsedPragma::StdGl(body),
1244                            raw,
1245                        });
1246                    }
1247                    "optimize" => {
1248                        return if let Some(value) = Self::parse_function_pragma(&tokens) {
1249                            Ok(Self {
1250                                value: ParsedPragma::Optimize(value),
1251                                raw,
1252                            })
1253                        } else {
1254                            Err(Self::Error::IncorrectSyntax { name })
1255                        };
1256                    }
1257                    "debug" => {
1258                        return if let Some(value) = Self::parse_function_pragma(&tokens) {
1259                            Ok(Self {
1260                                value: ParsedPragma::Debug(value),
1261                                raw,
1262                            })
1263                        } else {
1264                            Err(Self::Error::IncorrectSyntax { name })
1265                        };
1266                    }
1267                    _ => {}
1268                }
1269            }
1270        }
1271
1272        Ok(Self {
1273            value: ParsedPragma::Unknown(body),
1274            raw,
1275        })
1276    }
1277}