Skip to content

Commit

Permalink
Initial implementation of iospec
Browse files Browse the repository at this point in the history
  • Loading branch information
cairomassimo committed May 17, 2022
1 parent 279ff54 commit f57dd61
Show file tree
Hide file tree
Showing 159 changed files with 9,434 additions and 11 deletions.
288 changes: 277 additions & 11 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ task-maker-cache = { path = "./task-maker-cache" }
task-maker-exec = { path = "./task-maker-exec" }
task-maker-lang = { path = "./task-maker-lang" } # needed only by typescriptify
task-maker-format = { path = "./task-maker-format" }
task-maker-iospec = { path = "./task-maker-iospec" }

# Logging and setting up the global logger
log = "0.4"
Expand Down
5 changes: 5 additions & 0 deletions src/tools/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use task_maker_rust::tools::task_info::main_task_info;
use task_maker_rust::tools::typescriptify::main_typescriptify;
use task_maker_rust::tools::worker::main_worker;

use task_maker_iospec::tools::*;

fn main() {
let base_opt = Opt::parse();
base_opt.logger.enable_log();
Expand All @@ -30,6 +32,9 @@ fn main() {
Tool::Booklet(opt) => main_booklet(opt, base_opt.logger),
Tool::FuzzChecker(opt) => main_fuzz_checker(opt),
Tool::AddSolutionChecks(opt) => main_add_solution_checks(opt, base_opt.logger),
Tool::IospecCheck(opt) => iospec_check::do_main(opt, &mut std::io::stderr()),
Tool::IospecGen(opt) => iospec_gen::do_main(opt, &mut std::io::stderr()),
Tool::IospecGenAll(opt) => iospec_gen_all::do_main(opt, &mut std::io::stderr()),
Tool::InternalSandbox => return task_maker_rust::main_sandbox(),
}
.nice_unwrap()
Expand Down
8 changes: 8 additions & 0 deletions src/tools/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use crate::tools::task_info::TaskInfoOpt;
use crate::tools::worker::WorkerOpt;
use crate::LoggerOpt;

use task_maker_iospec::tools::*;

#[derive(Parser, Debug)]
#[clap(name = "task-maker-tools")]
pub struct Opt {
Expand Down Expand Up @@ -49,6 +51,12 @@ pub enum Tool {
FuzzChecker(FuzzCheckerOpt),
/// Add the @check comments to the solutions.
AddSolutionChecks(AddSolutionChecksOpt),
/// Check input/output files against a specification in the `iospec` language.
IospecCheck(iospec_check::Opt),
/// Generate graders or other I/O-related files given a specification in the `iospec` language.
IospecGen(iospec_gen::Opt),
/// Generate standard set of files given an I/O format specification in the `iospec` language.
IospecGenAll(iospec_gen_all::Opt),
/// Run the sandbox instead of the normal task-maker.
///
/// This option is left as undocumented as it's not part of the public API.
Expand Down
1 change: 1 addition & 0 deletions task-maker-exec/tmbox
Submodule tmbox added at c282fb
23 changes: 23 additions & 0 deletions task-maker-iospec/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "task-maker-iospec"
version = "0.5.4"
authors = ["Massimo Cairo <[email protected]>"]
edition = "2021"

[dependencies]
syn = { version = "1.0.92", features = ["extra-traits"] }
proc-macro2 = { version = "1.0.19", features = ["span-locations"] }
annotate-snippets = { version = "0.9.0", features = ["color"] }
codemap = "0.1.3"
num-traits = "0.2.12"
by_address = "1.0.4"
anyhow = "1.0.57"
clap = "3.1.18"
structopt = "0.3.26"

[dev-dependencies]
assert_cmd = "2.0.4"
goldenfile = "1.1.0"
tempdir = "0.3.7"
tempfile = "3.3.0"
walkdir = "2.3.2"
2 changes: 2 additions & 0 deletions task-maker-iospec/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
unstable_features = true
imports_granularity = "Item"
53 changes: 53 additions & 0 deletions task-maker-iospec/src/assets/IOSPEC.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! Sample I/O specification.
//! The input is a graph, with weights on the nodes.
//! The output is a single value, followed by a value for each node.

inputln {
/// Number of nodes
item N: i32;
assume 2 <= N < 100_000;

/// Number of edges
item M: i32;
assume 0 <= M < 500_000;
}

#[cfg(subtask_name = "quadratic")]
assume N <= 1_000;

inputln {
for u upto N {
/// Weight of `u`
item W[u]: i32;
assume 0 <= W[u] < 1_000_000_000;
}
}

for i upto M {
inputln {
/// Tail of `i`-th edge
item A[i]: i32;
/// Head of `i`-th edge
item B[i]: i32;

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

/// No self-loops
assume A[i] != B[i];
}
}

#[cfg(grader)]
/// Compute `S` and `X[u]` for every node `u`
@call solve(N = N, M = M, W = W, A = A, B = B, X = &X) -> S;

outputln {
item S: i32;
}

outputln {
for u upto N {
item X[u]: i32;
}
}
125 changes: 125 additions & 0 deletions task-maker-iospec/src/assets/iolib.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/// Template library to read/write I/O files.
/// Do not modify.

#ifndef GENERATOR_HPP
#define GENERATOR_HPP

#include <iostream>
#include <cassert>

const bool INPUT = 0;
const bool OUTPUT = 1;

template<typename IoData>
void resize_all(IoData const& data) {
bool needs_space = false;
process_io(
const_cast<IoData&>(data),
{},
[](auto stream, auto& value) {},
[](auto stream) {},
[](auto stream, auto value) {},
[](auto stream, auto& value, auto size) {
value.resize(size);
}
);
}

template<typename IoData, typename File = std::ostream>
void write_input(IoData const& data, File& file = std::cout) {
bool needs_space = false;
process_io(
const_cast<IoData&>(data),
{},
[&](auto stream, auto& value) {
if(stream == INPUT) {
if(needs_space) file << " ";
file << value;
needs_space = true;
}
},
[&](auto stream) {
if(stream == INPUT) {
file << std::endl;
needs_space = false;
}
},
[](auto stream, auto value) {},
[](auto stream, auto& value, auto size) {
if (stream == INPUT) {
assert(value.size() == size);
}
value.resize(size);
}
);
}

template<typename IoData, typename File = std::istream>
IoData read_input(File& file = std::cin) {
IoData data;

process_io(
data,
{},
[&](auto stream, auto& value) {
if(stream == INPUT) {
file >> value;
}
},
[](auto stream) {},
[](auto stream, auto value) {},
[](auto stream, auto& value, auto size) {
value.resize(size);
}
);

return data;
}

template<typename IoData, typename File = std::istream>
IoData run_solution(File& file = std::cin) {
IoData data;

process_io(
data,
IoData::global_funs(),
[&](auto stream, auto& value) {
if(stream == INPUT) {
file >> value;
}
},
[](auto stream) {},
[](auto stream, auto value) {},
[](auto stream, auto& value, auto size) {
value.resize(size);
}
);

return data;
}

template<typename IoData, typename IFile = std::istream, typename OFile = std::istream>
IoData read_input_output(IFile& input_file, OFile& output_file) {
IoData data;

process_io(
data,
{},
[&](auto stream, auto& value) {
if(stream == INPUT) {
input_file >> value;
}
if(stream == OUTPUT) {
output_file >> value;
}
},
[](auto stream) {},
[](auto stream, auto value) {
/* */
}
);

return data;
}

#endif
21 changes: 21 additions & 0 deletions task-maker-iospec/src/assets/sample.checker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "iolib.hpp"
#include "iospec.hpp"

#include <cassert>
#include <fstream>

int main(int argc, char** argv) {
std::ifstream input(argv[1]);
std::ifstream correct_output(argv[2]);
std::ifstream submission_output(argv[3]);

IoData correct_data = read_input_output<IoData>(input, correct_output);
IoData submission_data = read_input_output<IoData>(input, submission_output);

// Check `submission_data` against `correct_data`, e.g.:
// assert(submission_data.S == correct_data.S);

// TODO: verify output format and assertions in task-maker, before calling the custom checker

return 0;
}
13 changes: 13 additions & 0 deletions task-maker-iospec/src/assets/sample.generator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "iolib.hpp"
#include "iospec.hpp"

int main(int argc, char** argv) {
IoData data;

// Fill-in `data` with generated values, e.g.:
// data.N = atol(argv[1]);

write_input(data);

return 0;
}
18 changes: 18 additions & 0 deletions task-maker-iospec/src/assets/sample.validator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "iolib.hpp"
#include "iospec.hpp"

#include <cassert>
#include <fstream>

int main(int argc, char** argv) {
std::ifstream input(argv[1]);
auto data = read_input<IoData>(input);

// Check any non-trivial assumptions here, e.g.:
// assert(is_prime_number(data.N));
// assert(is_graph_connected(data));

// TODO: verify input format and assumptions in task-maker, before calling the custom validator

return 0;
}
5 changes: 5 additions & 0 deletions task-maker-iospec/src/bin/iospec-check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use task_maker_iospec::tools::iospec_check::*;

fn main() -> Result<(), anyhow::Error> {
return do_main(clap::Parser::parse(), &mut std::io::stderr());
}
5 changes: 5 additions & 0 deletions task-maker-iospec/src/bin/iospec-gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use task_maker_iospec::tools::iospec_gen::*;

fn main() -> Result<(), anyhow::Error> {
return do_main(clap::Parser::parse(), &mut std::io::stderr());
}
Loading

0 comments on commit f57dd61

Please sign in to comment.