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 | 74x 74x 74x 74x 37x 37x 37x 20x 20x 17x 37x 3x 37x 37x 37x 20x 20x 11x 9x 37x 271x 6x 6x 16x 2x 2x 6x 4x 33x | /**
* Serial Configuration Parser
*
* Analyzes Arduino code for Serial configuration issues:
* - Missing Serial.begin()
* - Wrong baud rate (not 115200)
* - Commented-out Serial.begin()
* - while(!Serial) antipattern
* - Serial.read() without Serial.available() check
*/
import type { ParserMessage } from "../schema";
import { randomUUID } from "node:crypto";
import {
SERIAL_PATTERNS,
stripComments,
findLineNumber,
} from "../parser-patterns";
/**
* Analyzer for Serial configuration issues
*/
export class SerialConfigurationParser {
constructor(private readonly code: string) {}
parse(): ParserMessage[] {
const messages: ParserMessage[] = [];
const uncommentedCode = stripComments(this.code);
// Check if Serial is used
if (!SERIAL_PATTERNS.USAGE.test(uncommentedCode)) return messages;
// Check Serial.begin
const serialBeginExists = SERIAL_PATTERNS.BEGIN.test(this.code);
const serialBeginActive = SERIAL_PATTERNS.BEGIN.test(uncommentedCode);
if (serialBeginActive) {
const baudMsg = this._detectBaudRateMismatch(uncommentedCode);
if (baudMsg) messages.push(baudMsg);
} else {
messages.push({
id: randomUUID(),
type: "warning",
category: "serial",
severity: 2,
message: serialBeginExists
? "Serial.begin() is commented out! Serial output may not work correctly."
: "Serial.begin(115200) is missing in setup(). Serial output may not work correctly.",
suggestion: "Serial.begin(115200);",
line: findLineNumber(this.code, /Serial\s*\.\s*begin/),
});
}
// Check for while (!Serial) antipattern
if (SERIAL_PATTERNS.WHILE_NOT.test(uncommentedCode)) {
messages.push({
id: randomUUID(),
type: "warning",
category: "serial",
severity: 2,
message: "while (!Serial) loop detected. This blocks the simulator - not recommended.",
suggestion: "// while (!Serial) { }",
line: findLineNumber(this.code, SERIAL_PATTERNS.WHILE_NOT),
});
}
// Check for Serial.read() without Serial.available() check
const readMsg = this._detectSerialReadWithoutAvailable(uncommentedCode.split("\n"));
if (readMsg) messages.push(readMsg);
return messages;
}
private _detectBaudRateMismatch(uncommentedCode: string): ParserMessage | null {
const baudRateMatch = SERIAL_PATTERNS.BEGIN_EXTRACT.exec(uncommentedCode);
if (baudRateMatch && baudRateMatch[1] !== "115200") {
return {
id: randomUUID(),
type: "warning",
category: "serial",
severity: 2,
message: `Serial.begin(${baudRateMatch[1]}) uses wrong baud rate. This simulator expects Serial.begin(115200).`,
suggestion: "Serial.begin(115200);",
line: findLineNumber(
this.code,
new RegExp(String.raw`Serial\s*\.\s*begin\s*\(\s*${baudRateMatch[1]}`),
),
};
}
return null;
}
private _detectSerialReadWithoutAvailable(lines: string[]): ParserMessage | null {
for (let i = 0; i < lines.length; i++) {
if (!SERIAL_PATTERNS.READ.test(lines[i])) continue;
let hasAvailableCheck = false;
for (let j = Math.max(0, i - 3); j <= i; j++) {
if (SERIAL_PATTERNS.AVAILABLE.test(lines[j])) {
hasAvailableCheck = true;
break;
}
}
if (!hasAvailableCheck) {
return {
id: randomUUID(),
type: "warning",
category: "serial",
severity: 2,
message: "Serial.read() used without checking Serial.available(). This may return -1 when no data is available.",
suggestion: "if (Serial.available()) { }",
line: findLineNumber(this.code, SERIAL_PATTERNS.READ),
};
}
}
return null;
}
}
|