// Copyright (c) 2016-2021 Fabian Schuiki use crate::ast; use crate::lexer::token; use crate::lexer::Lexer; use crate::parser::basic::BasicParser; use crate::parser::core::*; use crate::parser::rules::*; use moore_common::errors::*; use moore_common::grind::{self, Grinder}; use moore_common::source::*; use std::fmt::Debug; macro_rules! parse { ($content:expr, $parse_fn:expr) => {{ // Create an anonymous source file with the given content. let src = get_source_manager().add_anonymous($content); // Assemble a parser for the source. let content = src.get_content(); let bytes = grind::from_iter(content.bytes().iter().map(|x| *x)) .vent(|err: DiagBuilder2| eprintln!("{}", err)); let tokens = Lexer::new(bytes, src); let mut parser = BasicParser::new(tokens); // Check the result. parse_impl(&mut parser, $parse_fn) }}; } fn parse_impl(p: &mut P, mut parse_fn: F) -> R where P: Parser, F: FnMut(&mut P) -> Result, E: Debug, { // Apply the parser. let result = parse_fn(p).expect("parser failed"); // Check whether the entire input has been consumed. match p.peek(0) { Spanned { value: token::Eof, .. } => (), Spanned { value, span } => { panic!( "{}", DiagBuilder2::error("Not entire input consumed").span(span.begin()) ); } } result } #[test] fn name() { parse!("simple", parse_name); parse!("'x'", parse_name); parse!("\"add\"", parse_name); parse!("simple.simple", parse_name); parse!("simple.'x'", parse_name); parse!("simple.\"add\"", parse_name); parse!("simple.all", parse_name); parse!("simple'attr", parse_name); // parse!("simple[ signature goes here ]'attr", parse_name); parse!("simple(1)", parse_name); parse!("simple(1,2)", parse_name); parse!("simple(1 to 2)", parse_name); parse!("simple(2 downto 1)", parse_name); parse!("simple range 2 downto 1", parse_name); parse!("simple range 1 to 42", parse_name); } #[test] fn library_clause() { parse!("library ieee;", try_context_item); } #[test] fn use_clause() { parse!("use ieee;", try_context_item); parse!("use ieee, ieee.std_logic_1164.all;", try_context_item); parse!("use work.'X', work.\"+\";", try_context_item); } #[test] fn context_ref() { parse!("context ctx;", try_context_item); parse!("context ctx, work, stuff;", try_context_item); parse!("context work.'X', work'blah.text;", try_context_item); } #[test] fn design_unit() { parse!("entity foo is end;", parse_design_unit); // parse!("configuration foo is begin end;", parse_design_unit); parse!("package foo is end;", parse_design_unit); parse!("context foo is end;", parse_design_unit); } #[test] fn context_decl() { parse!("context foo is end;", parse_context_decl); parse!("context foo is end context;", parse_context_decl); parse!("context foo is end context foo;", parse_context_decl); // parse!("context foo is end context bar;", parse_context_decl); // check if this emits a warning parse!( " context project_context is library project_lib; use project_lib.project_defs.all; library IP_lib; context IP_lib.IP_context; end context project_context; ", parse_context_decl ); } #[test] fn entity_decl() { parse!("entity foo is end;", parse_entity_decl); parse!("entity foo is end entity;", parse_entity_decl); parse!("entity foo is end entity foo;", parse_entity_decl); // parse!("entity foo is end entity bar;", parse_entity_decl); // check if this emits a warning parse!("entity foo is begin end;", parse_entity_decl); } #[test] fn entity_header() { parse!( " entity Full_Adder is port (X, Y, Cin: in Bit; Cout, Sum: out Bit); end Full_Adder; ", parse_entity_decl ); parse!( " entity AndGate is generic (N: Natural := 2); port (Inputs: in Bit_Vector (1 to N); Result: out Bit); end entity AndGate; ", parse_entity_decl ); parse!( " entity TestBench is end TestBench; ", parse_entity_decl ); } #[test] fn entity_decl_part() { parse!( " entity ROM is port ( Addr: in Word; Data: out Word; Sel: in Bit); type Instruction is array (1 to 5) of Natural; type Program is array (Natural range <>) of Instruction; use Work.OpCodes.all, Work.RegisterNames.all; constant ROM_Code: Program := ( (STM, R14, R12, 12, R13), (LD, R7, 32, 0, R1 ), (BAL, R14, 0, 0, R7 ) ); end ROM; ", parse_entity_decl ); } #[test] fn intf_decl() { parse!( " constant a : std_logic; constant a, b, c : in std_logic; constant a, b, c : in std_logic := '0'; signal a : std_logic; signal a, b, c : in std_logic; signal a, b, c : out std_logic; signal a, b, c : inout std_logic; signal a, b, c : buffer std_logic; signal a, b, c : linkage std_logic; signal a, b, c : in std_logic bus; signal a, b, c : in std_logic bus := '0'; variable a : std_logic; variable a, b, c : in std_logic; variable a, b, c : out std_logic; variable a, b, c : inout std_logic; variable a, b, c : buffer std_logic; variable a, b, c : linkage std_logic; variable a, b, c : in std_logic := '0'; file i : integer; type foo; procedure foo; procedure \"+\"; procedure foo is bar; procedure foo is <>; procedure foo (hello : INTEGER); procedure foo parameter (hello: INTEGER); function foo return integer; function \"+\" return integer; function foo return integer is bar; function foo return integer is <>; function foo (hello : INTEGER) return integer; function foo parameter (hello : INTEGER) return integer; pure function foo return integer; impure function foo return integer; package foo is new bar generic map (a => b, c => d); package foo is new bar generic map (<>); package foo is new bar generic map (default); ", |p| separated_nonempty( p, token::Semicolon, token::Period, "interface declaration", |p| parse_intf_decl(p, None) ) ); // Default objects. parse!("a, b, c : in integer", |p| parse_intf_decl( p, Some(ast::IntfObjKind::Const) )); parse!("a, b, c : inout integer bus", |p| parse_intf_decl( p, Some(ast::IntfObjKind::Signal) )); parse!("a, b, c : inout integer", |p| parse_intf_decl( p, Some(ast::IntfObjKind::Var) )); } #[test] fn subtype_ind() { parse!("integer", parse_subtype_ind); parse!("resfunc integer", parse_subtype_ind); parse!("(elemresfunc) integer", parse_subtype_ind); parse!( "(a resfunc, b resfunc, c (elemresfunc)) integer", parse_subtype_ind ); parse!("integer range foo", parse_subtype_ind); parse!("integer range 1 to 8", parse_subtype_ind); } #[test] fn elem_resolution() { parse!("(func)", parse_paren_expr); parse!("((elemfunc))", parse_paren_expr); parse!("((elemfunc) stuff (1 to 4))", parse_paren_expr); parse!( "(a func, b func, c (elemfunc), d (x elemfunc, y elemenfunc))", parse_paren_expr ); } #[test] fn package_inst() { parse!("package foo is new bar;", |p| parse_package_inst(p, true)); parse!("package foo is new bar generic map (STUFF => 8);", |p| { parse_package_inst(p, true) }); } #[test] fn decl_items() { parse!( " package foo is -- package_{decl,body,inst} package bar is end; package body bar is end; package baz is new foo; package baz is new foo generic map (STUFF => 8); end; ", parse_package_decl ); } #[test] fn protected_type_decl() { parse!( " type SharedCounter is protected procedure increment (N: Integer := 1); procedure decrement (N: Integer := 1); impure function value return Integer; end protected SharedCounter; ", |p| parse_type_decl(p, true) ); parse!( " type ComplexNumber is protected procedure extract (variable r, i: out Real); procedure add (variable a, b: inout ComplexNumber); end protected ComplexNumber; ", |p| parse_type_decl(p, true) ); parse!( " type VariableSizeBitArray is protected procedure add_bit (index: Positive; value: Bit); impure function size return Natural; end protected VariableSizeBitArray; ", |p| parse_type_decl(p, true) ); } #[test] fn protected_type_body() { parse!( " type SharedCounter is protected body variable counter: Integer := 0; procedure increment (N: Integer := 1) is begin counter := counter + N; end procedure increment; procedure decrement (N: Integer := 1) is begin counter := counter - N; end procedure decrement; impure function value return Integer is begin return counter; end function value; end protected body SharedCounter; ", |p| parse_type_decl(p, true) ); parse!( " type ComplexNumber is protected body variable re, im: Real; procedure extract (r, i: out Real) is begin r := re; i := im; end procedure extract; procedure add (variable a, b: inout ComplexNumber) is variable a_real, b_real: Real; variable a_imag, b_imag: Real; begin a.extract (a_real, a_imag); b.extract (b_real, b_imag); re := a_real + b_real; im := a_imag + b_imag; end procedure add; end protected body ComplexNumber; ", |p| parse_type_decl(p, true) ); parse!( " type VariableSizeBitArray is protected body type bit_vector_access is access Bit_Vector; variable bit_array: bit_vector_access := null; variable bit_array_length: Natural := 0; procedure add_bit (index: Positive; value: Bit) is variable tmp: bit_vector_access; begin if index > bit_array_length then tmp := bit_array; bit_array := new bit_vector (1 to index); if tmp /= null then bit_array (1 to bit_array_length) := tmp.all; deallocate (tmp); end if; bit_array_length := index; end if; bit_array (index) := value; end procedure add_bit; impure function size return Natural is begin return bit_array_length; end function size; end protected body VariableSizeBitArray; ", |p| parse_type_decl(p, true) ); } #[test] fn alias_decl() { parse!("alias SIGN: BIT is REAL_NUMBER (0);", parse_alias_decl); parse!( "alias MANTISSA: BIT_VECTOR (23 downto 0) is REAL_NUMBER (8 to 31);", parse_alias_decl ); parse!( "alias EXPONENT: BIT_VECTOR (1 to 7) is REAL_NUMBER (1 to 7);", parse_alias_decl ); parse!("alias STD_BIT is STD.STANDARD.BIT;", parse_alias_decl); // parse!("alias '0' is STD.STANDARD.'0' [return STD.STANDARD.BIT];", parse_alias_decl); // parse!("alias '1' is STD.STANDARD.'1' [return STD.STANDARD.BIT];", parse_alias_decl); } #[test] fn object_decl() { parse!("constant TOLER: DISTANCE := 1.5 nm;", parse_object_decl); parse!("constant PI: REAL := 3.141592;", parse_object_decl); parse!("constant CYCLE_TIME: TIME := 100 ns;", parse_object_decl); parse!( "constant Propagation_Delay: DELAY_LENGTH;", parse_object_decl ); parse!( "signal S: STANDARD.BIT_VECTOR (1 to 10);", parse_object_decl ); parse!("signal CLK1, CLK2: TIME;", parse_object_decl); parse!( "signal OUTPUT: WIRED_OR MULTI_VALUED_LOGIC;", parse_object_decl ); parse!("signal CLK1, CLK2: TIME register;", parse_object_decl); parse!("signal CLK1, CLK2: TIME bus;", parse_object_decl); parse!("signal CLK1, CLK2: TIME := 5 ps;", parse_object_decl); parse!( "variable INDEX: INTEGER range 0 to 99 := 0;", parse_object_decl ); parse!("variable COUNT: POSITIVE;", parse_object_decl); parse!( "variable MEMORY: BIT_MATRIX (0 to 7, 0 to 1023);", parse_object_decl ); parse!("shared variable Counter: SharedCounter;", parse_object_decl); parse!( "shared variable addend, augend, result: ComplexNumber;", parse_object_decl ); parse!( "variable bit_stack: VariableSizeBitArray;", parse_object_decl ); parse!("file F1: IntegerFile;", parse_object_decl); parse!("file F2: IntegerFile is \"test.dat\";", parse_object_decl); parse!( "file F3: IntegerFile open WRITE_MODE is \"test.dat\";", parse_object_decl ); } #[test] fn expr() { parse!("null", parse_expr); parse!("open", parse_expr); parse!("others", parse_expr); } #[test] fn subtype_decl() { parse!("subtype foo is integer;", parse_subtype_decl); } #[test] fn discon_spec() { parse!("disconnect A : T after 0 ns;", parse_discon_spec); parse!("disconnect A,B,C : T after 800 ns;", parse_discon_spec); parse!("disconnect others : T after 20 ps;", parse_discon_spec); parse!("disconnect all : T after 0 ns;", parse_discon_spec); } #[test] fn config_decl() { parse!( " architecture Structure of Half_Adder is for L1: XOR_GATE use entity WORK.XOR_GATE(Behavior) generic map (3 ns, 3 ns) port map (I1 => I1, I2 => I2, O => O); for L2: AND_GATE use entity WORK.AND_GATE(Behavior) generic map (3 ns, 4 ns) port map (I1, open, O); begin end architecture Structure; ", parse_arch_body ); parse!( " configuration Different of Half_Adder is for Structure for L1: XOR_GATE generic map (2.9 ns, 3.6 ns); end for; for L2: AND_GATE generic map (2.8 ns, 3.25 ns) port map (I2 => Tied_High); end for; end for; end configuration Different; ", parse_config_decl ); }