├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── bin └── nim-gdb.py ├── images ├── debug-console-load-nim-gdb.png ├── debugger-mouseover-str.png ├── vscode-debug-console-print1.png ├── vscode-debug-console-print2.png ├── vscode-nim-config.png └── vscode-set-breakpoint.png └── main.nim /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | !bin/nim*.py -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug Nim Project", 9 | "type": "gdb", 10 | "request": "launch", 11 | "target": "./bin/main", 12 | "cwd": "${workspaceRoot}", 13 | "valuesFormatting": "parseText" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build Nim Project", 8 | "type": "shell", 9 | "command": "nim c --out:bin/main -d:debug --debugger:native main.nim", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jason Jones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to debug Nim code in VSCode 2 | 3 | Below are some steps to help you debug [Nim](https://nim-lang.org/) code in [Visual Studio Code](https://code.visualstudio.com/). 4 | 5 | ## Prerequisites 6 | 7 | 1. Nim is installed. 8 | 2. GDB is installed. 9 | 3. VSCode is installed. 10 | 4. Python 3 is installed (used for pretty-printing with GDB). 11 | 5. The [Nim VSCode extension](https://marketplace.visualstudio.com/items?itemName=kosz78.nim) is installed. 12 | 6. The [Native Debug VSCode extension](https://marketplace.visualstudio.com/items?itemName=webfreak.debug) is installed. 13 | 7. This example repository has been cloned with [Git](https://git-scm.com/). 14 | 15 | ## Steps to setup debugging 16 | 17 | This repository contains some files to help setup debugging in VSCode. 18 | 19 | 1. Open VSCode. 20 | 2. File -> Open Folder -> Open the cloned repository folder in VSCode. 21 | 3. Open `main.nim`. 22 | 4. Set a breakpoint on line 15 in the file: 23 | 24 | ![setting a breakpoint in main.nim on line 15](images/vscode-set-breakpoint.png) 25 | 26 | 5. Build the code (ctrl-shift-b), which should use the build definition in `.vscode/tasks.json`. 27 | My preference is to set "Build on Save" in the VSCode-Nim configuration: 28 | 29 | ![setting build on save in vscode-nim](images/vscode-nim-config.png) 30 | 31 | 5. Debug -> Start Debugging, which should use the launch config in `.vscode/launch.json`. 32 | 6. Once the breakpoint is hit, let's enable pretty-printing. Otherwise, when you mouse over a 33 | symbol in the debugger, you'll see memory addresses instead of values. 34 | 35 | Open the Debug Console (at the bottom) and type: 36 | 37 | ``` 38 | python exec(open("bin/nim-gdb.py").read()) 39 | ``` 40 | 41 | (Note: The version of GDB I have installed was built to run with Python 3, and this script works with Python 3) 42 | 43 | After pressing [enter], you should see success: 44 | 45 | ![load nim-gdb.py success](images/debug-console-load-nim-gdb.png) 46 | 47 | Note: The `nim-gdb.py` script was copied from [here](https://github.com/nim-lang/Nim/blob/master/tools/nim-gdb.py), 48 | and exists in the `bin/` directory simply to reduce the number of steps in setting this up. It might be a good 49 | idea to update your local copy of this file with the official latest file from the repository. 50 | 51 | If running the Python pretty-printing script succeeds, you should be able to mouse over a variable, 52 | e.g. a string, and see the value: 53 | 54 | ![mousing over a variable in the debugger and seeing the value contained in the variable](images/debugger-mouseover-str.png) 55 | 56 | You'll notice, however, if you mouseover `people[0].name`, that it does not show the name of the first person 57 | (perhaps the `nim-gdb.py` script could be modified to support this). Even though this functionality doesn't exist, 58 | you can type `print people[0].name` in the Debug Console to print the value: 59 | 60 | ![type print variable in vscode debug console](images/vscode-debug-console-print1.png) 61 | 62 | Press [enter], and you should see the contents of the variable: 63 | 64 | ![debug console showing variable contents](images/vscode-debug-console-print2.png) 65 | 66 | ## Notes 67 | 68 | GDB can be extended with Python, and the [nim-gdb.py](https://github.com/nim-lang/Nim/blob/master/tools/nim-gdb.py) script 69 | can be used from the command-line using [this bash script](https://github.com/nim-lang/Nim/blob/devel/bin/nim-gdb). 70 | 71 | The [nim-gdb repository](https://github.com/cooldome/Nim-gdb) seems to be the predecessor to the nim-gdb script 72 | in the official Nim repository. 73 | 74 | ## References 75 | 76 | [Debug Nim with GDB](https://internet-of-tomohiro.netlify.com/nim/gdb.en.html) 77 | [Nim GDB video](https://www.youtube.com/watch?v=DmYOPkI_LzU) 78 | [Nim Editor Support](https://github.com/nim-lang/Nim/wiki/Editor-Support) 79 | [repr method for debugging](https://nim-lang.org/docs/system.html#repr%2CT) 80 | [Nim VSCode plugin debug feature request](https://github.com/pragmagic/vscode-nim/issues/65) 81 | [Extending GDB using Python](https://sourceware.org/gdb/onlinedocs/gdb/Python.html#Python) -------------------------------------------------------------------------------- /bin/nim-gdb.py: -------------------------------------------------------------------------------- 1 | 2 | import gdb 3 | import re 4 | import sys 5 | 6 | # some feedback that the nim runtime support is loading, isn't a bad 7 | # thing at all. 8 | gdb.write("Loading Nim Runtime support.\n", gdb.STDERR) 9 | 10 | # When error occure they occur regularly. This 'caches' known errors 11 | # and prevents them from being reprinted over and over again. 12 | errorSet = set() 13 | def printErrorOnce(id, message): 14 | global errorSet 15 | if id not in errorSet: 16 | errorSet.add(id) 17 | gdb.write(message, gdb.STDERR) 18 | 19 | nimobjfile = gdb.current_objfile() or gdb.objfiles()[0] 20 | nimobjfile.type_printers = [] 21 | 22 | ################################################################################ 23 | ##### Type pretty printers 24 | ################################################################################ 25 | 26 | type_hash_regex = re.compile("^\w*_([A-Za-z0-9]*)$") 27 | 28 | def getNimRti(type_name): 29 | """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """ 30 | 31 | # Get static const TNimType variable. This should be available for 32 | # every non trivial Nim type. 33 | m = type_hash_regex.match(type_name) 34 | if m: 35 | try: 36 | return gdb.parse_and_eval("NTI_" + m.group(1) + "_") 37 | except: 38 | return None 39 | 40 | class NimTypeRecognizer: 41 | # this type map maps from types that are generated in the C files to 42 | # how they are called in nim. To not mix up the name ``int`` from 43 | # system.nim with the name ``int`` that could still appear in 44 | # generated code, ``NI`` is mapped to ``system.int`` and not just 45 | # ``int``. 46 | 47 | type_map_static = { 48 | 'NI': 'system.int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32', 'NI64': 'int64', 49 | 'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32', 'NU64': 'uint64', 50 | 'NF': 'float', 'NF32': 'float32', 'NF64': 'float64', 51 | 'NIM_BOOL': 'bool', 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 52 | 'NimStringDesc': 'string' 53 | } 54 | 55 | # Normally gdb distinguishes between the command `ptype` and 56 | # `whatis`. `ptype` prints a very detailed view of the type, and 57 | # `whatis` a very brief representation of the type. I haven't 58 | # figured out a way to know from the type printer that is 59 | # implemented here how to know if a type printer should print the 60 | # short representation or the long representation. As a hacky 61 | # workaround I just say I am not resposible for printing pointer 62 | # types (seq and string are exception as they are semantically 63 | # values). this way the default type printer will handle pointer 64 | # types and dive into the members of that type. So I can still 65 | # control with `ptype myval` and `ptype *myval` if I want to have 66 | # detail or not. I this this method stinks but I could not figure 67 | # out a better solution. 68 | 69 | object_type_pattern = re.compile("^(\w*):ObjectType$") 70 | 71 | def recognize(self, type_obj): 72 | 73 | tname = None 74 | if type_obj.tag is not None: 75 | tname = type_obj.tag 76 | elif type_obj.name is not None: 77 | tname = type_obj.name 78 | 79 | # handle pointer types 80 | if not tname: 81 | if type_obj.code == gdb.TYPE_CODE_PTR: 82 | target_type = type_obj.target() 83 | target_type_name = target_type.name 84 | if target_type_name: 85 | # visualize 'string' as non pointer type (unpack pointer type). 86 | if target_type_name == "NimStringDesc": 87 | tname = target_type_name # could also just return 'string' 88 | # visualize 'seq[T]' as non pointer type. 89 | if target_type_name.find('tySequence_') == 0: 90 | tname = target_type_name 91 | 92 | if not tname: 93 | # We are not resposible for this type printing. 94 | # Basically this means we don't print pointer types. 95 | return None 96 | 97 | result = self.type_map_static.get(tname, None) 98 | if result: 99 | return result 100 | 101 | rti = getNimRti(tname) 102 | if rti: 103 | return rti['name'].string("utf-8", "ignore") 104 | else: 105 | return None 106 | 107 | class NimTypePrinter: 108 | """Nim type printer. One printer for all Nim types.""" 109 | 110 | 111 | # enabling and disabling of type printers can be done with the 112 | # following gdb commands: 113 | # 114 | # enable type-printer NimTypePrinter 115 | # disable type-printer NimTypePrinter 116 | 117 | name = "NimTypePrinter" 118 | def __init__ (self): 119 | self.enabled = True 120 | 121 | def instantiate(self): 122 | return NimTypeRecognizer() 123 | 124 | 125 | nimobjfile.type_printers = [NimTypePrinter()] 126 | 127 | ################################################################################ 128 | ##### GDB Function, equivalent of Nim's $ operator 129 | ################################################################################ 130 | 131 | class DollarPrintFunction (gdb.Function): 132 | "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)" 133 | 134 | _gdb_dollar_functions = gdb.execute("info functions dollar__", True, True) 135 | dollar_functions = re.findall('NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);', _gdb_dollar_functions) 136 | 137 | def __init__ (self): 138 | super (DollarPrintFunction, self).__init__("dollar") 139 | 140 | @staticmethod 141 | def invoke_static(arg): 142 | 143 | for func, arg_typ in DollarPrintFunction.dollar_functions: 144 | 145 | if arg.type.name == arg_typ: 146 | func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() 147 | return func_value(arg) 148 | 149 | if arg.type.name + " *" == arg_typ: 150 | func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() 151 | return func_value(arg.address) 152 | 153 | typeName = arg.type.name 154 | printErrorOnce(typeName, "No suitable Nim $ operator found for type: " + typeName + ".\n") 155 | 156 | def invoke(self, arg): 157 | return self.invoke_static(arg) 158 | 159 | DollarPrintFunction() 160 | 161 | ################################################################################ 162 | ##### GDB Command, equivalent of Nim's $ operator 163 | ################################################################################ 164 | 165 | class DollarPrintCmd (gdb.Command): 166 | """Dollar print command for Nim, `$ expr` will invoke Nim's $ operator""" 167 | 168 | def __init__ (self): 169 | super (DollarPrintCmd, self).__init__ ("$", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION) 170 | 171 | def invoke (self, arg, from_tty): 172 | param = gdb.parse_and_eval(arg) 173 | gdb.write(str(DollarPrintFunction.invoke_static(param)) + "\n", gdb.STDOUT) 174 | 175 | DollarPrintCmd() 176 | 177 | ################################################################################ 178 | ##### Value pretty printers 179 | ################################################################################ 180 | 181 | class NimBoolPrinter: 182 | 183 | pattern = re.compile(r'^NIM_BOOL$') 184 | 185 | def __init__(self, val): 186 | self.val = val 187 | 188 | def to_string(self): 189 | if self.val == 0: 190 | return "false" 191 | else: 192 | return "true" 193 | 194 | ################################################################################ 195 | 196 | class NimStringPrinter: 197 | pattern = re.compile(r'^NimStringDesc \*$') 198 | 199 | def __init__(self, val): 200 | self.val = val 201 | 202 | def display_hint(self): 203 | return 'string' 204 | 205 | def to_string(self): 206 | if self.val: 207 | l = int(self.val['Sup']['len']) 208 | return self.val['data'][0].address.string("utf-8", "ignore", l) 209 | else: 210 | return "" 211 | 212 | ################################################################################ 213 | 214 | # proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = 215 | # ## Return string representation for enumeration values 216 | # var n = typ.node 217 | # if ntfEnumHole notin typ.flags: 218 | # let o = e - n.sons[0].offset 219 | # if o >= 0 and o <% typ.node.len: 220 | # return $n.sons[o].name 221 | # else: 222 | # # ugh we need a slow linear search: 223 | # var s = n.sons 224 | # for i in 0 .. n.len-1: 225 | # if s[i].offset == e: 226 | # return $s[i].name 227 | # result = $e & " (invalid data!)" 228 | 229 | def reprEnum(e, typ): 230 | """ this is a port of the nim runtime function `reprEnum` to python """ 231 | e = int(e) 232 | n = typ["node"] 233 | flags = int(typ["flags"]) 234 | # 1 << 2 is {ntfEnumHole} 235 | if ((1 << 2) & flags) == 0: 236 | o = e - int(n["sons"][0]["offset"]) 237 | if o >= 0 and 0 < int(n["len"]): 238 | return n["sons"][o]["name"].string("utf-8", "ignore") 239 | else: 240 | # ugh we need a slow linear search: 241 | s = n["sons"] 242 | for i in range(0, int(n["len"])): 243 | if int(s[i]["offset"]) == e: 244 | return s[i]["name"].string("utf-8", "ignore") 245 | 246 | return str(e) + " (invalid data!)" 247 | 248 | class NimEnumPrinter: 249 | pattern = re.compile(r'^tyEnum_(\w*)_([A-Za-z0-9]*)$') 250 | 251 | def __init__(self, val): 252 | self.val = val 253 | match = self.pattern.match(self.val.type.name) 254 | self.typeNimName = match.group(1) 255 | typeInfoName = "NTI_" + match.group(2) + "_" 256 | self.nti = gdb.lookup_global_symbol(typeInfoName) 257 | 258 | if self.nti is None: 259 | printErrorOnce(typeInfoName, "NimEnumPrinter: lookup global symbol '" + typeInfoName + " failed for " + self.val.type.name + ".\n") 260 | 261 | def to_string(self): 262 | if self.nti: 263 | arg0 = self.val 264 | arg1 = self.nti.value(gdb.newest_frame()) 265 | return reprEnum(arg0, arg1) 266 | else: 267 | return self.typeNimName + "(" + str(int(self.val)) + ")" 268 | 269 | ################################################################################ 270 | 271 | class NimSetPrinter: 272 | ## the set printer is limited to sets that fit in an integer. Other 273 | ## sets are compiled to `NU8 *` (ptr uint8) and are invisible to 274 | ## gdb (currently). 275 | pattern = re.compile(r'^tySet_tyEnum_(\w*)_([A-Za-z0-9]*)$') 276 | 277 | def __init__(self, val): 278 | self.val = val 279 | match = self.pattern.match(self.val.type.name) 280 | self.typeNimName = match.group(1) 281 | 282 | typeInfoName = "NTI_" + match.group(2) + "_" 283 | self.nti = gdb.lookup_global_symbol(typeInfoName) 284 | 285 | if self.nti is None: 286 | printErrorOnce(typeInfoName, "NimSetPrinter: lookup global symbol '"+ typeInfoName +" failed for " + self.val.type.name + ".\n") 287 | 288 | def to_string(self): 289 | if self.nti: 290 | nti = self.nti.value(gdb.newest_frame()) 291 | enumStrings = [] 292 | val = int(self.val) 293 | i = 0 294 | while val > 0: 295 | if (val & 1) == 1: 296 | enumStrings.append(reprEnum(i, nti)) 297 | val = val >> 1 298 | i += 1 299 | 300 | return '{' + ', '.join(enumStrings) + '}' 301 | else: 302 | return str(int(self.val)) 303 | 304 | ################################################################################ 305 | 306 | class NimHashSetPrinter: 307 | pattern = re.compile(r'^tyObject_(HashSet)_([A-Za-z0-9]*)$') 308 | 309 | def __init__(self, val): 310 | self.val = val 311 | 312 | def display_hint(self): 313 | return 'array' 314 | 315 | def to_string(self): 316 | counter = 0 317 | capacity = 0 318 | if self.val: 319 | counter = int(self.val['counter']) 320 | if self.val['data']: 321 | capacity = int(self.val['data']['Sup']['len']) 322 | 323 | return 'HashSet({0}, {1})'.format(counter, capacity) 324 | 325 | def children(self): 326 | if self.val: 327 | data = NimSeqPrinter(self.val['data']) 328 | for idxStr, entry in data.children(): 329 | if int(entry['Field0']) > 0: 330 | yield ("data." + idxStr + ".Field1", str(entry['Field1'])) 331 | 332 | ################################################################################ 333 | 334 | class NimSeqPrinter: 335 | # the pointer is explicity part of the type. So it is part of 336 | # ``pattern``. 337 | pattern = re.compile(r'^tySequence_\w* \*$') 338 | 339 | def __init__(self, val): 340 | self.val = val 341 | 342 | def display_hint(self): 343 | return 'array' 344 | 345 | def to_string(self): 346 | len = 0 347 | cap = 0 348 | if self.val: 349 | len = int(self.val['Sup']['len']) 350 | cap = int(self.val['Sup']['reserved']) 351 | 352 | return 'seq({0}, {1})'.format(len, cap) 353 | 354 | def children(self): 355 | if self.val: 356 | length = int(self.val['Sup']['len']) 357 | #align = len(str(length - 1)) 358 | for i in range(length): 359 | yield ("data[{0}]".format(i), self.val["data"][i]) 360 | 361 | ################################################################################ 362 | 363 | class NimArrayPrinter: 364 | pattern = re.compile(r'^tyArray_\w*$') 365 | 366 | def __init__(self, val): 367 | self.val = val 368 | 369 | def display_hint(self): 370 | return 'array' 371 | 372 | def to_string(self): 373 | return 'array' 374 | 375 | def children(self): 376 | length = self.val.type.sizeof // self.val[0].type.sizeof 377 | align = len(str(length-1)) 378 | for i in range(length): 379 | yield ("[{0:>{1}}]".format(i, align), self.val[i]) 380 | 381 | ################################################################################ 382 | 383 | class NimStringTablePrinter: 384 | pattern = re.compile(r'^tyObject_(StringTableObj)_([A-Za-z0-9]*)(:? \*)?$') 385 | 386 | def __init__(self, val): 387 | self.val = val 388 | 389 | def display_hint(self): 390 | return 'map' 391 | 392 | def to_string(self): 393 | counter = 0 394 | capacity = 0 395 | if self.val: 396 | counter = int(self.val['counter']) 397 | if self.val['data']: 398 | capacity = int(self.val['data']['Sup']['len']) 399 | 400 | return 'StringTableObj({0}, {1})'.format(counter, capacity) 401 | 402 | def children(self): 403 | if self.val: 404 | data = NimSeqPrinter(self.val['data']) 405 | for idxStr, entry in data.children(): 406 | if int(entry['Field2']) > 0: 407 | yield (idxStr + ".Field0", entry['Field0']) 408 | yield (idxStr + ".Field1", entry['Field1']) 409 | 410 | ################################################################ 411 | 412 | class NimTablePrinter: 413 | pattern = re.compile(r'^tyObject_(Table)_([A-Za-z0-9]*)(:? \*)?$') 414 | 415 | def __init__(self, val): 416 | self.val = val 417 | # match = self.pattern.match(self.val.type.name) 418 | 419 | def display_hint(self): 420 | return 'map' 421 | 422 | def to_string(self): 423 | counter = 0 424 | capacity = 0 425 | if self.val: 426 | counter = int(self.val['counter']) 427 | if self.val['data']: 428 | capacity = int(self.val['data']['Sup']['len']) 429 | 430 | return 'Table({0}, {1})'.format(counter, capacity) 431 | 432 | def children(self): 433 | if self.val: 434 | data = NimSeqPrinter(self.val['data']) 435 | for idxStr, entry in data.children(): 436 | if int(entry['Field0']) > 0: 437 | yield (idxStr + '.Field1', entry['Field1']) 438 | yield (idxStr + '.Field2', entry['Field2']) 439 | 440 | 441 | ################################################################ 442 | 443 | # this is untested, therefore disabled 444 | 445 | # class NimObjectPrinter: 446 | # pattern = re.compile(r'^tyObject_.*$') 447 | 448 | # def __init__(self, val): 449 | # self.val = val 450 | 451 | # def display_hint(self): 452 | # return 'object' 453 | 454 | # def to_string(self): 455 | # return str(self.val.type) 456 | 457 | # def children(self): 458 | # if not self.val: 459 | # yield "object", "" 460 | # raise StopIteration 461 | 462 | # for (i, field) in enumerate(self.val.type.fields()): 463 | # if field.type.code == gdb.TYPE_CODE_UNION: 464 | # yield _union_field 465 | # else: 466 | # yield (field.name, self.val[field]) 467 | 468 | # def _union_field(self, i, field): 469 | # rti = getNimRti(self.val.type.name) 470 | # if rti is None: 471 | # return (field.name, "UNION field can't be displayed without RTI") 472 | 473 | # node_sons = rti['node'].dereference()['sons'] 474 | # prev_field = self.val.type.fields()[i - 1] 475 | 476 | # descriminant_node = None 477 | # for i in range(int(node['len'])): 478 | # son = node_sons[i].dereference() 479 | # if son['name'].string("utf-8", "ignore") == str(prev_field.name): 480 | # descriminant_node = son 481 | # break 482 | # if descriminant_node is None: 483 | # raise ValueError("Can't find union descriminant field in object RTI") 484 | 485 | # if descriminant_node is None: raise ValueError("Can't find union field in object RTI") 486 | # union_node = descriminant_node['sons'][int(self.val[prev_field])].dereference() 487 | # union_val = self.val[field] 488 | 489 | # for f1 in union_val.type.fields(): 490 | # for f2 in union_val[f1].type.fields(): 491 | # if str(f2.name) == union_node['name'].string("utf-8", "ignore"): 492 | # return (str(f2.name), union_val[f1][f2]) 493 | 494 | # raise ValueError("RTI is absent or incomplete, can't find union definition in RTI") 495 | 496 | 497 | ################################################################################ 498 | 499 | def makematcher(klass): 500 | def matcher(val): 501 | typeName = str(val.type) 502 | try: 503 | if hasattr(klass, 'pattern') and hasattr(klass, '__name__'): 504 | # print(typeName + " <> " + klass.__name__) 505 | if klass.pattern.match(typeName): 506 | return klass(val) 507 | except Exception as e: 508 | print(klass) 509 | printErrorOnce(typeName, "No matcher for type '" + typeName + "': " + str(e) + "\n") 510 | return matcher 511 | 512 | nimobjfile.pretty_printers = [] 513 | nimobjfile.pretty_printers.extend([makematcher(var) for var in list(vars().values()) if hasattr(var, 'pattern')]) 514 | -------------------------------------------------------------------------------- /images/debug-console-load-nim-gdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonprogrammer/nim-debug-example/1d4d41bdc46dbee6e95c305ba26a1fc51d60cb1b/images/debug-console-load-nim-gdb.png -------------------------------------------------------------------------------- /images/debugger-mouseover-str.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonprogrammer/nim-debug-example/1d4d41bdc46dbee6e95c305ba26a1fc51d60cb1b/images/debugger-mouseover-str.png -------------------------------------------------------------------------------- /images/vscode-debug-console-print1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonprogrammer/nim-debug-example/1d4d41bdc46dbee6e95c305ba26a1fc51d60cb1b/images/vscode-debug-console-print1.png -------------------------------------------------------------------------------- /images/vscode-debug-console-print2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonprogrammer/nim-debug-example/1d4d41bdc46dbee6e95c305ba26a1fc51d60cb1b/images/vscode-debug-console-print2.png -------------------------------------------------------------------------------- /images/vscode-nim-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonprogrammer/nim-debug-example/1d4d41bdc46dbee6e95c305ba26a1fc51d60cb1b/images/vscode-nim-config.png -------------------------------------------------------------------------------- /images/vscode-set-breakpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonprogrammer/nim-debug-example/1d4d41bdc46dbee6e95c305ba26a1fc51d60cb1b/images/vscode-set-breakpoint.png -------------------------------------------------------------------------------- /main.nim: -------------------------------------------------------------------------------- 1 | 2 | # this code is copied from the Nim homepage: https://nim-lang.org/ 3 | type 4 | Person = object 5 | name: string 6 | age: Natural # Ensures the age is positive 7 | 8 | proc main() = 9 | let people = [ 10 | Person(name: "John", age: 45), 11 | Person(name: "Kate", age: 30) 12 | ] 13 | 14 | let name = "bob" 15 | echo name 16 | echo people[0].name 17 | 18 | main() --------------------------------------------------------------------------------