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 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 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 VALID_VERSION_NUMBERS
189 .binary_search(&version_number)
190 .map_err(|_| Self::Error::UnsupportedVersionNumber)?;
191
192 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 if version_number < 150 && profile != VersionProfile::None {
210 return Err(Self::Error::ProfileUnsupported { version_number });
211 }
212
213 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 Ok(Self {
227 number: version_number,
228 profile: VersionProfile::Es,
229 parsed_profile: Some(profile),
230 })
231 } else if version_number >= 150 {
232 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 fn as_feature_set_size_index(&self) -> usize {
282 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#[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#[derive(Debug, Clone, PartialEq, Eq)]
342pub enum ExtensionName {
343 All,
345 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 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#[derive(Debug, Clone, PartialEq, Eq)]
546pub struct Define {
547 name: SmolStr,
549 kind: DefineKind,
551 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 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 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 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 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 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 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 let subs_tokens = trim_ws(&subs_tokens);
777
778 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 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 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 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 let eval_results: Vec<_> = ExprEvaluator::new(
893 subs_events.iter().filter_map(Event::as_token),
894 current_state,
895 )
896 .collect();
897
898 if !eval_results.is_empty() && eval_results.len() <= 2 {
901 let token = &eval_results[0];
902 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 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 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 let eval_results: Vec<_> = ExprEvaluator::new(
1026 subs_events.iter().filter_map(Event::as_token),
1027 current_state,
1028 )
1029 .collect();
1030
1031 if eval_results.is_empty() {
1033 return (true, Some(IfEvalError::MissingExpr));
1034 }
1035
1036 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 let error = if !rest.is_empty() {
1045 Some(IfEvalError::ExtraTokens { tokens: rest })
1046 } else {
1047 None
1048 };
1049
1050 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 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}