原来 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 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c6f2e7611
commit
fa092d334c
9 files changed
+508
-39
No files matched your search
+24
-12
@@ -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<number>()
|
||||
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<number | null>(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 },
|
||||
)
|
||||
@@ -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) {
|
||||
<div v-show="!viewingFlowchart && (resultSegment === 'submit' || !canTrial)">
|
||||
<template v-if="syntaxErrorInfo">
|
||||
<ResultHeader kind="warning" title="代码有语法错误,还没有交上去" />
|
||||
<PythonErrorExplain :err-info="syntaxErrorInfo" />
|
||||
<CompileErrorExplain :err-info="syntaxErrorInfo" />
|
||||
</template>
|
||||
<template v-else-if="submission">
|
||||
<p v-if="formattedBeforeSubmit" class="formatted">
|
||||
|
||||
@@ -15,7 +15,7 @@ import SubmissionResultTag from "shared/components/SubmissionResultTag.vue"
|
||||
import { useProblemStore } from "oj/store/problem"
|
||||
import { useSubmissionStore } from "oj/store/submission"
|
||||
import { markdownToText } from "oj/problem/utils/plainText"
|
||||
import PythonErrorExplain from "./PythonErrorExplain.vue"
|
||||
import CompileErrorExplain from "./CompileErrorExplain.vue"
|
||||
import RuntimeErrorExplain from "./RuntimeErrorExplain.vue"
|
||||
import WrongAnswerExplain from "./WrongAnswerExplain.vue"
|
||||
import LessonNext from "./LessonNext.vue"
|
||||
@@ -62,14 +62,11 @@ function stripJudgePath(text: string) {
|
||||
return text.replace(/\/judger\/run\/[^/"]+\//g, "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Python 的编译错误交给 PythonErrorExplain 翻成中文。只管 Python:C / C++ 的
|
||||
* gcc 报错另是一套句式,还没做。
|
||||
*/
|
||||
const pythonCompileError = computed(() => {
|
||||
/** 编译错误交给 CompileErrorExplain 翻成中文:Python、C、C++ 各有一张翻译表 */
|
||||
const compileError = computed(() => {
|
||||
const submission = props.submission
|
||||
if (!submission || submission.result !== SubmissionStatus.compile_error) return ""
|
||||
if (submission.language !== "Python") return ""
|
||||
if (!["Python", "C", "C++"].includes(submission.language)) return ""
|
||||
return submission.statisticInfo?.err_info ?? ""
|
||||
})
|
||||
|
||||
@@ -117,8 +114,8 @@ const showSimilar = computed(
|
||||
|
||||
const msg = computed(() => {
|
||||
if (!props.submission) return ""
|
||||
// 走 PythonErrorExplain / RuntimeErrorExplain 那两张中文卡片
|
||||
if (pythonCompileError.value || runtimeError.value) return ""
|
||||
// 走 CompileErrorExplain / RuntimeErrorExplain 那两张中文卡片
|
||||
if (compileError.value || runtimeError.value) return ""
|
||||
|
||||
let msg = ""
|
||||
const result = props.submission.result
|
||||
@@ -318,7 +315,7 @@ const columns: DataTableColumn<JudgeCaseResult>[] = [
|
||||
<n-flex
|
||||
vertical
|
||||
v-if="
|
||||
pythonCompileError ||
|
||||
compileError ||
|
||||
runtimeError ||
|
||||
sampleCheck ||
|
||||
msg ||
|
||||
@@ -326,7 +323,11 @@ const columns: DataTableColumn<JudgeCaseResult>[] = [
|
||||
submission.statisticInfo?.ast_results?.length
|
||||
"
|
||||
>
|
||||
<PythonErrorExplain v-if="pythonCompileError" :err-info="pythonCompileError" />
|
||||
<CompileErrorExplain
|
||||
v-if="compileError"
|
||||
:err-info="compileError"
|
||||
:language="submission.language"
|
||||
/>
|
||||
<RuntimeErrorExplain v-if="runtimeError" :info="runtimeError" :code="submission.code" />
|
||||
<WrongAnswerExplain
|
||||
v-if="sampleCheck"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { SubmissionStatus } from "utils/constants"
|
||||
import type { StatisticInfo } from "@oj2/contract"
|
||||
import PythonErrorExplain from "./PythonErrorExplain.vue"
|
||||
import CompileErrorExplain from "./CompileErrorExplain.vue"
|
||||
import RuntimeErrorExplain from "./RuntimeErrorExplain.vue"
|
||||
|
||||
/**
|
||||
* 试跑没跑完时给学生看的中文说明。判题机回来的是英文原文(Traceback、gcc 的报错),
|
||||
* 学生读不懂,原文收进折叠区。Python 的语法错误是判题机上 CPython 的原文,和提交判出来的
|
||||
* 一样,直接交给 PythonErrorExplain(中文说明 + 编辑器里标出位置 + 一键换中文标点)。
|
||||
* 一样,直接交给 CompileErrorExplain(中文说明 + 编辑器里标出位置 + 一键换中文标点),
|
||||
* C / C++ 的 gcc 报错同理。
|
||||
*/
|
||||
const props = defineProps<{
|
||||
result: SubmissionStatus
|
||||
@@ -35,11 +36,11 @@ const runtimeExplain = computed(
|
||||
!!props.runtimeError,
|
||||
)
|
||||
|
||||
const pythonSyntaxError = computed(
|
||||
const compileError = computed(
|
||||
() =>
|
||||
!props.note &&
|
||||
props.result === SubmissionStatus.compile_error &&
|
||||
props.language === "Python" &&
|
||||
["Python", "C", "C++"].includes(props.language) &&
|
||||
!!props.output,
|
||||
)
|
||||
|
||||
@@ -67,7 +68,7 @@ const rawOutput = computed(() => props.output.replace(/\/judger\/run\/[^/"]+\//g
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PythonErrorExplain v-if="pythonSyntaxError" :err-info="output" />
|
||||
<CompileErrorExplain v-if="compileError" :err-info="output" :language="language" />
|
||||
<n-flex v-else vertical :size="8">
|
||||
<RuntimeErrorExplain v-if="runtimeExplain" :info="runtimeError!" :code="code ?? ''" />
|
||||
<n-alert v-else type="error" :show-icon="false">{{ message }}</n-alert>
|
||||
|
||||
Reference in new issue
Block a user