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
text/typescript
// 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;
}
}