13. Streams

Gazprea has two streams: std_output and std_input, which are used for writing to stdout and reading from stdin respectively.

13.1. Output Stream

Output streams use the following syntax:

<exp> -> std_output;

13.1.1. Output Format

Values of the following primitive types are treated as follows when sent to an output stream:

  • Character: Prints the character.

  • Integer: Converts it to a string representation, and then prints it.

  • Real: Converts it to a string representation, and then prints it. This is the same behavior as the %g specifier in printf.

  • Boolean: Prints T for true, and F for false.

Arrays print their contents according to the rules above, with square braces surrounding their elements and with spaces only between values. For example:

integer[*] v = 1..3;
v -> std_output;

Output

[1 2 3]

Vectors print exactly as arrays do, using whatever length the vector holds at the time of the output statement. A string is the sole exception: although a string is a vector of characters, it prints its characters contiguously rather than in bracketed array form, as shown next.

Strings print their contents as a contiguous sequence of characters. For example:

string str = "Hello, World!";
str -> std_output;

Output

Hello, World!

Matrices print like an array of arrays. This generalizes to arbitrary-rank arrays. For example:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]] -> std_output;

Output

[[1 2 3] [4 5 6] [7 8 9]]

No other type may be sent to a stream; the compiler must emit a TypeError (see Errors). For instance, a tuple or a struct cannot be sent to a stream. A procedure call may not appear as a stream operand at all, since that is not one of the positions in which a procedure call may appear; the compiler must emit a CallError (see Errors). Also, empty arrays and matrices can be sent to streams, but not empty literals (e.g. []), because they have no type; sending one must emit a TypeError (see Errors). A typed empty array prints as an empty pair of brackets:

integer[*] empty = [];
empty -> std_output;

Output

[]

Note that there is no automatic new line or spaces printed. To print a new line, a user must explicitly print the new line or space character. For example:

'\n' -> std_output;
' ' -> std_output;

13.2. Input Stream

Input streams use the following syntax:

<lvalue> <- std_input;

An lvalue may be anything that can appear on the left hand side of an assignment statement (see Expressions) – not only a plain variable but also, for example, an array element:

var integer[3] v = [0, 0, 0];
v[2] <- std_input;   // reads a single integer into element 2 of v
v -> std_output;

Input

5

Output

[0 5 0]

The primitive-only restriction below still applies: the target must designate a single primitive location.

Input streams may only work on the following primitive types:

  • character: Reads a single character from stdin. Note that a character read never sets error state 1; reaching the end of the stream still sets state 2.

  • integer: Reads an integer from stdin. If an integer could not be read, an error state is set on this stream.

  • real: Reads a real from stdin. If a real could not be read, an error state is set on this stream.

  • boolean: Reads a boolean from stdin. If a boolean value could not be read, an error state is set on this stream.

Implicit casting is not performed for stream input over any type.

13.2.1. Input Format

std_input expects an input stream of values which do not need to be whitespace separated. A read will consume the stream until a character or EOF occurs that breaks the pattern match for the given type’s specifier. The longest successful match is returned.

In general input stream semantics are designed for parity with scanf. The only differences are the Stream State, a boolean specifier and a restriction on the maximum number of bytes that can be consumed in a single read to 512.

For each of the allowed types the semantics are given below.

Reading a character from stdin consumes the first byte that can be read from the stream. If the end of the stream is encountered, the character read is 0xFF (255) – character values are unsigned bytes in 0 to 255, so there is no -1 – and the end-of-stream error state is set. Because a legitimate 0xFF byte is indistinguishable from end-of-stream by its value alone, a program must consult stream_state to tell the two apart; this is the reason stream_state exists. There is no concept of skipping whitespace for characters, since space and escaped characters must be readable.

An integer from stdin can take any legal format described in the integer literal section. It may also be preceded by a single negative or positive sign. All preceding whitespace before the number or sign character may be skipped up to the limit imposed by the 512 byte read restriction.

A real input from stdin can take any legal format described in the real literal section. It may be preceded by a single negative or positive sign, and preceding whitespace may be skipped in the same way as integers; the sign and the digits of the number itself, however, must be contiguous – no whitespace may appear within the value.

A boolean input from stdin is either T or F. Preceding whitespace may be skipped in the same way as integers and reals.

Given the following program and stdin (where \t is a tab, \n a newline, and a single space precedes 1.):

var boolean b;
var character c;
var integer i;
var real r;
b <- std_input;
i <- std_input;
c <- std_input;
r <- std_input;
format(b) || " " || format(r) -> std_output;

Input

\tF\n1\n 1.\n

Output

F 1

(1. reads as the real 1.0, which prints as 1 under the %g format rule above) because the white space is consumed for characters and skipped for other types.

13.2.2. Error Handling

When reading boolean, integer, and real from stdin, it is possible that the end of the stream or an error is encountered. In order to handle these situations Gazprea provides a built-in procedure that is implicitly defined in every file: stream_state (see Stream State for its signature). stream_state returns 0 if the last read succeeded, 1 if it encountered an error, and 2 if it encountered the end of the stream. Before any read has been issued it returns 0.

Reading a character can never set error state 1. The character will either be successfully read, or the end of the stream will be reached: the read then yields the character byte 0xFF (255, i.e. as<character>(-1)) and sets state 2.

When a read sets error state 1 – which is possible only for boolean, integer, and real (a character read never sets state 1) – the zero value for the type being read is assigned to the target, the implicit stream_state is set to 1, and the input stream remains pointing to the same position as before the read occurred. Reaching the end of the stream (state 2) instead assigns the value from the Return column of the table below – the type’s zero value for boolean/integer/real, and 0xFF (255, i.e. as<character>(-1)) for a character – and sets stream_state to 2.

The program below demonstrates 4 reads which set the error states 1,0,0,2 respectively.

var integer ss;
var integer i;
var character c;

i <- std_input;
i -> std_output;
ss = stream_state(std_input);
ss -> std_output;

c <- std_input; //eat the .

i <- std_input;
i -> std_output;

c <- std_input;
ss = stream_state(std_input);
ss -> std_output;

Input

.7

Output

0172

This table summarizes an input stream’s possible error states after a read of a particular data type.

Type

Situation

Return

stream_state

Boolean

error

false

1

end of stream

false

2

Character

error

N/A

N/A

end of stream

0xFF

2

Integer

error

0

1

end of stream

0

2

Real

error

0.0

1

end of stream

0.0

2