Crates.io | turingarena-iospec |
lib.rs | turingarena-iospec |
version | 0.1.0 |
source | src |
created_at | 2020-08-28 12:01:18.942179 |
updated_at | 2020-08-28 12:01:18.942179 |
description | Tool to specify I/O format and automate validation, parser generation, and more. Part of TuringArena: tools to create programming challenges. |
homepage | |
repository | https://github.com/turingarena/iospec/ |
max_upload_size | |
id | 281857 |
size | 138,938 |
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.
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:
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 <= 100_000;
assume 1 <= M && M <= 1_000_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 get_cycle_node(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:
bool
(either true, represented as 1
, or false, represented as 0
),n8
, n16
, n32
, n64
, (naturals are non-negative integers which fit in a signed integer of the given bit size, e.g., n32
can contain any number from 0
to 2^31 - 1
),i8
, i16
, i32
, i64
, (positive or negative integers which fit in a signed integer of the given bit size, excluding the most negative value, e.g., i32
can contain any number from -2^31 + 1
to 2^31 - 1
),for
loop,read
and write
),call
),
for
),loop
, break
, continue
) (TODO),if
, switch
) (TODO),let
) (TODO),assume
and assert
) (TODO).Restrictions:
A[i]: n32
in a for i upto N
loop defines A
as an array of n32
of size N
. Reading X: n32
inside an if
or switch
defines X
as an optional of n32
(TODO).let
statement before the call
(TODO). turingarena-iospec lint
[--spec-file <spec-file>]
Parses and lints the I/O specification in <spec-file>
.
turingarena-iospec code
[--spec-file <spec-file>]
[--target <file>]
[--kind skeleton|template]
[--language <lang>]
Generates skeleton or template code for a given language.
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.
Turingarena IOspec is implemented in Rust.
It parses the specification language exploting the parsing framework commonly used to implement Rust procedural macros.
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 get_cycle_node(i) -> u: n32; // i-th node in the cycle
write u;
}
Generated C++ code:
#include <cstdio>
#include <cstdint>
int32_t find_cycle(int32_t N, int32_t* D, int32_t** A);
int32_t get_cycle_node(int32_t 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);
}
}