Skip to main content

vhdl_parser/syntax/
component_declaration.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this file,
3// You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com
6
7use 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}