Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 71x 71x 71x 71x 6x 71x 2x 71x 71x 71x 71x 71x 3x 3x 3x 71x 142x 142x 136x 136x 136x 136x 136x 136x 3x 71x 136x 136x 136x 6868x 146x 146x 6722x 146x 146x 14x 14x 14x 14x | /**
* Performance Parser
*
* Analyzes Arduino code for performance issues:
* - while(true) infinite loops
* - for loops without exit condition
* - Large arrays (≥1000 elements)
* - Recursive functions
*/
import type { ParserMessage } from "../schema";
import { randomUUID } from "node:crypto";
import {
PERFORMANCE_PATTERNS,
FUNCTION_DEF_BASIC,
FUNCTION_DEF_UNSIGNED,
} from "../parser-patterns";
/**
* Analyzer for performance issues
*/
export class PerformanceParser {
constructor(
private readonly uncommentedCode: string,
private readonly fullCode: string,
) {}
/**
* Check for infinite loops and recursion
*/
analyzeComplexity(): ParserMessage[] {
const messages: ParserMessage[] = [];
// Check for while (true)
if (PERFORMANCE_PATTERNS.WHILE_TRUE.test(this.fullCode)) {
messages.push({
id: randomUUID(),
type: "warning",
category: "performance",
severity: 2,
message:
"Infinite while(true) loop detected. This may freeze the simulator.",
suggestion: "delay(100);",
line: this.findLineInFull(PERFORMANCE_PATTERNS.WHILE_TRUE),
});
}
// Check for for loops without exit condition
if (PERFORMANCE_PATTERNS.FOR_NO_EXIT.test(this.fullCode)) {
messages.push({
id: randomUUID(),
type: "warning",
category: "performance",
severity: 2,
message:
"for loop without exit condition detected. This creates an infinite loop.",
suggestion: "for (int i = 0; i < 10; i++) { }",
line: this.findLineInFull(PERFORMANCE_PATTERNS.FOR_NO_EXIT),
});
}
return messages;
}
/**
* Check for large arrays and recursion
*/
analyzeLargeArraysAndRecursion(): ParserMessage[] {
const messages: ParserMessage[] = [];
// Check for large arrays
const arrayRegex = PERFORMANCE_PATTERNS.LARGE_ARRAY;
const arrayMatch = arrayRegex.exec(this.fullCode);
if (arrayMatch) {
const arraySize = Number.parseInt(arrayMatch[1], 10);
Eif (arraySize > 1000) {
messages.push({
id: randomUUID(),
type: "warning",
category: "performance",
severity: 2,
message: `Large array of ${arraySize} elements detected. This may cause memory issues on Arduino.`,
suggestion: `// Use smaller array size: int array[100];`,
line: this.findLineInFull(arrayRegex),
});
}
}
// Check for recursion
let match;
for (const functionDefinitionRegex of [FUNCTION_DEF_BASIC, FUNCTION_DEF_UNSIGNED]) {
functionDefinitionRegex.lastIndex = 0;
while ((match = functionDefinitionRegex.exec(this.uncommentedCode)) !== null) {
const functionName = match[1];
const functionEnd = this._findFunctionBodyEnd(match.index);
// Extract function body
const functionBody = this.uncommentedCode.slice(match.index, functionEnd + 1);
// Check if function calls itself (recursive)
const functionCallRegex = new RegExp(String.raw`\b${functionName}\s*\(`, "g");
const calls = functionBody.match(functionCallRegex);
if (calls && calls.length > 1) {
messages.push({
id: randomUUID(),
type: "warning",
category: "performance",
severity: 2,
message: `Recursive function '${functionName}' detected. Deep recursion may cause stack overflow on Arduino.`,
suggestion: "// Use iterative approach instead",
line: this.findLineInFull(new RegExp(String.raw`\b${functionName}\s*\(`)),
});
}
}
}
return messages;
}
private _findFunctionBodyEnd(functionStart: number): number {
let braceCount = 0;
let foundOpenBrace = false;
for (let i = functionStart; i < this.uncommentedCode.length; i++) {
if (this.uncommentedCode[i] === "{") {
braceCount++;
foundOpenBrace = true;
} else if (this.uncommentedCode[i] === "}") {
braceCount--;
if (foundOpenBrace && braceCount === 0) return i;
}
}
return functionStart;
}
private findLineInFull(pattern: RegExp): number | undefined {
const match = pattern.exec(this.fullCode);
Iif (!match) return undefined;
const upToMatch = this.fullCode.slice(0, Math.max(0, match.index));
return upToMatch.split("\n").length;
}
}
|