clang-format 不认中文分号、缺了分号的语句,会把它和下一行并起来(int n; 和 scanf 成了 一行),报错的行号也对不上学生记得的位置。现在 C / C++ 格式化之前先套上题目模板、在 判题机上编一次,编不过就原样返回、不格式化,代码照常提交、照常判成编译错误。 api 容器里没有 gcc,判题机的编译器和参数本来就是判的标准,所以借它编。这一下不进判题 队列、不建提交记录;判题机连不上时照旧格式化。代价是每次交 C 多一次编译,零点几秒。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fa092d334c
commit
c2ef55b10b
5 files changed
+51
-3
No files matched your search
@@ -112,6 +112,27 @@ export async function postJudge(
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 这份代码在判题机上编不编得过。给提交前的自动格式化用:clang-format 碰上编译不过的
|
||||
* C 代码会把行并起来(`int n;` 和下一行、少了分号的两行),学生回头看到的就不是自己
|
||||
* 写的代码了,报错的行号也对不上他记得的位置。api 容器里没有 gcc,而判题机的编译器、
|
||||
* 编译参数本来就是判的标准,所以借它编一次。
|
||||
*
|
||||
* 判题机只有 /judge 一个入口,得带一组测试点:给一组空输入,跑的那一下限 1 秒。
|
||||
* 判题机连不上、超时,一律当编得过 —— 那就和原来一样照常格式化。
|
||||
*/
|
||||
export async function compileFails(language: string, source: string) {
|
||||
try {
|
||||
const response = await postJudge(language, source, 1000, 256, [{ input: "", output: "" }], {
|
||||
signal: AbortSignal.timeout(8000),
|
||||
})
|
||||
const body = (await response.json()) as JudgeResponse
|
||||
return body.err === "CompileError"
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function requestJudge(
|
||||
language: string,
|
||||
code: string,
|
||||
|
||||
@@ -17,7 +17,9 @@ import { optionalAuth, requireAuth, requireTeacher } from "../auth/middleware"
|
||||
import type { AuthUser } from "../auth/session"
|
||||
import { db, schema } from "../db"
|
||||
import { failure, parseBody, success } from "../http"
|
||||
import { compileFails, templateForLanguage } from "../judge/run"
|
||||
import { JudgeStatus } from "../judge/status"
|
||||
import { parseProblemTemplate } from "../judge/template"
|
||||
import { judgeQueue } from "../queue"
|
||||
import {
|
||||
canAccessContest,
|
||||
@@ -244,8 +246,28 @@ submissionRoutes.post("/submissions/:id/rejudge", requireTeacher, async (c) => {
|
||||
submissionRoutes.post("/code/format", requireAuth, async (c) => {
|
||||
const parsed = await parseBody(c, formatCodeRequestSchema, "Invalid format payload")
|
||||
if (!parsed.success) return parsed.response
|
||||
// C / C++ 编不过就原样返回,不格式化(理由见 judge/run.ts 的 compileFails)。
|
||||
// 代码照常提交、照常判成编译错误,只是不改动学生的代码
|
||||
const { code: source, language, problemId } = parsed.data
|
||||
if (language === "c" || language === "cpp") {
|
||||
const judgeLanguage = language === "c" ? "C" : "C++"
|
||||
const [problem] =
|
||||
problemId === undefined
|
||||
? []
|
||||
: await db
|
||||
.select({ template: schema.problem.template })
|
||||
.from(schema.problem)
|
||||
.where(eq(schema.problem.id, problemId))
|
||||
.limit(1)
|
||||
const rawTemplate = problem ? templateForLanguage(problem.template, judgeLanguage) : null
|
||||
const template = rawTemplate ? parseProblemTemplate(rawTemplate) : null
|
||||
const full = template ? `${template.prepend}\n${source}\n${template.append}` : source
|
||||
if (await compileFails(judgeLanguage, full)) {
|
||||
return success(c, { code: source } satisfies FormatCodeResponse)
|
||||
}
|
||||
}
|
||||
try {
|
||||
const code = await formatCode(parsed.data.code, parsed.data.language)
|
||||
const code = await formatCode(source, language)
|
||||
return success(c, { code } satisfies FormatCodeResponse)
|
||||
} catch (error) {
|
||||
if (error instanceof CodeFormatError) {
|
||||
|
||||
@@ -134,7 +134,7 @@ export function submitCode(data: SubmitCodePayload) {
|
||||
return api.post<CreateSubmissionResponse>("submissions", data)
|
||||
}
|
||||
|
||||
export function formatCode(data: { code: string; language: string }) {
|
||||
export function formatCode(data: { code: string; language: string; problemId: number }) {
|
||||
const languages: Record<string, string> = {
|
||||
Python: "python",
|
||||
C: "c",
|
||||
@@ -144,6 +144,7 @@ export function formatCode(data: { code: string; language: string }) {
|
||||
return api.post<FormatCodeResponse>("code/format", {
|
||||
code: data.code,
|
||||
language: languages[data.language] ?? data.language.toLowerCase(),
|
||||
problemId: data.problemId,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,8 @@ export const useSubmissionStore = defineStore("submission", () => {
|
||||
formattedBeforeSubmit.value = false
|
||||
|
||||
// 0. 提交前自动格式化(Python 用 ruff,C/C++ 用 clang-format,SQL 用 sqlparse)。
|
||||
// Python 在格式化之前先由服务端的 CPython 查一遍语法,有错就不提交
|
||||
// Python 在格式化之前先由服务端的 CPython 查一遍语法,有错就不提交;
|
||||
// C / C++ 先在判题机上编一遍,编不过就原样交(格式化会把错的几行并成一行)
|
||||
const formatLang = LANGUAGE_FORMAT_VALUE[codeStore.code.language]
|
||||
if (["python", "c", "cpp", "sql"].includes(formatLang)) {
|
||||
isFormatting.value = true
|
||||
@@ -111,6 +112,7 @@ export const useSubmissionStore = defineStore("submission", () => {
|
||||
const res = await formatCode({
|
||||
code: codeStore.code.value,
|
||||
language: formatLang,
|
||||
problemId: problem.id,
|
||||
})
|
||||
if (stale()) return
|
||||
if (res.code !== codeStore.code.value) {
|
||||
|
||||
@@ -534,6 +534,8 @@ export const todaySubmissionStatisticsSchema = z.object({
|
||||
export const formatCodeRequestSchema = z.object({
|
||||
code: z.string().max(1024 * 1024),
|
||||
language: z.enum(["python", "c", "cpp", "sql"]),
|
||||
/** C / C++ 格式化前要先编一遍,得套上这道题的模板才编得过 */
|
||||
problemId: z.number().int().optional(),
|
||||
})
|
||||
|
||||
export const formatCodeResponseSchema = z.object({ code: z.string() })
|
||||
|
||||
Reference in new issue
Block a user