122 lines
4.0 KiB
PHP
122 lines
4.0 KiB
PHP
<?php
|
|
|
|
require_once "PicturedTask.php";
|
|
|
|
class VerilogTask extends PicturedTask
|
|
{
|
|
private string $test_bench_fn; // test bench file name
|
|
private string $compile_log; // short explanation for the marking
|
|
private float $error_score; // points received for an error
|
|
private const FAILED_MARK = "[FAILED]"; // mark at the beginning of testbench results indicating a failed combination
|
|
|
|
public function __construct(array &$a = null)
|
|
{
|
|
parent::__construct("verilog", $a);
|
|
|
|
$this->compile_log = $a["compile_log"] ?? "";
|
|
$this->test_bench_fn = $a["test_bench_fn"] ?? "";
|
|
$this->error_score = $a["error_score"] ?? -1.0;
|
|
}
|
|
|
|
private function verifyCode(): bool
|
|
{
|
|
// check that no $function calls are in the code
|
|
if (str_contains($this->player_answer, "$")) {
|
|
$this->compile_log .= "A kód nem tartalmazhat \$függvényhívásokat!\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private function executeTest(): int
|
|
{
|
|
// store the user's answer
|
|
$module_code_fn = tempnam(sys_get_temp_dir(), "verilogtask_user_module_");
|
|
file_put_contents($module_code_fn, $this->getPlayerAnswer());
|
|
|
|
// modify the test bench and save into a separate file
|
|
$test_bench_fn = tempnam(sys_get_temp_dir(), "verilogtask_test_bench_");
|
|
$include_line = "`include \"$module_code_fn\"\n\n";
|
|
$tb = $include_line . file_get_contents($this->getGameDir() . DIRECTORY_SEPARATOR . $this->test_bench_fn);
|
|
file_put_contents($test_bench_fn, $tb);
|
|
|
|
// run the simulation
|
|
$output_fn = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid("verilogtask_output_");
|
|
$iverilog_cmd = "iverilog $test_bench_fn -o $output_fn -g2012 2>&1";
|
|
$compilation_log = shell_exec($iverilog_cmd);
|
|
$failed_count = 0;
|
|
if (!is_null($compilation_log)) {
|
|
$compilation_log = str_replace([$module_code_fn, $test_bench_fn], ["[kód]", "[tesztkörnyezet]"], $compilation_log);
|
|
$this->compile_log .= "Fordítási hiba:\n\n" . (string)($compilation_log);
|
|
$failed_count = PHP_INT_MAX;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (file_exists($output_fn)) {
|
|
$tb_output = shell_exec("$output_fn");
|
|
$tb_output_lines = array_map(fn($line) => trim($line), explode("\n", $tb_output));
|
|
$failed_trimlen = strlen(self::FAILED_MARK);
|
|
foreach ($tb_output_lines as $line) {
|
|
if (str_starts_with($line, self::FAILED_MARK)) {
|
|
$this->compile_log .= substr($line, $failed_trimlen) . "\n";
|
|
$failed_count++;
|
|
}
|
|
}
|
|
|
|
if ($failed_count == 0) {
|
|
$this->compile_log .= "Minden rendben! :)";
|
|
} else {
|
|
$this->compile_log = "$failed_count db hiba:\n\n" . $this->compile_log;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
// remove the temporary files
|
|
@unlink($module_code_fn);
|
|
@unlink($test_bench_fn);
|
|
@unlink($output_fn);
|
|
|
|
return $failed_count;
|
|
}
|
|
|
|
public function setErrorScore(float $error_score): void
|
|
{
|
|
$this->error_score = $error_score;
|
|
}
|
|
|
|
public function getErrorScore(): float
|
|
{
|
|
return $this->error_score;
|
|
}
|
|
|
|
public function staticCheck(): void
|
|
{
|
|
$this->compile_log = "";
|
|
|
|
// verify code
|
|
$mark = 0.0;
|
|
if ($this->verifyCode()) {
|
|
// run the simulation
|
|
$failed_count = $this->executeTest();
|
|
if ($failed_count != PHP_INT_MAX) {
|
|
$mark = $this->getMaxMark() + $failed_count * $this->error_score;
|
|
}
|
|
}
|
|
|
|
$this->setMark($mark);
|
|
}
|
|
|
|
public function toArray(string $mode = "all"): array
|
|
{
|
|
$a = parent::toArray($mode);
|
|
|
|
$a["compile_log"] = $this->compile_log;
|
|
|
|
if ($mode == "all") {
|
|
$a["test_bench_fn"] = $this->test_bench_fn;
|
|
$a["error_score"] = $this->error_score;
|
|
}
|
|
|
|
return $a;
|
|
}
|
|
}
|