feat(代码执行): 增强远程引擎多语言 Unicode 支持
- 改进错误信息处理,提供更清晰的错误提示
- 添加 Go 语言非 ASCII 字符转义支持
- 添加 Kotlin、C#、C/C++ 标准 Unicode 转义
- 添加 Rust、Swift 大括号格式转义 \u{XXXX}
- 添加 Ruby、PHP 字符串转义支持
This commit is contained in:
parent
c2dcf9b23f
commit
30156458ba
@ -110,14 +110,29 @@ class RemoteEngine implements IExecutionEngine {
|
||||
}
|
||||
|
||||
// 处理运行结果
|
||||
const hasError = result.run.code !== 0 || result.run.signal !== null;
|
||||
const runCode = result.run.code;
|
||||
const hasError = runCode !== 0 || result.run.signal !== null;
|
||||
const output = this.formatOutput(result.run.stdout || result.run.output);
|
||||
const errorOutput = result.run.stderr ? this.formatOutput(result.run.stderr) : undefined;
|
||||
|
||||
// 构建错误信息
|
||||
let errorMessage: string | undefined;
|
||||
if (hasError) {
|
||||
if (errorOutput) {
|
||||
errorMessage = errorOutput;
|
||||
} else if (result.run.signal) {
|
||||
errorMessage = `进程被信号终止: ${result.run.signal}`;
|
||||
} else if (runCode === null || runCode === undefined) {
|
||||
errorMessage = '执行异常: 未获取到退出代码';
|
||||
} else {
|
||||
errorMessage = `退出代码: ${runCode}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: !hasError || (output.length > 0 && !errorOutput),
|
||||
output: output.slice(0, MAX_OUTPUT_LENGTH),
|
||||
error: hasError ? errorOutput || `退出代码: ${result.run.code}` : undefined,
|
||||
error: errorMessage,
|
||||
executionTime,
|
||||
engine: 'remote',
|
||||
};
|
||||
@ -170,11 +185,51 @@ class RemoteEngine implements IExecutionEngine {
|
||||
private preprocessCode(code: string, language: string): string {
|
||||
const lang = language.toLowerCase();
|
||||
|
||||
// 只对 Java 进行处理(Java 字符串支持 \uXXXX 转义)
|
||||
// Java:\uXXXX 格式 + UTF-8 输出流注入
|
||||
if (lang === 'java') {
|
||||
return this.escapeNonAsciiForJava(code);
|
||||
}
|
||||
|
||||
// Go:\uXXXX 格式
|
||||
if (lang === 'go' || lang === 'golang') {
|
||||
return this.escapeNonAsciiForGo(code);
|
||||
}
|
||||
|
||||
// Kotlin:\uXXXX 格式(类似 Java,但不需要注入 UTF-8 设置)
|
||||
if (lang === 'kotlin' || lang === 'kt') {
|
||||
return this.escapeNonAsciiStandard(code);
|
||||
}
|
||||
|
||||
// C#:\uXXXX 格式
|
||||
if (lang === 'csharp' || lang === 'cs') {
|
||||
return this.escapeNonAsciiStandard(code);
|
||||
}
|
||||
|
||||
// C/C++:\uXXXX 格式 (C11/C++11)
|
||||
if (lang === 'c' || lang === 'cpp' || lang === 'c++') {
|
||||
return this.escapeNonAsciiStandard(code);
|
||||
}
|
||||
|
||||
// Rust:\u{XXXX} 格式(大括号语法)
|
||||
if (lang === 'rust' || lang === 'rs') {
|
||||
return this.escapeNonAsciiBraceFormat(code);
|
||||
}
|
||||
|
||||
// Swift:\u{XXXX} 格式(大括号语法)
|
||||
if (lang === 'swift') {
|
||||
return this.escapeNonAsciiBraceFormat(code);
|
||||
}
|
||||
|
||||
// Ruby:\u{XXXX} 格式(大括号语法)
|
||||
if (lang === 'ruby' || lang === 'rb') {
|
||||
return this.escapeNonAsciiRuby(code);
|
||||
}
|
||||
|
||||
// PHP:\u{XXXX} 格式(PHP 7+,仅双引号字符串)
|
||||
if (lang === 'php') {
|
||||
return this.escapeNonAsciiPHP(code);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
@ -234,6 +289,85 @@ class RemoteEngine implements IExecutionEngine {
|
||||
return quote + escaped + quote;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Go 代码中字符串内的非 ASCII 字符转换为 Unicode 转义序列
|
||||
*/
|
||||
private escapeNonAsciiForGo(code: string): string {
|
||||
// 处理双引号字符串
|
||||
let result = code.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
||||
return this.escapeGoStringContent(match);
|
||||
});
|
||||
|
||||
// 处理反引号字符串(Go 的原始字符串,不支持转义,需要特殊处理)
|
||||
// 对于反引号字符串,我们将其转换为双引号字符串并添加转义
|
||||
result = result.replace(/`([^`]*)`/g, (match, content) => {
|
||||
// 检查是否包含非 ASCII 字符
|
||||
if (/[^\x00-\x7F]/.test(content)) {
|
||||
// 转换为双引号字符串,并转义非 ASCII 字符
|
||||
let escaped = '';
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
const char = content[i];
|
||||
const code = char.charCodeAt(0);
|
||||
|
||||
if (char === '"') {
|
||||
escaped += '\\"';
|
||||
} else if (char === '\\') {
|
||||
escaped += '\\\\';
|
||||
} else if (char === '\n') {
|
||||
escaped += '\\n';
|
||||
} else if (char === '\r') {
|
||||
escaped += '\\r';
|
||||
} else if (char === '\t') {
|
||||
escaped += '\\t';
|
||||
} else if (code > 127) {
|
||||
escaped += '\\u' + code.toString(16).padStart(4, '0');
|
||||
} else {
|
||||
escaped += char;
|
||||
}
|
||||
}
|
||||
return '"' + escaped + '"';
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义 Go 字符串内容中的非 ASCII 字符
|
||||
*/
|
||||
private escapeGoStringContent(str: string): string {
|
||||
const content = str.slice(1, -1); // 移除引号
|
||||
let escaped = '';
|
||||
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
const char = content[i];
|
||||
const code = char.charCodeAt(0);
|
||||
|
||||
// 处理转义序列(保留原样)
|
||||
if (char === '\\' && i + 1 < content.length) {
|
||||
escaped += char + content[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 非 ASCII 字符转换为 \uXXXX 或 \UXXXXXXXX
|
||||
if (code > 127) {
|
||||
// 对于 BMP 范围内的字符使用 \uXXXX
|
||||
if (code <= 0xFFFF) {
|
||||
escaped += '\\u' + code.toString(16).padStart(4, '0');
|
||||
} else {
|
||||
// 超出 BMP 的字符使用 \UXXXXXXXX
|
||||
escaped += '\\U' + code.toString(16).padStart(8, '0');
|
||||
}
|
||||
} else {
|
||||
escaped += char;
|
||||
}
|
||||
}
|
||||
|
||||
return '"' + escaped + '"';
|
||||
}
|
||||
|
||||
private formatOutput(output: string, prefix?: string): string {
|
||||
let result = output.trim();
|
||||
|
||||
@ -246,6 +380,113 @@ class RemoteEngine implements IExecutionEngine {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准 \uXXXX 格式转义(用于 Kotlin, C#, C/C++)
|
||||
* 只处理双引号字符串
|
||||
*/
|
||||
private escapeNonAsciiStandard(code: string): string {
|
||||
return code.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
||||
return this.escapeStringContent(match, '"');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 大括号格式转义 \u{XXXX}(用于 Rust, Swift)
|
||||
*/
|
||||
private escapeNonAsciiBraceFormat(code: string): string {
|
||||
return code.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
||||
const content = match.slice(1, -1);
|
||||
let escaped = '';
|
||||
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
const char = content[i];
|
||||
const charCode = char.charCodeAt(0);
|
||||
|
||||
// 处理转义序列(保留原样)
|
||||
if (char === '\\' && i + 1 < content.length) {
|
||||
escaped += char + content[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 非 ASCII 字符转换为 \u{XXXX}
|
||||
if (charCode > 127) {
|
||||
escaped += '\\u{' + charCode.toString(16) + '}';
|
||||
} else {
|
||||
escaped += char;
|
||||
}
|
||||
}
|
||||
|
||||
return '"' + escaped + '"';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruby 字符串转义
|
||||
* 双引号字符串支持 \u{XXXX},单引号字符串不支持转义
|
||||
*/
|
||||
private escapeNonAsciiRuby(code: string): string {
|
||||
// 只处理双引号字符串
|
||||
return code.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
||||
const content = match.slice(1, -1);
|
||||
let escaped = '';
|
||||
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
const char = content[i];
|
||||
const charCode = char.charCodeAt(0);
|
||||
|
||||
// 处理转义序列(保留原样)
|
||||
if (char === '\\' && i + 1 < content.length) {
|
||||
escaped += char + content[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 非 ASCII 字符转换为 \u{XXXX}
|
||||
if (charCode > 127) {
|
||||
escaped += '\\u{' + charCode.toString(16).padStart(4, '0') + '}';
|
||||
} else {
|
||||
escaped += char;
|
||||
}
|
||||
}
|
||||
|
||||
return '"' + escaped + '"';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP 字符串转义
|
||||
* 只有双引号字符串支持 \u{XXXX},单引号字符串不处理
|
||||
*/
|
||||
private escapeNonAsciiPHP(code: string): string {
|
||||
// 只处理双引号字符串
|
||||
return code.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
||||
const content = match.slice(1, -1);
|
||||
let escaped = '';
|
||||
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
const char = content[i];
|
||||
const charCode = char.charCodeAt(0);
|
||||
|
||||
// 处理转义序列(保留原样)
|
||||
if (char === '\\' && i + 1 < content.length) {
|
||||
escaped += char + content[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 非 ASCII 字符转换为 \u{XXXX}
|
||||
if (charCode > 127) {
|
||||
escaped += '\\u{' + charCode.toString(16) + '}';
|
||||
} else {
|
||||
escaped += char;
|
||||
}
|
||||
}
|
||||
|
||||
return '"' + escaped + '"';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
|
||||
Loading…
Reference in New Issue
Block a user