1 ------------------------------------------------------------------------------
3 -- GNAT SYSTEM UTILITIES --
9 -- Copyright (C) 2008-2014, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
26 -- The base name of the template file is given by Argument (1). This program
27 -- generates the spec for this specified unit (let's call it UNIT_NAME).
29 -- It works in conjunction with a C template file which must be pre-processed
30 -- and compiled using the cross compiler. Two input files are used:
31 -- - the preprocessed C file: UNIT_NAME-tmplt.i
32 -- - the generated assembly file: UNIT_NAME-tmplt.s
34 -- The generated files are UNIT_NAME.ads and UNIT_NAME.h
36 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
37 with Ada
.Command_Line
; use Ada
.Command_Line
;
38 with Ada
.Exceptions
; use Ada
.Exceptions
;
39 with Ada
.Streams
.Stream_IO
; use Ada
.Streams
.Stream_IO
;
40 with Ada
.Strings
.Fixed
; use Ada
.Strings
.Fixed
;
41 with Ada
.Strings
.Maps
; use Ada
.Strings
.Maps
;
42 with Ada
.Strings
.Maps
.Constants
; use Ada
.Strings
.Maps
.Constants
;
43 with Ada
.Text_IO
; use Ada
.Text_IO
;
45 pragma Warnings
(Off
);
46 -- System.Unsigned_Types is an internal GNAT unit
47 with System
.Unsigned_Types
; use System
.Unsigned_Types
;
51 with GNAT
.String_Split
; use GNAT
.String_Split
;
54 with XUtil
; use XUtil
;
60 Unit_Name
: constant String := Argument
(1);
61 Tmpl_Name
: constant String := Unit_Name
& "-tmplt";
63 -------------------------------------------------
64 -- Information retrieved from assembly listing --
65 -------------------------------------------------
67 type String_Access
is access all String;
68 -- Note: we can't use GNAT.Strings for this definition, since that unit
69 -- is not available in older base compilers.
71 -- We need to deal with integer values that can be signed or unsigned, so
72 -- we need to accommodate the maximum range of both cases.
74 type Int_Value_Type
is record
76 Abs_Value
: Long_Unsigned
:= 0;
79 function ">" (V1
, V2
: Int_Value_Type
) return Boolean;
80 function "<" (V1
, V2
: Int_Value_Type
) return Boolean;
83 (CND
, -- Named number (decimal)
84 CNU
, -- Named number (decimal, unsigned)
85 CNS
, -- Named number (freeform text)
89 -- Recognized markers found in assembly file. These markers are produced by
90 -- the same-named macros from the C template.
92 subtype Asm_Int_Kind
is Asm_Info_Kind
range CND
.. CNU
;
93 -- Asm_Info_Kind values with int values in input
95 subtype Named_Number
is Asm_Info_Kind
range CND
.. CNS
;
96 -- Asm_Info_Kind values with named numbers in output
98 type Asm_Info
(Kind
: Asm_Info_Kind
:= TXT
) is record
99 Line_Number
: Integer;
100 -- Line number in C source file
102 Constant_Name
: String_Access
;
103 -- Name of constant to be defined
105 Constant_Type
: String_Access
;
106 -- Type of constant (case of Kind = C)
108 Value_Len
: Natural := 0;
109 -- Length of text representation of constant's value
111 Text_Value
: String_Access
;
112 -- Value for CNS / C constant
114 Int_Value
: Int_Value_Type
;
115 -- Value for CND / CNU constant
117 Comment
: String_Access
;
118 -- Additional descriptive comment for constant, or free-form text (TXT)
121 package Asm_Infos
is new GNAT
.Table
122 (Table_Component_Type
=> Asm_Info
,
123 Table_Index_Type
=> Integer,
124 Table_Low_Bound
=> 1,
125 Table_Initial
=> 100,
126 Table_Increment
=> 10);
128 Max_Constant_Name_Len
: Natural := 0;
129 Max_Constant_Value_Len
: Natural := 0;
130 Max_Constant_Type_Len
: Natural := 0;
131 -- Lengths of longest name and longest value
133 Size_Of_Unsigned_Int
: Integer := 0;
134 -- Size of unsigned int on target
136 type Language
is (Lang_Ada
, Lang_C
);
138 function Parse_Int
(S
: String; K
: Asm_Int_Kind
) return Int_Value_Type
;
139 -- Parse a decimal number, preceded by an optional '$' or '#' character,
140 -- and return its value.
142 procedure Output_Info
145 Info_Index
: Integer);
146 -- Output information from the indicated asm info line
148 procedure Parse_Asm_Line
(Line
: String);
149 -- Parse one information line from the assembly source
151 function Contains_Template_Name
(S
: String) return Boolean;
152 -- True if S contains Tmpl_Name, possibly with different casing
154 function Spaces
(Count
: Integer) return String;
155 -- If Count is positive, return a string of Count spaces, else return an
162 function ">" (V1
, V2
: Int_Value_Type
) return Boolean is
163 P1
: Boolean renames V1
.Positive;
164 P2
: Boolean renames V2
.Positive;
165 A1
: Long_Unsigned
renames V1
.Abs_Value
;
166 A2
: Long_Unsigned
renames V2
.Abs_Value
;
168 return (P1
and then not P2
)
169 or else (P1
and then P2
and then A1
> A2
)
170 or else (not P1
and then not P2
and then A1
< A2
);
177 function "<" (V1
, V2
: Int_Value_Type
) return Boolean is
179 return not (V1
> V2
) and then not (V1
= V2
);
182 ----------------------------
183 -- Contains_Template_Name --
184 ----------------------------
186 function Contains_Template_Name
(S
: String) return Boolean is
188 if Index
(Source
=> To_Lower
(S
), Pattern
=> Tmpl_Name
) > 0 then
193 end Contains_Template_Name
;
199 procedure Output_Info
202 Info_Index
: Integer)
204 Info
: Asm_Info
renames Asm_Infos
.Table
(Info_Index
);
206 procedure Put
(S
: String);
213 procedure Put
(S
: String) is
218 -- Start of processing for Output_Info
224 -- Handled in the common code for comments below
231 Put
(" subtype " & Info
.Constant_Name
.all
232 & " is Interfaces.C."
233 & Info
.Text_Value
.all & ";");
235 Put
("#define " & Info
.Constant_Name
.all & " "
236 & Info
.Text_Value
.all);
241 -- All named number cases
245 Put
(" " & Info
.Constant_Name
.all);
246 Put
(Spaces
(Max_Constant_Name_Len
247 - Info
.Constant_Name
'Length));
249 if Info
.Kind
in Named_Number
then
250 Put
(" : constant := ");
252 Put
(" : constant " & Info
.Constant_Type
.all);
253 Put
(Spaces
(Max_Constant_Type_Len
254 - Info
.Constant_Type
'Length));
259 Put
("#define " & Info
.Constant_Name
.all & " ");
260 Put
(Spaces
(Max_Constant_Name_Len
261 - Info
.Constant_Name
'Length));
264 if Info
.Kind
in Asm_Int_Kind
then
265 if not Info
.Int_Value
.Positive then
269 Put
(Trim
(Info
.Int_Value
.Abs_Value
'Img, Side
=> Left
));
273 Is_String
: constant Boolean :=
275 and then Info
.Constant_Type
.all = "String";
282 Put
(Info
.Text_Value
.all);
290 if Lang
= Lang_Ada
then
293 if Info
.Comment
'Length > 0 then
294 Put
(Spaces
(Max_Constant_Value_Len
- Info
.Value_Len
));
300 if Lang
= Lang_Ada
then
301 Put
(Info
.Comment
.all);
311 procedure Parse_Asm_Line
(Line
: String) is
312 Index1
, Index2
: Integer := Line
'First;
314 function Field_Alloc
return String_Access
;
315 -- Allocate and return a copy of Line (Index1 .. Index2 - 1)
317 procedure Find_Colon
(Index
: in out Integer);
318 -- Increment Index until the next colon in Line
324 function Field_Alloc
return String_Access
is
326 return new String'(Line (Index1 .. Index2 - 1));
333 procedure Find_Colon (Index : in out Integer) is
337 exit when Index > Line'Last or else Line (Index) = ':';
341 -- Start of processing for Parse_Asm_Line
347 Info : Asm_Info (Kind => Asm_Info_Kind'Value
348 (Line (Line'First .. Index2 - 1)));
350 Index1 := Index2 + 1;
354 Integer (Parse_Int (Line (Index1 .. Index2 - 1), CNU).Abs_Value);
357 when CND | CNU | CNS | C | SUB =>
358 Index1 := Index2 + 1;
361 Info.Constant_Name := Field_Alloc;
365 Info.Constant_Name'Length > Max_Constant_Name_Len
367 Max_Constant_Name_Len := Info.Constant_Name'Length;
370 Index1 := Index2 + 1;
373 if Info.Kind = C then
374 Info.Constant_Type := Field_Alloc;
376 if Info.Constant_Type'Length > Max_Constant_Type_Len then
377 Max_Constant_Type_Len := Info.Constant_Type'Length;
380 Index1 := Index2 + 1;
384 if Info.Kind = CND or else Info.Kind = CNU then
386 Parse_Int (Line (Index1 .. Index2 - 1), Info.Kind);
387 Info.Value_Len := Info.Int_Value.Abs_Value'Img'Length
- 1;
389 if not Info
.Int_Value
.Positive then
390 Info
.Value_Len
:= Info
.Value_Len
+ 1;
394 Info
.Text_Value
:= Field_Alloc
;
395 Info
.Value_Len
:= Info
.Text_Value
'Length;
398 if Info
.Constant_Name
.all = "SIZEOF_unsigned_int" then
399 Size_Of_Unsigned_Int
:=
400 8 * Integer (Info
.Int_Value
.Abs_Value
);
407 Index1
:= Index2
+ 1;
408 Index2
:= Line
'Last + 1;
409 Info
.Comment
:= Field_Alloc
;
411 if Info
.Kind
= TXT
then
412 Info
.Text_Value
:= Info
.Comment
;
414 -- Update Max_Constant_Value_Len, but only if this constant has a
415 -- comment (else the value is allowed to be longer).
417 elsif Info
.Comment
'Length > 0 then
418 if Info
.Value_Len
> Max_Constant_Value_Len
then
419 Max_Constant_Value_Len
:= Info
.Value_Len
;
423 Asm_Infos
.Append
(Info
);
429 (Standard_Error
, "can't parse " & Line
);
431 (Standard_Error
, "exception raised: " & Exception_Information
(E
));
441 Tmpl_File
: Ada
.Text_IO
.File_Type
;
442 Ada_Ofile
, C_Ofile
: Sfile
;
443 Current_Line
: in out Integer)
445 function Get_Value
(Name
: String) return Int_Value_Type
;
446 -- Returns the value of the variable Name
452 function Get_Value
(Name
: String) return Int_Value_Type
is
454 if Is_Subset
(To_Set
(Name
), Decimal_Digit_Set
) then
455 return Parse_Int
(Name
, CND
);
458 for K
in 1 .. Asm_Infos
.Last
loop
459 if Asm_Infos
.Table
(K
).Constant_Name
/= null then
460 if Name
= Asm_Infos
.Table
(K
).Constant_Name
.all then
461 return Asm_Infos
.Table
(K
).Int_Value
;
466 -- Not found returns 0
475 Line
: String (1 .. 256);
477 Value1
: Int_Value_Type
;
478 Value2
: Int_Value_Type
;
481 -- Start of processing for Parse_Cond
484 Create
(Sline
, If_Line
, " ");
486 if Slice_Count
(Sline
) /= 4 then
487 Put_Line
(Standard_Error
, "can't parse " & If_Line
);
490 Value1
:= Get_Value
(Slice
(Sline
, 2));
491 Value2
:= Get_Value
(Slice
(Sline
, 4));
493 if Slice
(Sline
, 3) = ">" then
494 Res
:= Cond
and (Value1
> Value2
);
496 elsif Slice
(Sline
, 3) = "<" then
497 Res
:= Cond
and (Value1
< Value2
);
499 elsif Slice
(Sline
, 3) = "=" then
500 Res
:= Cond
and (Value1
= Value2
);
502 elsif Slice
(Sline
, 3) = "/=" then
503 Res
:= Cond
and (Value1
/= Value2
);
506 -- No other operator can be used
508 Put_Line
(Standard_Error
, "unknown operator in " & If_Line
);
512 Current_Line
:= Current_Line
+ 1;
515 Get_Line
(Tmpl_File
, Line
, Last
);
516 Current_Line
:= Current_Line
+ 1;
517 exit when Line
(1 .. Last
) = "@END_IF";
519 if Last
> 4 and then Line
(1 .. 4) = "@IF " then
521 (Line
(1 .. Last
), Res
,
522 Tmpl_File
, Ada_Ofile
, C_Ofile
, Current_Line
);
524 elsif Line
(1 .. Last
) = "@ELSE" then
525 Res
:= Cond
and not Res
;
528 Put_Line
(Ada_OFile
, Line
(1 .. Last
));
529 Put_Line
(C_OFile
, Line
(1 .. Last
));
540 K
: Asm_Int_Kind
) return Int_Value_Type
542 First
: Integer := S
'First;
543 Result
: Int_Value_Type
;
546 -- On some platforms, immediate integer values are prefixed with
547 -- a $ or # character in assembly output.
549 if S
(First
) = '$' or else S
(First
) = '#' then
553 if S
(First
) = '-' then
554 Result
.Positive := False;
557 Result
.Positive := True;
560 Result
.Abs_Value
:= Long_Unsigned
'Value (S
(First
.. S
'Last));
562 if not Result
.Positive and then K
= CNU
then
564 -- Negative value, but unsigned expected: take 2's complement
565 -- reciprocical value.
567 Result
.Abs_Value
:= ((not Result
.Abs_Value
) + 1)
569 (Shift_Left
(1, Size_Of_Unsigned_Int
) - 1);
570 Result
.Positive := True;
577 Put_Line
(Standard_Error
, "can't parse decimal value: " & S
);
585 function Spaces
(Count
: Integer) return String is
590 return (1 .. Count
=> ' ');
594 -- Local declarations
598 Tmpl_File_Name
: constant String := Tmpl_Name
& ".i";
599 Asm_File_Name
: constant String := Tmpl_Name
& ".s";
603 Ada_File_Name
: constant String := Unit_Name
& ".ads";
604 C_File_Name
: constant String := Unit_Name
& ".h";
606 Asm_File
: Ada
.Text_IO
.File_Type
;
607 Tmpl_File
: Ada
.Text_IO
.File_Type
;
611 Line
: String (1 .. 256);
613 -- Line being processed
615 Current_Line
: Integer;
616 Current_Info
: Integer;
617 In_Comment
: Boolean;
618 In_Template
: Boolean;
620 -- Start of processing for XOSCons
623 -- Load values from assembly file
625 Open
(Asm_File
, In_File
, Asm_File_Name
);
626 while not End_Of_File
(Asm_File
) loop
627 Get_Line
(Asm_File
, Line
, Last
);
628 if Last
> 2 and then Line
(1 .. 2) = "->" then
629 Parse_Asm_Line
(Line
(3 .. Last
));
635 -- Load C template and output definitions
637 Open
(Tmpl_File
, In_File
, Tmpl_File_Name
);
638 Create
(Ada_OFile
, Out_File
, Ada_File_Name
);
639 Create
(C_OFile
, Out_File
, C_File_Name
);
642 Current_Info
:= Asm_Infos
.First
;
645 while not End_Of_File
(Tmpl_File
) loop
647 Get_Line
(Tmpl_File
, Line
, Last
);
649 if Last
>= 2 and then Line
(1 .. 2) = "# " then
655 while Index
<= Last
and then Line
(Index
) in '0' .. '9' loop
659 if Contains_Template_Name
(Line
(Index
+ 1 .. Last
)) then
660 Current_Line
:= Integer'Value (Line
(3 .. Index
- 1));
664 In_Template
:= False;
668 elsif In_Template
then
670 if Line
(1 .. Last
) = "*/" then
671 Put_Line
(C_OFile
, Line
(1 .. Last
));
674 elsif Last
> 4 and then Line
(1 .. 4) = "@IF " then
676 (Line
(1 .. Last
), True,
677 Tmpl_File
, Ada_Ofile
, C_Ofile
, Current_Line
);
680 Put_Line
(Ada_OFile
, Line
(1 .. Last
));
681 Put_Line
(C_OFile
, Line
(1 .. Last
));
684 elsif Line
(1 .. Last
) = "/*" then
685 Put_Line
(C_OFile
, Line
(1 .. Last
));
688 elsif Asm_Infos
.Table
(Current_Info
).Line_Number
= Current_Line
then
689 if Fixed
.Index
(Line
, "/*NOGEN*/") = 0 then
690 Output_Info
(Lang_Ada
, Ada_OFile
, Current_Info
);
691 Output_Info
(Lang_C
, C_OFile
, Current_Info
);
694 Current_Info
:= Current_Info
+ 1;
697 Current_Line
:= Current_Line
+ 1;
705 Put_Line
("raised " & Ada
.Exceptions
.Exception_Information
(E
));
706 GNAT
.OS_Lib
.OS_Exit
(1);