Reputation: 115
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.
Upvotes: 1
Views: 186
Reputation:
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...
build your executable with debugging and exception trace information
run the executable to raise the exception
use addr2line with the exception trace to see the call sequence.
In more detail (referring to Gnat-8.3 on Debian 10)
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
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
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.
Upvotes: 2
Reputation: 25501
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.
Upvotes: 2