aboutsummaryrefslogtreecommitdiffstats
path: root/src/grt/grt-wave_opt_file-parse.adb
diff options
context:
space:
mode:
Diffstat (limited to 'src/grt/grt-wave_opt_file-parse.adb')
-rw-r--r--src/grt/grt-wave_opt_file-parse.adb354
1 files changed, 354 insertions, 0 deletions
diff --git a/src/grt/grt-wave_opt_file-parse.adb b/src/grt/grt-wave_opt_file-parse.adb
new file mode 100644
index 000000000..fe598a9b9
--- /dev/null
+++ b/src/grt/grt-wave_opt_file-parse.adb
@@ -0,0 +1,354 @@
+-- GHDL Run Time (GRT) - Wave option file package for parsing.
+-- Copyright (C) 2016 Jonas Baggett
+--
+-- GHDL is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU General Public License as published by the Free
+-- Software Foundation; either version 2, or (at your option) any later
+-- version.
+--
+-- GHDL is distributed in the hope that it will be useful, but WITHOUT ANY
+-- WARRANTY; without even the implied warranty of MERCHANTABILITY or
+-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+-- for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with GCC; see the file COPYING. If not, write to the Free
+-- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+-- 02111-1307, USA.
+--
+-- As a special exception, if other files instantiate generics from this
+-- unit, or you link this unit with other files to produce an executable,
+-- this unit does not by itself cause the resulting executable to be
+-- covered by the GNU General Public License. This exception does not
+-- however invalidate any other reasons why the executable file might be
+-- covered by the GNU Public License.
+
+-- Description: See package specifications
+
+-------------------------------------------------------------------------------
+
+-- TODO:
+-- * Currently the elements of the paths parsed are converted to lowercase.
+-- This is fine now, but maybe on day Verilog and/or System-C will be
+-- supported by GHDL and they are case-sensitive languages. In this case, we
+-- will need to find a different approach. Here are 2 possibilities :
+-- 1) Create 2 trees when parsing : one case sensitive and one case
+-- insensitive, then latter when we have more informations, prune VHDL
+-- paths from the case sensitive tree and prune verilog / system-C paths
+-- from the case insensitive tree (maybe it's not really needed). Then use
+-- the right tree while looking for signals to be displayed in the design.
+-- 2) Create only 1 case sensitive tree then latter when we have more
+-- informations, look for VHDL paths in the tree and merge elements who
+-- have the same name after lowering their characters.
+
+with System; use System;
+with Grt.Stdio; use Grt.Stdio;
+with Grt.Strings; use Grt.Strings;
+with Grt.Vstrings; use Grt.Vstrings;
+with Grt.Errors; use Grt.Errors;
+
+--~ with Grt.Wave_Opt_File.Parse.Debug;
+
+package body Grt.Wave_Opt_File.Parse is
+ -- Open the wave option file
+ function File_Open (Option_File : String) return FILEs;
+
+ -- Update the tree with the current VHDL element parsed from the current
+ -- path. Returns True if the tree was actually updated.
+ function Update_Tree (Elem_Name : String; Tree_Index : Tree_Index_Type)
+ return Boolean;
+
+ -- Parse the line where the version is set
+ procedure Parse_Version (Line : String_Access);
+
+ -- Print the version variable given as parameter
+ procedure Print_Version (Version : Version_Type);
+
+ -- Parse a line where a signal path is set
+ procedure Parse_Path (Line : String_Access);
+
+ procedure Start (Option_File : String)
+ is
+ Stream : constant FILEs := File_Open (Option_File);
+ First, Last : Integer;
+ Line : String (1 .. Buf_Size);
+ Lineno : Natural;
+ begin
+ File_Path := new String'(Option_File);
+ Lineno := 0;
+
+ -- Processes line after line.
+ loop
+ exit when fgets (Line'Address, Line'Length, Stream) = Null_Address;
+ Lineno := Lineno + 1;
+
+ -- Determine end of line.
+ Last := New_Line_Pos (Line) - 1;
+ if Last < 0 then
+ Last := Line'Last;
+ end if;
+
+ -- Skips empty lines and comments.
+ First := First_Non_Whitespace_Pos (Line (Line'First .. Last));
+ if First = -1 or else Line (First) = '#' then
+ goto Continue;
+ end if;
+
+ -- Create a line string without beginning and ending whitespaces
+ Last := Last_Non_Whitespace_Pos (Line (First .. Last));
+ Line_Context := new Line_Context_Type'(
+ Str => new String'(Line (First .. Last)),
+ Num => Lineno,
+ Max_Level => 0);
+
+
+ if Line (First) = '$' then
+ Parse_Version (Line_Context.Str);
+ -- TODO : Line_Context should be deallocated here but the memory
+ -- gain shouldn't be significative
+ else
+ Parse_Path (Line_Context.Str);
+ end if;
+
+ <<Continue>> null;
+ end loop;
+
+ if Version.Major = -1 then
+ Report_C ("warning: version wasn't set at the beginning of the" &
+ " file; currently supported version is ");
+ Print_Version (Current_Version);
+ Report_E ("");
+ end if;
+
+ if Trees = Tree_Array'(others => null) then
+ Report_E ("No signal path was found in the wave option file," &
+ " then every signals will be displayed.");
+ end if;
+
+ --~ Debug.Dump_Tree;
+
+ end Start;
+
+-------------------------------------------------------------------------------
+
+ -- An error/warning message start with the context or the error/warning.
+ -- This procedure print this context
+ procedure Print_Context (Severity : Severity_Type);
+
+ -- Print an error/warning with it's context
+ procedure Error_Context (Msg : String; Severity : Severity_Type := Error);
+
+ procedure Parse_Version (Line : String_Access)
+ is
+ Msg_Invalid_Format : constant String := "invalid version format";
+ First, Dot_Index, Num : Integer;
+ begin
+
+ if Version /= (others => -1) then
+ Error_Context ("version is set more than once");
+ end if;
+
+ if Trees /= Tree_Array'(others => null) then
+ Error_Context ("version cannot be set after signal paths");
+ end if;
+
+ First := First_Non_Whitespace_Pos (Line (Line'First + 1 .. Line'Last));
+ if Line (First .. First + 6) /= "version" then
+ Error_Context (Msg_Invalid_Format);
+ end if;
+
+ -- Catch "version\n", "version1.0"
+ First := First + 7;
+ if not Is_Whitespace (Line (First)) then
+ Error_Context (Msg_Invalid_Format);
+ end if;
+
+ -- Catch "version \n", "version \n", etc
+ First := First_Non_Whitespace_Pos (Line (First + 1 .. Line'Last));
+ if First = -1 then
+ Error_Context (Msg_Invalid_Format);
+ end if;
+
+ -- Catch the absence of "." or "version ."
+ Dot_Index := Find (Line (First + 1 .. Line'Last), '.');
+ if Dot_Index = -1 then
+ Error_Context (Msg_Invalid_Format);
+ end if;
+
+ -- Catch version a.2
+ Num := Value (Line (First .. Dot_Index - 1));
+ if Num = -1 then
+ Error_Context (Msg_Invalid_Format);
+ end if;
+ Version.Major := Num;
+
+ -- Catch version 1.a
+ Num := Value (Line (Dot_Index + 1 .. Line'Last));
+ if Num = -1 then
+ Error_Context (Msg_Invalid_Format);
+ end if;
+ Version.Minor := Num;
+
+ if Version.Major /= Current_Version.Major
+ or else Version.Minor > Current_Version.Minor
+ then
+ Print_Context (Error);
+ Error_C ("unsupported format version; it must be ");
+ if Current_Version.Minor /= 0 then
+ Error_C ("between ");
+ Print_Version (Version_Type'(Current_Version.Major, 0));
+ Error_C (" and ");
+ end if;
+ Print_Version (Current_Version);
+ Error_E;
+ end if;
+
+ end Parse_Version;
+
+ procedure Print_Version (Version : Version_Type)
+ is
+ Num_Str : String (1 .. Value_String_Size);
+ First : Positive;
+ begin
+ To_String (Num_Str, First, Ghdl_I32 (Version.Major));
+ Report_C (Num_Str (First .. Num_Str'Last));
+ Report_C (".");
+ To_String (Num_Str, First, Ghdl_I32 (Version.Minor));
+ Report_C (Num_Str (First .. Num_Str'Last));
+ end Print_Version;
+
+ --------------------------------------------------------------------------
+
+ procedure Parse_Path (Line : String_Access)
+ is
+ -- Can equal to 0 in case of error (like '.' as a full path)
+ First, Last : Natural;
+ Tree_Updated : Boolean;
+ Tree_Index : Tree_Index_Type;
+ begin
+ To_Lower (Line_Context.Str.all);
+ Last := Line'First;
+ if Line (Line'First) = '/' then
+ Tree_Index := Entity;
+ Last := Last + 1;
+ -- Catch '/' as a full path
+ if Last > Line'Length then
+ Error_Context ("invalid signal path");
+ end if;
+ else
+ -- '/' not allowed for package signal paths in a. Catch also the
+ -- absence a first slash in entity signal paths, which misleads the
+ -- code to believe it's inside a package
+ if Find (Line.all, '/') > 0 then
+ Error_Context ("invalid signal path");
+ end if;
+ Tree_Index := Pkg;
+ end if;
+ Tree_Cursor := Trees (Tree_Index);
+ Previous_Tree_Cursor := null;
+
+ loop
+ First := Last;
+
+ -- Find next identifier
+ loop
+ if Line (Last) = Seps (Tree_Index) then
+ Last := Last - 1;
+ exit;
+ elsif Last = Line'Last then
+ exit;
+ end if;
+ Last := Last + 1;
+ end loop;
+
+ Tree_Updated := Update_Tree (Line (First .. Last), Tree_Index);
+ Line_Context.Max_Level := Line_Context.Max_Level + 1;
+
+ if Last = Line'Last then
+ if not Tree_Updated then
+ Error_Context ("ignored already known signal path", Warning);
+ end if;
+ return;
+ end if;
+
+ -- Skip the separator
+ Last := Last + 2;
+ -- Catch signal paths ending with / or .
+ if Last > Line'Last then
+ Error_Context ("invalid signal path");
+ end if;
+
+ end loop;
+
+ end Parse_Path;
+
+ function Update_Tree (Elem_Name : String; Tree_Index : Tree_Index_Type)
+ return Boolean
+ is
+ Sibling_Cursor, Previous_Sibling_Cursor : Elem_Acc;
+ Elem : Elem_Acc;
+ begin
+ Sibling_Cursor := Tree_Cursor;
+ Previous_Sibling_Cursor := null;
+
+ loop
+ -- Already reached the last sibling and current identifier corresponds
+ -- to no existing element ? Then we will create an element
+ if Sibling_Cursor = null then
+ Elem := new Elem_Type'(Name => new String'(Elem_Name),
+ Line_Context => Line_Context,
+ Kind => Not_Found,
+ Next_Sibling | Next_Child => null);
+ -- First element of level ?
+ if Previous_Sibling_Cursor = null then
+ -- Is a top level ?
+ if Previous_Tree_Cursor = null then
+ Trees (Tree_Index) := Elem;
+ else
+ Previous_Tree_Cursor.Next_Child := Elem;
+ end if;
+ else
+ Previous_Sibling_Cursor.Next_Sibling := Elem;
+ end if;
+ Previous_Tree_Cursor := Elem;
+ -- Point to Elem.Next_Child which is null
+ Tree_Cursor := null;
+ return True;
+ -- Identifier was found in the tree ? Then move to its first child
+ elsif Elem_Name = Sibling_Cursor.Name.all then
+ Previous_Tree_Cursor := Sibling_Cursor;
+ Tree_Cursor := Sibling_Cursor.Next_Child;
+ return False;
+ end if;
+ Previous_Sibling_Cursor := Sibling_Cursor;
+ Sibling_Cursor := Sibling_Cursor.Next_Sibling;
+ end loop;
+ end Update_Tree;
+
+ --------------------------------------------------------------------------
+
+ procedure Print_Context (Severity : Severity_Type) is
+ begin
+ Print_Context (Line_Context, Severity);
+ end Print_Context;
+
+ procedure Error_Context (Msg : String; Severity : Severity_Type := Error) is
+ begin
+ Error_Context (Msg, Line_Context, Severity);
+ end Error_Context;
+
+ function File_Open (Option_File : String) return FILEs
+ is
+ Mode : constant String := "rt" & ASCII.Nul;
+ Stream : FILEs;
+ begin
+ Stream := fopen (Option_File'Address, Mode'Address);
+ if Stream = NULL_Stream then
+ Error_C ("cannot open '");
+ Error_C (Option_File (Option_File'First .. Option_File'Last - 1));
+ Error_E ("'");
+ end if;
+ return Stream;
+ end File_Open;
+
+end Grt.Wave_Opt_File.Parse;