6.7 KiB
Transpiler Testing Guide
This guide explains how to test changes to the INAV JavaScript transpiler to ensure all components work together correctly.
Overview
The transpiler has 4 main components that must stay in sync:
- Parser (
transpiler/parser.js) - Parses JavaScript code into AST - Analyzer (
transpiler/analyzer.js) - Validates syntax and checks for errors - Codegen (
transpiler/codegen.js) - Generates INAV CLI commands from AST - Decompiler (
transpiler/decompiler.js) - Converts CLI commands back to JavaScript
When Adding a New Feature
When implementing a new operation, function, or syntax feature, you must update:
1. Analyzer (transpiler/analyzer.js)
- Add validation for new syntax patterns
- Update property access validation if needed
- Add warnings for unsupported variations
Example: For RC channel states, added validation for rc[0-17] with .low/.mid/.high properties
2. Codegen (transpiler/codegen.js)
- Add code generation for the new feature
- Handle the new AST node types
- Generate correct INAV operation codes
Example: For RC channel states, added handler in generateCondition() to detect rc[n].low and generate LOW operation (4)
3. Decompiler (transpiler/decompiler.js)
- Add reverse mapping from operation codes to JavaScript
- Ensure generated code matches expected syntax
Example: For RC channel states, map operations 4/5/6 to .low/.mid/.high properties
4. Diagnostics (optional, editor/diagnostics.js)
- Add helpful warnings for common mistakes
- Suggest correct syntax alternatives
Round-Trip Testing
The most reliable way to test transpiler changes is round-trip testing: JavaScript → CLI commands → JavaScript
Basic Test Template
import { Transpiler } from './transpiler/index.js';
import { Decompiler } from './transpiler/decompiler.js';
const testCode = `
// Your test code here
if (inav.flight.altitude > 100) {
inav.gvar[0] = 1;
}
`;
console.log('=== Original JavaScript ===\n');
console.log(testCode);
// Transpile
const transpiler = new Transpiler();
const transpileResult = transpiler.transpile(testCode);
console.log('\n=== Generated CLI Commands ===\n');
transpileResult.commands.forEach(cmd => console.log(cmd));
// Parse commands to LC format for decompiler
const logicConditions = transpileResult.commands.map((cmd) => {
const parts = cmd.split(/\s+/);
return {
index: parseInt(parts[1]),
enabled: parseInt(parts[2]),
activatorId: parseInt(parts[3]),
operation: parseInt(parts[4]),
operandAType: parseInt(parts[5]),
operandAValue: parseInt(parts[6]),
operandBType: parseInt(parts[7]),
operandBValue: parseInt(parts[8]),
flags: parseInt(parts[9])
};
});
// Decompile
const decompiler = new Decompiler();
const decompileResult = decompiler.decompile(logicConditions);
console.log('\n=== Decompiled JavaScript ===\n');
console.log(decompileResult.code);
if (decompileResult.success) {
console.log('\n✅ Round-trip successful!');
} else {
console.error('\n❌ Decompilation failed:', decompileResult.error);
}
Running Tests
cd /path/to/inav-configurator/js/transpiler
# Create your test file
nano test_feature.js
# Run the test
node test_feature.js
# Clean up after testing
rm test_feature.js
Test Cases to Verify
When making changes, test these scenarios:
1. Basic Functionality
if (inav.flight.altitude > 100) {
inav.gvar[0] = 1;
}
Expected: 2 commands (condition + action)
2. Error Handling
// Invalid syntax - should produce helpful error
if (inav.flight.invalidProperty > 100) {
inav.gvar[0] = 1;
}
Expected: Error with suggestion for correct property
3. Edge Cases
// Test boundary values
if (inav.rc[0].low) { // Channel 0 (first)
inav.gvar[0] = 1;
}
if (inav.rc[17].high) { // Channel 17 (last valid)
inav.gvar[1] = 1;
}
Expected: Valid generation for both
4. Complex Combinations
// Test multiple operations together
if (xor(inav.rc[0].low, inav.flight.armed)) {
inav.gvar[0] = Math.max(100, inav.flight.altitude);
}
Expected: Proper nesting of operations
Common Issues
Analyzer Rejects Valid Syntax
Problem: Analyzer validation is too strict
Solution: Update checkPropertyAccess() or relevant validation method
Example: RC channels needed special handling for array syntax rc[n]
Codegen Produces Wrong Operation Code
Problem: Operation constant name incorrect or not imported
Solution: Check OPERATION.* constants match inav_constants.js
Example: MODULUS was incorrectly OPERATION.MOD instead of OPERATION.MODULUS
Decompiler Output Doesn't Match Input
Problem: Decompiler reverse mapping incomplete
Solution: Add case for new operation in decompiler
Example: LOW/MID/HIGH operations needed to map to .low/.mid/.high properties
Line Numbers Off in Errors
Problem: Auto-import adds lines before parsing Solution: Track lineOffset and adjust all error/warning line numbers Fixed: Session 1 of 2025-11-25
Verification Checklist
Before committing changes:
- Analyzer validates new syntax without false positives
- Codegen generates correct operation codes
- Decompiler correctly reverses the operation
- Round-trip test passes (JS → CLI → JS)
- Error messages are helpful and accurate
- Edge cases are handled (boundary values, empty inputs)
- Documentation updated (OPERATIONS_REFERENCE.md, API definitions)
Example: Adding a New Operation
Let's say you want to add support for a new INAV operation FOOBAR (operation 99):
1. Check if operation exists in firmware
// Look in transpiler/inav_constants.js
const OPERATION = {
// ...
FOOBAR: 99, // Check this exists
// ...
};
2. Update Codegen
// In transpiler/codegen.js
case 'CallExpression': {
const funcName = condition.callee?.name;
if (funcName === 'foobar') {
// Generate FOOBAR operation
const resultIndex = this.lcIndex;
this.commands.push(
`logic ${this.lcIndex} 1 ${activatorId} ${OPERATION.FOOBAR} ...`
);
this.lcIndex++;
return resultIndex;
}
}
3. Update Decompiler
// In transpiler/decompiler.js
case OPERATION.FOOBAR:
return 'foobar()';
4. Update Analyzer (if needed)
// In transpiler/analyzer.js
// Add validation for foobar() usage
5. Test Round-Trip
const testCode = `
if (foobar()) {
inav.gvar[0] = 1;
}
`;
// Run round-trip test...
6. Update Documentation
- Add to OPERATIONS_REFERENCE.md
- Add usage examples
- Update API definitions if needed
Last Updated
2025-11-25 - Created after implementing RC channel state detection and completing all INAV operations