原来 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
@@ -0,0 +1,152 @@
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { useCodeStore } from "oj/store/code"
|
||||
import { findChinesePunctuation } from "oj/problem/utils/chinesePunctuation"
|
||||
import {
|
||||
applyFixes,
|
||||
clearErrorMark,
|
||||
currentCode,
|
||||
showErrorMark,
|
||||
showPunctuationMarks,
|
||||
} from "oj/problem/utils/errorMark"
|
||||
import { explainCCompileError } from "oj/problem/utils/cError"
|
||||
import { explainPythonCompileError } from "oj/problem/utils/pythonError"
|
||||
|
||||
/**
|
||||
* 编译错误的中文说明卡片:Python 的语法错误走 pythonError.ts,C / C++ 的 gcc 报错走
|
||||
* cError.ts,两张表给出同一种形状。Python 有两处来源:提交前的语法检查(SubmitCode,
|
||||
* 报错来自服务端的 CPython)和判题回来的编译错误(来自判题机),同一个大版本的
|
||||
* CPython,报错原文一样。
|
||||
*
|
||||
* 挂上就在编辑器里标出位置,卸掉就清掉。
|
||||
*/
|
||||
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(() =>
|
||||
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, ""))
|
||||
|
||||
/**
|
||||
* 报错只点名第一处。别的行也有中文标点的话,一句话带过(设计稿「语法没过」:
|
||||
* 「第 4 行也有一个中文冒号,编辑器里都标出来了」)。原来这里还摆一段出错那行的代码摘录,
|
||||
* 编辑器里已经标着红,设计稿里去掉了
|
||||
*/
|
||||
const otherPunctuationLines = computed(() => {
|
||||
const ex = explanation.value
|
||||
if (!ex?.punctuation) return []
|
||||
const code = codeStore.code.value
|
||||
const lines = new Set<number>()
|
||||
for (const fix of findChinesePunctuation(code, props.language)) {
|
||||
lines.add(code.slice(0, fix.from).split("\n").length)
|
||||
}
|
||||
lines.delete(ex.line ?? -1)
|
||||
return [...lines].sort((a, b) => a - b)
|
||||
})
|
||||
|
||||
// 数的是编辑器里**现在**的代码:学生可能已经自己改掉几处了
|
||||
const punctuationFixCount = computed(() =>
|
||||
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, props.language)
|
||||
if (applyFixes(fixes)) fixedCount.value = fixes.length
|
||||
}
|
||||
|
||||
watch(
|
||||
explanation,
|
||||
(ex) => {
|
||||
fixedCount.value = null
|
||||
if (ex?.line) showErrorMark(ex.line, ex.sourceLine, ex.caret)
|
||||
else clearErrorMark()
|
||||
// 中文标点一处不漏地标出来,和按钮上的「N 处」对得上
|
||||
const code = ex?.punctuation ? currentCode() : null
|
||||
if (code !== null) showPunctuationMarks(findChinesePunctuation(code, props.language))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
onUnmounted(clearErrorMark)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="explain-card">
|
||||
<template v-if="explanation">
|
||||
<p class="explain">
|
||||
<b v-if="explanation.line">第 {{ explanation.line }} 行:</b>{{ explanation.message }}
|
||||
</p>
|
||||
<p v-if="otherPunctuationLines.length" class="explain other">
|
||||
第 {{ otherPunctuationLines.join("、") }} 行也有中文标点,编辑器里都标出来了。
|
||||
</p>
|
||||
<div v-if="explanation.punctuation">
|
||||
<n-button v-if="punctuationFixCount" type="primary" @click="fixPunctuation">
|
||||
把中文标点都换成英文({{ punctuationFixCount }} 处)
|
||||
</n-button>
|
||||
<n-text v-else-if="fixedCount" type="success">
|
||||
换好了 {{ fixedCount }} 处,再提交一次试试。
|
||||
</n-text>
|
||||
</div>
|
||||
<n-collapse>
|
||||
<n-collapse-item title="原始报错(英文)" name="raw">
|
||||
<div class="raw">{{ rawError }}</div>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
</template>
|
||||
<!-- 翻译表没覆盖到的句式(生产数据里约 0.3%):照旧给原文 -->
|
||||
<template v-else>
|
||||
<p class="explain">请仔细检查,看看代码的格式是不是写错了!</p>
|
||||
<div class="raw">{{ rawError }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 和答错的说明同一种浅灰卡片(设计稿「语法没过」) */
|
||||
.explain-card {
|
||||
padding: 12px 14px;
|
||||
border-radius: 6px;
|
||||
background-color: rgba(128, 128, 128, 0.06);
|
||||
border: 1px solid v-bind("theme.dividerColor");
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.explain {
|
||||
margin: 0;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.explain.other {
|
||||
font-size: 14px;
|
||||
color: v-bind("theme.textColor2");
|
||||
}
|
||||
|
||||
.raw {
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
line-height: 1.5;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user