♻️ refactor(reinfo): 重构 diff 解析逻辑并支持多格式

- 将原本分散在 `parse_diff_side_by_side` 与 `parse_pipe_diff_table` 的两套解析逻辑合并,新增统一的 `parse_one_diff_line` 工具函数
- `parse_one_diff_line` 同时识别 `|a|b`、`a | b`、`a<b` / `a>b` 等多种 diff 输出格式,便于后续扩展
- 简化 `parse_diff_blocks` 中 full mode / simple mode 的内联解析流程,消除代码重复
- 优化 `raw_error` 换行符归一化处理,兼容 `\r\n` 与单独 `\r` 结尾的情况
This commit is contained in:
2026-07-08 21:24:08 +08:00
parent 0e5cac3d4e
commit 9b13c95e8a

View File

@@ -117,37 +117,85 @@ function parse_diff_blocks($text) {
$chunk = trim($chunk); $chunk = trim($chunk);
if ($chunk === '') continue; if ($chunk === '') continue;
// full mode: 形如 "========[test1.out]=========" 开头 // 格式 1: 整段就是一个 =====[name]===== 块(包含表头+数据+结尾 ====
if (preg_match('/^=+\[([^\]]+)\]=+$/m', $chunk, $m_head)) { // 形如 "========[test1.out]========\nExpected | Yours\nFail | Fall\n=============================="
$name = $m_head[1]; if (preg_match('/^=+\[([^\]]+)\]\=+(.+?)\n=+\s*$/s', $chunk, $m)) {
// 提取 "------diff out 200 lines-----" 后面的内容 $name = trim($m[1]);
if (preg_match('/------diff out[^\n]*-+\n(.*?)\n=+\s*$/s', $chunk, $m_diff)) { $body = $m[2];
$diff_text = trim($m_diff[1]); // body 形如 "\nExpected | Yours\nFail | Fall"
} else { // 去掉首尾的换行符
continue; $body = trim($body, "\n\r");
$lines_in_body = explode("\n", $body);
$expected = array();
$yours = array();
$found_header = false;
foreach ($lines_in_body as $bl) {
$bl = rtrim($bl);
if ($bl === '') continue;
// 跳过表头
if (!$found_header && preg_match('/^Expected.*\|.*Yours/i', $bl)) {
$found_header = true;
continue;
}
if (!$found_header && (preg_match('/^\|?--\s*\|/', $bl) || trim($bl) === '--')) {
continue;
}
$parsed = parse_one_diff_line($bl);
if ($parsed !== null) {
$expected[] = $parsed[0];
$yours[] = $parsed[1];
$found_header = true;
} else {
$expected[] = $bl;
$yours[] = '';
$found_header = true;
}
} }
$lines = parse_diff_side_by_side($diff_text); if (!empty($expected)) {
if (!empty($lines)) {
$blocks[] = array( $blocks[] = array(
'name' => $name, 'name' => $name,
'expected' => $lines['expected'], 'expected' => $expected,
'yours' => $lines['yours'], 'yours' => $yours,
'full_mode' => true, 'full_mode' => true,
); );
} }
continue; continue;
} }
// simple mode: 形如 "test1.out\n--\n|Expected|Yours\n|--|--\n|..." // 格式 2: simple mode "test1.out\n--\n|Expected|Yours\n|--|--\n|row1|row2"
if (preg_match('/^([^\n|]+)\n--\n\|Expected\|Yours\n\|--\|--\n(.*)$/s', $chunk, $m)) { if (preg_match('/^([^\n=|]+)\n--\n([\s\S]+)$/', $chunk, $m)) {
$name = trim($m[1]); $name = trim($m[1]);
$table = $m[2]; $body = $m[2];
$lines = parse_pipe_diff_table($table); $lines_in_body = explode("\n", $body);
if (!empty($lines)) { $expected = array();
$yours = array();
$found_header = false;
foreach ($lines_in_body as $bl) {
$bl = rtrim($bl);
if ($bl === '') continue;
if (!$found_header && preg_match('/Expected.*\|.*Yours/i', $bl)) {
$found_header = true;
continue;
}
if (!$found_header && (preg_match('/^\|?--\s*\|/', $bl) || trim($bl) === '--')) {
continue;
}
$parsed = parse_one_diff_line($bl);
if ($parsed !== null) {
$expected[] = $parsed[0];
$yours[] = $parsed[1];
$found_header = true;
} else {
$expected[] = $bl;
$yours[] = '';
$found_header = true;
}
}
if (!empty($expected)) {
$blocks[] = array( $blocks[] = array(
'name' => $name, 'name' => $name,
'expected' => $lines['expected'], 'expected' => $expected,
'yours' => $lines['yours'], 'yours' => $yours,
'full_mode' => false, 'full_mode' => false,
); );
} }
@@ -157,47 +205,40 @@ function parse_diff_blocks($text) {
} }
/** /**
* 解析 "|expected|yours\n" 格式的表格 * 解析单行 "expected | yours" 格式
* 支持 "|a|b"、"a | b"、"a|b" 等
* 返回 [left, right] 或 null无法解析
*/ */
function parse_pipe_diff_table($text) { function parse_one_diff_line($line) {
$expected = array(); $line = rtrim($line);
$yours = array(); if ($line === '') return null;
$lines = explode("\n", $text);
foreach ($lines as $line) { // 格式 A: "|a|b" (首尾带 |)
if (strlen($line) < 2 || $line[0] !== '|') continue; if ($line[0] === '|') {
$rest = substr($line, 1); $rest = substr($line, 1);
$pos = strpos($rest, '|'); $pos = strpos($rest, '|');
if ($pos === false) continue; if ($pos !== false) {
$left = substr($rest, 0, $pos); return array(substr($rest, 0, $pos), substr($rest, $pos + 1));
$right = substr($rest, $pos + 1);
$expected[] = $left;
$yours[] = $right;
}
if (empty($expected)) return array();
return array('expected' => $expected, 'yours' => $yours);
}
/**
* 解析 `diff -y` 格式: " < expected > yours" 或 " expected | yours"
*/
function parse_diff_side_by_side($text) {
$expected = array();
$yours = array();
$lines = explode("\n", $text);
foreach ($lines as $line) {
if (preg_match('/^(.*?)\s+[<>|]\s+(.*)$/', $line, $m)) {
$expected[] = $m[1];
$yours[] = $m[2];
} elseif (trim($line) === '') {
// 空行:两边都空
} else {
// 单边(一边空):整行属于另一边
$expected[] = $line;
$yours[] = '';
} }
} }
if (empty($expected)) return array();
return array('expected' => $expected, 'yours' => $yours); // 格式 B: "a | b" 或 "a | b"(带空格的 markdown 风格)
// 注意:可能行内有空格,所以用 " | " 或 " | " 等宽松匹配
if (preg_match('/^(.*?)\s+\|\s+(.*)$/', $line, $m)) {
// 确保左右两边都非空
$left = trim($m[1]);
$right = trim($m[2]);
if ($left !== '' || $right !== '') {
return array($left, $right);
}
}
// 格式 C: "a<b" 或 "a>b"diff -y 风格)
if (preg_match('/^(.*?)\s+([<>])\s+(.*)$/', $line, $m)) {
return array($m[1], $m[3]);
}
return null;
} }
$view_reinfo = ""; $view_reinfo = "";
@@ -218,7 +259,9 @@ if($ok){
$rt = pdo_query($sql,$id); $rt = pdo_query($sql,$id);
$raw_error = ""; $raw_error = "";
if(isset($rt[0])){ if(isset($rt[0])){
$raw_error = str_replace("\n\r","\n",$rt[0]['error']); $raw_error = $rt[0]['error'];
// 统一换行符:先替换 \r\n再替换单独的 \r
$raw_error = str_replace(array("\r\n", "\r"), "\n", $raw_error);
} }
// === 阶段 1基于 solution.result 给"主解释"(永远非空)=== // === 阶段 1基于 solution.result 给"主解释"(永远非空)===