From: Shark8 <onewingedshark@gmail.com>
Subject: Re: Little tutorial about streams
Date: Fri, 4 Mar 2011 17:08:28 -0800 (PST)
Date: 2011-03-04T17:08:28-08:00 [thread overview]
Message-ID: <3f98da3c-cbe0-4d62-9420-8ad1bd467752@d26g2000prn.googlegroups.com> (raw)
In-Reply-To: fba7e41d-288d-419a-ae55-86c2fd954903@glegroupsg2000goo.googlegroups.com
I was debating on whether or not to submit to you a little 'toy' LISP
interpreter I've started. I've only got the reading and writing here,
no eval or special-forms like 'if', but since they're Stream-based it
might be appropriate.
---------------------------------
-- Lisp Interpreter in Ada.
-- By Joey Fish
---------------
-- LISP.ADS --
---------------
With
Ada.Containers.Indefinite_Vectors,
Ada.Streams;
Package LISP is
-----------------------------------------------------------------------
-- In LISP a List is defined as either a single element or an --
-- element followed by a lisr; as a consequence of this
definition --
-- there is no such thing as an "Empty List." --
-----------------------------------------------------------------------
Type List is Private;
-- ToDo: Add primitive functions for list construction/
manipulation.
-- ToDo: Add primitive functions for determining if it is an
executable list.
Private
-- Alias Ads.Streams.Root_Stream_Type for ease of use.
SubType Stream_Root is Ada.Streams.Root_Stream_Type;
-- Element_Type describes the possible types of an Atom
-- Note: Empty_Type is the NULL-element.
Type Element_Type is
( Empty_Type, Boolean_Type, Integer_Type, Real_Type,
String_Type );
-- Type Atom is a record containing the type and value of an
element.
Type Atom( Element : Element_Type:= Empty_Type ) is record
Case Element is
When Empty_Type => Null;
When Boolean_Type => Boolean_Value : Boolean:= False;
When Integer_Type => Integer_Value : Integer:= 0;
When Real_Type => Real_Value : Float := 0.0;
When String_Type => Is_Name : Boolean:= False;
String_Value : Not Null Access String;
End Case;
end record;
Procedure Write_Atom( Stream : Not Null Access Stream_Root'Class;
Item : In Atom
);
For Atom'Write Use Write_Atom;
Type Atom_Array is Array (Positive Range <>) of Atom;
Type Elementry_List( Terminal : Boolean:= True ) is record
Head : Atom:= ( Element => Empty_Type );
Case Terminal is
When False => Tail : Not Null Access Elementry_List;
When True => Null;
End Case;
end record;
Procedure Append( Head : In Out Elementry_List; Tail : In
Elementry_List );
Function Convert( List : Elementry_List ) Return Atom_Array;
Function Convert( List : Atom_Array ) Return Elementry_List;
Procedure Write_EList( Stream : Not Null Access Stream_Root'Class;
Item : In Elementry_List
);
Procedure Read_EList( Stream : Not Null Access Stream_Root'Class;
Item : Out Elementry_List
);
For Elementry_List'Read Use Read_EList;
For Elementry_List'Write Use Write_EList;
Type List_Element_Type is ( Atom_Element, EList_Type,
Recursive_List );
Type List_Element( Element : List_Element_Type:= Atom_Element ) is
record
Case Element is
When Atom_Element => Atom_Data : Atom;
When EList_Type => EList_Data : Elementry_List;
When Recursive_List => RList_Data : Not Null Access List;
end case;
end record;
Function As_List( Input : in List_Element ) Return List;
Package Elements is New Ada.Containers.Indefinite_Vectors
( Index_Type => Positive, Element_Type => List_Element );
Type List is Tagged record
Data : Elements.Vector:= Elements.Empty_Vector;
end record;
Procedure Write_List( Stream : Not Null Access Stream_Root'Class;
Item : In List
);
Procedure Read_List( Stream : Not Null Access Stream_Root'Class;
Item : Out List
);
For List'Read Use Read_List;
For List'Write Use Write_List;
End LISP;
---------------
-- LISP.ADB --
---------------
With
Ada.Strings.Fixed;
Package Body LISP is
FALSE_Text : Constant String:= Boolean'Image( False );
TRUE_Text : Constant String:= Boolean'Image( True );
NULL_Text : Constant String:= "NULL";
Function Trim_Image( Input : In Float ) Return String is
Use Ada.Strings, Ada.Strings.Fixed;
Begin
Return Result: String:=
Trim( Side => Left, Source => Float'Image( Input ) );
End Trim_Image;
------------------
-- WRITE_ATOM --
------------------
Procedure Write_Atom( Stream : Not Null Access Stream_Root'Class;
Item : In Atom ) is
Text : Access String;
begin
Case Item.Element is
When Empty_Type =>
Text:= New String'( NULL_Text );
When Boolean_Type =>
Text:= New String'( Boolean'Image(Item.Boolean_Value) );
When Integer_Type =>
Text:= New String'( Integer'Image(Item.Integer_Value) );
When Real_Type =>
Text:= New String'( Float'Image( Item.Real_Value ) );
When String_Type =>
If Not Item.Is_Name then
Text:= New String'( '"' & Item.String_Value.All & '"' );
else
Text:= New String'( Item.String_Value.All );
end if;
End Case;
String'Write( Stream, Text.All );
end Write_Atom;
--------------
-- Append --
--------------
Procedure Append( Head : In Out Elementry_List; Tail : In
Elementry_List ) is
begin
If Head.Terminal then
Declare
Result : Elementry_List:= Elementry_List'( Terminal =>
False,
Head => Head.Head, Tail => New Elementry_List'(Tail) );
For Result'Address Use Head'Address;
Begin -- Here the initialization of Result changes the value
Null; -- of Head because they are overlaid; further, we
are
End; -- guarenteed that the sizes are both the same because
else -- Elementry_List is a simple varient record and cannot
Declare -- be extended because it is not a tagged-type.
This : Access Elementry_List:= Head.Tail;
Begin
While Not This.Terminal loop -- If Head is not terminal
then
This:= This.Tail; -- we must traverse the list
end loop; -- until the end to append Tail.
Append( This.All, Tail );
End;
end if;
end Append;
Function Convert( List : Elementry_List ) Return Atom_Array is
Function Get_Element( Item : Elementry_List ) Return Atom_Array
is
begin
if Item.Terminal then
Return Result : Atom_Array:= ( 1 => Item.Head );
else
Return Result : Atom_Array:= Atom_Array'( 1 => Item.Head )
& Get_Element( Item.Tail.All );
end if;
end Get_Element;
begin
Return Get_Element( List );
end Convert;
Function Create( Input : Atom ) Return Elementry_List is
begin
Return Result: Elementry_List:= ( Terminal => True, Head =>
Input );
end;
Function Convert( List : Atom_Array ) Return Elementry_List is
begin
if List'Length = 0 then
Raise Program_Error;
end if;
Declare
Working : Elementry_List:= Create( Input => List(List'First) );
Begin
For Index in Positive'Succ(List'First)..List'Last loop
Append( Head => Working, Tail => Create( List(Index) ) );
end loop;
Return Working;
End;
end Convert;
---------------------------------------------------
-- Elementry_List's 'Read & 'Write Attributes --
---------------------------------------------------
Procedure Write_EList( Stream : Not Null Access Stream_Root'Class;
Item : In Elementry_List
) is
begin
Character'Write( Stream, '(' ); -- When saving to a Stream
every
Declare -- elementry-list begins with an
This : Elementry_List:= Item; -- opening parenthisis,
followed
Begin -- by list of Atoms (whitespace
List: -- separated), terminated by a
loop -- closing parenthisis.
Atom'Write( Stream, This.Head );
Exit List When This.Terminal;
This:= This.Tail.All;
Character'Write( Stream, ',' );
end loop List;
Character'Write( Stream, ')' );
End;
end Write_EList;
Procedure Read_EList( Stream : Not Null Access Stream_Root'Class;
Item : Out Elementry_List
) is
Function Parse_Atom( S : String ) Return Atom is
Function Get_Type_Indicator(Input : String) Return
Element_Type is
SubType Digit is Character Range '0'..'9';
begin
Case Input(Input'First) is -- A floating-point numeric is
When Digit => -- distinguished from an integer
For Index in Input'Range loop -- by the decimal
point or the
Case Input(Index) is -- exponent notation.
When 'e' | 'E' | '.' => Return Real_Type;
When Others => null;
End case;
end loop;
Return Integer_Type;
When Others =>
if Input = NULL_Text then -- NULL, TRUE, & FALSE are
texts
Return Empty_Type; -- which are not String_Type.
elsif Input = TRUE_Text or Input = FALSE_Text then
Return Boolean_Type;
else -- Everything else, however, is
Return String_Type; -- either a String_Type or a
end if; -- name. Strings start w/ '"'.
end case;
end Get_Type_Indicator;
Use Ada.Strings, Ada.Strings.Fixed; -- Parse_Atom
Input : String:= Ada.Strings.Fixed.Trim( S, Both ); -- starts
here.
begin
Return Result : Atom(Get_Type_Indicator(Input)) do
Case Result.Element is
When Empty_Type => Null;
When String_Type =>
Result.Is_Name:= Input(Input'First) = '"';
if Not Result.Is_Name then
Result.String_Value:= New String'(
Input(Positive'Succ(Input'First)..Positive'Pred(Input'Last))
);
else
Result.String_Value:= New String'(Input);
end if;
When Integer_Type => Result.Integer_Value:=
Integer'Value(Input);
When Real_Type => Result.Real_Value:= Float'Value(Input);
When Boolean_Type => Result.Boolean_Value:= Input =
TRUE_Text;
End case;
End Return;
end Parse_Atom;
WhiteSpace : Constant Array(Character) Of Boolean:=
( ASCII.VT | ASCII.HT | ASCII.LF | ASCII.CR | ' ' => True,
Others => False );
Type Index_Vector is Array (Positive Range <>) of Positive;
Seperators : Not Null Access Index_Vector:=
New Index_Vector'( 2..1 => 1 );
Working : Not Null Access String:= New String'( "" );
C : Character;
Result : Elementry_List;
In_String : Boolean:= False;
Is_Terminated : Boolean:= False;
begin
Loop
Character'Read( Stream, C );
Case C is
When '"' => In_String:= Not In_String;
Is_Terminated:= False;
When ')' => Exit When Not In_String;
When Others =>
If WhiteSpace(C) then
if Is_Terminated then
GoTo Bypass;
else
Is_Terminated:= True;
end if;
else
Is_Terminated:= False;
end if;
End Case;
Working:= New String'( Working.All & C ); -- We update "working."
if WhiteSpace(C) then -- and delimit with whitespace.
Seperators:= New Index_Vector'( Seperators.All & Working'Last );
end if;
<<Bypass>>
Null;
End Loop;
-- We simulate a terminating space by appending one more
-- than the last index to Seperators.
Seperators:= New Index_Vector'(Seperators.All & (Working'Last+1));
Declare
-- We use one more than Working'First to ignore the '(' that starts
-- this list, just like we terminated the string just before
-- copying the trailing ')' into Working.
Start : Positive:= Working'First+1;
Stop : Positive;
Begin
for Index in Seperators'Range loop -- Here is where we create an
Stop:= Seperators( Index )-1; -- atom from every interval we
Append(Result, -- recorded in "seperators".
(Terminal => True, Head => Parse_Atom(Working(Start..Stop)))
);
Start:= Seperators( Index )+1;
end loop;
End;
-- And we return everything we
Item:= Result.Tail.All; -- previously appended to result.
end Read_EList;
---------------
-- As_List --
---------------
Function As_List( Input : in List_Element ) Return List is
Begin
Return Result : List do
Case Input.Element is
When Atom_Element => Result.Data.Append( New_Item =>
Input );
When Recursive_List => Result:= Input.RList_Data.All;
When EList_Type =>
Declare
Data : Elementry_List:= Input.EList_Data;
Begin
loop
Result.Data.Append( New_Item => (Atom_Element, Data.Head) );
Exit When Data.Terminal;
Data:= Data.Tail.All;
end loop;
End;
End Case;
End Return;
End As_List;
Procedure Write_List( Stream : Not Null Access Stream_Root'Class;
Item : In List
) is
SubType ELIST is Elementry_List;
begin
Character'Write( Stream, '(' );
Declare
Procedure Process_Data( Position : Elements.Cursor ) is
Item : List_Element Renames Elements.Element( Position );
begin
Case Item.Element is
When Atom_Element => Atom'Write( Stream, Item.Atom_Data );
When EList_Type => ELIST'Write(Stream, Item.EList_Data);
When Recursive_List => List'Write( Stream,
Item.RList_Data.All );
end case;
end Process_Data;
Begin
Item.Data.Iterate( Process_Data'Access );
End;
Character'Write( Stream, ')' );
end Write_List;
Procedure Read_List( Stream : Not Null Access Stream_Root'Class;
Item : Out List
) is
begin
null;
end Read_List;
End LISP;
next prev parent reply other threads:[~2011-03-05 1:08 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-28 17:00 Ann: Little tutorial about streams mockturtle
2011-02-28 20:32 ` Ludovic Brenta
2011-03-01 3:48 ` Randy Brukardt
2011-03-01 9:54 ` AdaMagica
2011-03-01 10:16 ` Dmitry A. Kazakov
2011-03-01 13:56 ` Simon Wright
2011-03-01 14:38 ` Dmitry A. Kazakov
2011-03-04 20:58 ` mockturtle
2011-03-05 1:08 ` Shark8 [this message]
2011-03-05 1:35 ` Randy Brukardt
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox