⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

{{implementation|SNUSP}}{{collection|RCSNUSP}} {{works with|Ada 2005}}

Interpreter for Modular [[SNUSP]].

First need some kind of storage for memory. Using Ada.Containers.Ordered_Maps for this. Only storing memory cells that have value /= 0, to save memory.

memory.ads:

with Ada.Finalization;
generic
   type Byte is mod <>;
   type Key_Type is private;
   with function "<" (Left, Right : in Key_Type) return Boolean is <>;
package Memory is

   type Memory is new Ada.Finalization.Limited_Controlled with private;

   function Get_Value (Storage : in Memory; Key : in Key_Type) return Byte;

   procedure Set_Value
     (Storage : in out Memory;
      Key     : in Key_Type;
      Value   : in Byte);

   procedure Increment (Storage : in out Memory; Key : in Key_Type);

   procedure Decrement (Storage : in out Memory; Key : in Key_Type);

private

   type Memory_Type;

   type Memory_Access is access Memory_Type;

   type Memory is new Ada.Finalization.Limited_Controlled with record
      Cells : Memory_Access;
   end record;

   procedure Initialize (Storage : in out Memory);
   procedure Finalize (Storage : in out Memory);

end Memory;

memory.adb:

with Ada.Containers.Ordered_Maps;
with Ada.Unchecked_Deallocation;
package body Memory is

   package Memory_Maps is new Ada.Containers.Ordered_Maps (
      Key_Type => Key_Type,
      Element_Type => Byte);
   use type Memory_Maps.Cursor;

   type Memory_Type is record
      Map : Memory_Maps.Map;
   end record;

   function Get_Value (Storage : in Memory; Key : in Key_Type) return Byte is
      Result   : Byte               := 0;
      Position : Memory_Maps.Cursor :=
         Memory_Maps.Find (Storage.Cells.Map, Key);
   begin
      if Position /= Memory_Maps.No_Element then
         Result := Memory_Maps.Element (Position);
      end if;
      return Result;
   end Get_Value;

   procedure Set_Value
     (Storage : in out Memory;
      Key     : in Key_Type;
      Value   : in Byte)
   is
      Position : Memory_Maps.Cursor :=
         Memory_Maps.Find (Storage.Cells.Map, Key);
   begin
      if Position = Memory_Maps.No_Element then
         if Value /= 0 then
            Memory_Maps.Insert (Storage.Cells.Map, Key, Value);
         end if;
      else
         if Value = 0 then
            Memory_Maps.Delete (Storage.Cells.Map, Key);
         else
            Memory_Maps.Replace (Storage.Cells.Map, Key, Value);
         end if;
      end if;
   end Set_Value;

   procedure Increment (Storage : in out Memory; Key : in Key_Type) is
      Value : Byte := Get_Value (Storage, Key) + 1;
   begin
      Set_Value (Storage, Key, Value);
   end Increment;

   procedure Decrement (Storage : in out Memory; Key : in Key_Type) is
      Value : Byte := Get_Value (Storage, Key) - 1;
   begin
      Set_Value (Storage, Key, Value);
   end Decrement;

   procedure Initialize (Storage : in out Memory) is
   begin
      Storage.Cells := new Memory_Type;
   end Initialize;

   procedure Finalize (Storage : in out Memory) is
      procedure Free is new Ada.Unchecked_Deallocation (
         Object => Memory_Type,
         Name => Memory_Access);
   begin
      Memory_Maps.Clear (Storage.Cells.Map);
      Free (Storage.Cells);
   end Finalize;

end Memory;

Next is a Machine for interpreting SNUSP code.

snusp.ads:

with Ada.Containers.Vectors;
with Ada.Strings.Fixed;
with Ada.Text_IO;
with Memory;
package SNUSP is

   type SNUSP_Machine is limited private;

   type Code is array (Positive range <>, Positive range <>) of Character;

   procedure Run (Machine : in out SNUSP_Machine; Input : in Code);

private

   type Byte is mod 2 ** 8;
   package Byte_Memory is new Memory (Byte => Byte, Key_Type => Positive);

   type Direction is (Up, Down, Left, Right);
   type Code_Pointer is record
      X, Y : Positive  := 1;
      Dir  : Direction := Right;
   end record;
   package Call_Stacks is new Ada.Containers.Vectors (
      Index_Type => Positive,
      Element_Type => Code_Pointer);

   type SNUSP_Machine is limited record
      Main_Memory            : Byte_Memory.Memory;
      Current_Memory_Pointer : Positive := 1;
      Call_Stack             : Call_Stacks.Vector;
      IP                     : Code_Pointer;
   end record;

end SNUSP;

snusp.adb:

package body SNUSP is

   procedure Move_IP (IP : in out Code_Pointer) is
   begin
      case IP.Dir is
         when Up =>
            IP.Y := IP.Y - 1;
         when Down =>
            IP.Y := IP.Y + 1;
         when Left =>
            IP.X := IP.X - 1;
         when Right =>
            IP.X := IP.X + 1;
      end case;
   end Move_IP;

   procedure Process_Next_Instruction
     (Machine : in out SNUSP_Machine;
      Input   : in Code)
   is
      Instruction : Character := Input (Machine.IP.Y, Machine.IP.X);
   begin
      case Instruction is
      when '>' =>
         Machine.Current_Memory_Pointer := Machine.Current_Memory_Pointer +
                                           1;
      when '<' =>
         Machine.Current_Memory_Pointer := Machine.Current_Memory_Pointer -
                                           1;
      when '+' =>
         Byte_Memory.Increment
           (Machine.Main_Memory,
            Machine.Current_Memory_Pointer);
      when '-' =>
         Byte_Memory.Decrement
           (Machine.Main_Memory,
            Machine.Current_Memory_Pointer);
      when ',' =>
         declare
            User_Input : Character;
            Value      : Byte;
         begin
            Ada.Text_IO.Get_Immediate (User_Input);
            Value := Character'Pos (User_Input);
            Byte_Memory.Set_Value
              (Machine.Main_Memory,
               Machine.Current_Memory_Pointer,
               Value);
         end;
      when '.' =>
         declare
            Value  : Byte      :=
               Byte_Memory.Get_Value
                 (Machine.Main_Memory,
                  Machine.Current_Memory_Pointer);
            Output : Character := Character'Val (Value);
         begin
            Ada.Text_IO.Put (Output);
         end;
      when '/' =>
         case Machine.IP.Dir is
            when Up =>
               Machine.IP.Dir := Right;
            when Down =>
               Machine.IP.Dir := Left;
            when Left =>
               Machine.IP.Dir := Down;
            when Right =>
               Machine.IP.Dir := Up;
         end case;
      when '\' =>
         case Machine.IP.Dir is
            when Up =>
               Machine.IP.Dir := Left;
            when Down =>
               Machine.IP.Dir := Right;
            when Left =>
               Machine.IP.Dir := Up;
            when Right =>
               Machine.IP.Dir := Down;
         end case;
      when '!' =>
         Move_IP (Machine.IP);
      when '?' =>
         if Byte_Memory.Get_Value
               (Machine.Main_Memory,
                Machine.Current_Memory_Pointer) =
            0
         then
            Move_IP (Machine.IP);
         end if;
      when '@' =>
         Call_Stacks.Append (Machine.Call_Stack, Machine.IP);
      when '#' =>
         Machine.IP := Call_Stacks.Last_Element (Machine.Call_Stack);
         Call_Stacks.Delete_Last (Machine.Call_Stack);
         Move_IP (Machine.IP);
      when others =>
         null;
      end case;
      Move_IP (Machine.IP);
   end Process_Next_Instruction;

   procedure Run (Machine : in out SNUSP_Machine; Input : in Code) is
   begin
      -- find begin ($)
      declare
         Start_Found : Boolean := False;
      begin
         for Row in Input'Range (1) loop
            for Col in Input'Range (2) loop
               if Input (Row, Col) = '$' then
                  if Start_Found then
                     raise Program_Error;
                  end if;
                  Start_Found  := True;
                  Machine.IP.Y := Row;
                  Machine.IP.X := Col;
               end if;
            end loop;
         end loop;
      end;

      loop
         Process_Next_Instruction (Machine, Input);
      end loop;
   exception
      when Constraint_Error =>
         null;
   end Run;

end SNUSP;

Sample usage:

main.adb:

with Ada.Text_IO;
with SNUSP;
procedure Main is

   Test_Code : SNUSP.Code :=
      (1 => "Example taken from RosettaCode.org                                       ",
       2 => "                                                                         ",
       3 => "$@\G.@\o.o.@\d.--b.@\y.@\e.>@\comma.@\.<-@\W.+@\o.+++r.------l.@\d.>+.! #",
       4 => "  |   |     \@------|#  |    \@@+@@++|+++#-    \\               -        ",
       5 => "  |   \@@@@=+++++#  |   \===--------!\===!\-----|-------#-------/        ",
       6 => "  \@@+@@@+++++#     \!#+++++++++++++++++++++++#!/                        ");

   My_SNUSP_Machine : SNUSP.SNUSP_Machine;

begin

   SNUSP.Run (My_SNUSP_Machine, Test_Code);

end Main;

Output:

Goodbye, World!

[[Category:Ada]]