1
votes

I am trying to read from a text file, slice each line into pieces separated by spaces, put into circular single linked list, perform insert/delete operations and print results. The code compiles but upon running give me "raised GNAT.STRING_SPLIT.INDEX_ERROR : g-arrspl.adb:335 instantiated at g-strspl.ads:39".

I looked at the documentation for g-arrspl.adb line 335 which reads:

elsif Index > S.D.N_Slice then raise Index_Error;

From this I am guessing that I have somehow gone over an index limit somewhere but I don't know how or where.

This is my code so far

with Ada.Text_IO;
use Ada.Text_IO;
with Ada.Float_Text_IO;
use Ada.Float_Text_IO;
with GNAT.String_Split;
use GNAT.String_Split;

procedure Main is
   package IIO is new Ada.Text_IO.Integer_IO(Integer);
   use IIO;
   type DepartmentType is(Sales, Crew, IT, Media, Accounting, noDept);
   type TitleType is(manager, sales_person, designer, programmer, software_engineer, spokes_person, pilot, copilot, scientist, mission_specialist, notitle);
   type NameType is(Bob, Mary, Sally, David, Marty, Vallerie, Sam, Joe, noName);
   type CodeType is(IL, IR, DL, DR, PA, PD, DD);
   
   type fileContent is record
      code: CodeType;
      dept: DepartmentType;
      name: NameType;
      title: TitleType;
      id: Integer := 0;
      payrate: Float := 0.00;
   end record;

   type empNode;
   type empPt is access empNode;
   type empNode is record
      deptName: DepartmentType := noDept;
      empName: NameType := noName;
      title: TitleType := notitle;
      id: Integer := 0;
      payrate: Float := 0.00;
      next: empPt := null;
   end record;

   type departmentNode;
   type deptPt is access departmentNode;
   type departmentNode is record
      deptName: DepartmentType;
      empName: NameType;
      num: Integer := 0;
      next: empPt := null;
   end record;
   
   
   fileOut: File_Type;
   fileLine: fileContent;
   limit: Positive;
   
   package FloatIO is new Ada.Text_IO.Float_IO(Float);
   use FloatIO;
   package DeptTypeIO is new Ada.Text_IO.Enumeration_IO(DepartmentType);
   use DeptTypeIO;
   package TitleTypeIO is new Ada.Text_IO.Enumeration_IO(TitleType);
   use TitleTypeIO;
   package NameTypeIO is new Ada.Text_IO.Enumeration_IO(NameType);
   use NameTypeIO;
   package CodeTypeIO is new Ada.Text_IO.Enumeration_IO(CodeType);
   use CodeTypeIO;
   
   Department: array(DepartmentType) of departmentNode;
   
   inFile: FILE_TYPE;
   lineSection: Slice_Set;

   procedure inLeftNode(deptNameIn: DepartmentType; empNameIn: NameType; titleIn: TitleType; IDin: Integer; payRateIn: float) is
      input: empPt;
   begin
      input := new empNode'(deptNameIn, empNameIn, titleIn, IDin, payRateIn, null);
      if(Department(deptNameIn).next = null) then      --empty list
         input.next := input;
         Department(deptNameIn).next := input;
      else                                             --populated list
         input.next := Department(deptNameIn).next;
         Department(deptNameIn).next := input;
      end if;

      Department(deptNameIn).num := Department(deptNameIn).num + 1;        --number in list
   end inLeftNode;

   procedure inRightNode(deptNameIn: DepartmentType; empNameIn: NameType; titleIn: TitleType; IDin: Integer; payRateIn: float) is
      last: empPt;
      first: empPt;
      input: empPt;
   begin
      input := new empNode'(deptNameIn, empNameIn, titleIn, IDin, payRateIn, null);
      if(Department(deptNameIn).next = null) then
         input.next := input;
         Department(deptNameIn).next := input;
      else
         last := Department(deptNameIn).next;
         first := last.next;
         input.next := Department(deptNameIn).next;
         Department(deptNameIn).next := input;
      end if;
      Department(deptNameIn).num := Department(deptNameIn).num + 1;
      Department(deptNameIn).next := input;
   end inRightNode;

   procedure deleteLeftNode(deptNameIn: DepartmentType) is
      p: empPt;
   begin
      if(Department(deptNameIn).next = null) then
         put_line("Underflow");
      else
         p := Department(deptNameIn).next;
         Department(deptNameIn).next := p.next;
            if p = Department(deptNameIn).next then
               Department(deptNameIn).next := null;
            end if;
      end if;
         
   end deleteLeftNode;

   procedure deleteRightNode(deptNameIn: DepartmentType) is
      Dp, prev, p: empPt;
   begin
      if(Department(deptNameIn).next = null) then
         put_line("Underflow");
      else
         p := Department(deptNameIn).next;
         while p.next /= Department(deptNameIn).next loop
            p := p.next;
         end loop;
         Dp := p.next;
         prev := p;
         Department(deptNameIn).next := p;
         p.next := Department(deptNameIn).next;
         if Dp = Department(deptNameIn).next then
            Department(deptNameIn).next := null;
         end if;
      end if;
   end deleteRightNode;

   procedure deleteDepartment(deptNameIn: DepartmentType) is
   begin
      null;
   end deleteDepartment;

   procedure printAll is
   begin
      null;
   end printAll;
   
   procedure printDept(deptNameIn: DepartmentType) is
      pt: empPt;
   begin
      if(Department(deptNameIn).next = null) then
         put_line("Department is empty");
         put_line(fileOut, "Department is empty");
      else
         pt := Department(deptNameIn).next;
         for emp in 1 .. 5 loop
            put(pt.empName);
            put(" ");
            put(fileOut, pt.empName);
            put(fileOut, " ");
            pt := pt.next;
         end loop;
      end if;
   end printDept;
    
begin
      Open(inFile, In_File, "Cinput.txt");
      skip_line(inFile);
   while not End_Of_File (inFile) loop
      declare
      begin
         Create(fileOut, Out_File, "output.txt");
         Create(lineSection, get_line(inFile), " ", Multiple);
         
         CodeTypeIO.Get(slice(lineSection, 1), fileLine.code, limit);
         DeptTypeIO.Get(slice(lineSection, 2), fileLine.dept, limit);
         NameTypeIO.Get(slice(lineSection, 3), fileLine.name, limit);
         TitleTypeIO.Get(slice(lineSection, 4), fileLine.title, limit);
         IIO.Get(slice(lineSection, 5), fileLine.id, limit);
         FloatIO.Get(slice(lineSection, 6), fileLine.payrate, limit);
         
         case fileLine.code is
            when IL =>
               inLeftNode(fileLine.dept, fileLine.name, fileLine.title, fileLine.id, fileLine.payrate);
            
            when IR =>
               inRightNode(fileLine.dept, fileLine.name, fileLine.title, fileLine.id, fileLine.payrate);
                                                        
            when DL =>
               deleteLeftNode(fileLine.dept);
                                                             
            when DR =>
               deleteRightNode(fileLine.dept);
                                                        
            when PA =>
               printAll;
                        
            when PD =>
               printDept(fileLine.dept);
                                                        
            when DD =>
               deleteDepartment(fileLine.dept);
                                      
            when others =>
               null;
         end case;
      end;
   end loop;
end Main;

I've tried changing the numerical values in the printDept for loop thinking it might be going over there but I get the same result.

2
I just read through it and tried to replicate with no luck. I guess I'm missing something.Jesse Schultz

2 Answers

2
votes

I think you should create the output file outside the loop. That aside, this seems to be data-dependent (i.e. when I tried it, on a short data set, no problems). Compiling with -g -gnateE may give more help (hopefully showing where in your code you’ve supplied bad input to String_Split). Using the debugger and saying catch exception, then getting the backtrace, should help. Also, you could print each line as it’s read, so you can tell what input led to the problem.

On the other hand, the exception clearly (well, to me) indicates that you’re trying to read the Nth token from a line that only has N - 1 tokens.

2
votes

What you need here is a stack trace, to find out the call sequence that led to the exception being raised...

This is compiler and OS specific; but assuming you are using Gnat, and can find "addr2line" for your OS...

  1. build your executable with debugging and exception trace information

  2. run the executable to raise the exception

  3. use addr2line with the exception trace to see the call sequence.

In more detail (referring to Gnat-8.3 on Debian 10)

  1. build the executable. Instead of

    gnatmake spliterror.adb

add debugging (-g) to the compile flags, exception tracing (-bargs -E) to the binder args, and depending on the version of gnat and addr2line, turn off position-independent linking (-largs -no-pie) ... see results.

(If you're using gprbuild, or running from the GPS IDE, you'll put the additional options into the .gpr file)

gnatmake -g spliterror.adb -bargs -E -largs -no-pie
  1. Run the executable to collect the exception trace.

You'll notice mine errors out with a different exception : with your incomplete reproducer, best I can do.

./spliterror
`Execution of ./spliterror terminated by unhandled exception  
raised ADA.IO_EXCEPTIONS.NAME_ERROR : Cinput.txt: No such file or directory  
Call stack traceback locations:
0x7f64c5a89782 0x7f64c59f5a37 0x403ca1 0x403a9f 0x7f64c5690099 0x4034f8 0xfffffffffffffffe
  1. Use addr2line to see the call sequence:

Incomplete because I haven't installed the debug version of the Ada runtime system ( or have, but linked in the non-debug version, or haven't compiled the RTS with the debug flag -g, or something...)

addr2line --exe=spliterror 0x7f6c16529782 0x7f6c16495a37 0x403ca1 0x403a9f 0x7f6c16130099 0x4034f8 0xfffffffffffffffe
??:0
??:0
/home/brian/Ada/Play/spliterror.adb:164
/home/brian/Ada/Play/b~spliterror.adb:260
??:0
??:?
??:0

But you can see the important bit : the error relates to line 164 in our source file: which happens to be: Open(inFile, In_File, "Cinput.txt");

As expected. But reproducing this WITH the input file will probably point spookily close to the real error. You can also get stack traces with e.g. gdb but I find this a much lighter weight process and can't remember ever having to use gdb on an Ada program.

One last point to bear in mind: This process USED to work without -largs -no-pie until a fairly recent version of gcc (possibly 6.3) on Debian, but the OS policy changed with regard to PIE and address space randomisation : addr2line hasn't caught up with this change and reports (uselessly)

addr2line --exe=spliterror 0x7fad79382782 0x7fad792eea37 0x55a9b4f96cb5 0x55a9b4f96ab3 0x7fad78f89099 0x55a9b4f96508 0xfffffffffffffffe
??:0
??:0
??:0
??:0
??:0
??:0
??:0

A corollary to this is that position-dependent executables are probably regarded as a potential security risk, so building with -largs -no-pie should probably only be for debugging, and turned off when done.