vhdl_parser/syntax/
component_declaration.rs1use super::common::error_on_end_identifier_mismatch;
8use super::common::ParseResult;
9use super::interface_declaration::{parse_generic_interface_list, parse_port_interface_list};
10use super::tokens::{Kind::*, TokenStream};
11use crate::ast::{ComponentDeclaration, InterfaceDeclaration};
12use crate::data::{push_some, Diagnostic, DiagnosticHandler};
13
14pub fn parse_optional_generic_list(
15 stream: &mut TokenStream,
16 diagnostics: &mut dyn DiagnosticHandler,
17) -> ParseResult<Option<Vec<InterfaceDeclaration>>> {
18 let mut list = None;
19 loop {
20 let token = stream.peek_expect()?;
21 match token.kind {
22 Generic => {
23 stream.move_after(&token);
24 let new_list = parse_generic_interface_list(stream, diagnostics)?;
25 stream.expect_kind(SemiColon)?;
26 if list.is_some() {
27 diagnostics.push(Diagnostic::error(token, "Duplicate generic clause"));
28 } else {
29 list = Some(new_list);
30 }
31 }
32 _ => break,
33 }
34 }
35
36 Ok(list)
37}
38
39pub fn parse_optional_port_list(
40 stream: &mut TokenStream,
41 diagnostics: &mut dyn DiagnosticHandler,
42) -> ParseResult<Option<Vec<InterfaceDeclaration>>> {
43 let mut list = None;
44 loop {
45 let token = stream.peek_expect()?;
46 match token.kind {
47 Port => {
48 stream.move_after(&token);
49 let new_list = parse_port_interface_list(stream, diagnostics)?;
50 stream.expect_kind(SemiColon)?;
51 if list.is_some() {
52 diagnostics.push(Diagnostic::error(token, "Duplicate port clause"));
53 } else {
54 list = Some(new_list);
55 }
56 }
57 Generic => {
58 stream.move_after(&token);
59 parse_generic_interface_list(stream, diagnostics)?;
60 stream.expect_kind(SemiColon)?;
61 diagnostics.push(Diagnostic::error(
62 token,
63 "Generic clause must come before port clause",
64 ));
65 }
66 _ => break,
67 }
68 }
69
70 Ok(list)
71}
72
73pub fn parse_component_declaration(
74 stream: &mut TokenStream,
75 diagnostics: &mut dyn DiagnosticHandler,
76) -> ParseResult<ComponentDeclaration> {
77 stream.expect_kind(Component)?;
78 let ident = stream.expect_ident()?;
79 stream.pop_if_kind(Is)?;
80
81 let generic_list = parse_optional_generic_list(stream, diagnostics)?;
82 let port_list = parse_optional_port_list(stream, diagnostics)?;
83 stream.expect_kind(End)?;
84 stream.expect_kind(Component)?;
85 if let Some(token) = stream.pop_if_kind(Identifier)? {
86 push_some(
87 diagnostics,
88 error_on_end_identifier_mismatch(&ident, &Some(token.expect_ident()?)),
89 );
90 }
91 stream.expect_kind(SemiColon)?;
92
93 Ok(ComponentDeclaration {
94 ident,
95 generic_list: generic_list.unwrap_or_default(),
96 port_list: port_list.unwrap_or_default(),
97 })
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 use crate::ast::Ident;
105 use crate::syntax::test::Code;
106
107 fn to_component(
108 ident: Ident,
109 generic_list: Vec<InterfaceDeclaration>,
110 port_list: Vec<InterfaceDeclaration>,
111 ) -> ComponentDeclaration {
112 ComponentDeclaration {
113 ident,
114 generic_list,
115 port_list,
116 }
117 }
118
119 #[test]
120 fn test_component() {
121 let code = Code::new(
122 "\
123component foo
124end component;
125",
126 );
127 let component = code.with_stream_no_diagnostics(parse_component_declaration);
128 assert_eq!(
129 component,
130 to_component(code.s1("foo").ident(), vec![], vec![])
131 );
132
133 let code = Code::new(
134 "\
135component foo is
136end component;
137",
138 );
139 let component = code.with_stream_no_diagnostics(parse_component_declaration);
140 assert_eq!(
141 component,
142 to_component(code.s1("foo").ident(), vec![], vec![])
143 );
144
145 let code = Code::new(
146 "\
147component foo is
148end component foo;
149",
150 );
151 let component = code.with_stream_no_diagnostics(parse_component_declaration);
152 assert_eq!(
153 component,
154 to_component(code.s1("foo").ident(), vec![], vec![])
155 );
156 }
157
158 #[test]
159 fn test_component_with_generic() {
160 let code = Code::new(
161 "\
162component foo is
163 generic (
164 foo : natural
165 );
166end component;
167",
168 );
169 let component = code.with_stream_no_diagnostics(parse_component_declaration);
170 assert_eq!(
171 component,
172 to_component(
173 code.s1("foo").ident(),
174 vec![code.s1("foo : natural").generic()],
175 vec![]
176 )
177 );
178 }
179
180 #[test]
181 fn test_component_with_port() {
182 let code = Code::new(
183 "\
184component foo is
185 port (
186 foo : natural
187 );
188end component;
189",
190 );
191 let component = code.with_stream_no_diagnostics(parse_component_declaration);
192 assert_eq!(
193 component,
194 to_component(
195 code.s1("foo").ident(),
196 vec![],
197 vec![code.s1("foo : natural").port()]
198 )
199 );
200 }
201
202 #[test]
203 fn error_on_duplicate_generic_clause() {
204 let code = Code::new(
205 "\
206generic (
207 foo : natural
208);
209generic (
210 bar : natural
211);
212end
213",
214 );
215
216 let (result, diagnostics) =
217 code.with_partial_stream_diagnostics(parse_optional_generic_list);
218 assert_eq!(
219 diagnostics,
220 vec![Diagnostic::error(
221 &code.s("generic", 2).pos(),
222 "Duplicate generic clause"
223 )]
224 );
225 assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").generic()])),);
226 }
227
228 #[test]
229 fn error_on_duplicate_port_clause() {
230 let code = Code::new(
231 "\
232port (
233 foo : natural
234);
235port (
236 bar : natural
237);
238end
239",
240 );
241 let (result, diagnostics) = code.with_partial_stream_diagnostics(parse_optional_port_list);
242 assert_eq!(
243 diagnostics,
244 vec![Diagnostic::error(
245 code.s("port", 2),
246 "Duplicate port clause"
247 )]
248 );
249 assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").port()])),);
250 }
251
252 #[test]
253 fn error_generic_after_port_clause() {
254 let code = Code::new(
255 "\
256port (
257 foo : natural
258);
259generic (
260 bar : natural
261);
262end
263",
264 );
265 let (result, diagnostics) = code.with_partial_stream_diagnostics(parse_optional_port_list);
266 assert_eq!(
267 diagnostics,
268 vec![Diagnostic::error(
269 code.s1("generic"),
270 "Generic clause must come before port clause"
271 )]
272 );
273 assert_eq!(result, Ok(Some(vec![code.s1("foo : natural").port()])),);
274 }
275}