floem_css_parser/
analyzer.rs1use crate::lexer::Token;
2
3pub struct SyntaxError<'a> {
4 pub line: usize,
5 pub column: usize,
6 pub error: &'static str,
7 pub value: &'a str,
8}
9
10impl std::fmt::Display for SyntaxError<'_> {
11 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12 write!(
13 f,
14 "error: {}\n {}:{} | {}",
15 self.error, self.line, self.column, self.value
16 )
17 }
18}
19
20fn find_line_with_value<'a>(lines: &[&'a str], line: usize, m: &str) -> (usize, &'a str) {
21 let line = lines[line - 1];
22 let col = line
23 .find(m)
24 .unwrap_or_else(|| panic!("Analyzer error: cant find column for {m}"));
25 (col, line)
26}
27
28#[must_use]
30pub fn analyze_tokens<'a>(tokens: &[Token<'a>], input: &'a str) -> Vec<SyntaxError<'a>> {
31 let mut errors: Vec<SyntaxError> = Vec::with_capacity(tokens.len());
32 let mut tokens = tokens.iter().peekable();
33 let lines = input.lines().collect::<Vec<_>>();
34 loop {
35 let Some(token) = tokens.next() else {
36 break;
37 };
38
39 match token {
40 Token::Selector { line, value } => {
41 let line = *line;
42 let (column, value) = find_line_with_value(&lines, line, value);
43 if let Some(peek) = tokens.peek() {
44 if !matches!(peek, Token::BlockOpen { .. } | Token::Selector { .. }) {
45 let error = SyntaxError {
46 line,
47 column,
48 error: "Expecting { after selector",
49 value,
50 };
51 errors.push(error);
52 }
53 } else {
54 let error = SyntaxError {
55 line,
56 column,
57 error: "Unexpected EOF",
58 value,
59 };
60 errors.push(error);
61 }
62 }
63 Token::Property { line, value } => {
64 if let Some(peek) = tokens.peek() {
65 if !matches!(peek, Token::Colon { .. }) {
66 let line = *line;
67 let (column, value) = find_line_with_value(&lines, line, value);
68 let error = SyntaxError {
69 line,
70 column,
71 error: "Unexpected token after property",
72 value,
73 };
74 errors.push(error);
75 }
76 } else {
77 todo!()
78 }
79 }
80 Token::Value { value, line } => {
81 if let Some(peek) = tokens.peek() {
82 if !matches!(peek, Token::Semicolon { .. }) {
83 let line = *line;
84 let (column, value) = find_line_with_value(&lines, line, value);
85 let error = SyntaxError {
86 line,
87 column,
88 error: "Expecting ; after value",
89 value,
90 };
91 errors.push(error);
92 }
93 } else {
94 todo!()
95 }
96 }
97 Token::Semicolon { line } => {
98 if let Some(peek) = tokens.peek() {
99 if !matches!(peek, Token::BlockClose { .. } | Token::Property { .. }) {
100 let line = *line;
101 let (column, value) = find_line_with_value(&lines, line, ";");
102 let error = SyntaxError {
103 line,
104 column,
105 error: "Expecting property or } after ;",
106 value,
107 };
108 errors.push(error);
109 }
110 } else {
111 todo!()
112 }
113 }
114 Token::Colon { line } => {
115 if let Some(peek) = tokens.peek() {
116 if !matches!(peek, Token::Value { .. }) {
117 let line = *line;
118 let (column, value) = find_line_with_value(&lines, line, ":");
119 let error = SyntaxError {
120 line,
121 column,
122 error: "Expecting value after :",
123 value,
124 };
125 errors.push(error);
126 }
127 } else {
128 todo!()
129 }
130 }
131 _ => (),
132 }
133 }
134
135 errors
136}