107 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			107 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
require_once "PicturedTask.php";
 | 
						|
 | 
						|
class VerilogTask extends PicturedTask
 | 
						|
{
 | 
						|
    private string $test_bench_fn; // test bench file name
 | 
						|
    private string $explanation; // short explanation for the marking
 | 
						|
    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->explanation = $a["explanation"] ?? "";
 | 
						|
        $this->test_bench_fn = $a["test_bench_fn"] ?? "";
 | 
						|
    }
 | 
						|
 | 
						|
    private function verifyCode(): bool
 | 
						|
    {
 | 
						|
        // check that no $function calls are in the code
 | 
						|
        if (str_contains($this->player_answer, "$")) {
 | 
						|
            $this->explanation .= "A kód nem tartalmazhat \$függvényhívásokat!\n";
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    private function executeTest(): bool
 | 
						|
    {
 | 
						|
        // 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 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->explanation .= "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->explanation .= substr($line, $failed_trimlen) . "\n";
 | 
						|
                    $failed_count++;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if ($failed_count == 0) {
 | 
						|
                $this->explanation .= "Minden rendben! :)";
 | 
						|
            } else {
 | 
						|
                $this->explanation = "$failed_count db hiba:\n\n" . $this->explanation;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        cleanup:
 | 
						|
        // remove the temporary files
 | 
						|
        @unlink($module_code_fn);
 | 
						|
        @unlink($test_bench_fn);
 | 
						|
        @unlink($output_fn);
 | 
						|
 | 
						|
        return $failed_count == 0;
 | 
						|
    }
 | 
						|
 | 
						|
    public function staticCheck(): void
 | 
						|
    {
 | 
						|
        $this->explanation = "";
 | 
						|
 | 
						|
        // verify code
 | 
						|
        $mark = 0.0;
 | 
						|
        if ($this->verifyCode()) {
 | 
						|
            // run the simulation
 | 
						|
            $test_ok = $this->executeTest();
 | 
						|
 | 
						|
            $mark = $test_ok ? 1.0 : 0.0;  // FIXME
 | 
						|
        }
 | 
						|
        $this->setMark($mark);
 | 
						|
    }
 | 
						|
 | 
						|
    public function toArray(string $mode = "all"): array
 | 
						|
    {
 | 
						|
        $a = parent::toArray($mode);
 | 
						|
 | 
						|
        $a["explanation"] = $this->explanation;
 | 
						|
 | 
						|
        if ($mode == "all") {
 | 
						|
            $a["test_bench_fn"] = $this->test_bench_fn;
 | 
						|
        }
 | 
						|
 | 
						|
        return $a;
 | 
						|
    }
 | 
						|
}
 |