vhdl_lang/syntax/
type_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::check_end_identifier_mismatch;
8use super::common::ParseResult;
9use super::declarative_part::parse_declarative_part;
10use super::names::parse_identifier_list;
11use super::range::{parse_array_index_constraint, parse_range};
12use super::subprogram::parse_subprogram_declaration;
13use super::subtype_indication::parse_subtype_indication;
14use super::tokens::{Kind::*, TokenSpan};
15use crate::ast::token_range::WithTokenSpan;
16use crate::ast::*;
17use crate::ast::{AbstractLiteral, Range};
18use crate::named_entity::Reference;
19use crate::syntax::names::parse_type_mark;
20use crate::syntax::recover::{expect_semicolon, expect_semicolon_or_last};
21use itertools::Itertools;
22use vhdl_lang::syntax::parser::ParsingContext;
23use vhdl_lang::TokenId;
24
25/// LRM 5.2.2 Enumeration types
26fn parse_enumeration_type_definition(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeDefinition> {
27    let mut enum_literals = Vec::new();
28    loop {
29        expect_token!(ctx.stream,
30            literal_token,
31            literal_token_id,
32            Identifier | Character => {
33                let enum_literal = match literal_token.kind {
34                    Identifier => literal_token.to_identifier_value(literal_token_id)?.map_into(EnumerationLiteral::Identifier),
35                    Character => literal_token.to_character_value(literal_token_id)?.map_into(EnumerationLiteral::Character),
36                    _ => unreachable!()
37                };
38                enum_literals.push(WithDecl::new(enum_literal));
39
40                expect_token!(ctx.stream, token,
41                    RightPar => { break; },
42                    Comma => {}
43                );
44            }
45        );
46    }
47
48    Ok(TypeDefinition::Enumeration(enum_literals))
49}
50
51fn parse_array_index_constraints(ctx: &mut ParsingContext<'_>) -> ParseResult<Vec<ArrayIndex>> {
52    ctx.stream.expect_kind(LeftPar)?;
53    let mut indexes = Vec::new();
54    loop {
55        indexes.push(parse_array_index_constraint(ctx)?);
56
57        expect_token!(ctx.stream, token,
58            RightPar => {
59                return Ok(indexes);
60            },
61            Comma => {}
62        )
63    }
64}
65
66/// LRM 5.3.2 Array types
67fn parse_array_type_definition(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeDefinition> {
68    let index_constraints = parse_array_index_constraints(ctx)?;
69    let of_token = ctx.stream.expect_kind(Of)?;
70    let element_subtype = parse_subtype_indication(ctx)?;
71    Ok(TypeDefinition::Array(
72        index_constraints,
73        of_token,
74        element_subtype,
75    ))
76}
77
78/// LRM 5.3.3 Record types
79fn parse_record_type_definition(
80    ctx: &mut ParsingContext<'_>,
81) -> ParseResult<(TypeDefinition, Option<Ident>)> {
82    let mut elem_decls = Vec::new();
83
84    loop {
85        if ctx.stream.skip_if_kind(End) {
86            ctx.stream.pop_if_kind(Record);
87            let end_ident = ctx.stream.pop_optional_ident();
88            return Ok((TypeDefinition::Record(elem_decls), end_ident));
89        };
90
91        let start_token = ctx.stream.get_current_token_id();
92
93        let idents = parse_identifier_list(ctx)?
94            .into_iter()
95            .map(WithDecl::new)
96            .collect_vec();
97        let colon_token = ctx.stream.expect_kind(Colon)?;
98        let subtype = parse_subtype_indication(ctx)?;
99        let end_token = expect_semicolon_or_last(ctx);
100        elem_decls.push(ElementDeclaration {
101            idents,
102            subtype: subtype.clone(),
103            colon_token,
104            span: TokenSpan::new(start_token, end_token),
105        });
106    }
107}
108
109pub fn parse_subtype_declaration(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeDeclaration> {
110    let start_token = ctx.stream.expect_kind(Subtype)?;
111    let ident = ctx.stream.expect_ident()?;
112    ctx.stream.expect_kind(Is)?;
113    let subtype_indication = parse_subtype_indication(ctx)?;
114    let end_token = expect_semicolon_or_last(ctx);
115    Ok(TypeDeclaration {
116        span: TokenSpan::new(start_token, end_token),
117        ident: ident.into(),
118        def: TypeDefinition::Subtype(subtype_indication),
119        end_ident_pos: None,
120    })
121}
122
123/// LRM 5.6.2 Protected type declarations
124pub fn parse_protected_type_declaration(
125    ctx: &mut ParsingContext<'_>,
126) -> ParseResult<(ProtectedTypeDeclaration, Option<Ident>)> {
127    let mut items = Vec::new();
128
129    loop {
130        let token = ctx.stream.peek_expect()?;
131
132        try_init_token_kind!(
133            token,
134            Impure | Function | Procedure => items.push(ProtectedTypeDeclarativeItem::Subprogram(
135                parse_subprogram_declaration(ctx)?,
136            )),
137            End => {
138                ctx.stream.skip();
139                break;
140            }
141        );
142    }
143    ctx.stream.expect_kind(Protected)?;
144    let end_ident = ctx.stream.pop_optional_ident();
145    Ok((ProtectedTypeDeclaration { items }, end_ident))
146}
147
148/// LRM 5.2.4 Physical types
149fn parse_physical_type_definition(
150    ctx: &mut ParsingContext<'_>,
151    range: Range,
152    units_token: TokenId,
153) -> ParseResult<(TypeDefinition, Option<Ident>)> {
154    let primary_unit = WithDecl::new(ctx.stream.expect_ident()?);
155    expect_semicolon(ctx);
156
157    let mut secondary_units = Vec::new();
158
159    loop {
160        peek_token!(
161            ctx.stream, token, token_id,
162            End => {
163                break;
164            },
165            Identifier => {
166                ctx.stream.skip();
167                let ident = WithDecl::new(token.to_identifier_value(token_id)?);
168                ctx.stream.expect_kind(EQ)?;
169                let literal = {
170                    expect_token!(ctx.stream,
171                        value_token,
172                        value_token_id,
173                        AbstractLiteral => {
174                            let value = value_token.to_abstract_literal(value_token_id)?.item;
175                            let unit_token = ctx.stream.get_current_token_id();
176                            let unit = ctx.stream.expect_ident()?;
177                            WithTokenSpan::new(PhysicalLiteral {value, unit: unit.into_ref()}, TokenSpan::new(value_token_id, unit_token))
178                        },
179                        Identifier => {
180                            let unit = value_token.to_identifier_value(value_token_id)?;
181                            WithTokenSpan::new(PhysicalLiteral {value: AbstractLiteral::Integer(1), unit: unit.into_ref()}, TokenSpan::new(value_token_id, value_token_id))
182                        }
183                    )
184                };
185
186                secondary_units.push((ident, literal));
187                expect_semicolon(ctx);
188            }
189        )
190    }
191
192    ctx.stream.expect_kind(End)?;
193    ctx.stream.expect_kind(Units)?;
194    let end_ident = ctx.stream.pop_optional_ident();
195
196    Ok((
197        TypeDefinition::Physical(PhysicalTypeDeclaration {
198            range,
199            units_token,
200            primary_unit,
201            secondary_units,
202        }),
203        end_ident,
204    ))
205}
206
207/// LRM 6.2
208pub fn parse_type_declaration(ctx: &mut ParsingContext<'_>) -> ParseResult<TypeDeclaration> {
209    let start_token = ctx.stream.get_current_token_id();
210    peek_token!(
211        ctx.stream, token,
212        Subtype => {
213            return parse_subtype_declaration(ctx);
214        },
215        Type => {
216            ctx.stream.skip();
217        }
218    );
219
220    let ident = WithDecl::new(ctx.stream.expect_ident()?);
221    let mut end_ident_pos = None;
222
223    expect_token!(
224        ctx.stream, token,
225        Is => {},
226        SemiColon => {
227            return Ok(TypeDeclaration {
228                span: TokenSpan::new(start_token, ctx.stream.get_last_token_id()),
229                ident,
230                def: TypeDefinition::Incomplete(Reference::undefined()),
231                end_ident_pos
232            });
233        }
234    );
235
236    let def = expect_token!(
237        ctx.stream, token,
238        // Integer
239        Range => {
240            let constraint = parse_range(ctx)?.item;
241            expect_token!(
242                ctx.stream, token, token_id,
243                SemiColon => {
244                    ctx.stream.back(); // The ';' is consumed at the end of the function
245                    TypeDefinition::Numeric(constraint)
246                },
247                Units => {
248                    let (def, end_ident) = parse_physical_type_definition(ctx, constraint, token_id)?;
249                    end_ident_pos = check_end_identifier_mismatch(ctx, &ident.tree, end_ident);
250                    def
251                }
252            )
253        },
254
255        Access => {
256            let subtype_indication = parse_subtype_indication(ctx)?;
257            TypeDefinition::Access(subtype_indication)
258        },
259
260        Protected => {
261            if ctx.stream.skip_if_kind(Body) {
262                let decl = parse_declarative_part(ctx)?;
263                ctx.stream.expect_kind(End)?;
264                ctx.stream.expect_kind(Protected)?;
265                ctx.stream.expect_kind(Body)?;
266                let end_ident = ctx.stream.pop_optional_ident();
267                end_ident_pos = check_end_identifier_mismatch(ctx, &ident.tree, end_ident);
268
269                TypeDefinition::ProtectedBody(ProtectedTypeBody {decl})
270            } else {
271                let (protected_type_decl, end_ident) = parse_protected_type_declaration(ctx)?;
272                end_ident_pos = check_end_identifier_mismatch(ctx, &ident.tree, end_ident);
273                TypeDefinition::Protected(protected_type_decl)
274            }
275        },
276        File => {
277            ctx.stream.expect_kind(Of)?;
278            let type_mark = parse_type_mark(ctx)?;
279            TypeDefinition::File(type_mark)
280        },
281        Array => parse_array_type_definition(ctx)?,
282        Record =>  {
283            let (def, end_ident) = parse_record_type_definition(ctx)?;
284            end_ident_pos = check_end_identifier_mismatch(ctx, &ident.tree, end_ident);
285            def
286        },
287        // Enumeration
288        LeftPar => parse_enumeration_type_definition(ctx)?
289    );
290
291    let end_token = expect_semicolon_or_last(ctx);
292    Ok(TypeDeclaration {
293        span: TokenSpan::new(start_token, end_token),
294        ident,
295        def,
296        end_ident_pos,
297    })
298}
299
300#[cfg(test)]
301mod tests {
302    use itertools::Itertools;
303
304    use super::*;
305
306    use crate::{HasTokenSpan, TokenId};
307
308    use crate::ast::{DiscreteRange, Ident};
309    use crate::syntax::test::{token_to_string, Code};
310
311    #[test]
312    fn parse_integer_scalar_type_definition() {
313        let code = Code::new("type foo is range 0 to 1;");
314
315        let type_decl = TypeDeclaration {
316            span: code.token_span(),
317            ident: code.s1("foo").decl_ident(),
318            def: TypeDefinition::Numeric(code.s1("0 to 1").range()),
319            end_ident_pos: None,
320        };
321        assert_eq!(
322            code.with_stream_no_diagnostics(parse_type_declaration),
323            type_decl
324        );
325    }
326
327    #[test]
328    fn parse_enumeration_scalar_type_definition() {
329        let code = Code::new("type foo is (alpha, beta);");
330
331        let type_decl = TypeDeclaration {
332            span: code.token_span(),
333            ident: code.s1("foo").decl_ident(),
334            def: TypeDefinition::Enumeration(vec![
335                code.s1("alpha")
336                    .ident()
337                    .map_into(EnumerationLiteral::Identifier)
338                    .into(),
339                code.s1("beta")
340                    .ident()
341                    .map_into(EnumerationLiteral::Identifier)
342                    .into(),
343            ]),
344            end_ident_pos: None,
345        };
346        assert_eq!(
347            code.with_stream_no_diagnostics(parse_type_declaration),
348            type_decl
349        );
350    }
351
352    #[test]
353    fn parse_enumeration_scalar_type_definition_character() {
354        let code = Code::new("type foo is ('a', 'b');");
355
356        let type_decl = TypeDeclaration {
357            span: code.token_span(),
358            ident: code.s1("foo").decl_ident(),
359            def: TypeDefinition::Enumeration(vec![
360                code.s1("'a'")
361                    .character()
362                    .map_into(EnumerationLiteral::Character)
363                    .into(),
364                code.s1("'b'")
365                    .character()
366                    .map_into(EnumerationLiteral::Character)
367                    .into(),
368            ]),
369            end_ident_pos: None,
370        };
371        assert_eq!(
372            code.with_stream_no_diagnostics(parse_type_declaration),
373            type_decl
374        );
375    }
376
377    #[test]
378    fn mixing_identifier_and_scalar_in_enumerations() {
379        let code = Code::new("type foo is (ident, 'b');");
380
381        let type_decl = TypeDeclaration {
382            span: code.token_span(),
383            ident: code.s1("foo").decl_ident(),
384            def: TypeDefinition::Enumeration(vec![
385                code.s1("ident")
386                    .ident()
387                    .map_into(EnumerationLiteral::Identifier)
388                    .into(),
389                code.s1("'b'")
390                    .character()
391                    .map_into(EnumerationLiteral::Character)
392                    .into(),
393            ]),
394            end_ident_pos: None,
395        };
396        assert_eq!(
397            code.with_stream_no_diagnostics(parse_type_declaration),
398            type_decl
399        );
400    }
401
402    #[test]
403    fn parse_array_type_definition_with_index_subtype_definition() {
404        let code = Code::new("type foo is array (natural range <>) of boolean;");
405
406        let type_decl = TypeDeclaration {
407            span: code.token_span(),
408            ident: code.s1("foo").decl_ident(),
409            def: TypeDefinition::Array(
410                vec![ArrayIndex::IndexSubtypeDefintion(
411                    code.s1("natural").type_mark(),
412                )],
413                code.s1("of").token(),
414                code.s1("boolean").subtype_indication(),
415            ),
416            end_ident_pos: None,
417        };
418
419        assert_eq!(
420            code.with_stream_no_diagnostics(parse_type_declaration),
421            type_decl
422        );
423    }
424
425    #[test]
426    fn parse_array_type_definition_with_discrete_subtype_definition() {
427        let code = Code::new("type foo is array (natural) of boolean;");
428
429        let type_decl = TypeDeclaration {
430            span: code.token_span(),
431            ident: code.s1("foo").decl_ident(),
432            def: TypeDefinition::Array(
433                vec![ArrayIndex::Discrete(WithTokenSpan::new(
434                    DiscreteRange::Discrete(code.s1("natural").type_mark(), None),
435                    code.s1("natural").token_span(),
436                ))],
437                code.s1("of").token(),
438                code.s1("boolean").subtype_indication(),
439            ),
440            end_ident_pos: None,
441        };
442
443        assert_eq!(
444            code.with_stream_no_diagnostics(parse_type_declaration),
445            type_decl
446        );
447    }
448
449    #[test]
450    fn parse_array_type_definition_with_selected_name() {
451        let code = Code::new("type foo is array (lib.pkg.foo) of boolean;");
452
453        let type_decl = TypeDeclaration {
454            span: code.token_span(),
455            ident: code.s1("foo").decl_ident(),
456            def: TypeDefinition::Array(
457                vec![ArrayIndex::Discrete(WithTokenSpan::new(
458                    DiscreteRange::Discrete(code.s1("lib.pkg.foo").type_mark(), None),
459                    code.s1("lib.pkg.foo").token_span(),
460                ))],
461                code.s1("of").token(),
462                code.s1("boolean").subtype_indication(),
463            ),
464            end_ident_pos: None,
465        };
466
467        assert_eq!(
468            code.with_stream_no_diagnostics(parse_type_declaration),
469            type_decl
470        );
471    }
472
473    #[test]
474    fn parse_array_type_definition_with_range_attribute_name() {
475        let code = Code::new("type foo is array (arr_t'range) of boolean;");
476
477        let type_decl = TypeDeclaration {
478            span: code.token_span(),
479            ident: code.s1("foo").decl_ident(),
480            def: TypeDefinition::Array(
481                vec![ArrayIndex::Discrete(WithTokenSpan::new(
482                    DiscreteRange::Range(code.s1("arr_t'range").range()),
483                    code.s1("arr_t'range").token_span(),
484                ))],
485                code.s1("of").token(),
486                code.s1("boolean").subtype_indication(),
487            ),
488            end_ident_pos: None,
489        };
490
491        assert_eq!(
492            code.with_stream_no_diagnostics(parse_type_declaration),
493            type_decl
494        );
495    }
496
497    #[test]
498    fn parse_array_type_definition_with_constraint() {
499        let code = Code::new("type foo is array (2-1 downto 0) of boolean;");
500
501        let index = ArrayIndex::Discrete(WithTokenSpan::new(
502            DiscreteRange::Range(code.s1("2-1 downto 0").range()),
503            code.s1("2-1 downto 0").token_span(),
504        ));
505
506        let type_decl = TypeDeclaration {
507            span: code.token_span(),
508            ident: code.s1("foo").decl_ident(),
509            def: TypeDefinition::Array(
510                vec![index],
511                code.s1("of").token(),
512                code.s1("boolean").subtype_indication(),
513            ),
514            end_ident_pos: None,
515        };
516
517        assert_eq!(
518            code.with_stream_no_diagnostics(parse_type_declaration),
519            type_decl
520        );
521    }
522
523    #[test]
524    fn parse_array_type_definition_mixed() {
525        let code = Code::new("type foo is array (2-1 downto 0, integer range <>) of boolean;");
526
527        let index0 = ArrayIndex::Discrete(WithTokenSpan::new(
528            DiscreteRange::Range(code.s1("2-1 downto 0").range()),
529            code.s1("2-1 downto 0").token_span(),
530        ));
531
532        let index1 = ArrayIndex::IndexSubtypeDefintion(code.s1("integer").type_mark());
533
534        let type_decl = TypeDeclaration {
535            span: code.token_span(),
536            ident: code.s1("foo").decl_ident(),
537            def: TypeDefinition::Array(
538                vec![index0, index1],
539                code.s1("of").token(),
540                code.s1("boolean").subtype_indication(),
541            ),
542            end_ident_pos: None,
543        };
544
545        assert_eq!(
546            code.with_stream_no_diagnostics(parse_type_declaration),
547            type_decl
548        );
549    }
550
551    #[test]
552    fn parse_record_type_definition() {
553        let code = Code::new(
554            "\
555type foo is record
556  element : boolean;
557end record;",
558        );
559
560        let elem_decl = ElementDeclaration {
561            idents: vec![code.s1("element").decl_ident()],
562            subtype: code.s1("boolean").subtype_indication(),
563            colon_token: code.s1(":").token(),
564            span: code.s1("element : boolean;").token_span(),
565        };
566
567        let type_decl = TypeDeclaration {
568            span: code.token_span(),
569            ident: code.s1("foo").decl_ident(),
570            def: TypeDefinition::Record(vec![elem_decl]),
571            end_ident_pos: None,
572        };
573
574        assert_eq!(
575            code.with_stream_no_diagnostics(parse_type_declaration),
576            type_decl
577        );
578    }
579
580    #[test]
581    fn parse_record_type_definition_many() {
582        let code = Code::new(
583            "\
584            type foo is record
585  element, field : boolean;
586  other_element : std_logic_vector(0 to 1);
587end foo;",
588        );
589
590        let elem_decl0 = ElementDeclaration {
591            idents: vec![
592                code.s1("element").decl_ident(),
593                code.s1("field").decl_ident(),
594            ],
595            colon_token: code.s1(":").token(),
596            subtype: code.s1("boolean").subtype_indication(),
597            span: code.s1("element, field : boolean;").token_span(),
598        };
599
600        let elem_decl1 = ElementDeclaration {
601            idents: vec![code.s1("other_element").decl_ident()],
602            colon_token: code.s(":", 2).token(),
603            subtype: code.s1("std_logic_vector(0 to 1)").subtype_indication(),
604            span: code
605                .s1("other_element : std_logic_vector(0 to 1);")
606                .token_span(),
607        };
608
609        let type_decl = TypeDeclaration {
610            span: code.token_span(),
611            ident: code.s1("foo").decl_ident(),
612            def: TypeDefinition::Record(vec![elem_decl0, elem_decl1]),
613            end_ident_pos: Some(code.s("foo", 2).token()),
614        };
615
616        assert_eq!(
617            code.with_stream_no_diagnostics(parse_type_declaration),
618            type_decl
619        );
620    }
621
622    #[test]
623    fn test_parse_subtype_declaration() {
624        let code = Code::new("subtype vec_t is integer_vector(2-1 downto 0);");
625
626        assert_eq!(
627            code.with_stream_no_diagnostics(parse_type_declaration),
628            TypeDeclaration {
629                span: code.token_span(),
630                ident: code.s1("vec_t").decl_ident(),
631                def: TypeDefinition::Subtype(
632                    code.s1("integer_vector(2-1 downto 0)").subtype_indication()
633                ),
634                end_ident_pos: None,
635            }
636        );
637    }
638
639    #[test]
640    fn test_parse_access_type_declaration() {
641        let code = Code::new("type ptr_t is access integer_vector(2-1 downto 0);");
642
643        assert_eq!(
644            code.with_stream_no_diagnostics(parse_type_declaration),
645            TypeDeclaration {
646                span: code.token_span(),
647                ident: code.s1("ptr_t").decl_ident(),
648                def: TypeDefinition::Access(
649                    code.s1("integer_vector(2-1 downto 0)").subtype_indication()
650                ),
651                end_ident_pos: None,
652            }
653        );
654    }
655
656    #[test]
657    fn test_incomplete_type_declaration() {
658        let code = Code::new("type incomplete;");
659
660        assert_eq!(
661            code.with_stream_no_diagnostics(parse_type_declaration),
662            TypeDeclaration {
663                span: code.token_span(),
664                ident: code.s1("incomplete").decl_ident(),
665                def: TypeDefinition::Incomplete(Reference::undefined()),
666                end_ident_pos: None,
667            }
668        );
669    }
670
671    #[test]
672    fn test_file_type_declaration() {
673        let code = Code::new("type foo is file of character;");
674
675        assert_eq!(
676            code.with_stream_no_diagnostics(parse_type_declaration),
677            TypeDeclaration {
678                span: code.token_span(),
679                ident: code.s1("foo").decl_ident(),
680                def: TypeDefinition::File(code.s1("character").type_mark()),
681                end_ident_pos: None,
682            }
683        );
684    }
685
686    fn protected_decl(
687        ident: Ident,
688        token_span: TokenSpan,
689        items: Vec<ProtectedTypeDeclarativeItem>,
690        end_ident_pos: Option<TokenId>,
691    ) -> TypeDeclaration {
692        TypeDeclaration {
693            span: token_span,
694            ident: ident.into(),
695            def: TypeDefinition::Protected(ProtectedTypeDeclaration { items }),
696            end_ident_pos,
697        }
698    }
699
700    #[test]
701    fn test_protected_type_declaration() {
702        let code = Code::new(
703            "\
704type foo is protected
705end protected;
706",
707        );
708        assert_eq!(
709            code.with_stream_no_diagnostics(parse_type_declaration),
710            protected_decl(code.s1("foo").ident(), code.token_span(), vec![], None)
711        )
712    }
713
714    #[test]
715    fn test_protected_type_declaration_simple_name_suffix() {
716        let code = Code::new(
717            "\
718type foo is protected
719end protected foo;
720",
721        );
722        assert_eq!(
723            code.with_stream_no_diagnostics(parse_type_declaration),
724            protected_decl(
725                code.s1("foo").ident(),
726                code.token_span(),
727                vec![],
728                Some(code.s("foo", 2).token())
729            )
730        )
731    }
732
733    #[test]
734    fn test_protected_type_declaration_with_subprograms() {
735        let code = Code::new(
736            "\
737type foo is protected
738  procedure proc;
739  function fun return ret;
740end protected;
741",
742        );
743        let items = vec![
744            ProtectedTypeDeclarativeItem::Subprogram(code.s1("procedure proc;").subprogram_decl()),
745            ProtectedTypeDeclarativeItem::Subprogram(
746                code.s1("function fun return ret;").subprogram_decl(),
747            ),
748        ];
749
750        assert_eq!(
751            code.with_stream_no_diagnostics(parse_type_declaration),
752            protected_decl(code.s1("foo").ident(), code.token_span(), items, None)
753        )
754    }
755
756    #[test]
757    fn test_protected_type_body() {
758        let code = Code::new(
759            "\
760type foo is protected body
761  variable foo : natural;
762  procedure proc is
763  begin
764  end;
765end protected body;
766",
767        );
768
769        let decl = code
770            .s1("\
771  variable foo : natural;
772  procedure proc is
773  begin
774  end;")
775            .declarative_part();
776
777        let ident = code.s1("foo").decl_ident();
778        assert_eq!(
779            code.with_stream_no_diagnostics(parse_type_declaration),
780            TypeDeclaration {
781                span: code.token_span(),
782                ident,
783                def: TypeDefinition::ProtectedBody(ProtectedTypeBody { decl }),
784                end_ident_pos: None,
785            }
786        )
787    }
788
789    #[test]
790    fn test_physical_type_declaration() {
791        let code = Code::new(
792            "\
793type phys is range 0 to 15 units
794   primary_unit;
795end units phys;
796",
797        );
798
799        assert_eq!(
800            code.with_stream_no_diagnostics(parse_type_declaration),
801            TypeDeclaration {
802                span: code.token_span(),
803                ident: code.s1("phys").decl_ident(),
804                def: TypeDefinition::Physical(PhysicalTypeDeclaration {
805                    range: code.s1("0 to 15").range(),
806                    units_token: code.s1("units").token(),
807                    primary_unit: code.s1("primary_unit").decl_ident(),
808                    secondary_units: vec![]
809                }),
810                end_ident_pos: Some(code.s("phys", 2).token()),
811            }
812        )
813    }
814
815    #[test]
816    fn test_physical_type_declaration_secondary_units() {
817        let code = Code::new(
818            "\
819type phys is range 0 to 15 units
820   primary_unit;
821   secondary_unit = 5 primary_unit;
822end units;
823",
824        );
825
826        assert_eq!(
827            code.with_stream_no_diagnostics(parse_type_declaration),
828            TypeDeclaration {
829                span: code.token_span(),
830                ident: code.s1("phys").decl_ident(),
831                def: TypeDefinition::Physical(PhysicalTypeDeclaration {
832                    range: code.s1("0 to 15").range(),
833                    units_token: code.s1("units").token(),
834                    primary_unit: code.s1("primary_unit").decl_ident(),
835                    secondary_units: vec![(
836                        code.s1("secondary_unit").decl_ident(),
837                        WithTokenSpan::new(
838                            PhysicalLiteral {
839                                value: AbstractLiteral::Integer(5),
840                                unit: code.s("primary_unit", 2).ident().into_ref()
841                            },
842                            code.s1("5 primary_unit").token_span()
843                        )
844                    ),]
845                }),
846                end_ident_pos: None,
847            }
848        )
849    }
850
851    #[test]
852    fn test_physical_type_declaration_implicit_secondary_units() {
853        let code = Code::new(
854            "\
855type phys is range 0 to 15 units
856   primary_unit;
857   secondary_unit = primary_unit;
858end units;
859",
860        );
861
862        assert_eq!(
863            code.with_stream_no_diagnostics(parse_type_declaration),
864            TypeDeclaration {
865                span: code.token_span(),
866                ident: code.s1("phys").decl_ident(),
867                def: TypeDefinition::Physical(PhysicalTypeDeclaration {
868                    range: code.s1("0 to 15").range(),
869                    units_token: code.s1("units").token(),
870                    primary_unit: code.s1("primary_unit").decl_ident(),
871                    secondary_units: vec![(
872                        code.s1("secondary_unit").decl_ident(),
873                        WithTokenSpan::new(
874                            PhysicalLiteral {
875                                value: AbstractLiteral::Integer(1),
876                                unit: code.s("primary_unit", 2).ident().into_ref()
877                            },
878                            code.s("primary_unit", 2).token_span()
879                        )
880                    ),]
881                }),
882                end_ident_pos: None,
883            }
884        )
885    }
886
887    #[test]
888    pub fn test_token_span() {
889        let code = Code::new(
890            "\
891package pkg is
892    subtype negative is integer range -2**31 to -1;
893    type incomplete;
894    type new_integer is range 100 downto -100;
895    type line is access string;
896    type prot is
897        protected
898            procedure proc;
899        end protected;
900    type IntegerFile is file of INTEGER;
901    type arr is array (natural range <>) of positive;
902    type dummy_rec is
903        record
904            dummy: bit;
905        end record;
906    type enum is (V1, V2, V3);
907end package;
908",
909        );
910        let ctx = code.tokenize();
911        let pkg = code.package_declaration();
912        let type_decls = pkg
913            .decl
914            .iter()
915            .map(|d| {
916                if let Declaration::Type(obj) = &d.item {
917                    obj
918                } else {
919                    panic!("Only object declarations are expected!")
920                }
921            })
922            .collect_vec();
923
924        let type_decl_strings: Vec<Vec<String>> = type_decls
925            .iter()
926            .map(|decl| {
927                decl.get_token_slice(&ctx)
928                    .iter()
929                    .map(token_to_string)
930                    .collect()
931            })
932            .collect_vec();
933
934        assert_eq!(
935            type_decl_strings[0],
936            vec![
937                "subtype", "negative", "is", "integer", "range", "-", "2", "**", "31", "to", "-",
938                "1", ";"
939            ],
940        );
941        assert_eq!(type_decl_strings[1], vec!["type", "incomplete", ";"],);
942        assert_eq!(
943            type_decl_strings[2],
944            vec![
945                "type",
946                "new_integer",
947                "is",
948                "range",
949                "100",
950                "downto",
951                "-",
952                "100",
953                ";"
954            ],
955        );
956        assert_eq!(
957            type_decl_strings[3],
958            vec!["type", "line", "is", "access", "string", ";"],
959        );
960        assert_eq!(
961            type_decl_strings[4],
962            vec![
963                "type",
964                "prot",
965                "is",
966                "protected",
967                "procedure",
968                "proc",
969                ";",
970                "end",
971                "protected",
972                ";"
973            ],
974        );
975        assert_eq!(
976            type_decl_strings[5],
977            vec!["type", "IntegerFile", "is", "file", "of", "INTEGER", ";"],
978        );
979        assert_eq!(
980            type_decl_strings[6],
981            vec![
982                "type", "arr", "is", "array", "(", "natural", "range", "<>", ")", "of", "positive",
983                ";",
984            ],
985        );
986        assert_eq!(
987            type_decl_strings[7],
988            vec![
989                "type",
990                "dummy_rec",
991                "is",
992                "record",
993                "dummy",
994                ":",
995                "bit",
996                ";",
997                "end",
998                "record",
999                ";",
1000            ],
1001        );
1002    }
1003}