Etch provides comprehensive debugging support through the Debug Adapter Protocol (DAP), allowing you to debug Etch scripts both standalone and embedded within C/C++ applications.
Etch supports three debugging modes:
.etch scripts directly from VSCodeAll modes support the full DAP feature set including breakpoints, stepping, variable inspection, and call stack navigation.
Direct debugging is the simplest mode for debugging standalone Etch scripts.
.etch file) in VSCodeAdd to your .vscode/launch.json:
{
"type": "etch",
"request": "launch",
"name": "Launch Etch VM Program",
"program": "${file}",
"stopAtEntry": true
}
Remote debugging allows you to debug Etch scripts embedded in C/C++ applications, similar to Python’s debugpy for embedded Python interpreters.
When you set the ETCH_DEBUG_PORT environment variable, the Etch VM automatically:
This is completely transparent - no code changes required in your C/C++ application!
Add to your .vscode/launch.json:
{
"type": "etch",
"request": "attach",
"name": "Attach to Etch Remote Debugger",
"host": "127.0.0.1",
"port": 9823,
"timeout": 30000,
"stopAtEntry": true
}
When launching your C/C++ application, set:
export ETCH_DEBUG_PORT=9823 # Port for debug server
export ETCH_DEBUG_TIMEOUT=30000 # Connection timeout in ms
Run your C/C++ application with the environment variables set. The Etch VM will:
DEBUG: Remote debug server listening on port 9823In VSCode, select “Attach to Etch Remote Debugger” and press F5. The debugger will connect and pause at the entry point if stopAtEntry: true.
#include <etch.hpp>
int main(int argc, char** argv) {
// Create context with debugging enabled
etch::Context ctx(false, true); // verbose=false, debug=true
// The ETCH_DEBUG_PORT environment variable triggers remote debugging
ctx.compileFile("script.etch");
ctx.execute(); // Waits for debugger if ETCH_DEBUG_PORT is set
return 0;
}
Compile and run with:
export ETCH_DEBUG_PORT=9823
export ETCH_DEBUG_TIMEOUT=30000
./my_app script.etch
Compound debugging allows you to debug both your C++ application and the embedded Etch scripts simultaneously, switching between them seamlessly.
Add a compound configuration to .vscode/launch.json:
{
"compounds": [
{
"name": "Debug C++ + Etch (Remote)",
"configurations": [
"Attach to Etch Remote Debugger",
"Debug C++ with Embedded Etch"
],
"stopAll": true,
"presentation": {
"hidden": false,
"group": "Hybrid Debugging",
"order": 1
}
}
],
"configurations": [
{
"type": "etch",
"request": "attach",
"name": "Attach to Etch Remote Debugger",
"host": "127.0.0.1",
"port": 9823,
"timeout": 30000,
"stopAtEntry": true
},
{
"type": "cppdbg",
"request": "launch",
"name": "Debug C++ with Embedded Etch",
"program": "${workspaceFolder}/my_app",
"args": ["${workspaceFolder}/script.etch"],
"cwd": "${workspaceFolder}",
"environment": [
{"name": "ETCH_DEBUG_PORT", "value": "9823"},
{"name": "ETCH_DEBUG_TIMEOUT", "value": "30000"}
],
"externalConsole": false,
"MIMode": "lldb", // or "gdb" on Linux
"stopAtEntry": false
}
]
}
.etch filesBoth debuggers will start simultaneously:
When debugging C++ code:
etch_execute()When debugging Etch code:
etch_execute() callSwitching between debuggers:
Set breakpoints in Etch source files by clicking in the gutter. Breakpoints support:
When stepping, the debugger will automatically skip the breakpoint at the current location to avoid getting stuck.
The Variables panel shows:
Globals: Global variables and module-level definitions
The Call Stack panel displays:
The Debug Console shows:
print() statementsVSCode ←→ stdin/stdout ←→ regvm_debugserver.nim ←→ Etch VM
VSCode ←→ TCP socket ←→ regvm_debugserver_remote.nim ←→ regvm_debugserver.nim ←→ Etch VM
The architecture follows clean separation of concerns:
regvm_debugger.nim: Core debugger logic (breakpoints, stepping, VM inspection)regvm_debugserver.nim: DAP protocol implementation (command handling, JSON responses)regvm_debugserver_remote.nim: TCP transport layer (socket management, connection handling)This design allows:
When Etch is embedded inside a C or C++ host via the C API, enabling remote debugging
(ETCH_DEBUG_PORT=<port> with debug=true) now spins up a long-lived TCP debug server as soon
as the script is compiled. The server:
etch_execute (single-shot scripts) and repeated etch_call_function loops (e.g., <global> init + update per frame in demo/main.cpp).etch_call_function).Workflow:
debug=true and set ETCH_DEBUG_PORT.etch_execute/etch_call_function polls for connections and services pending DAP requests.All debugging events flow through the onDebugEvent handler:
This abstraction ensures consistent behavior across modes while supporting different transport layers.
If you see “Connection timeout” when attaching:
ETCH_DEBUG_PORT set"timeout": 60000 (60 seconds)If stepping doesn’t move to the next line:
justStepped flag should skip the current breakpointIf stopped events aren’t triggering UI updates:
This is expected behavior! The compound configuration works sequentially:
stopAtEntry: false to not pause immediatelyetch_execute()If your C++ app executes multiple Etch scripts:
ETCH_DEBUG_PORT set.etch_execute / etch_call_function call in that process.Remote debugging has minimal performance overhead:
ETCH_DEBUG_PORT is not setETCH_DEBUG_PORT set: Remove from production environmentverbose flag should be false in productionHere’s a complete example of debugging an embedded Etch script:
script.etch:
fn factorial(n: int) -> int {
if n <= 1 {
return 1;
}
return n * factorial(n - 1);
}
fn main() -> void {
let x: int = 5;
let result: int = factorial(x);
print(result);
}
app.cpp:
#include <etch.hpp>
#include <iostream>
int main() {
std::cout << "=== Starting Etch Integration ===" << std::endl;
etch::Context ctx(false, true); // debug enabled
ctx.compileFile("script.etch");
std::cout << "=== Executing Etch Script ===" << std::endl;
ctx.execute(); // Remote debugging happens here
std::cout << "=== Etch Complete ===" << std::endl;
return 0;
}
Debugging session:
script.etch at lines 9 and 10export ETCH_DEBUG_PORT=9823ctx.execute() is calledx in Variables panel (should show 5)This workflow gives you complete visibility into both the C++ host and the embedded Etch script!