Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gazprea/impl/errors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ Here is an example invalid program and a corresponding compile-time error:

ReturnError on line 1: procedure "main" does not have a return statement reachable by all control flows

.. _ssec:errors_sizeErrors:

Run-time Errors
---------------

Expand Down
19 changes: 18 additions & 1 deletion gazprea/spec/built_in_functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ If a declaration or a definition with the same name as a built-in function is
encountered in a *Gazprea* program, then the compiler should issue an error.

Note that although the examples below all use arrays, all the built-ins work
on Vectors and Strings, since they are always compatible with arrays.
on vectors and strings as well, since a vector may be used as an array value of
its current length. When a built-in is applied to a vector it therefore reports
on, or operates over, the length that vector has *at the moment of the call* —
unlike an array, whose length has been fixed since
:ref:`elaboration <sssec:array_sizing>`.

.. _ssec:builtIn_length:

Expand All @@ -30,6 +34,19 @@ representing the number of elements in the array.

length(v) -> std_output; /* Prints 5 */

Applied to an array, ``length`` is constant for the lifetime of that variable.
Applied to a :ref:`vector <ssec:vector>` or :ref:`string <ssec:string>` it
returns the current length and may return different values at different points
in the program; it is equivalent to that vector's ``len`` method.

::

var vector<integer> u = 1..5;

length(u) -> std_output; /* Prints 5 */
u.push(6);
length(u) -> std_output; /* Prints 6 */


.. _ssec:builtIn_rows_cols:

Expand Down
11 changes: 9 additions & 2 deletions gazprea/spec/declarations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ In *Gazprea* all variables must be initialized in a well defined manner in order
to ensure functional purity. If the variables are not initialized to a known
value their initial value might change depending on when the program is run.
Therefore, the second declaration style is equivalent to setting the value to
zero.
zero. For an :ref:`array <ssec:array>` this means every element of its
(already determined) length is set to the element type's zero; for a
:ref:`vector <ssec:vector>` or :ref:`string <ssec:string>`, which carries no
length in its type, it means the empty collection.

Elaborating a declaration is also the moment at which an array's length is
fixed, permanently. See :ref:`sssec:array_sizing` for what that means, and
:ref:`sssec:array_vs_vector` for how it differs from a vector.

For simplicity *Gazprea* assumes that declarations can only appear at
the beginning of a block. For instance this would not be legal in
Expand Down Expand Up @@ -60,7 +67,7 @@ illegal to refer to a variable within its own initialization statement.

/* All of these declarations are illegal, they would result in garbage values. */
integer i = i;
integer[10] v = v[0] * 2;
integer[10] v = v[1] * 2;

An error message should be raised about the use of undeclared variables
in these cases. If a variable of the same name is declared in an
Expand Down
24 changes: 18 additions & 6 deletions gazprea/spec/expressions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,18 @@ This additional expression is used to create the generated values. For example:
The expression to the right of the bar (``|``), is used to generate the
value at the given index.
Let ``T`` be the type of the expression to the right of the bar (``|``). Then,
if the domain of the generator is an array of size ``N``, the result will be a
array of size ``N`` with element type ``T``. Otherwise, if the domain of the
generator is a matrix of size ``N`` x ``M``, the result will be a matrix of size
``N`` x ``M`` with element type ``T``.
if the generator has a single domain expression whose array has size ``N``, the
result is an array of size ``N`` with element type ``T``. If it has two domain
expressions whose arrays have sizes ``N`` and ``M``, the result is a matrix of
size ``N`` x ``M`` with element type ``T``.

A generator always yields an *array* value, never a
:ref:`vector <ssec:vector>`, and the size of that value is settled when the
generator is evaluated — from the sizes of the domain arrays at that moment.
Using a generator to initialize an array with an inferred size is therefore one
of the ways an array's length is fixed at
:ref:`elaboration <sssec:array_sizing>`.

Generators may be nested, and
may be used within domain expressions. For instance, the generator below
is perfectly legal:
Expand All @@ -91,7 +99,11 @@ Domain Expressions
------------------

Domain expressions consist of an identifier denoting an iterator variable and
an expression that evaluates to **any** array type.
an expression that evaluates to **any** array type. A
:ref:`vector <ssec:vector>` or :ref:`string <ssec:string>` may be used as the
domain expression, in which case its length at the moment the domain is
evaluated determines the number of iterations; growing the vector inside the
body does not add iterations, for the same reason given below.
Domain expressions can only appear within iterator loops and generators.
A domain expression is a way of declaring a variable that
is local to the loop or generator, that takes on values from
Expand Down Expand Up @@ -127,7 +139,7 @@ commas, such as in matrix generators.
/* The "i"s both domain expressions are at the same scope, which is
* the one enclosing the loop. Therefore the matrix is: [[0 0 0] [0 1 2] [0 2 4]]
*/
integer[3,3] mat = [ i in 0..i, j in 0..i | i*j ];
integer[3][3] mat = [ i in 0..i, j in 0..i | i*j ];

The domain for the domain expression is only evaluated once. For
instance:
Expand Down
37 changes: 28 additions & 9 deletions gazprea/spec/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,41 +164,60 @@ do not have to match the argument names in the function definition.

.. _ssec:function_vec_mat:

Array and Matrix Parameters and Returns
----------------------------------------
Array, Vector and Matrix Parameters and Returns
-----------------------------------------------

The arguments and return value of functions can have both explicit and inferred sizes. For example:

::

function to_real_vec(integer[*] x) returns real[*] {
function to_real_vec(integer[*] x) returns vector<real> {
/* Some code here */
}

function transpose3x3(real[3,3] x) returns real[3,3] {
function transpose3x3(real[3][3] x) returns real[3][3] {
/* Some code here */
}

An array parameter written with an explicit size, such as ``real[3][3]``, is
part of the signature: the argument's length must match at the call, or a
``SizeError`` is raised. An array parameter written with an inferred size, such
as ``integer[*]``, is elaborated at the call — it takes the length of the
argument that is actually passed, and that length is then fixed for the
duration of the call. An inferred-size *return* type works the same way and is
elaborated at the ``return``, from the length of the value being returned.

A ``vector<T>`` parameter or return type carries no length at all, so no length
check applies to it in either direction.

Like Rust, array *slices* may be passed as arguments:

::

function to_real_vec(integer[*] x) returns real[*] {
real[*] rvec = x;
return rvec;
function to_real_vec(integer[*] x) returns vector<real> {
real[*] rvec = x; /* elaborated to the length of 'x' */
return rvec; /* array value becomes the returned vector */
}

function slicer() returns real[*] {
integer[10] a = 1..10;
var vector<real> two_halves = to_real_vec(a[1..5]);
var vector<real> two_halves = to_real_vec(a[1..6]);
two_halves.append(to_real_vec(a[6..]));

/* 'two_halves' is a length-10 vector here, so the inferred-size
return type is elaborated to a length-10 array. */
return two_halves;
}

Both directions of the array/vector conversion appear above, and neither one
changes the sizing discipline of the variables involved. ``rvec`` is an array
and its length is fixed the moment it is declared; ``two_halves`` is a vector
and its length grows with ``append``. Converting between them copies a value —
it does not make ``rvec`` growable or pin ``two_halves`` to a length.

Remember that all function parameters are ``const`` in *Gazprea*, so that all
functions are pure. That means that while it is legal to pass arrays and slices
*be reference*, the array contents cannot be modified inside the function,
*by reference*, the array contents cannot be modified inside the function,
because the change would be visible outside the function. You must check that
the ``const`` requirement is honored.

Expand Down
16 changes: 15 additions & 1 deletion gazprea/spec/globals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ All global statements are considered declarations. Global statements may occur
in any order, given respective symbols are defined before being referenced.

Variable Declarations
=====================
---------------------

In *Gazprea* values can be assigned to a global identifier. All globals
must be immutable (``const``). If a global identifier is declared with
Expand All @@ -31,4 +31,18 @@ evaluate variables and functions at compile time. Global expression evaluation c
be deferred to runtime, but that has the disadvantage of changing errors from compile
time to run time.

For the same reason, a global :ref:`array <ssec:array>` must state its size
explicitly, and that size expression is subject to the same restriction as a
:ref:`typealias <sec:typealias>` size: it must be composed exclusively of
arithmetic on scalar literals, so that the array's length is settled at compile
time rather than at some runtime elaboration point. An inferred size (``[*]``)
is not available at global scope, because the scalar literal on the RHS carries
no length to infer from.

::

const integer[4] g = 0; /* legal -- [0, 0, 0, 0] */
const integer[2 + 2] h = 1; /* legal -- constant folded to 4 */
const integer[*] i = 0; /* illegal -- nothing to infer a length from */


51 changes: 39 additions & 12 deletions gazprea/spec/procedures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ These procedures can be called as follows:

::

integer x = 12;
integer y = 21;
integer[5] v = 13;
var integer x = 12;
var integer y = 21;
var integer[5] v = 13;

call change_first(v); /* v == [7, 13, 13, 13, 13] */
call increment(x); /* x == 13 */
Expand Down Expand Up @@ -151,11 +151,11 @@ call by reference, and are therefore *l-values* (pointers).
::


procedure byvalue(String x) returns integer {
return len(x);
procedure byvalue(string x) returns integer {
return length(x);
}
procedure byreference(var String x) returns integer {
return len(x);
procedure byreference(var string x) returns integer {
return length(x);
}
procedure main() returns integer {
const character[3] y = ['y', 'e', 's'];
Expand All @@ -166,6 +166,13 @@ call by reference, and are therefore *l-values* (pointers).
return 0;
}

The call to ``byvalue`` promotes the ``character[3]`` array to a ``string``,
which is a :ref:`vector <ssec:vector>` of ``character``. This is a conversion
of the argument's *value*: the parameter ``x`` is a runtime-sized string, while
``y`` remains a length-3 array in the caller. The call to ``byreference`` is
illegal because a ``var`` parameter is call by reference and so admits no
promotion — and separately because ``y`` is ``const``.


Aliasing
--------
Expand Down Expand Up @@ -228,19 +235,39 @@ aliasing.

.. _ssec:procedure_vec_mat:

Array Parameters and Returns
----------------------------------------
Array and Vector Parameters and Returns
---------------------------------------

:ref:`As with functions <ssec:function_vec_mat>`, the arguments and return
value of procedures can have both explicit and inferred sizes.
value of procedures can have both explicit and inferred sizes, with the same
rule: an explicit size must be matched by the argument, and an inferred size
(``[*]``) is elaborated at the call from the argument that is passed.

Similarly, slices can be used whereever arrays are declared as parameters, and
unlike functions, array parameters in procedures can be ``var``.

.. _ssec:function_namespacing:
A ``var`` array parameter is mutable in its *contents* only. Because an array
is :ref:`elaboration-time sized <sssec:array_sizing>`, a procedure cannot
change the length of an array it was passed, no matter how the parameter is
qualified — assigning a longer value to it raises a ``SizeError`` just as it
would at the call site. A ``var vector<T>`` parameter, by contrast, may be
grown with ``push`` and ``append``, and the caller observes the new length.

::

procedure fill(var integer[*] a) {
a = 1; /* legal: every element becomes 1 */
a = a || a; /* SizeError: 'a' cannot double in length */
}

procedure extend(var vector<integer> v) {
v.push(1); /* legal: caller's vector is now one longer */
}

.. _ssec:procedure_namespacing:

Procedure Namespacing
--------------------
---------------------

In *Gazprea* procedure declarations occur in the global scope.
This means that two procedures with the same name cannot coexist in the same
Expand Down
44 changes: 43 additions & 1 deletion gazprea/spec/statements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,31 @@ For instance, with single dimensional arrays:
/* Change 'v' to [1, 0, 3] */
v[2] = 0;

Assigning a whole array value changes the array's *contents*, never its
*length*. An array is :ref:`elaboration-time sized <sssec:array_sizing>`, so by
the time any assignment to it executes its length is already fixed. The RHS is
therefore fitted to the LHS using the same rules as initialization: a shorter
value is padded with the element type's zero, and a longer value raises a
``SizeError``.

::

var integer[*] v = [0, 0, 0]; /* length fixed at 3 */

v = [1, 2]; /* v == [1, 2, 0] -- padded */
v = [1, 2, 3, 4]; /* SizeError -- v cannot grow */

Assignment to a :ref:`vector <ssec:vector>` behaves differently, because a
vector is runtime sized: the assignment replaces the vector's contents and its
length together, so neither padding nor a ``SizeError`` arises.

::

var vector<integer> u = [0, 0, 0];

u = [1, 2]; /* u == [1, 2] -- length is now 2 */
u = [1, 2, 3, 4]; /* u == [1, 2, 3, 4] -- length is now 4 */

This applies to arrays of any dimension.

::
Expand Down Expand Up @@ -332,7 +357,8 @@ semicolon.
Iterator Loop
~~~~~~~~~~~~~

Loops can be used to iterate over the elements of an array of any type.
Loops can be used to iterate over the elements of an array of any type, and
equally over a :ref:`vector <ssec:vector>` or :ref:`string <ssec:string>`.
This is done by using domain expressions (for instance ``i in v``) in
conjunction with a loop statement.

Expand Down Expand Up @@ -374,6 +400,22 @@ defines a constant domain variable from it's respective index. For instance:
i -> std_output; "\n" -> std_output;
}

The same holds for a :ref:`vector <ssec:vector>` or
:ref:`string <ssec:string>` domain: the domain is evaluated once, so the
iteration count is the vector's length at that moment. Growing the vector in
the loop body does not lengthen the loop.

::

var vector<integer> v = [1, 2, 3];

/* Prints 1, 2, 3 -- three iterations, fixed when the domain was
evaluated, even though 'v' ends up with six elements. */
loop i in v {
v.push(i);
i -> std_output; "\n" -> std_output;
}

Note that multiple domain expressions are *not* allowed:

::
Expand Down
4 changes: 4 additions & 0 deletions gazprea/spec/streams.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ prints the following:

[1 2 3]

:ref:`Vectors <ssec:vector>` print exactly as arrays do, using whatever length
they hold at the time of the output statement. ``string`` is the sole
exception, described next.

:ref:`strings <ssec:string>` print their contents as a contiguous sequence of characters.
For example:

Expand Down
Loading