From fa092d334cdcd9560a5918372b63ba5df06eb33c Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Wed, 30 Sep 2026 11:22:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BC=96=E8=AF=91=E9=94=99=E8=AF=AF):=20C?= =?UTF-8?q?=20/=20C++=20=E7=BC=96=E8=AF=91=E6=B2=A1=E8=BF=87=E6=97=B6?= =?UTF-8?q?=E4=B9=9F=E7=BB=99=E4=B8=AD=E6=96=87=E8=AF=B4=E6=98=8E=E3=80=81?= =?UTF-8?q?=E6=A0=87=E5=87=BA=E5=87=BA=E9=94=99=E4=BD=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E6=A0=87=E7=82=B9=E8=83=BD=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E6=8D=A2=E6=88=90=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来 C / C++ 的编译错误学生看到的是 gcc 的英文原文。现在和 Python 一样一张翻译表 (cError.ts),照全库 2851 条 C / C++ 编译错误用现在的判题机(gcc-14)重新编译出来的 报错定的,只看第一条 error,98% 能给出说明。 三件事先于句式判,因为 gcc 对同一个错报法五花八门: - 交成了 Python 代码(330 条):gcc 报的是 request for member 之类,直接说换语言; - C 里写了 C++(iostream、cout); - 中文标点:gcc-14 不再报 stray,而是把「;」吞进变量名、错报到下一行,所以直接在 代码里找引号和注释之外的中文标点,行号按代码算。中文标点查找认 C 的 // 和 /* */。 句式上:缺分号报在下一句开头的往回挪一行,把分号写成冒号、关键字拼错(coutinue、 RETURN、return0)、文字没加引号、#include 拼错、main 外面写语句等各有说法。 gcc 的 did you mean 会从库函数里挑(num → enum),只信学生代码里有的名字。 列范围按 gcc 的显示宽度换算(中文占两格),含中文的行也能标到具体位置。 卡片改名 CompileErrorExplain,提交结果、运行例子、提交列表弹框三处都接上了。 Co-Authored-By: Claude Opus 5.5 --- ...rorExplain.vue => CompileErrorExplain.vue} | 36 +- .../src/oj/problem/components/ResultPane.vue | 4 +- .../problem/components/SubmissionResult.vue | 23 +- .../oj/problem/components/TrialErrorNote.vue | 11 +- apps/web/src/oj/problem/utils/cError.ts | 434 ++++++++++++++++++ .../oj/problem/utils/chinesePunctuation.ts | 22 +- apps/web/src/oj/store/submission.ts | 2 +- .../submission/components/SubmissionPane.vue | 11 +- ...2026-09-28-problem-page-redesign-design.md | 4 +- 9 files changed, 508 insertions(+), 39 deletions(-) rename apps/web/src/oj/problem/components/{PythonErrorExplain.vue => CompileErrorExplain.vue} (77%) create mode 100644 apps/web/src/oj/problem/utils/cError.ts diff --git a/apps/web/src/oj/problem/components/PythonErrorExplain.vue b/apps/web/src/oj/problem/components/CompileErrorExplain.vue similarity index 77% rename from apps/web/src/oj/problem/components/PythonErrorExplain.vue rename to apps/web/src/oj/problem/components/CompileErrorExplain.vue index 4876d87..3947a62 100644 --- a/apps/web/src/oj/problem/components/PythonErrorExplain.vue +++ b/apps/web/src/oj/problem/components/CompileErrorExplain.vue @@ -9,24 +9,34 @@ import { showErrorMark, showPunctuationMarks, } from "oj/problem/utils/errorMark" +import { explainCCompileError } from "oj/problem/utils/cError" import { explainPythonCompileError } from "oj/problem/utils/pythonError" /** - * Python 语法错误的中文说明卡片。两处用它:提交前的语法检查(SubmitCode,报错来自 - * 服务端的 CPython)和判题回来的编译错误(SubmissionResult,来自判题机)。两边是 - * 同一个大版本的 CPython,报错原文一样,所以共用一张翻译表。 + * 编译错误的中文说明卡片:Python 的语法错误走 pythonError.ts,C / C++ 的 gcc 报错走 + * cError.ts,两张表给出同一种形状。Python 有两处来源:提交前的语法检查(SubmitCode, + * 报错来自服务端的 CPython)和判题回来的编译错误(来自判题机),同一个大版本的 + * CPython,报错原文一样。 * * 挂上就在编辑器里标出位置,卸掉就清掉。 */ -const props = defineProps<{ - /** CPython 的报错原文(判题机的 `err_info` 或 `/code/format` 的 syntax-error) */ - errInfo: string -}>() +const props = withDefaults( + defineProps<{ + /** 编译器的报错原文(判题机的 `err_info`,或 `/code/format` 的 syntax-error) */ + errInfo: string + language?: string + }>(), + { language: "Python" }, +) const codeStore = useCodeStore() const theme = useThemeVars() -const explanation = computed(() => explainPythonCompileError(props.errInfo, codeStore.code.value)) +const explanation = computed(() => + props.language === "Python" + ? explainPythonCompileError(props.errInfo, codeStore.code.value) + : explainCCompileError(props.errInfo, codeStore.code.value, props.language), +) /** 判题机的临时目录名(`/judger/run/<32 位随机串>/`)对学生没有意义,只剩文件名 */ const rawError = computed(() => props.errInfo.replace(/\/judger\/run\/[^/"]+\//g, "")) @@ -41,7 +51,7 @@ const otherPunctuationLines = computed(() => { if (!ex?.punctuation) return [] const code = codeStore.code.value const lines = new Set() - for (const fix of findChinesePunctuation(code)) { + for (const fix of findChinesePunctuation(code, props.language)) { lines.add(code.slice(0, fix.from).split("\n").length) } lines.delete(ex.line ?? -1) @@ -50,14 +60,16 @@ const otherPunctuationLines = computed(() => { // 数的是编辑器里**现在**的代码:学生可能已经自己改掉几处了 const punctuationFixCount = computed(() => - explanation.value?.punctuation ? findChinesePunctuation(codeStore.code.value).length : 0, + explanation.value?.punctuation + ? findChinesePunctuation(codeStore.code.value, props.language).length + : 0, ) const fixedCount = ref(null) function fixPunctuation() { const code = currentCode() if (code === null) return - const fixes = findChinesePunctuation(code) + const fixes = findChinesePunctuation(code, props.language) if (applyFixes(fixes)) fixedCount.value = fixes.length } @@ -69,7 +81,7 @@ watch( else clearErrorMark() // 中文标点一处不漏地标出来,和按钮上的「N 处」对得上 const code = ex?.punctuation ? currentCode() : null - if (code !== null) showPunctuationMarks(findChinesePunctuation(code)) + if (code !== null) showPunctuationMarks(findChinesePunctuation(code, props.language)) }, { immediate: true }, ) diff --git a/apps/web/src/oj/problem/components/ResultPane.vue b/apps/web/src/oj/problem/components/ResultPane.vue index 448b134..79bd679 100644 --- a/apps/web/src/oj/problem/components/ResultPane.vue +++ b/apps/web/src/oj/problem/components/ResultPane.vue @@ -21,7 +21,7 @@ import ResultHeader from "./ResultHeader.vue" // 第一次切到「结果」时才加载:它带着 DataTable 和 Markdown 渲染,只看题不提交的人不用付 const SubmissionResult = defineAsyncComponent(() => import("./SubmissionResult.vue")) -const PythonErrorExplain = defineAsyncComponent(() => import("./PythonErrorExplain.vue")) +const CompileErrorExplain = defineAsyncComponent(() => import("./CompileErrorExplain.vue")) const SampleRunResult = defineAsyncComponent(() => import("./SampleRunResult.vue")) const CustomRun = defineAsyncComponent(() => import("./CustomRun.vue")) const FlowchartResult = defineAsyncComponent(() => import("./FlowchartResult.vue")) @@ -136,7 +136,7 @@ function pick(segment: Segment) {