1mod error;
22pub mod lexer;
23pub mod parser;
24pub mod preprocessor;
25use ariadne::ReportBuilder;
26use ariadne::{Color, Label, ReportKind};
27pub use ariadne::{Report, Source, sources};
28pub use error::*;
29use lexer::*;
30pub use lexer::{LexedSource, Token, lex};
31pub use parser::parse;
32use parser::*;
33pub use preprocessor::*;
34use winnow::Parser;
35use winnow::stream::TokenSlice;
36#[cfg(test)]
37pub mod test;
38pub use scarf_syntax::Span;
39#[cfg(test)]
40pub use test::*;
41
42#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct SpannedString<'a>(pub &'a str, pub Span<'a>);
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct SpannedToken<'s>(pub Token<'s>, pub Span<'s>);
49impl<'s> PartialEq<Token<'s>> for SpannedToken<'s> {
50 fn eq(&self, other: &Token) -> bool {
51 self.0 == *other
52 }
53}
54impl<'s> From<(Token<'s>, Span<'s>)> for SpannedToken<'s> {
55 fn from(item: (Token<'s>, Span<'s>)) -> Self {
56 (item.0, item.1).into()
57 }
58}
59
60fn get_expansion_string(expansion_depth: usize, is_first: bool) -> String {
61 if expansion_depth == 0 {
62 "Original token".to_string()
63 } else if (expansion_depth == 1) && is_first {
64 "Expanded here".to_string()
65 } else {
66 let suffix = match expansion_depth % 10 {
67 1 => "st",
68 2 => "nd",
69 3 => "rd",
70 _ => "th",
71 };
72 format!("Expanded here {}{}", expansion_depth, suffix)
73 }
74}
75
76pub(crate) fn kind_color<'s>(kind: &ariadne::ReportKind<'s>) -> Color {
77 match kind {
78 ReportKind::Error => Color::Red,
79 ReportKind::Warning => Color::Yellow,
80 ReportKind::Advice => Color::Fixed(147),
81 ReportKind::Custom(_, color) => color.clone(),
82 }
83}
84
85pub(crate) fn attach_span_label<'s, M>(
86 span: &Span<'s>,
87 color: ariadne::Color,
88 msg: M,
89 mut report: ReportBuilder<'s, (String, std::ops::Range<usize>)>,
90) -> ReportBuilder<'s, (String, std::ops::Range<usize>)>
91where
92 M: ToString,
93{
94 let mut curr_span: &Span<'s> = span;
95 let mut expansion_depth: usize = curr_span.expansion_depth();
96 let mut expanded = false;
97 loop {
98 if let Some(expanded_span) = curr_span.expanded_from {
99 report = report.with_label(
100 Label::new((
101 curr_span.file.to_string(),
102 curr_span.bytes.clone(),
103 ))
104 .with_message(get_expansion_string(
105 expansion_depth,
106 expanded == false,
107 ))
108 .with_color(Color::BrightGreen),
109 );
110 curr_span = expanded_span;
111 expansion_depth -= 1;
112 expanded = true;
113 } else {
114 break;
115 }
116 }
117 if expanded {
118 report = report.with_label(
120 Label::new((curr_span.file.to_string(), curr_span.bytes.clone()))
121 .with_message(get_expansion_string(expansion_depth, false))
122 .with_color(Color::BrightGreen),
123 );
124 }
125 curr_span = &span;
126 report = report.with_label(
127 Label::new((curr_span.file.to_string(), curr_span.bytes.clone()))
128 .with_message(msg)
129 .with_color(color)
130 .with_priority(1),
131 );
132 let mut note = "".to_string();
133 let mut note_pad = "".to_string();
134 let total_inclusion_depth = curr_span.inclusion_depth();
135 let mut curr_inclusion_depth = 0;
136 loop {
138 if (curr_inclusion_depth == 3) & (total_inclusion_depth > 7) {
139 for _ in 7..=total_inclusion_depth {
140 curr_span = curr_span.included_from.unwrap();
141 }
142 note = format!("{}\n{}╰- ...", note, note_pad);
143 note_pad += " ";
144 }
145 if let Some(included_span) = curr_span.included_from {
146 curr_inclusion_depth += 1;
147 curr_span = included_span;
148 if note.is_empty() {
149 note = format!("Included from {}", curr_span.file);
150 } else {
151 note = format!(
152 "{}\n{}╰-Included from {}",
153 note, note_pad, curr_span.file
154 );
155 note_pad += " ";
156 }
157 } else {
158 break;
159 }
160 }
161 debug_assert!(curr_span.included_from.is_none());
162 if !note.is_empty() {
163 report = report.with_note(note);
164 }
165 report
166}