Turingarena IOspec

A language to specify input/output format, and tools to automate I/O validation, parser generation, and more.

NB: Turingarena IOspec is work-in-progress. Features still to be implemented are marked with "TODO" in this README.

What is Turingarena IOspec?

When defining a programming problem based on file-like input and output, one needs to define a contract with the problem solver, where the following are specified:

Turingarena IOspec allows to specify formally the format of input and output, and some of the assumptions on them (as long as some conventions are followed, which are very common in IOI-like problems).

Once the format is specifies, the following tasks can be automated:

The specification language

In IOspec, the format is described in a language similar to a programming language, which defines, from the perspective of the problem solver:

An example is worth a thousand words. Here is the I/O specification for the problem of finding a cycle in a graph encoded as an edge list.

``` read N: n32, M: n32;

assume 2 <= N && N <= 100000; assume 1 <= M && M <= 1000_000;

for i upto M { read A[i]: n32, B[i]: n32;

assume 0 <= A[i] && A[i] < B[i] && B[i] < N;

}

call find_cycle(N, M, A, B) -> L: n32; // length of the cycle assert 2 <= L && L <= N;

write L;

for i upto L { call getcyclenode(i) -> u: n32; assert 0 <= u && u < N;

write u;

} ```

The language supports many features common of programming languages, but it also has many restrictions which allow it to be used to automate some tasks (which would otherwise be harder or impossible), and at the same time making it more compact.

Features:

Restrictions:

Usage

Linting I/O specification

turingarena-iospec lint [--spec-file <spec-file>]

Parses and lints the I/O specification in <spec-file>.

Generating code

turingarena-iospec code [--spec-file <spec-file>] [--target <file>] [--kind skeleton|template] [--language <lang>]

Generates skeleton or template code for a given language.

Validating input and output files/streams (TODO)

turingarena-iospec run [--spec-file <spec-file>] [--input-source <input-file-or-pipe>] [--output-source <output-file-or-pipe>] [--input-target <input-file-or-pipe>] [--output-target <output-file-or-pipe>] [--ignore-assumptions] [--ignore-assertions]

Parses and checks input, and optionally output, files or streams, according to an I/O specification. Issues are reported on stderr. If desired, generate the canonicalized for of the input or the output.

Implementation design

Turingarena IOspec is implemented in Rust.

It parses the specification language exploting the parsing framework commonly used to implement Rust procedural macros.

Example of generated code

An example of generated code for a probjem asking to find a cycle in a graph, already encoded in the input as adjacency lists.

Spec file:

``` read N: n32; // number of nodes

for u upto N { read D[u]: n32; // degree of u for i upto D[u] { read A[u][i]: n32; // adjacency list } }

call find_cycle(N, D, A) -> L: n32; // length of cycle

write L;

for i upto L { call getcyclenode(i) -> u: n32; // i-th node in the cycle write u; } ```

Generated C++ code:

```c++

include

include

int32t findcycle(int32t N, int32t* D, int32_t** A);

int32t getcyclenode(int32t i);

int main() { int32_t N; scanf("%d", &N);

int32_t* D = new int32_t[N];
int32_t** A = new int32_t*[N];
for(int32_t u = 0; u < N; u++) {
    scanf("%d", &D[u]);

    A[u] = new int32_t[D[u]];
    for(int i = 0; i < D[u]; i++) {
        scanf("%d", &A[u][i]);
    }
}

int32_t L = find_cycle(N, D, A);

printf("%d\n", L);

for(int32_t i = 0; i < L; i++) {
    int32_t u = get_cycle_node(i);

    printf("%d\n", u);
}

} ```