bmc-tpl-code-all-around/src/core/rules/SyntaxRule.ts

81 lines
4.8 KiB
TypeScript
Raw Normal View History

import * as vscode from 'vscode';
import { LinterRule } from './LinterRule';
import { LinterContext } from '../LinterContext';
export class SyntaxRule implements LinterRule {
public checkLine(line: string, rawLine: string, lineIdx: number, ctx: LinterContext): void {
this.checkTabs(rawLine, lineIdx, ctx);
this.checkBrackets(line, rawLine, lineIdx, ctx);
this.checkAssignments(rawLine, lineIdx, ctx);
this.checkSemicolons(line, rawLine, lineIdx, ctx);
}
public checkEnd(lines: string[], ctx: LinterContext): void {
for (const unclosedLine of ctx.parenStack) { ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Öffnende Klammer '(' wird nicht geschlossen!", vscode.DiagnosticSeverity.Error); }
for (const unclosedLine of ctx.bracketStack) { ctx.addDiag(unclosedLine, 0, lines[unclosedLine].length, "Öffnende eckige Klammer '[' wird nicht geschlossen!", vscode.DiagnosticSeverity.Error); }
}
private checkTabs(rawLine: string, lineIdx: number, ctx: LinterContext) {
const tabIndex = rawLine.indexOf('\t');
if (tabIndex !== -1) {
const diag = ctx.addDiag(lineIdx, tabIndex, tabIndex + 1, "Bitte Tabs durch Spaces ersetzen", vscode.DiagnosticSeverity.Warning);
diag.code = 'invalid_tab';
}
}
private checkBrackets(line: string, rawLine: string, lineIdx: number, ctx: LinterContext) {
let cleanLine = line.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/g, '');
const commentIdx = cleanLine.indexOf('//');
if (commentIdx !== -1) { cleanLine = cleanLine.substring(0, commentIdx); }
for (let charIdx = 0; charIdx < cleanLine.length; charIdx++) {
const char = cleanLine[charIdx];
if (char === '(') { ctx.parenStack.push(lineIdx); }
else if (char === ')') {
if (ctx.parenStack.length > 0) { ctx.parenStack.pop(); }
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Unerwartete schließende Klammer ')'. Es fehlt eine öffnende Klammer.", vscode.DiagnosticSeverity.Error); }
}
else if (char === '[') { ctx.bracketStack.push(lineIdx); }
else if (char === ']') {
if (ctx.bracketStack.length > 0) { ctx.bracketStack.pop(); }
else { ctx.addDiag(lineIdx, 0, rawLine.length, "Unerwartete schließende eckige Klammer ']'. Es fehlt eine öffnende Klammer.", vscode.DiagnosticSeverity.Error); }
}
}
}
private checkAssignments(rawLine: string, lineIdx: number, ctx: LinterContext) {
const lineWithoutStrings = rawLine.replace(/(["'])(?:(?=(\\?))\2.)*?\1/g, '""');
if (/\b(if|elif|where)\b/.test(lineWithoutStrings)) { ctx.inConditionBlock = true; }
if (lineWithoutStrings.includes('=') && !lineWithoutStrings.includes(':=') && !lineWithoutStrings.includes('>=') && !lineWithoutStrings.includes('<=')) {
if (!ctx.inConditionBlock && ctx.parenStack.length === 0 && ctx.bracketStack.length === 0 && !/\b(matches)\b/.test(lineWithoutStrings)) {
const idx = rawLine.indexOf('=');
if (idx !== -1) {
ctx.addDiag(lineIdx, idx, idx + 1, "Falsche Zuweisung. In TPL wird ':=' für Zuweisungen genutzt.", vscode.DiagnosticSeverity.Error);
}
}
}
if (/\b(then|do)\b/.test(lineWithoutStrings) || lineWithoutStrings.endsWith(';')) { ctx.inConditionBlock = false; }
}
private checkSemicolons(line: string, rawLine: string, lineIdx: number, ctx: LinterContext) {
if (ctx.isClosingString) {
if (ctx.stringNeedsSemicolon && !line.endsWith(';')) {
ctx.addDiag(lineIdx, Math.max(0, rawLine.length - 1), rawLine.length, "Fehlendes Semikolon am Ende der Zuweisung.", vscode.DiagnosticSeverity.Error);
}
} else {
const noSemicolonNeeded = /^(pattern|syncmapping|traverse|table|enumeration|configuration|definitions?|define|metadata|overview|constants|triggers|mapping|removal|body|end\s+|if\s+|else|elif\s+|for\s+|while\s+|\/\/|\/\*|\*)/;
const continuesOnNextLine = /(,|and|or|not|\+|-|\*|\/|=|:=|matches)$/i.test(line);
if (!noSemicolonNeeded.test(line) && !ctx.justOpenedString && ctx.parenStack.length === 0 && ctx.bracketStack.length === 0 && ctx.inBlock !== 'enumeration' && !ctx.inConditionBlock && !continuesOnNextLine) {
const lineToCheck = line.split('//')[0].trim();
if (!lineToCheck.endsWith(';') && !lineToCheck.endsWith('{') && !lineToCheck.endsWith(',')) {
ctx.addDiag(lineIdx, Math.max(0, rawLine.length - 1), rawLine.length, "Fehlendes Semikolon am Ende der Anweisung.", vscode.DiagnosticSeverity.Error);
}
}
}
}
}