1
-------------------------------------------------------------------------------
3
-- This file is part of AdaBrowse.
5
-- <STRONG>Copyright (c) 2002 by Thomas Wolf.</STRONG>
7
-- AdaBrowse is free software; you can redistribute it and/or modify it
8
-- under the terms of the GNU General Public License as published by the
9
-- Free Software Foundation; either version 2, or (at your option) any
10
-- later version. AdaBrowse is distributed in the hope that it will be
11
-- useful, but <EM>without any warranty</EM>; without even the implied
12
-- warranty of <EM>merchantability or fitness for a particular purpose.</EM>
13
-- See the GNU General Public License for more details. You should have
14
-- received a copy of the GNU General Public License with this distribution,
15
-- see file "<A HREF="GPL.txt">GPL.txt</A>". If not, write to the Free
16
-- Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21
-- Author:</STRONG><DD>
23
-- <ADDRESS><A HREF="mailto:twolf@acm.org">twolf@acm.org</A></ADDRESS></DL>
26
-- Purpose:</STRONG><DD>
27
-- Parsing and evaluation of expressions; storage of expressions.</DL>
32
-- 18-JUN-2003 TW Initial version.
33
-- 08-JUL-2003 TW Added the "package" predefined predicate and the @
36
-------------------------------------------------------------------------------
40
with Ada.Characters.Handling;
42
with Ada.Unchecked_Deallocation;
46
with GAL.ADT.Hash_Tables;
47
with GAL.Storage.Standard;
48
with GAL.Support.Hashing;
52
pragma Elaborate_All (GAL.ADT.Hash_Tables);
54
package body AD.Expressions is
56
package ACH renames Ada.Characters.Handling;
57
package ASU renames Ada.Strings.Unbounded;
60
new GAL.ADT.Hash_Tables
63
Memory => GAL.Storage.Standard,
64
Hash => GAL.Support.Hashing.Hash_Case_Insensitive,
65
"=" => Util.Strings.Equal);
66
-- Initial_Size is the default (23);
68
Macros : Hashing.Hash_Table;
69
Predef : Hashing.Hash_Table;
72
(Expr : in Expression)
76
return Expr.Ptr = null;
80
(Expr : in Expression)
84
return Expr.Ptr /= null and then Expr.Ptr.all in Bool_Exp'Class;
92
Curr : Natural := Text'First;
93
-- Current position in 'Text'.
99
Ada.Exceptions.Raise_Exception (Parse_Error'Identity, Msg);
104
Expr : out Expression;
110
Expr := Hashing.Retrieve (Predef, Name);
112
when Hashing.Not_Found =>
114
Expr := Hashing.Retrieve (Macros, Name);
116
when Hashing.Container_Empty |
119
Expr := Nil_Expression;
125
(May_Fail : in Boolean := False)
127
Start : constant Natural := Curr;
129
Curr := Util.Strings.Next_Non_Blank (Text (Curr .. Text'Last));
130
if not May_Fail and then Curr = 0 then
132
("Unexpected end of expression: " & Text (Start .. Text'Last));
134
if Curr = 0 then Curr := Text'Last + 1; end if;
139
Left, Right : in Expression;
143
-- Combine expressions, including semantic checks!
147
Left, Right : in Expression;
149
return Expression_Ptr
154
if not Is_Nil (Right) then
155
Ada.Exceptions.Raise_Exception
156
(Program_Error'Identity,
157
"Error in expression parser: binary 'not' operator?!");
159
if Left.Ptr.all not in Bool_Exp'Class then
160
Error ("'not' needs boolean argument: " &
161
Text (Pos .. Text'Last));
163
return new Not_Exp'(Exp with Arg => Left);
164
when Op_Or | Op_Xor | Op_And =>
165
if Left.Ptr.all not in Bool_Exp'Class or else
166
Right.Ptr.all not in Bool_Exp'Class
168
Error ("boolean operator with string argument: " &
169
Text (Pos .. Text'Last));
173
new Or_Exp'(Exp with Left => Left, Right => Right);
174
elsif Op = Op_Xor then
176
new Xor_Exp'(Exp with Left => Left, Right => Right);
179
new And_Exp'(Exp with Left => Left, Right => Right);
181
when Op_Eq | Op_Neq =>
182
if Left.Ptr.all in Bool_Exp'Class xor
183
Right.Ptr.all in Bool_Exp'Class
185
Error ("equality operator with mixed arguments: " &
186
Text (Pos .. Text'Last));
189
Expr : constant Expression_Ptr :=
190
new Eq_Exp'(Exp with Left => Left, Right => Right);
196
Arg => (Ada.Finalization.Controlled with
202
when Op_Concat | Op_Prefix =>
203
if Left.Ptr.all not in String_Exp'Class or else
204
Right.Ptr.all not in String_Exp'Class
206
Error ("'&' and '@' need string arguments: " &
207
Text (Pos .. Text'Last));
209
if Op = Op_Concat then
211
new Concat_Exp'(Exp with Left => Left, Right => Right);
214
new Prefix_Exp'(Exp with Left => Left, Right => Right);
217
Ada.Exceptions.Raise_Exception
218
(Program_Error'Identity,
219
"Error in expression parser (Op_None in Create)");
226
(Ada.Finalization.Controlled with
227
Ptr => Create (Op, Left, Right, Pos));
230
Precedence : constant array (Operator) of Natural :=
231
(Op_Not | Op_Concat => 1,
238
Lowest_Precedence : constant Natural := Precedence (Op_None);
240
Last_Op : Operator := Op_None;
241
-- An operator precedence parser needs a one-token look-ahead. We could
242
-- have implemented this by setting 'Curr' at the beginning of the last
243
-- operator and later rescanning, but that would incur a higher
246
Last_Pos : Natural := 0;
247
-- But we still keep the position of the last operator for error
248
-- reporting purposes!
250
function Parse_Operator
253
-- Binary operators only!
255
Skip (May_Fail => True);
256
-- If nothing follows, we've hit the end of the expression.
257
if Curr > Text'Last then return Op_None; end if;
258
if Text (Curr) = ')' or else Text (Curr) = ';' then
260
elsif Text (Curr) = '=' then
263
elsif Text (Curr) = '&' then
266
elsif Text (Curr) = '@' then
269
elsif Curr < Text'Last and then
270
Text (Curr .. Curr + 1) = "/=" then
277
I : constant Natural :=
278
Util.Strings.Identifier (Text (Curr .. Text'Last));
282
Error ("Operator missing: " &
283
Text (Curr .. Text'Last));
285
Get (Text (Curr .. I), Expr, Found);
287
Error ("Unknown function """ & Text (Curr .. I) & '"');
289
if Expr.Ptr.all in Predefined'Class then
290
Op := Predefined (Expr.Ptr.all).Op;
292
Error ("Operator missing: " &
293
Text (Curr .. Text'Last));
297
("'not' not allowed here: " &
298
Text (Curr .. Text'Last));
308
(Max_Op : in Natural := Lowest_Precedence)
316
function Parse_Factor
323
if Text (Curr) = '(' then
325
Start : constant Natural := Curr;
329
Skip (May_Fail => True);
330
-- We allow failing because we want to emit a more
331
-- meaningful error message.
332
if Curr > Text'Last or else Text (Curr) /= ')' then
333
Error ("Missing ')': " & Text (Start .. Curr - 1));
337
elsif Text (Curr) = '"' then
340
Start : constant Natural := Curr;
341
I : constant Natural :=
342
Util.Strings.Skip_String
343
(Text (Curr .. Text'Last), '"', '"');
347
("String not closed: " & Text (Curr .. Text'Last));
351
(Ada.Finalization.Controlled with
352
Ptr => new Exp'Class'
357
ASU.To_Unbounded_String
358
(Util.Strings.Unquote
359
(Text (Start + 1 .. I - 1),
365
I : constant Natural :=
366
Util.Strings.Identifier (Text (Curr .. Text'Last));
370
("Identifier expected: " & Text (Curr .. Text'Last));
372
Get (Text (Curr .. I), Expr, Found);
374
Error ("Unknown function """ & Text (Curr .. I) & '"');
376
if Expr.Ptr.all in Predefined'Class then
378
("Unexpected operator: " & Text (Curr .. Text'Last));
390
Start : constant Natural := Curr;
391
I : constant Natural :=
392
Util.Strings.Identifier (Text (Curr .. Text'Last));
394
if I = Curr + 2 and then
395
Util.Strings.To_Lower (Text (Curr .. I)) = "not"
399
(Op_Not, Parse_Factor, Nil_Expression, Start);
410
-- This is an operator precedence parser.
412
while Curr <= Text'Last loop
413
if Last_Op = Op_None then
415
Op := Parse_Operator;
417
-- OK if text exhausted, or at a probable expression end.
419
Curr > Text'Last or else
420
Text (Curr) = ')' or else
422
Error ("Operator expected: " & Text (Start .. Text'Last));
428
if Precedence (Op) >= Max_Op then
436
(Op, Expr, Parse_Expr (Max_Op => Precedence (Op)), Start);
445
if Last_Op /= Op_None then
446
Error ("Spurious operator at end of expression: " &
447
Text (Last_Pos .. Text'Last));
449
if Curr <= Text'Last then
450
-- We allow a semicolon at the end.
451
if Text (Curr) = ';' then Curr := Curr + 1; end if;
452
Skip (May_Fail => True);
453
-- If there's still something following, we have an error.
454
if Curr <= Text'Last then
456
("Garbage following expression: " & Text (Curr .. Text'Last));
462
procedure Define_Macro
464
Expr : in Expression;
465
Redefined : out Boolean)
468
if Hashing.Contains (Predef, Name) then
469
Ada.Exceptions.Raise_Exception
470
(Parse_Error'Identity,
471
"Predefined functions and operators cannot be redefined!");
473
Redefined := Hashing.Contains (Macros, Name);
474
Hashing.Replace (Macros, Name, Expr);
478
(Expr : in Expression;
479
Argument : in Asis.Element)
483
return Eval (Bool_Exp'Class (Expr.Ptr.all), Argument);
488
Argument : in Asis.Element)
492
return E.P /= null and then E.P (Argument);
497
Argument : in Asis.Element)
500
pragma Warnings (Off, Argument); -- silence -gnatwa
507
Argument : in Asis.Element)
511
return Eval (Bool_Exp'Class (E.Left.Ptr.all), Argument)
513
Eval (Bool_Exp'Class (E.Right.Ptr.all), Argument);
518
Argument : in Asis.Element)
522
return Eval (Bool_Exp'Class (E.Left.Ptr.all), Argument)
524
Eval (Bool_Exp'Class (E.Right.Ptr.all), Argument);
529
Argument : in Asis.Element)
533
return Eval (Bool_Exp'Class (E.Left.Ptr.all), Argument)
535
Eval (Bool_Exp'Class (E.Right.Ptr.all), Argument);
540
Argument : in Asis.Element)
544
if E.Left.Ptr.all in Bool_Exp'Class then
545
return Eval (Bool_Exp'Class (E.Left.Ptr.all), Argument)
547
Eval (Bool_Exp'Class (E.Right.Ptr.all), Argument);
549
return Util.Strings.Equal
550
(Eval (String_Exp'Class (E.Left.Ptr.all), Argument),
551
Eval (String_Exp'Class (E.Right.Ptr.all), Argument));
557
Argument : in Asis.Element)
561
-- We know we have two string expressions!
563
Left : constant String :=
564
Util.Strings.To_Lower
565
(Eval (String_Exp'Class (E.Left.Ptr.all), Argument));
566
Right : constant String :=
567
Util.Strings.To_Lower
568
(Eval (String_Exp'Class (E.Right.Ptr.all), Argument));
570
return Util.Strings.Is_Prefix (Left, Right);
576
Argument : in Asis.Element)
580
return not Eval (Bool_Exp'Class (E.Arg.Ptr.all), Argument);
583
----------------------------------------------------------------------------
586
(E : in String_Terminal;
587
Argument : in Asis.Element)
591
if E.P = null then return ""; end if;
592
return ACH.To_String (E.P (Argument));
597
Argument : in Asis.Element)
600
pragma Warnings (Off, Argument); -- silence -gnatwa
602
return ASU.To_String (E.Val);
607
Argument : in Asis.Element)
611
return Eval (String_Exp'Class (E.Left.Ptr.all), Argument) &
612
Eval (String_Exp'Class (E.Right.Ptr.all), Argument);
615
----------------------------------------------------------------------------
618
(E : in out Expression)
621
if E.Ptr /= null then
622
E.Ptr.Ref_Count := E.Ptr.Ref_Count + 1;
627
(E : in out Expression)
631
new Ada.Unchecked_Deallocation (Exp'Class, Expression_Ptr);
634
if E.Ptr /= null then
635
E.Ptr.Ref_Count := E.Ptr.Ref_Count - 1;
636
if E.Ptr.Ref_Count = 0 then
642
----------------------------------------------------------------------------
645
Hashing.Set_Resize (Macros, 0.75);
646
Hashing.Set_Resize (Predef, 0.75);
648
Linear_Growth : GAL.Support.Hashing.Linear_Growth_Policy (20);
650
Hashing.Set_Growth_Policy (Macros, Linear_Growth);
651
Hashing.Set_Growth_Policy (Predef, Linear_Growth);
656
procedure Add_Expression
661
(Ada.Finalization.Controlled with Ptr => new Exp'Class'(Expr));
663
Hashing.Insert (Predef, Name, E);
669
("private", Terminal'(Exp with P => Is_Private'Access));
671
("separate", Terminal'(Exp with P => Is_Separate'Access));
673
("unit", Terminal'(Exp with P => Is_Unit'Access));
675
("package", Terminal'(Exp with P => Is_Package'Access));
677
("child", Terminal'(Exp with P => Is_Child'Access));
679
("constant", Terminal'(Exp with P => Is_Constant'Access));
681
("pragma", Terminal'(Exp with P => Is_Pragma'Access));
683
("representation", Terminal'(Exp with P => Is_Clause'Access));
685
("variable", Terminal'(Exp with P => Is_Variable'Access));
687
("type", Terminal'(Exp with P => Is_Type'Access));
689
("subtype", Terminal'(Exp with P => Is_Subtype'Access));
691
("procedure", Terminal'(Exp with P => Is_Procedure'Access));
693
("function", Terminal'(Exp with P => Is_Function'Access));
695
("subprogram", Terminal'(Exp with P => Is_Subprogram'Access));
697
("entry", Terminal'(Exp with P => Is_Entry'Access));
699
("elementary", Terminal'(Exp with P => Is_Elementary'Access));
701
("scalar", Terminal'(Exp with P => Is_Scalar'Access));
703
("discrete", Terminal'(Exp with P => Is_Discrete'Access));
705
("enum", Terminal'(Exp with P => Is_Enumeration'Access));
707
("integral", Terminal'(Exp with P => Is_Integral'Access));
709
("signed", Terminal'(Exp with P => Is_Signed'Access));
711
("modular", Terminal'(Exp with P => Is_Modular'Access));
713
("real", Terminal'(Exp with P => Is_Real'Access));
715
("float", Terminal'(Exp with P => Is_Float'Access));
717
("fixed", Terminal'(Exp with P => Is_Fixed'Access));
719
("ordinary_fixed", Terminal'(Exp with P => Is_Ordinary_Fixed'Access));
721
("decimal_fixed", Terminal'(Exp with P => Is_Decimal_Fixed'Access));
723
("numeric", Terminal'(Exp with P => Is_Numeric'Access));
725
("access", Terminal'(Exp with P => Is_Access'Access));
727
("access_object", Terminal'(Exp with P => Is_Access_To_Object'Access));
729
("access_subprogram",
730
Terminal'(Exp with P => Is_Access_To_Subprogram'Access));
732
("composite", Terminal'(Exp with P => Is_Composite'Access));
734
("array", Terminal'(Exp with P => Is_Array'Access));
736
("record", Terminal'(Exp with P => Is_Record'Access));
738
("tagged", Terminal'(Exp with P => Is_Tagged'Access));
740
("task", Terminal'(Exp with P => Is_Task'Access));
742
("protected", Terminal'(Exp with P => Is_Protected'Access));
744
("limited", Terminal'(Exp with P => Is_Limited'Access));
746
("class_wide", Terminal'(Exp with P => Is_Class_Wide'Access));
748
("controlled", Terminal'(Exp with P => Is_Controlled'Access));
750
("private_type", Terminal'(Exp with P => Is_Private_Type'Access));
752
("incomplete", Terminal'(Exp with P => Is_Incomplete'Access));
754
("aliased", Terminal'(Exp with P => Is_Aliased'Access));
756
("exception", Terminal'(Exp with P => Is_Exception'Access));
758
("renaming", Terminal'(Exp with P => Is_Renaming'Access));
760
("generic", Terminal'(Exp with P => Is_Generic'Access));
762
("formal", Terminal'(Exp with P => Is_Generic_Formal'Access));
764
("instance", Terminal'(Exp with P => Is_Instance'Access));
766
("abstract", Terminal'(Exp with P => Is_Abstract'Access));
768
("full_name", String_Terminal'(Exp with P => Unique_Name'Access));
770
("name", String_Terminal'(Exp with P => Simple_Name'Access));
772
("true", Value'(Exp with Val => True));
774
("false", Value'(Exp with Val => False));
775
-- Also insert the keywords (facilitates checking whether someone had
776
-- the glorious idea to name a macro "not").
778
("not", Predefined'(Exp with Op => Op_Not));
780
("and", Predefined'(Exp with Op => Op_And));
782
("or", Predefined'(Exp with Op => Op_Or));
784
("xor", Predefined'(Exp with Op => Op_Xor));