191 行
5.4 KiB
TypeScript
191 行
5.4 KiB
TypeScript
export type Cpp14PolicySeverity = "error" | "warning" | "hint";
|
||
|
||
export type Cpp14PolicyIssue = {
|
||
id: string;
|
||
severity: Cpp14PolicySeverity;
|
||
message: string;
|
||
detail: string;
|
||
line: number;
|
||
column: number;
|
||
endLine: number;
|
||
endColumn: number;
|
||
};
|
||
|
||
type RegexRule = {
|
||
id: string;
|
||
severity: Cpp14PolicySeverity;
|
||
pattern: RegExp;
|
||
message: string;
|
||
detail: string;
|
||
};
|
||
|
||
const REGEX_RULES: RegexRule[] = [
|
||
{
|
||
id: "cpp17-header",
|
||
severity: "error",
|
||
pattern: /#\s*include\s*<\s*(optional|variant|any|string_view|filesystem|charconv|execution)\s*>/g,
|
||
message: "检测到 C++17+ 头文件",
|
||
detail: "福建 CSP-J/S 环境通常以 C++14 为准,建议改为 C++14 可用写法。",
|
||
},
|
||
{
|
||
id: "if-constexpr",
|
||
severity: "error",
|
||
pattern: /\bif\s+constexpr\b/g,
|
||
message: "检测到 if constexpr(C++17)",
|
||
detail: "请改用普通条件分支或模板特化方案。",
|
||
},
|
||
{
|
||
id: "structured-binding",
|
||
severity: "error",
|
||
pattern: /\b(?:const\s+)?auto(?:\s*&|\s*&&)?\s*\[[^\]\n]+\]\s*=/g,
|
||
message: "检测到结构化绑定(C++17)",
|
||
detail: "可改为 pair.first/second 或自定义结构体字段。",
|
||
},
|
||
{
|
||
id: "cpp17-stdlib",
|
||
severity: "error",
|
||
pattern: /\bstd::(optional|variant|any|string_view|filesystem|byte|clamp|gcd|lcm)\b/g,
|
||
message: "检测到 C++17+ 标准库符号",
|
||
detail: "请替换为 C++14 可用实现,避免提交到老版本 GCC 报 CE。",
|
||
},
|
||
{
|
||
id: "void-main",
|
||
severity: "error",
|
||
pattern: /\bvoid\s+main\s*\(/g,
|
||
message: "main 函数返回类型不规范",
|
||
detail: "请使用 int main(),并在末尾 return 0;",
|
||
},
|
||
{
|
||
id: "windows-i64d",
|
||
severity: "warning",
|
||
pattern: /%I64d/g,
|
||
message: "检测到 %I64d(Windows 特有)",
|
||
detail: "Linux 评测机请使用 %lld 读写 long long。",
|
||
},
|
||
{
|
||
id: "windows-int64",
|
||
severity: "warning",
|
||
pattern: /\b__int64\b/g,
|
||
message: "检测到 __int64(非标准)",
|
||
detail: "建议改为标准类型 long long。",
|
||
},
|
||
{
|
||
id: "bits-header",
|
||
severity: "warning",
|
||
pattern: /#\s*include\s*<\s*bits\/stdc\+\+\.h\s*>/g,
|
||
message: "检测到 <bits/stdc++.h>",
|
||
detail: "福建实战建议优先使用标准头文件,提升环境兼容性。",
|
||
},
|
||
];
|
||
|
||
export const CSP_CPP14_TIPS: string[] = [
|
||
"编译标准固定为 C++14(建议按 -std=gnu++14 习惯编码),不要使用 C++17+ 特性。",
|
||
"main 必须是 int main(),结尾写 return 0;。",
|
||
"long long 的 scanf/printf 请使用 %lld,不要使用 %I64d。",
|
||
"命名/提交包按考场须知执行:题目目录与源码名使用英文小写。",
|
||
"福建二轮常见要求是文件读写(freopen),赛前请按官方样例再核对一次。",
|
||
];
|
||
|
||
function buildLineStarts(text: string): number[] {
|
||
const starts = [0];
|
||
for (let i = 0; i < text.length; i += 1) {
|
||
if (text[i] === "\n") starts.push(i + 1);
|
||
}
|
||
return starts;
|
||
}
|
||
|
||
function offsetToPosition(lineStarts: number[], offset: number): { line: number; column: number } {
|
||
let lo = 0;
|
||
let hi = lineStarts.length - 1;
|
||
while (lo <= hi) {
|
||
const mid = (lo + hi) >> 1;
|
||
if (lineStarts[mid] <= offset) lo = mid + 1;
|
||
else hi = mid - 1;
|
||
}
|
||
const lineIdx = Math.max(0, hi);
|
||
return {
|
||
line: lineIdx + 1,
|
||
column: offset - lineStarts[lineIdx] + 1,
|
||
};
|
||
}
|
||
|
||
function pushIssue(
|
||
issues: Cpp14PolicyIssue[],
|
||
id: string,
|
||
severity: Cpp14PolicySeverity,
|
||
message: string,
|
||
detail: string,
|
||
line: number,
|
||
column: number,
|
||
endLine: number,
|
||
endColumn: number
|
||
) {
|
||
issues.push({ id, severity, message, detail, line, column, endLine, endColumn });
|
||
}
|
||
|
||
export function analyzeCpp14Policy(code: string): Cpp14PolicyIssue[] {
|
||
const text = (code ?? "").replace(/\r\n?/g, "\n");
|
||
if (!text.trim()) return [];
|
||
|
||
const issues: Cpp14PolicyIssue[] = [];
|
||
const lineStarts = buildLineStarts(text);
|
||
|
||
for (const rule of REGEX_RULES) {
|
||
const matcher = new RegExp(rule.pattern.source, rule.pattern.flags);
|
||
let match = matcher.exec(text);
|
||
while (match) {
|
||
const start = match.index;
|
||
const end = start + Math.max(1, match[0].length);
|
||
const p1 = offsetToPosition(lineStarts, start);
|
||
const p2 = offsetToPosition(lineStarts, end);
|
||
pushIssue(
|
||
issues,
|
||
rule.id,
|
||
rule.severity,
|
||
rule.message,
|
||
rule.detail,
|
||
p1.line,
|
||
p1.column,
|
||
p2.line,
|
||
p2.column
|
||
);
|
||
if (matcher.lastIndex === match.index) matcher.lastIndex += 1;
|
||
match = matcher.exec(text);
|
||
}
|
||
}
|
||
|
||
if (/\bint\s+main\s*\(/.test(text) && !/\breturn\s+0\s*;/.test(text)) {
|
||
const idx = text.search(/\bint\s+main\s*\(/);
|
||
const pos = offsetToPosition(lineStarts, Math.max(0, idx));
|
||
pushIssue(
|
||
issues,
|
||
"main-return-zero",
|
||
"warning",
|
||
"建议在 main 末尾显式 return 0;",
|
||
"部分考场与评测环境会严格检查主函数返回行为。",
|
||
pos.line,
|
||
pos.column,
|
||
pos.line,
|
||
pos.column + 3
|
||
);
|
||
}
|
||
|
||
if (/\blong\s+long\b/.test(text) && /\b(?:scanf|printf)\s*\(/.test(text) && !/%lld/.test(text)) {
|
||
const idx = text.search(/\b(?:scanf|printf)\s*\(/);
|
||
const pos = offsetToPosition(lineStarts, Math.max(0, idx));
|
||
pushIssue(
|
||
issues,
|
||
"ll-format",
|
||
"warning",
|
||
"检测到 long long + scanf/printf,建议确认格式符为 %lld",
|
||
"Linux 评测环境不支持 %I64d。",
|
||
pos.line,
|
||
pos.column,
|
||
pos.line,
|
||
pos.column + 6
|
||
);
|
||
}
|
||
|
||
return issues;
|
||
}
|