UNPKG

react-openqasm-editor

Version:

A powerful, customizable code editor for OpenQASM (Open Quantum Assembly Language) based on Monaco Editor.

540 lines (479 loc) 19 kB
// import { parseAndGetSyntaxErrors } from './utils/parser' import { IError, ErrorListener } from './errorListener' import { CharStreams, CommonTokenStream } from 'antlr4ts'; import { openqasmLexer } from '../antlr/openqasmLexer'; import { openqasmParser, QopContext, StatementContext } from '../antlr/openqasmParser'; import { Interval } from 'antlr4ts/misc/Interval'; interface SymbolInfo { name: string; type: 'qreg' | 'creg' | 'gate'; size?: number; line: number; column: number; } function getOriginalText(code: string, startIndex: number, endIndex: number) { const inputStream = CharStreams.fromString(code); return inputStream.getText(new Interval(startIndex, endIndex)); } export default class LanguageService { private symbols: Map<string, SymbolInfo> = new Map(); private indentSize = 2; // 缩进大小 // 验证代码,返回错误列表 public validate(code: string): IError[] { // const errors = parseAndGetSyntaxErrors(code); const errorListener = new ErrorListener(); const syntaxErrors = this.getLexicalAndSyntaxErrors(code, errorListener); const semanticErrors = this.getSemanticErrors(code); return [...syntaxErrors, ...semanticErrors]; } // 格式化代码 public format(code: string): string { const ast = this.parseCode(code); if (!ast) return code; // 如果解析失败,返回原始代码 const inputStream = CharStreams.fromString(code); const lexer = new openqasmLexer(inputStream); const allTokens = lexer.getAllTokens(); // 收集所有注释及其位置 const comments = allTokens .filter(t => { const name = lexer.vocabulary.getSymbolicName(t.type); return name === 'COMMENT' || name === 'LINE_COMMENT'; }) .map(t => ({ text: t.text || '', // 确保 text 不会是 undefined line: t.line, type: lexer.vocabulary.getSymbolicName(t.type), startIndex: t.startIndex, stopIndex: t.stopIndex })); let result = ''; let indentLevel = 0; let currentLine = 1; try { // 格式化版本声明 const version = ast.version(); // 插入版本声明之前的注释 result = this.insertCommentsBeforeLine(comments, currentLine, 0, result); result += 'OPENQASM ' + version.REAL().text + ';\n'; currentLine++; // 格式化include语句 const includeStmt = ast.includeStatement(); if (includeStmt) { // 插入include语句之前的注释 result = this.insertCommentsBeforeLine(comments, currentLine, 0, result); result += 'include ' + includeStmt.STRING_LITERAL().text + ';\n'; currentLine++; } // 添加一个空行分隔头部声明 result += '\n'; currentLine++; // 格式化所有语句 ast.statement().forEach(stmt => { // 插入当前语句之前的注释 result = this.insertCommentsBeforeLine(comments, stmt.start.line, indentLevel, result); // 处理声明语句 if (stmt.decl()) { const decl = stmt.decl()!; result += this.getIndent(indentLevel) + getOriginalText(code, decl.start.startIndex, decl.stop!.stopIndex) + '\n'; currentLine = decl.stop!.line + 1; } // 处理门声明 else if (stmt.gatedecl()) { const gateDecl = stmt.gatedecl()!; result += this.getIndent(indentLevel) + gateDecl.text.trim() + '\n'; indentLevel++; currentLine = gateDecl.start.line + 1; // 处理门操作列表 if (stmt.goplist()) { const gopList = stmt.goplist()!; gopList.children?.forEach(gop => { // 插入门操作之前的注释 result = this.insertCommentsBeforeLine(comments, gop.sourceInterval.a, indentLevel, result); result += this.getIndent(indentLevel) + gop.text.trim() + '\n'; currentLine = gop.sourceInterval.b + 1; }); } indentLevel--; result += this.getIndent(indentLevel) + '}\n'; currentLine++; } // 处理量子操作 else if (stmt.qop()) { const qop = stmt.qop()!; // 处理测量操作 if (qop.text.startsWith('measure')) { result += this.formatMeasureOp(qop, indentLevel, code); } // 处理其他量子操作 else { result += this.formatQuantumOp(qop, indentLevel, code); } currentLine = qop.stop!.line + 1; } // 处理条件语句 else if (stmt.text.startsWith('if')) { result += this.getIndent(indentLevel) + this.formatIfStatement(stmt) + '\n'; currentLine = stmt.stop!.line + 1; } // 处理barrier语句 else if (stmt.text.startsWith('barrier')) { result += this.getIndent(indentLevel) + 'barrier ' + this.formatArgumentList(stmt.text.substring(7).trim()) + ';\n'; currentLine = stmt.stop!.line + 1; } }); // 插入文件末尾的注释 result = this.insertRemainingComments(comments, indentLevel, result); } catch (e) { console.log("格式化错误:", e); return code; // 如果格式化失败,返回原始代码 } return result; } // 在指定行之前插入注释 private insertCommentsBeforeLine( comments: Array<{ text: string; line: number; type: string | undefined; startIndex: number; stopIndex: number }>, targetLine: number, indentLevel: number, code: string ): string { let result = code; while (comments.length > 0 && comments[0].line < targetLine) { const comment = comments.shift()!; if (comment.type === 'LINE_COMMENT') { result += this.getIndent(indentLevel) + comment.text + '\n'; } else { // 多行注释,保持原有格式 const commentLines = comment.text.split('\n'); commentLines.forEach((line, index) => { if (index === 0) { result += this.getIndent(indentLevel) + line + '\n'; } else { result += this.getIndent(indentLevel) + line.trimLeft() + '\n'; } }); } } return result; } // 插入剩余的注释 private insertRemainingComments( comments: Array<{ text: string; line: number; type: string | undefined; startIndex: number; stopIndex: number }>, indentLevel: number, code: string ): string { let result = code; comments.forEach(comment => { if (comment.type === 'LINE_COMMENT') { result += this.getIndent(indentLevel) + comment.text + '\n'; } else { const commentLines = comment.text.split('\n'); commentLines.forEach((line, index) => { if (index === 0) { result += this.getIndent(indentLevel) + line + '\n'; } else { result += this.getIndent(indentLevel) + line.trimLeft() + '\n'; } }); } }); return result; } // 格式化测量操作 private formatMeasureOp(qop: QopContext, indentLevel: number, code: string): string { const text = getOriginalText(code, qop.start.startIndex, qop.stop!.stopIndex) const args = text.match(/measure\s+(.*?)\s*->\s*(.*?);/); if (!args) return this.getIndent(indentLevel) + text.trim() + '\n'; return this.getIndent(indentLevel) + 'measure ' + this.formatArgument(args[1]) + ' -> ' + this.formatArgument(args[2]) + ';\n'; } // 格式化量子操作 private formatQuantumOp(qop: QopContext, indentLevel: number, code: string): string { if (!qop.uop()) return this.getIndent(indentLevel) + getOriginalText(code, qop.start.startIndex, qop.stop!.stopIndex) + '\n'; const uop = qop.uop()!; const text = getOriginalText(code, uop.start.startIndex, uop.stop!.stopIndex) // 处理U门 if (text.startsWith('U')) { const match = text.match(/U\s*\((.*?)\)\s*(.*?);/); if (!match) return this.getIndent(indentLevel) + text.trim() + '\n'; return this.getIndent(indentLevel) + 'U(' + this.formatExpressionList(match[1]) + ') ' + this.formatArgument(match[2]) + ';\n'; } // 处理CX门 else if (text.startsWith('CX')) { const match = text.match(/CX\s+(.*?)\s*,\s*(.*?);/); if (!match) return this.getIndent(indentLevel) + text.trim() + '\n'; return this.getIndent(indentLevel) + 'CX ' + this.formatArgument(match[1]) + ', ' + this.formatArgument(match[2]) + ';\n'; } // 处理其他门 else { const match = text.match(/([a-zA-Z0-9_]+)\s*(?:\((.*?)\))?\s*(.*?);/); if (!match) return this.getIndent(indentLevel) + text.trim() + '\n'; let formatted = this.getIndent(indentLevel) + match[1]; if (match[2]) { formatted += '(' + this.formatExpressionList(match[2]) + ')'; } formatted += ' ' + this.formatArgumentList(match[3]) + ';\n'; return formatted; } } // 格式化if语句 private formatIfStatement(stmt: StatementContext): string { const match = stmt.text.match(/if\s*\(\s*(.*?)\s*==\s*(.*?)\s*\)\s*(.*)/); if (!match) return stmt.text.trim(); return 'if (' + match[1].trim() + ' == ' + match[2].trim() + ') ' + match[3].trim(); } // 格式化参数列表 private formatArgumentList(args: string): string { return args.split(',') .map(arg => this.formatArgument(arg)) .join(', '); } // 格式化单个参数 private formatArgument(arg: string): string { const match = arg.match(/([a-zA-Z0-9_]+)(?:\[(\d+)\])?/); if (!match) return arg.trim(); if (match[2]) { return match[1].trim() + '[' + match[2] + ']'; } return match[1].trim(); } // 格式化表达式列表 private formatExpressionList(exps: string): string { return exps.split(',') .map(exp => this.formatExpression(exp)) .join(', '); } // 格式化单个表达式 private formatExpression(exp: string): string { // 移除多余的空格 exp = exp.trim(); // 在操作符两边添加单个空格 exp = exp.replace(/\s*([+\-*/^])\s*/g, ' $1 '); // 移除括号内侧的空格 exp = exp.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')'); return exp; } // 获取缩进字符串 private getIndent(level: number): string { return ' '.repeat(level * this.indentSize); } // 解析代码,返回AST private parseCode(code: string, errorListener?: ErrorListener) { try { const inputStream = CharStreams.fromString(code); const lexer = new openqasmLexer(inputStream); if (errorListener) { lexer.removeErrorListeners(); lexer.addErrorListener(errorListener); } const tokenStream = new CommonTokenStream(lexer); const parser = new openqasmParser(tokenStream); if (errorListener) { parser.removeErrorListeners(); parser.addErrorListener(errorListener); } // 调用你的语法的起始规则 return parser.mainprog() } catch (e) { console.error("解析错误:", e); return null; } } // 获取词法和语法错误 private getLexicalAndSyntaxErrors(code: string, errorListener: ErrorListener): IError[] { this.parseCode(code, errorListener); return errorListener.getErrors(); } // 获取语义错误 private getSemanticErrors(code: string): IError[] { const ast = this.parseCode(code); console.log('ast', ast) if (!ast) return []; const errors: IError[] = []; this.symbols.clear(); try { // 遍历所有声明语句 ast.statement().forEach(stmt => { // 检查寄存器声明 if (stmt.decl()) { const decl = stmt.decl()!; const declText = decl.text; // 解析声明文本 const qregMatch = declText.match(/qreg+([a-z][A-Za-z0-9_]*)\s*\[\s*(\d+)\s*\]/); const cregMatch = declText.match(/creg+([a-z][A-Za-z0-9_]*)\s*\[\s*(\d+)\s*\]/); if (qregMatch) { const [, name, sizeStr] = qregMatch; const size = parseInt(sizeStr); // 检查重复声明 if (this.symbols.has(name)) { errors.push({ startLineNumber: decl.start.line, endLineNumber: decl.start.line, startColumn: decl.start.charPositionInLine, endColumn: decl.start.charPositionInLine + name.length, message: `量子寄存器 '${name}' 已被声明`, severity: 8, code: "duplicate_declaration" }); } else if (size <= 0) { errors.push({ startLineNumber: decl.start.line, endLineNumber: decl.start.line, startColumn: decl.start.charPositionInLine, endColumn: decl.start.charPositionInLine + name.length, message: `量子寄存器 '${name}' 的大小必须大于0`, severity: 8, code: "invalid_size" }); } else { this.symbols.set(name, { name, type: 'qreg', size, line: decl.start.line, column: decl.start.charPositionInLine }); } } else if (cregMatch) { const [, name, sizeStr] = cregMatch; const size = parseInt(sizeStr); if (this.symbols.has(name)) { errors.push({ startLineNumber: decl.start.line, endLineNumber: decl.start.line, startColumn: decl.start.charPositionInLine, endColumn: decl.start.charPositionInLine + name.length, message: `经典寄存器 '${name}' 已被声明`, severity: 8, code: "duplicate_declaration" }); } else if (size <= 0) { errors.push({ startLineNumber: decl.start.line, endLineNumber: decl.start.line, startColumn: decl.start.charPositionInLine, endColumn: decl.start.charPositionInLine + name.length, message: `经典寄存器 '${name}' 的大小必须大于0`, severity: 8, code: "invalid_size" }); } else { this.symbols.set(name, { name, type: 'creg', size, line: decl.start.line, column: decl.start.charPositionInLine }); } } } // 检查量子操作 if (stmt.qop()) { const qop = stmt.qop()!; // 检查测量操作 if (qop.text.startsWith('measure')) { const args = qop.argument(); if (args && args.length >= 2) { const qreg = args[0].text.split('[')[0]; const creg = args[1].text.split('[')[0]; // 检查量子寄存器 if (!this.symbols.has(qreg)) { errors.push({ startLineNumber: qop.start.line, endLineNumber: qop.start.line, startColumn: qop.start.charPositionInLine, endColumn: qop.start.charPositionInLine + qreg.length, message: `未声明的量子寄存器 '${qreg}'`, severity: 8, code: "undefined_qreg" }); } else if (this.symbols.get(qreg)!.type !== 'qreg') { errors.push({ startLineNumber: qop.start.line, endLineNumber: qop.start.line, startColumn: qop.start.charPositionInLine, endColumn: qop.start.charPositionInLine + qreg.length, message: `'${qreg}' 不是量子寄存器`, severity: 8, code: "invalid_register_type" }); } // 检查经典寄存器 if (!this.symbols.has(creg)) { errors.push({ startLineNumber: qop.start.line, endLineNumber: qop.start.line, startColumn: qop.start.charPositionInLine, endColumn: qop.start.charPositionInLine + creg.length, message: `未声明的经典寄存器 '${creg}'`, severity: 8, code: "undefined_creg" }); } else if (this.symbols.get(creg)!.type !== 'creg') { errors.push({ startLineNumber: qop.start.line, endLineNumber: qop.start.line, startColumn: qop.start.charPositionInLine, endColumn: qop.start.charPositionInLine + creg.length, message: `'${creg}' 不是经典寄存器`, severity: 8, code: "invalid_register_type" }); } } } // 检查其他量子操作(U门、CX门等) if (qop.uop()) { const uop = qop.uop()!; const args = uop.argument(); if (args) { args.forEach(arg => { const regName = arg.text.split('[')[0]; // 检查未声明的寄存器 if (!this.symbols.has(regName)) { errors.push({ startLineNumber: arg.start.line, endLineNumber: arg.start.line, startColumn: arg.start.charPositionInLine, endColumn: arg.start.charPositionInLine + regName.length, message: `未声明的寄存器 '${regName}'`, severity: 8, code: "undefined_register" }); } else { // 检查索引范围 const match = arg.text.match(/\[(\d+)\]/); if (match) { const index = parseInt(match[1]); const symbol = this.symbols.get(regName)!; if (index >= symbol.size!) { errors.push({ startLineNumber: arg.start.line, endLineNumber: arg.start.line, startColumn: arg.start.charPositionInLine, endColumn: arg.start.charPositionInLine + arg.text.length, message: `索引 ${index} 超出寄存器 '${regName}' 的范围 [0, ${symbol.size! - 1}]`, severity: 8, code: "index_out_of_range" }); } } } }); } } } }); } catch (e) { console.error("语义分析错误:", e); } return errors; } }