blob: c38234b052e6ae71bc84b3b13ddeb0d23f9f8d62 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- P R E P --
-- --
-- B o d y --
-- --
-- Copyright (C) 2002-2013, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT 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 distributed with GNAT; see file COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Csets; use Csets;
with Err_Vars; use Err_Vars;
with Opt; use Opt;
with Osint; use Osint;
with Output; use Output;
with Scans; use Scans;
with Snames; use Snames;
with Sinput;
with Stringt; use Stringt;
with Table;
with Uintp; use Uintp;
with GNAT.Heap_Sort_G;
package body Prep is
use Symbol_Table;
type Token_Name_Array is array (Token_Type) of Name_Id;
Token_Names : constant Token_Name_Array :=
(Tok_Abort => Name_Abort,
Tok_Abs => Name_Abs,
Tok_Abstract => Name_Abstract,
Tok_Accept => Name_Accept,
Tok_Aliased => Name_Aliased,
Tok_All => Name_All,
Tok_Array => Name_Array,
Tok_And => Name_And,
Tok_At => Name_At,
Tok_Begin => Name_Begin,
Tok_Body => Name_Body,
Tok_Case => Name_Case,
Tok_Constant => Name_Constant,
Tok_Declare => Name_Declare,
Tok_Delay => Name_Delay,
Tok_Delta => Name_Delta,
Tok_Digits => Name_Digits,
Tok_Else => Name_Else,
Tok_Elsif => Name_Elsif,
Tok_End => Name_End,
Tok_Entry => Name_Entry,
Tok_Exception => Name_Exception,
Tok_Exit => Name_Exit,
Tok_For => Name_For,
Tok_Function => Name_Function,
Tok_Generic => Name_Generic,
Tok_Goto => Name_Goto,
Tok_If => Name_If,
Tok_Is => Name_Is,
Tok_Limited => Name_Limited,
Tok_Loop => Name_Loop,
Tok_Mod => Name_Mod,
Tok_New => Name_New,
Tok_Null => Name_Null,
Tok_Of => Name_Of,
Tok_Or => Name_Or,
Tok_Others => Name_Others,
Tok_Out => Name_Out,
Tok_Package => Name_Package,
Tok_Pragma => Name_Pragma,
Tok_Private => Name_Private,
Tok_Procedure => Name_Procedure,
Tok_Protected => Name_Protected,
Tok_Raise => Name_Raise,
Tok_Range => Name_Range,
Tok_Record => Name_Record,
Tok_Rem => Name_Rem,
Tok_Renames => Name_Renames,
Tok_Requeue => Name_Requeue,
Tok_Return => Name_Return,
Tok_Reverse => Name_Reverse,
Tok_Select => Name_Select,
Tok_Separate => Name_Separate,
Tok_Subtype => Name_Subtype,
Tok_Tagged => Name_Tagged,
Tok_Task => Name_Task,
Tok_Terminate => Name_Terminate,
Tok_Then => Name_Then,
Tok_Type => Name_Type,
Tok_Until => Name_Until,
Tok_Use => Name_Use,
Tok_When => Name_When,
Tok_While => Name_While,
Tok_With => Name_With,
Tok_Xor => Name_Xor,
others => No_Name);
Already_Initialized : Boolean := False;
-- Used to avoid repetition of the part of the initialisation that needs
-- to be done only once.
Empty_String : String_Id;
-- "", as a string_id
String_False : String_Id;
-- "false", as a string_id
---------------
-- Behaviour --
---------------
-- Accesses to procedure specified by procedure Initialize
Error_Msg : Error_Msg_Proc;
-- Report an error
Scan : Scan_Proc;
-- Scan one token
Set_Ignore_Errors : Set_Ignore_Errors_Proc;
-- Indicate if error should be taken into account
Put_Char : Put_Char_Proc;
-- Output one character
New_EOL : New_EOL_Proc;
-- Output an end of line indication
-------------------------------
-- State of the Preprocessor --
-------------------------------
type Pp_State is record
If_Ptr : Source_Ptr;
-- The location of the #if statement (used to flag #if with no
-- corresponding #end if, at the end).
Else_Ptr : Source_Ptr;
-- The location of the #else statement (used to detect multiple #else's)
Deleting : Boolean;
-- Set to True when the code should be deleted or commented out
Match_Seen : Boolean;
-- Set to True when a condition in an #if or an #elsif is True. Also set
-- to True if Deleting at the previous level is True. Used to decide if
-- Deleting should be set to True in a following #elsif or #else.
end record;
type Pp_Depth is new Nat;
Ground : constant Pp_Depth := 0;
package Pp_States is new Table.Table
(Table_Component_Type => Pp_State,
Table_Index_Type => Pp_Depth,
Table_Low_Bound => 1,
Table_Initial => 10,
Table_Increment => 100,
Table_Name => "Prep.Pp_States");
-- A stack of the states of the preprocessor, for nested #if
type Operator is (None, Op_Or, Op_And);
-----------------
-- Subprograms --
-----------------
function Deleting return Boolean;
-- Return True if code should be deleted or commented out
function Expression
(Evaluate_It : Boolean;
Complemented : Boolean := False) return Boolean;
-- Evaluate a condition in an #if or an #elsif statement. If Evaluate_It
-- is False, the condition is effectively evaluated, otherwise, only the
-- syntax is checked.
procedure Go_To_End_Of_Line;
-- Advance the scan pointer until we reach an end of line or the end of the
-- buffer.
function Matching_Strings (S1, S2 : String_Id) return Boolean;
-- Returns True if the two string parameters are equal (case insensitive)
---------------------------------------
-- Change_Reserved_Keyword_To_Symbol --
---------------------------------------
procedure Change_Reserved_Keyword_To_Symbol
(All_Keywords : Boolean := False)
is
New_Name : constant Name_Id := Token_Names (Token);
begin
if New_Name /= No_Name then
case Token is
when Tok_If | Tok_Else | Tok_Elsif | Tok_End |
Tok_And | Tok_Or | Tok_Then =>
if All_Keywords then
Token := Tok_Identifier;
Token_Name := New_Name;
end if;
when others =>
Token := Tok_Identifier;
Token_Name := New_Name;
end case;
end if;
end Change_Reserved_Keyword_To_Symbol;
------------------------------------------
-- Check_Command_Line_Symbol_Definition --
------------------------------------------
procedure Check_Command_Line_Symbol_Definition
(Definition : String;
Data : out Symbol_Data)
is
Index : Natural := 0;
Result : Symbol_Data;
begin
-- Look for the character '='
for J in Definition'Range loop
if Definition (J) = '=' then
Index := J;
exit;
end if;
end loop;
-- If no character '=', then the value is True
if Index = 0 then
-- Put the symbol in the name buffer
Name_Len := Definition'Length;
Name_Buffer (1 .. Name_Len) := Definition;
Result := True_Value;
elsif Index = Definition'First then
Fail ("invalid symbol definition """ & Definition & """");
else
-- Put the symbol in the name buffer
Name_Len := Index - Definition'First;
Name_Buffer (1 .. Name_Len) :=
String'(Definition (Definition'First .. Index - 1));
-- Check the syntax of the value
if Definition (Index + 1) /= '"'
or else Definition (Definition'Last) /= '"'
then
for J in Index + 1 .. Definition'Last loop
case Definition (J) is
when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
null;
when others =>
Fail ("illegal value """
& Definition (Index + 1 .. Definition'Last)
& """");
end case;
end loop;
end if;
-- Even if the value is a string, we still set Is_A_String to False,
-- to avoid adding additional quotes in the preprocessed sources when
-- replacing $<symbol>.
Result.Is_A_String := False;
-- Put the value in the result
Start_String;
Store_String_Chars (Definition (Index + 1 .. Definition'Last));
Result.Value := End_String;
end if;
-- Now, check the syntax of the symbol (we don't allow accented or
-- wide characters).
if Name_Buffer (1) not in 'a' .. 'z'
and then Name_Buffer (1) not in 'A' .. 'Z'
then
Fail ("symbol """
& Name_Buffer (1 .. Name_Len)
& """ does not start with a letter");
end if;
for J in 2 .. Name_Len loop
case Name_Buffer (J) is
when 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' =>
null;
when '_' =>
if J = Name_Len then
Fail ("symbol """
& Name_Buffer (1 .. Name_Len)
& """ end with a '_'");
elsif Name_Buffer (J + 1) = '_' then
Fail ("symbol """
& Name_Buffer (1 .. Name_Len)
& """ contains consecutive '_'");
end if;
when others =>
Fail ("symbol """
& Name_Buffer (1 .. Name_Len)
& """ contains illegal character(s)");
end case;
end loop;
Result.On_The_Command_Line := True;
-- Put the symbol name in the result
declare
Sym : constant String := Name_Buffer (1 .. Name_Len);
begin
for Index in 1 .. Name_Len loop
Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
end loop;
Result.Symbol := Name_Find;
Name_Len := Sym'Length;
Name_Buffer (1 .. Name_Len) := Sym;
Result.Original := Name_Find;
end;
Data := Result;
end Check_Command_Line_Symbol_Definition;
--------------
-- Deleting --
--------------
function Deleting return Boolean is
begin
-- Always return False when not inside an #if statement
if Opt.No_Deletion or else Pp_States.Last = Ground then
return False;
else
return Pp_States.Table (Pp_States.Last).Deleting;
end if;
end Deleting;
----------------
-- Expression --
----------------
function Expression
(Evaluate_It : Boolean;
Complemented : Boolean := False) return Boolean
is
Evaluation : Boolean := Evaluate_It;
-- Is set to False after an "or else" when left term is True and after
-- an "and then" when left term is False.
Final_Result : Boolean := False;
Current_Result : Boolean := False;
-- Value of a term
Current_Operator : Operator := None;
Symbol1 : Symbol_Id;
Symbol2 : Symbol_Id;
Symbol_Name1 : Name_Id;
Symbol_Name2 : Name_Id;
Symbol_Pos1 : Source_Ptr;
Symbol_Pos2 : Source_Ptr;
Symbol_Value1 : String_Id;
Symbol_Value2 : String_Id;
Relop : Token_Type;
begin
-- Loop for each term
loop
Change_Reserved_Keyword_To_Symbol;
Current_Result := False;
-- Scan current term, starting with Token
case Token is
-- Handle parenthesized expression
when Tok_Left_Paren =>
Scan.all;
Current_Result := Expression (Evaluation);
if Token = Tok_Right_Paren then
Scan.all;
else
Error_Msg -- CODEFIX
("`)` expected", Token_Ptr);
end if;
-- Handle not expression
when Tok_Not =>
Scan.all;
Current_Result :=
not Expression (Evaluation, Complemented => True);
-- Handle sequence starting with identifier
when Tok_Identifier =>
Symbol_Name1 := Token_Name;
Symbol_Pos1 := Token_Ptr;
Scan.all;
if Token = Tok_Apostrophe then
-- symbol'Defined
Scan.all;
if Token = Tok_Identifier
and then Token_Name = Name_Defined
then
Scan.all;
else
Error_Msg ("identifier `Defined` expected", Token_Ptr);
end if;
if Evaluation then
Current_Result := Index_Of (Symbol_Name1) /= No_Symbol;
end if;
-- Handle relational operator
elsif
Token = Tok_Equal or else
Token = Tok_Less or else
Token = Tok_Less_Equal or else
Token = Tok_Greater or else
Token = Tok_Greater_Equal
then
Relop := Token;
Scan.all;
Change_Reserved_Keyword_To_Symbol;
if Token = Tok_Integer_Literal then
-- symbol = integer
-- symbol < integer
-- symbol <= integer
-- symbol > integer
-- symbol >= integer
declare
Value : constant Int := UI_To_Int (Int_Literal_Value);
Data : Symbol_Data;
Symbol_Value : Int;
-- Value of symbol as Int
begin
if Evaluation then
Symbol1 := Index_Of (Symbol_Name1);
if Symbol1 = No_Symbol then
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg ("unknown symbol %", Symbol_Pos1);
Symbol_Value1 := No_String;
else
Data := Mapping.Table (Symbol1);
if Data.Is_A_String then
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg
("symbol % value is not integer",
Symbol_Pos1);
else
begin
String_To_Name_Buffer (Data.Value);
Symbol_Value :=
Int'Value (Name_Buffer (1 .. Name_Len));
case Relop is
when Tok_Equal =>
Current_Result :=
Symbol_Value = Value;
when Tok_Less =>
Current_Result :=
Symbol_Value < Value;
when Tok_Less_Equal =>
Current_Result :=
Symbol_Value <= Value;
when Tok_Greater =>
Current_Result :=
Symbol_Value > Value;
when Tok_Greater_Equal =>
Current_Result :=
Symbol_Value >= Value;
when others =>
null;
end case;
exception
when Constraint_Error =>
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg
("symbol % value is not an integer",
Symbol_Pos1);
end;
end if;
end if;
end if;
Scan.all;
end;
-- Error if relational operator other than = if not numbers
elsif Relop /= Tok_Equal then
Error_Msg ("number expected", Token_Ptr);
-- Equality comparison of two strings
elsif Token = Tok_Identifier then
-- symbol = symbol
Symbol_Name2 := Token_Name;
Symbol_Pos2 := Token_Ptr;
Scan.all;
if Evaluation then
Symbol1 := Index_Of (Symbol_Name1);
if Symbol1 = No_Symbol then
if Undefined_Symbols_Are_False then
Symbol_Value1 := String_False;
else
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg ("unknown symbol %", Symbol_Pos1);
Symbol_Value1 := No_String;
end if;
else
Symbol_Value1 :=
Mapping.Table (Symbol1).Value;
end if;
Symbol2 := Index_Of (Symbol_Name2);
if Symbol2 = No_Symbol then
if Undefined_Symbols_Are_False then
Symbol_Value2 := String_False;
else
Error_Msg_Name_1 := Symbol_Name2;
Error_Msg ("unknown symbol %", Symbol_Pos2);
Symbol_Value2 := No_String;
end if;
else
Symbol_Value2 := Mapping.Table (Symbol2).Value;
end if;
if Symbol_Value1 /= No_String
and then
Symbol_Value2 /= No_String
then
Current_Result :=
Matching_Strings (Symbol_Value1, Symbol_Value2);
end if;
end if;
elsif Token = Tok_String_Literal then
-- symbol = "value"
if Evaluation then
Symbol1 := Index_Of (Symbol_Name1);
if Symbol1 = No_Symbol then
if Undefined_Symbols_Are_False then
Symbol_Value1 := String_False;
else
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg ("unknown symbol %", Symbol_Pos1);
Symbol_Value1 := No_String;
end if;
else
Symbol_Value1 := Mapping.Table (Symbol1).Value;
end if;
if Symbol_Value1 /= No_String then
Current_Result :=
Matching_Strings
(Symbol_Value1,
String_Literal_Id);
end if;
end if;
Scan.all;
else
Error_Msg
("literal integer, symbol or literal string expected",
Token_Ptr);
end if;
-- Handle True or False
else
if Evaluation then
Symbol1 := Index_Of (Symbol_Name1);
if Symbol1 = No_Symbol then
if Undefined_Symbols_Are_False then
Symbol_Value1 := String_False;
else
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg ("unknown symbol %", Symbol_Pos1);
Symbol_Value1 := No_String;
end if;
else
Symbol_Value1 := Mapping.Table (Symbol1).Value;
end if;
if Symbol_Value1 /= No_String then
String_To_Name_Buffer (Symbol_Value1);
for Index in 1 .. Name_Len loop
Name_Buffer (Index) :=
Fold_Lower (Name_Buffer (Index));
end loop;
if Name_Buffer (1 .. Name_Len) = "true" then
Current_Result := True;
elsif Name_Buffer (1 .. Name_Len) = "false" then
Current_Result := False;
else
Error_Msg_Name_1 := Symbol_Name1;
Error_Msg
("value of symbol % is not True or False",
Symbol_Pos1);
end if;
end if;
end if;
end if;
-- Unrecognized sequence
when others =>
Error_Msg ("`(`, NOT or symbol expected", Token_Ptr);
end case;
-- Update the cumulative final result
case Current_Operator is
when None =>
Final_Result := Current_Result;
when Op_Or =>
Final_Result := Final_Result or Current_Result;
when Op_And =>
Final_Result := Final_Result and Current_Result;
end case;
-- Handle AND
if Token = Tok_And then
if Complemented then
Error_Msg
("mixing NOT and AND is not allowed, parentheses are required",
Token_Ptr);
elsif Current_Operator = Op_Or then
Error_Msg ("mixing OR and AND is not allowed", Token_Ptr);
end if;
Current_Operator := Op_And;
Scan.all;
if Token = Tok_Then then
Scan.all;
if Final_Result = False then
Evaluation := False;
end if;
end if;
-- Handle OR
elsif Token = Tok_Or then
if Complemented then
Error_Msg
("mixing NOT and OR is not allowed, parentheses are required",
Token_Ptr);
elsif Current_Operator = Op_And then
Error_Msg ("mixing AND and OR is not allowed", Token_Ptr);
end if;
Current_Operator := Op_Or;
Scan.all;
if Token = Tok_Else then
Scan.all;
if Final_Result then
Evaluation := False;
end if;
end if;
-- No AND/OR operator, so exit from the loop through terms
else
exit;
end if;
end loop;
return Final_Result;
end Expression;
-----------------------
-- Go_To_End_Of_Line --
-----------------------
procedure Go_To_End_Of_Line is
begin
-- Scan until we get an end of line or we reach the end of the buffer
while Token /= Tok_End_Of_Line
and then Token /= Tok_EOF
loop
Scan.all;
end loop;
end Go_To_End_Of_Line;
--------------
-- Index_Of --
--------------
function Index_Of (Symbol : Name_Id) return Symbol_Id is
begin
if Mapping.Table /= null then
for J in Symbol_Id range 1 .. Symbol_Table.Last (Mapping) loop
if Mapping.Table (J).Symbol = Symbol then
return J;
end if;
end loop;
end if;
return No_Symbol;
end Index_Of;
----------------
-- Initialize --
----------------
procedure Initialize is
begin
if not Already_Initialized then
Start_String;
Store_String_Chars ("True");
True_Value.Value := End_String;
Start_String;
Empty_String := End_String;
Start_String;
Store_String_Chars ("False");
String_False := End_String;
Already_Initialized := True;
end if;
end Initialize;
------------------
-- List_Symbols --
------------------
procedure List_Symbols (Foreword : String) is
Order : array (0 .. Integer (Symbol_Table.Last (Mapping)))
of Symbol_Id;
-- After alphabetical sorting, this array stores the indexes of the
-- symbols in the order they are displayed.
function Lt (Op1, Op2 : Natural) return Boolean;
-- Comparison routine for sort call
procedure Move (From : Natural; To : Natural);
-- Move routine for sort call
--------
-- Lt --
--------
function Lt (Op1, Op2 : Natural) return Boolean is
S1 : constant String :=
Get_Name_String (Mapping.Table (Order (Op1)).Symbol);
S2 : constant String :=
Get_Name_String (Mapping.Table (Order (Op2)).Symbol);
begin
return S1 < S2;
end Lt;
----------
-- Move --
----------
procedure Move (From : Natural; To : Natural) is
begin
Order (To) := Order (From);
end Move;
package Sort_Syms is new GNAT.Heap_Sort_G (Move, Lt);
Max_L : Natural;
-- Maximum length of any symbol
-- Start of processing for List_Symbols_Case
begin
if Symbol_Table.Last (Mapping) = 0 then
return;
end if;
if Foreword'Length > 0 then
Write_Eol;
Write_Line (Foreword);
for J in Foreword'Range loop
Write_Char ('=');
end loop;
end if;
-- Initialize the order
for J in Order'Range loop
Order (J) := Symbol_Id (J);
end loop;
-- Sort alphabetically
Sort_Syms.Sort (Order'Last);
Max_L := 7;
for J in 1 .. Symbol_Table.Last (Mapping) loop
Get_Name_String (Mapping.Table (J).Original);
Max_L := Integer'Max (Max_L, Name_Len);
end loop;
Write_Eol;
Write_Str ("Symbol");
for J in 1 .. Max_L - 5 loop
Write_Char (' ');
end loop;
Write_Line ("Value");
Write_Str ("------");
for J in 1 .. Max_L - 5 loop
Write_Char (' ');
end loop;
Write_Line ("------");
for J in 1 .. Order'Last loop
declare
Data : constant Symbol_Data := Mapping.Table (Order (J));
begin
Get_Name_String (Data.Original);
Write_Str (Name_Buffer (1 .. Name_Len));
for K in Name_Len .. Max_L loop
Write_Char (' ');
end loop;
String_To_Name_Buffer (Data.Value);
if Data.Is_A_String then
Write_Char ('"');
for J in 1 .. Name_Len loop
Write_Char (Name_Buffer (J));
if Name_Buffer (J) = '"' then
Write_Char ('"');
end if;
end loop;
Write_Char ('"');
else
Write_Str (Name_Buffer (1 .. Name_Len));
end if;
end;
Write_Eol;
end loop;
Write_Eol;
end List_Symbols;
----------------------
-- Matching_Strings --
----------------------
function Matching_Strings (S1, S2 : String_Id) return Boolean is
begin
String_To_Name_Buffer (S1);
for Index in 1 .. Name_Len loop
Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
end loop;
declare
String1 : constant String := Name_Buffer (1 .. Name_Len);
begin
String_To_Name_Buffer (S2);
for Index in 1 .. Name_Len loop
Name_Buffer (Index) := Fold_Lower (Name_Buffer (Index));
end loop;
return String1 = Name_Buffer (1 .. Name_Len);
end;
end Matching_Strings;
--------------------
-- Parse_Def_File --
--------------------
-- This procedure REALLY needs some more comments ???
procedure Parse_Def_File is
Symbol : Symbol_Id;
Symbol_Name : Name_Id;
Original_Name : Name_Id;
Data : Symbol_Data;
Value_Start : Source_Ptr;
Value_End : Source_Ptr;
Ch : Character;
use ASCII;
begin
Def_Line_Loop :
loop
Scan.all;
exit Def_Line_Loop when Token = Tok_EOF;
if Token /= Tok_End_Of_Line then
Change_Reserved_Keyword_To_Symbol;
if Token /= Tok_Identifier then
Error_Msg ("identifier expected", Token_Ptr);
goto Cleanup;
end if;
Symbol_Name := Token_Name;
Name_Len := 0;
for Ptr in Token_Ptr .. Scan_Ptr - 1 loop
Name_Len := Name_Len + 1;
Name_Buffer (Name_Len) := Sinput.Source (Ptr);
end loop;
Original_Name := Name_Find;
Scan.all;
if Token /= Tok_Colon_Equal then
Error_Msg -- CODEFIX
("`:=` expected", Token_Ptr);
goto Cleanup;
end if;
Scan.all;
if Token = Tok_Integer_Literal then
declare
Ptr : Source_Ptr := Token_Ptr;
begin
Start_String;
while Ptr < Scan_Ptr loop
Store_String_Char (Sinput.Source (Ptr));
Ptr := Ptr + 1;
end loop;
Data := (Symbol => Symbol_Name,
Original => Original_Name,
On_The_Command_Line => False,
Is_A_String => False,
Value => End_String);
end;
Scan.all;
if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
Error_Msg ("extraneous text in definition", Token_Ptr);
goto Cleanup;
end if;
elsif Token = Tok_String_Literal then
Data := (Symbol => Symbol_Name,
Original => Original_Name,
On_The_Command_Line => False,
Is_A_String => True,
Value => String_Literal_Id);
Scan.all;
if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
Error_Msg ("extraneous text in definition", Token_Ptr);
goto Cleanup;
end if;
elsif Token = Tok_End_Of_Line or else Token = Tok_EOF then
Data := (Symbol => Symbol_Name,
Original => Original_Name,
On_The_Command_Line => False,
Is_A_String => False,
Value => Empty_String);
else
Value_Start := Token_Ptr;
Value_End := Token_Ptr - 1;
Scan_Ptr := Token_Ptr;
Value_Chars_Loop :
loop
Ch := Sinput.Source (Scan_Ptr);
case Ch is
when '_' | '.' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' =>
Value_End := Scan_Ptr;
Scan_Ptr := Scan_Ptr + 1;
when ' ' | HT | VT | CR | LF | FF =>
exit Value_Chars_Loop;
when others =>
Error_Msg ("illegal character", Scan_Ptr);
goto Cleanup;
end case;
end loop Value_Chars_Loop;
Scan.all;
if Token /= Tok_End_Of_Line and then Token /= Tok_EOF then
Error_Msg ("extraneous text in definition", Token_Ptr);
goto Cleanup;
end if;
Start_String;
while Value_Start <= Value_End loop
Store_String_Char (Sinput.Source (Value_Start));
Value_Start := Value_Start + 1;
end loop;
Data := (Symbol => Symbol_Name,
Original => Original_Name,
On_The_Command_Line => False,
Is_A_String => False,
Value => End_String);
end if;
-- Now that we have the value, get the symbol index
Symbol := Index_Of (Symbol_Name);
if Symbol /= No_Symbol then
-- If we already have an entry for this symbol, replace it
-- with the new value, except if the symbol was declared on
-- the command line.
if Mapping.Table (Symbol).On_The_Command_Line then
goto Continue;
end if;
else
-- As it is the first time we see this symbol, create a new
-- entry in the table.
if Mapping.Table = null then
Symbol_Table.Init (Mapping);
end if;
Symbol_Table.Increment_Last (Mapping);
Symbol := Symbol_Table.Last (Mapping);
end if;
Mapping.Table (Symbol) := Data;
goto Continue;
<<Cleanup>>
Set_Ignore_Errors (To => True);
while Token /= Tok_End_Of_Line and then Token /= Tok_EOF loop
Scan.all;
end loop;
Set_Ignore_Errors (To => False);
<<Continue>>
null;
end if;
end loop Def_Line_Loop;
end Parse_Def_File;
----------------
-- Preprocess --
----------------
procedure Preprocess (Source_Modified : out Boolean) is
Start_Of_Processing : Source_Ptr;
Cond : Boolean;
Preprocessor_Line : Boolean := False;
No_Error_Found : Boolean := True;
Modified : Boolean := False;
procedure Output (From, To : Source_Ptr);
-- Output the characters with indexes From .. To in the buffer to the
-- output file.
procedure Output_Line (From, To : Source_Ptr);
-- Output a line or the end of a line from the buffer to the output
-- file, followed by an end of line terminator. Depending on the value
-- of Deleting and the switches, the line may be commented out, blank or
-- not output at all.
------------
-- Output --
------------
procedure Output (From, To : Source_Ptr) is
begin
for J in From .. To loop
Put_Char (Sinput.Source (J));
end loop;
end Output;
-----------------
-- Output_Line --
-----------------
procedure Output_Line (From, To : Source_Ptr) is
begin
if Deleting or else Preprocessor_Line then
if Blank_Deleted_Lines then
New_EOL.all;
elsif Comment_Deleted_Lines then
Put_Char ('-');
Put_Char ('-');
Put_Char ('!');
if From < To then
Put_Char (' ');
Output (From, To);
end if;
New_EOL.all;
end if;
else
Output (From, To);
New_EOL.all;
end if;
end Output_Line;
-- Start of processing for Preprocess
begin
Start_Of_Processing := Scan_Ptr;
-- First a call to Scan, because Initialize_Scanner is not doing it
Scan.all;
Input_Line_Loop : loop
exit Input_Line_Loop when Token = Tok_EOF;
Preprocessor_Line := False;
if Token /= Tok_End_Of_Line then
-- Preprocessor line
if Token = Tok_Special and then Special_Character = '#' then
Modified := True;
Preprocessor_Line := True;
Scan.all;
case Token is
-- #if
when Tok_If =>
declare
If_Ptr : constant Source_Ptr := Token_Ptr;
begin
Scan.all;
Cond := Expression (not Deleting);
-- Check for an eventual "then"
if Token = Tok_Then then
Scan.all;
end if;
-- It is an error to have trailing characters after
-- the condition or "then".
if Token /= Tok_End_Of_Line
and then Token /= Tok_EOF
then
Error_Msg
("extraneous text on preprocessor line",
Token_Ptr);
No_Error_Found := False;
Go_To_End_Of_Line;
end if;
declare
-- Set the initial state of this new "#if". This
-- must be done before incrementing the Last of
-- the table, otherwise function Deleting does
-- not report the correct value.
New_State : constant Pp_State :=
(If_Ptr => If_Ptr,
Else_Ptr => 0,
Deleting => Deleting
or else not Cond,
Match_Seen => Deleting or else Cond);
begin
Pp_States.Increment_Last;
Pp_States.Table (Pp_States.Last) := New_State;
end;
end;
-- #elsif
when Tok_Elsif =>
Cond := False;
if Pp_States.Last = 0
or else Pp_States.Table (Pp_States.Last).Else_Ptr /= 0
then
Error_Msg ("no IF for this ELSIF", Token_Ptr);
No_Error_Found := False;
else
Cond :=
not Pp_States.Table (Pp_States.Last).Match_Seen;
end if;
Scan.all;
Cond := Expression (Cond);
-- Check for an eventual "then"
if Token = Tok_Then then
Scan.all;
end if;
-- It is an error to have trailing characters after the
-- condition or "then".
if Token /= Tok_End_Of_Line
and then Token /= Tok_EOF
then
Error_Msg
("extraneous text on preprocessor line",
Token_Ptr);
No_Error_Found := False;
Go_To_End_Of_Line;
end if;
-- Depending on the value of the condition, set the new
-- values of Deleting and Match_Seen.
if Pp_States.Last > 0 then
if Pp_States.Table (Pp_States.Last).Match_Seen then
Pp_States.Table (Pp_States.Last).Deleting := True;
else
if Cond then
Pp_States.Table (Pp_States.Last).Match_Seen :=
True;
Pp_States.Table (Pp_States.Last).Deleting :=
False;
end if;
end if;
end if;
-- #else
when Tok_Else =>
if Pp_States.Last = 0 then
Error_Msg ("no IF for this ELSE", Token_Ptr);
No_Error_Found := False;
elsif
Pp_States.Table (Pp_States.Last).Else_Ptr /= 0
then
Error_Msg -- CODEFIX
("duplicate ELSE line", Token_Ptr);
No_Error_Found := False;
end if;
-- Set the possibly new values of Deleting and Match_Seen
if Pp_States.Last > 0 then
if Pp_States.Table (Pp_States.Last).Match_Seen then
Pp_States.Table (Pp_States.Last).Deleting :=
True;
else
Pp_States.Table (Pp_States.Last).Match_Seen :=
True;
Pp_States.Table (Pp_States.Last).Deleting :=
False;
end if;
-- Set the Else_Ptr to check for illegal #elsif later
Pp_States.Table (Pp_States.Last).Else_Ptr :=
Token_Ptr;
end if;
Scan.all;
-- Error of character present after "#else"
if Token /= Tok_End_Of_Line
and then Token /= Tok_EOF
then
Error_Msg
("extraneous text on preprocessor line",
Token_Ptr);
No_Error_Found := False;
Go_To_End_Of_Line;
end if;
-- #end if;
when Tok_End =>
if Pp_States.Last = 0 then
Error_Msg ("no IF for this END", Token_Ptr);
No_Error_Found := False;
end if;
Scan.all;
if Token /= Tok_If then
Error_Msg -- CODEFIX
("IF expected", Token_Ptr);
No_Error_Found := False;
else
Scan.all;
if Token /= Tok_Semicolon then
Error_Msg -- CODEFIX
("`;` Expected", Token_Ptr);
No_Error_Found := False;
else
Scan.all;
-- Error of character present after "#end if;"
if Token /= Tok_End_Of_Line
and then Token /= Tok_EOF
then
Error_Msg
("extraneous text on preprocessor line",
Token_Ptr);
No_Error_Found := False;
end if;
end if;
end if;
-- In case of one of the errors above, skip the tokens
-- until the end of line is reached.
Go_To_End_Of_Line;
-- Decrement the depth of the #if stack
if Pp_States.Last > 0 then
Pp_States.Decrement_Last;
end if;
-- Illegal preprocessor line
when others =>
No_Error_Found := False;
if Pp_States.Last = 0 then
Error_Msg -- CODEFIX
("IF expected", Token_Ptr);
elsif
Pp_States.Table (Pp_States.Last).Else_Ptr = 0
then
Error_Msg
("IF, ELSIF, ELSE, or `END IF` expected",
Token_Ptr);
else
Error_Msg ("IF or `END IF` expected", Token_Ptr);
end if;
-- Skip to the end of this illegal line
Go_To_End_Of_Line;
end case;
-- Not a preprocessor line
else
-- Do not report errors for those lines, even if there are
-- Ada parsing errors.
Set_Ignore_Errors (To => True);
if Deleting then
Go_To_End_Of_Line;
else
while Token /= Tok_End_Of_Line
and then Token /= Tok_EOF
loop
if Token = Tok_Special
and then Special_Character = '$'
then
Modified := True;
declare
Dollar_Ptr : constant Source_Ptr := Token_Ptr;
Symbol : Symbol_Id;
begin
Scan.all;
Change_Reserved_Keyword_To_Symbol;
if Token = Tok_Identifier
and then Token_Ptr = Dollar_Ptr + 1
then
-- $symbol
Symbol := Index_Of (Token_Name);
-- If symbol exists, replace by its value
if Symbol /= No_Symbol then
Output (Start_Of_Processing, Dollar_Ptr - 1);
Start_Of_Processing := Scan_Ptr;
String_To_Name_Buffer
(Mapping.Table (Symbol).Value);
if Mapping.Table (Symbol).Is_A_String then
-- Value is an Ada string
Put_Char ('"');
for J in 1 .. Name_Len loop
Put_Char (Name_Buffer (J));
if Name_Buffer (J) = '"' then
Put_Char ('"');
end if;
end loop;
Put_Char ('"');
else
-- Value is a sequence of characters, not
-- an Ada string.
for J in 1 .. Name_Len loop
Put_Char (Name_Buffer (J));
end loop;
end if;
end if;
end if;
end;
end if;
Scan.all;
end loop;
end if;
Set_Ignore_Errors (To => False);
end if;
end if;
pragma Assert (Token = Tok_End_Of_Line or else Token = Tok_EOF);
-- At this point, the token is either end of line or EOF. The line to
-- possibly output stops just before the token.
Output_Line (Start_Of_Processing, Token_Ptr - 1);
-- If we are at the end of a line, the scan pointer is at the first
-- non-blank character (may not be the first character of the line),
-- so we have to deduct Start_Of_Processing from the token pointer.
if Token = Tok_End_Of_Line then
if (Sinput.Source (Token_Ptr) = ASCII.CR
and then Sinput.Source (Token_Ptr + 1) = ASCII.LF)
or else
(Sinput.Source (Token_Ptr) = ASCII.CR
and then Sinput.Source (Token_Ptr + 1) = ASCII.LF)
then
Start_Of_Processing := Token_Ptr + 2;
else
Start_Of_Processing := Token_Ptr + 1;
end if;
end if;
-- Now, scan the first token of the next line. If the token is EOF,
-- the scan pointer will not move, and the token will still be EOF.
Set_Ignore_Errors (To => True);
Scan.all;
Set_Ignore_Errors (To => False);
end loop Input_Line_Loop;
-- Report an error for any missing some "#end if;"
for Level in reverse 1 .. Pp_States.Last loop
Error_Msg ("no `END IF` for this IF", Pp_States.Table (Level).If_Ptr);
No_Error_Found := False;
end loop;
Source_Modified := No_Error_Found and Modified;
end Preprocess;
-----------------
-- Setup_Hooks --
-----------------
procedure Setup_Hooks
(Error_Msg : Error_Msg_Proc;
Scan : Scan_Proc;
Set_Ignore_Errors : Set_Ignore_Errors_Proc;
Put_Char : Put_Char_Proc;
New_EOL : New_EOL_Proc)
is
begin
pragma Assert (Already_Initialized);
Prep.Error_Msg := Error_Msg;
Prep.Scan := Scan;
Prep.Set_Ignore_Errors := Set_Ignore_Errors;
Prep.Put_Char := Put_Char;
Prep.New_EOL := New_EOL;
end Setup_Hooks;
end Prep;