comp.lang.ada
 help / color / mirror / Atom feed
From: Maciej Sobczak <no.spam@no.spam.com>
Subject: Re: Get_Line problem (GNAT bug?)
Date: Fri, 08 Dec 2006 09:22:09 +0100
Date: 2006-12-08T09:22:09+01:00	[thread overview]
Message-ID: <elb7bh$nb5$1@cernne03.cern.ch> (raw)
In-Reply-To: <121egf5isyr6y$.1xabuj3pbre7l$.dlg@40tude.net>

Dmitry A. Kazakov wrote:

>> Why do you assign the special meaning to the line terminator?
> 
> Because a line can contain any character.

That's fine, but it doesn't change much from the Get_Line point of view.

>>> My answer is no. Exception is not an error. It indicates an exceptional
>>> state. Note that an exceptional state is a *valid* state. While an error
>>> (bug) has no corresponding program state at all.
>> It's not about bugs. I have presented an example of truncated XML file - 
>> there's no bug in a program that happened to be given a broken file to 
>> digest. It's an error in a sense that the program cannot read the data 
>> that it genuinely expects. Still, the program should handle this case 
>> reasonably, so we have valid state.
> 
> It is an error in a file, it is not an error in the program. Consider a
> defect HDD. Were an exception appropriate here?

Yes. As in disconnected NFS, and so on.

>> Sorry, I'm not convinced that exception might be a correct design choice 
>> for breaking the loop that reads data from well formatted file.
> 
> Not only that. I am using exceptions for parsing sources. It fits very
> nicely for recursive descent parsing, makes things a lot cleaner and
> easier.

That's a different kettle of fish. Recursvie descent parser does not 
really iterate over things - it *accepts* tokens. The accepting is what 
makes a difference between parsing and iterating. There is a failure 
logic build in the parser that is not present in iteration.

It's interesting that you mention recursive descent parser, because this 
was actually the background for my original problem.
Just few days ago I wanted to practice with some Ada "homework" and 
decided to write a simple line-oriented calculator. There is a parser, 
of course, and it has a simple grammar with just four productions. The 
parser accepts tokens that it expects according to its grammar and 
raises an exception when the expected token is not there. The exception 
is then handled at the top level, where the user is notified that 
ill-formed expression was provided. I have absolutely no problems with 
exceptions here - as note above, there is a failure logic in the parser.
But the top level (main subprogram) uses a regular loop for reading 
lines of text and End_Of_File predicate to decide whether it's OK to 
finish. There is no place for exceptions, it's just pure linear 
iteration with single end-of-sequence condition.

Actually, I found this Get_Line problem while having fun with my 
calculator "homework".

>> So how do you write iteration routines?
> 
> If you mean the case when the number of iterations is statically
> indeterminable, then yes, using exceptions.

What do you mean "statically indeterminable"? What about iterating over 
a container?

"Programming in Ada 2005", John Barnes, chapter 19.5 "Iterators".
I don't see any exceptions in there.

> Especially when iteration is
> mixed with recursion.

It's not really mixed, becaue if you decouple parser from tokenizer, 
then iteration and recursion work in separated levels of program 
structure. :-)

> Protocol_Error : exception;
> 
> begin
>    loop
>       Line := Get_Line (Source);
>       -- do something. This may raise an exception as well
>    end loop;
> exception
>    when End_Error =>
>       -- done due to file end
>    when Data_Error =>
>       -- due to I/O error
>    when Protocol_Error =>
>       -- due to protocol error
>    ...
> end;
> 
>> Looks like goto in disguise.
> 
> Any execution flow control is. So exceptions are as well.

But you still didn't convince me why exceptions should be preferred in 
this case. :-)

>>> End_Of_File in your program serves the
>>> purpose of return code.
>> Nope. It's the end-of-sequence condition. Just like with iterators.
> 
> But Get_Line already has a result, which is a string. String is not a
> condition.

Same with iterators. The end-of-sequence condition is the iterator's 
state, not the value it returns.

> That's a different idiom. Iterators assume an indexed container.

What about linked lists? They are not indexed.
I've been also using iterators without any containers - that's a nice 
solution for function generators, for example (for Python aficionados - 
think about range vs. xrange).

> You could
> use iterators for dealing with a container of strings.

That's what I want to see on input when reading consecutinve lines.

> But a stream isn't
> one.

I'm not reading a stream. I'm reading lines - the structure is already 
there, and I don't want to care about what is below.

> It is again about mixing abstraction levels. You can convert a
> character stream into a sequence of strings, but the stream itself is a
> container of characters, not lines. While a text file is a third thing.

How does it relate to my problem with Get_Line?

>> If the specs says "read until end", then this means single exit 
>> condition to me.
> 
> No. This is mixing problem and solution spaces. What if I had a concurrent
> program, which would map the file into virtual memory. Then I could split
> that memory into 10 pieces and let 10 tasks "read it until end."

Then you will have 10 tasks reading their own sequences, very likely 
using loops with single exit conditions. It doesn't change the nature of 
the problem at all.

>>> Your code didn't managed that either!
>> Why?
> 
> Because it contained a hidden goto: "exit when!" (:-))

That is one exit point. Just what the specs says.

>>> Neither manages it inputs longer than 99 characters.
>> Good point. How should I solve this?
> 
> By making the main loop dealing with lines instead of reads.

Isn't Get_Line dealing with lines?

>> string line;
>> while (getline(cin, line))
>> {
>>      // play with line here
>> }
> 
> That's OK to me. However, it is not that clean. line outlives the loop.

It is the price we sometimes pay for more compact representations. 
Exactly the same considerations apply to Ada - most loops in Ada I have 
seen were written this way.

> But
> it is not equivalent to your Ada code, because you chose fixed-length
> strings. An Ada equivalent of your C++ example would use Unbounded_String.

Of course, but that doesn't change the problem. It's the Get_Line in Ada 
vs. getline in C++ that shows the difference.

> Then what happens upon read error, reading the system paging file?

I'd expect std::bad_alloc. That's STORAGE_ERROR in Ada.

-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



  reply	other threads:[~2006-12-08  8:22 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-12-06 14:25 Get_Line problem (GNAT bug?) Maciej Sobczak
2006-12-06 18:06 ` Adam Beneschan
2006-12-06 20:34 ` Gautier
2006-12-06 21:47 ` Dmitry A. Kazakov
2006-12-06 23:40   ` Adam Beneschan
2006-12-07  0:02     ` Björn Persson
2006-12-07  1:09       ` Adam Beneschan
2006-12-07  1:28         ` Björn Persson
2006-12-07  5:00         ` Jeffrey R. Carter
2006-12-07  8:26   ` Maciej Sobczak
2006-12-07  9:21     ` Jean-Pierre Rosen
2006-12-07 13:35       ` Ludovic Brenta
2006-12-07 22:23       ` Robert A Duff
2006-12-07 10:22     ` Dmitry A. Kazakov
2006-12-07 14:51       ` Maciej Sobczak
2006-12-07 16:29         ` Dmitry A. Kazakov
2006-12-08  8:22           ` Maciej Sobczak [this message]
2006-12-07 22:50       ` Robert A Duff
2006-12-08  0:13         ` Randy Brukardt
2006-12-08  4:04         ` Larry Kilgallen
2006-12-08  9:11         ` Dmitry A. Kazakov
2006-12-07  9:14   ` Jean-Pierre Rosen
2006-12-07  3:34 ` Steve
2006-12-07 17:42   ` Adam Beneschan
2006-12-07 22:35     ` Robert A Duff
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox