AI22-0151-1

!standard 2.4.1(5)                                      26-03-16  AI22-0151-1/04

!standard A.10.8(8/7)

!standard A.10.8(10)

!standard A.10.9(19)

!class binding interpretation 26-01-08

!status Revision-202Y  26-05-12

!status ARG Approved  8-0-2  26-05-12

!status work item 26-01-08

!status received 26-01-08

!assigned author Christoph Karl Walter Grein

!submitter Christoph Karl Walter Grein

!priority Low

!difficulty Easy

!subject Syntax rules disagree with Text_IO.Get results

!summary

Additional cases of the behavior of Text_IO.Get from a file are clarified.

!issue

Legacy ACATS tests for Text_IO.Integer_IO.Get reading from file do not handle some cases, another case disagrees with syntax rules. The specific legacy tests are CE3704D, CE3704F, CE3704M, CE3704N. (Note: “Legacy” tests come from the Ada 83 ACVC.) Text_IO.Modular_IO.Get is not tested at all since in Ada 83, there were no modular types. The problems were encountered when writing a C-test for AI22-0144-1.

There are three problematic cases.

1.   2.4.1(5) is a syntax rule, thus reading a string like “10E-2” must stop after the exponent character ‘E’ and raise Data_Error. The current test CE3740F consumes the whole string.

2.   A.10.8(8) for reading Width > 0 characters is not explicit what happens with trailing blanks. Examples for Width = 10:

   "12#11B0# X"  (note: 'X' at position 10) will raise Data_Error, the
                 whole sequence will be evaluated and a blank inside is
                 illegal. (This case is not questionable.)

   "12#11B0#  X" (note: 'X' at position 11, not read) IMHO must also raise
                 Data_Error. There is nowhere a statement that trailing
                 blanks are ignored when reading from a file. They always
                 stop reading. There is at least one compiler that does
                 not raise Data_Error.

3.   There is no C-test for reading modular integers. A.10.8(8) for unsigned types explicitly excludes reading a minus sign. This is clearly a syntax rule. Thus reading a string like
“-123” must raise Data_Error without reading anything. There is at least one compiler that consumes the whole string, then raises Data_Error.

AI22-0144-1 insists that these cases be clarified.

!recommendation

Case 1:

Insert a new category header “Legality Rules” before 2.4.1(5) (changing the category of this text).

Case 2:

Add some wording to A.10.8 confirming that also trailing blanks raise Data_Error, perhaps as a Ramification or To be honest like AI22-144-1 with the example input "2#10101T".

Note that A.10.9 for Real Types is also affected by this rule. For enums Get, there is no Width parameter.

Case 3:

Change A.10.8(8) so that “-123” becomes a legality rule.

!wording

Modify 2.4.1(5):

{Legality Rules}

An exponent for an integer literal shall not have a minus sign.

Add after AARM 2.4.1(5.a):

AARM Reason: We define this to be a Legality Rule to clarify that it has no effect on the characters read by Text_IO.Get for integer types (see A.10.8) (only syntax rules have such an effect).

Modify A.10.8(8/7):

If the value of the parameter Width is zero, skips any leading blanks, line terminators, or page terminators, then reads a [plus sign if present or (for a signed type only) a minus ]sign if present, then reads the longest possible sequence of characters matching the syntax of [a numeric]{an integer} literal {(see 2.4)}[without a point], or a prefix of such syntax. If a nonzero value of Width is supplied, then exactly Width characters are input, or the characters (possibly none) up to a line terminator, whichever comes first; any skipped leading blanks are included in the count.

Add after AARM A.10.8(8.a/7):

AARM Ramification: This means that for instance for Width=6 an otherwise legal string like bb123b nevertheless raises Data_Error since trailing blanks are not ignored and do not belong to the syntax (the lower case letter b here stands for a blank character to reveal the layout).

Modify A.10.8(10/3):

The exception Data_Error is propagated if the sequence of characters read does not form a legal integer literal{,}[ or] if the value obtained is not of the subtype Num{, or for an unsigned type, if the sequence of characters read includes a minus sign}.

Add after A.10.9(19):

AARM Ramification: This means that for instance for Width=6 an otherwise legal string like bb1.3b nevertheless raises Data_Error since trailing blanks are not ignored and do not belong to the syntax (the lower case letter b here stands for a blank character to reveal the layout).

!discussion

Case 1:

Because test CE3704F is a legacy test, insisting on the interpretation of the Reference Manual rules would mean that each and every still existing Ada compiler would have to change if it wants to comply with the ACVC.

This is not deemed a viable option. So the recommendation is to make 2.4.1(5) a Legality Rule rather than a Syntax rule. We can do that by inserting a new category header “Legality Rules” before 2.4.1(5).

With this change, the legacy test CE3704F with the test case "10E-2X"  leaving only 'X' in the input stream is still correct.

(Note that headlines do not have paragraph numbers, thus there will be no change in 2.4.1.(5).)

We do not believe that this reclassification should require any change to existing compilers. One could imagine that the reclassification in theory would require a change to lexical processing, but as a practical matter neither users nor the ACATS cares precisely why a program is rejected - and there is no change to legality of program text.

Case 2:

In all nested generic IO packages for integer, modular, floating point, fixed point, decimal, and enumeration types, trailing blanks are never read for Width=0 (parameter does not exist for enums), they do not belong to the syntax. For Width>0, characters foreign to syntax read after a legal string like “123ab” raise Data_Error as does “123 A”. So why should this be different with blanks alone like “123 ”? Blanks definitely do not belong to the syntax.

The same goes for reading from string. This proceeds nearly like reading from file except that there is no Width parameter and the position of the character last read is returned. (Reading from file instead sets the column number to the next character position to be read.) End of the string is treated like end of the file.

The 'Value attribute is different: Leading and trailing blanks are explicitly ignored. The rest must be legal syntax. (I cannot find a reason for this decision in the RM 83 rationale.)

The essence of the RM: Blanks are not part of the syntax (see 2.4), they are either never read, explicitly ignored or lead to Data_Error.

The wording of the RM suggests this is the case as well. The last sentence of A.10.8(8) has always read “If a nonzero value of Width is supplied, then exactly Width characters are input, or the characters (possibly none) up to a line terminator, whichever comes first; any skipped leading blanks are included in the count.” This explicitly mentions “skipped leading blanks” and says nothing about trailing blanks. If there was any intent to ignore trailing blanks, it clearly would have been indicated here in the same way. (The same wording is used in A.10.9(19)).

In the ARG meeting 63M, it was proposed that the purpose of Width>0 is for reading otherwise unformatted tables of numbers. If one gets the table alignment wrong such that an extra column of spaces is included, the error is detected immediately. If trailing blanks are skipped, then the error will be ignored, and it is very possible that one could get only parts of values from following columns as the alignment gets further and further out of alignment. (This happened at least once to one of the attendees.)

Moreover, the Width parameter of Get corresponds to the Width parameter of Put for integers, to the sum of Fore, Aft, Exp of Put for reals. Put writes the values right-justified; thus reading what Put wrote must not have trailing blanks. If I remember correctly, it was Randy who once wrote:

"Text_IO was designed for file operations with an important feature: If  your program writes into a file, you can take the sequence of code that writes the file, replace every Put_* operation by the symmetric Get_* operation, and you will read the file without error."

Case 3:

The rule A.10.8(8) ... or (for a signed type only) a minus sign if present... is clearly a syntax rule. However, in ARG Meeting #63M, it was discussed and decided: “The intent of (3) seems similar to (1) as it is about “-”. So it should read the number, then raise Data_Error. That agrees with the result of 3 out of 4 compilers.“

The solution is simple: Take out the rule for the negative values and add it to para (10) as an explicit check. This is handled differently than Case 1 as the sign of an exponent is part of the syntax of a numeric literal, while (for this case) the overall sign of a literal is separate (it is syntactically an operator applied to a literal).


 

We either tested or asked others to test a proposed ACATS test on this topic. The results are as follows:

Case 1: As expected, all compilers follow the behavior of the legacy tests. DDCI Score fails to update the column number (Col) properly (something not tested in the legacy tests).

Case 2: GNAT (several versions tried) ignores the trailing blanks in this case (different than the recommended behavior). Janus/Ada 3.2.2 raises Data_Error (matching the recommended behavior). ObjectAda 10.5 raises Data_Error (matching the recommended behavior). DDCI Score raises Data_Error (matching the recommended behavior).

Case 3: GNAT (several versions tried) reads the entire number before raising Data_Error. Janus/Ada 3.2.2 reads the entire number before raising Data_Error. ObjectAda 10.5 reads the entire number before raising Data_Error. (Is there an echo in here??). DDCI Score raises Data_Error as expected (without reading anything). [We’d never expect 4 implementers to agree on anything, would we??]

!corrigendum 2.4.1(5)

@drepl

An @fa{exponent} for an integer literal shall not have a minus sign.

@dby

@s8{@i{Legality Rules}}

An @fa{exponent} for an integer literal shall not have a minus sign.

!corrigendum A.10.8(8/7)

@drepl

If the value of the parameter Width is zero, skips any leading blanks, line terminators, or page terminators, then reads a plus sign if present or (for a signed type only) a minus sign if present, then reads the longest possible sequence of characters matching the syntax of a numeric literal without a point, or a prefix of such syntax. If a nonzero value of Width is supplied, then exactly Width characters are input, or the characters (possibly none) up to a line terminator, whichever comes first; any skipped leading blanks are included in the count.

@dby

If the value of the parameter Width is zero, skips any leading blanks, line terminators, or page terminators, then reads a sign if present, then reads the longest possible sequence of characters matching the syntax of an integer literal (see 2.4), or a prefix of such syntax. If a nonzero value of Width is supplied, then exactly Width characters are input, or the characters (possibly none) up to a line terminator, whichever comes first; any skipped leading blanks are included in the count.

!corrigendum A.10.8(10/3)

@drepl

@xindent{The exception Data_Error is propagated if the sequence of characters read does not form a legal integer literal or if the value obtained is not of the subtype Num.}

@dby

@xindent{The exception Data_Error is propagated if the sequence of characters read does not form a legal integer literal, if the value obtained is not of the subtype Num, or for an unsigned type, if the sequence of characters read includes a minus sign.}

!ACATS test

No additional C-test is needed since problems were detected when writing a C-test for AI22-0144-1. That C-Test needs to be finished, of course.

!appendix

This has been created in response to ARG GitHub issue #158.