├── pyproject.toml ├── .gitignore ├── setup.cfg ├── tests ├── test.py ├── test.cc └── test.gdb ├── LICENSE ├── duel ├── help.py ├── __init__.py ├── expr.py ├── help.md └── parser.py ├── pretty_printer └── __init__.py └── README.md /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ "setuptools" ] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .*.swp 3 | .gdb_history 4 | MANIFEST 5 | build/ 6 | dist/ 7 | gdb_tools.egg-info/ 8 | tests/a.out 9 | tests/test.in 10 | tests/test.out 11 | tests/test.reject 12 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = gdb-tools 3 | version = attr:duel.VERSION 4 | author = Sergei Golubchik 5 | author_email = vuvova@gmail.com 6 | description = Various tools to improve the gdb experience 7 | long_description = file: README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/vuvova/gdb-tools 10 | classifiers = 11 | Development Status :: 5 - Production/Stable 12 | Environment :: Plugins 13 | Intended Audience :: Developers 14 | License :: OSI Approved :: BSD License 15 | Programming Language :: Python 16 | Programming Language :: Python :: 2 17 | Programming Language :: Python :: 3 18 | Topic :: Software Development :: Debuggers 19 | 20 | [options] 21 | packages = 22 | duel 23 | pretty_printer 24 | install_requires = arpeggio 25 | 26 | [options.package_data] 27 | duel = help.md 28 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from subprocess import Popen, PIPE 4 | from os import execlp 5 | from os.path import dirname 6 | import re 7 | 8 | stem=__file__.rstrip('py') 9 | with open(stem+'gdb', 'r') as fg, open(stem+'in', 'w') as fi, open(stem+'out', 'w') as fo: 10 | for l in fg: 11 | if l.startswith('(gdb) '): 12 | fi.write(l[6:]) 13 | else: 14 | fo.write(l) 15 | p=Popen(['gdb', '-batch', '-n', '-x', 'test.in'], cwd=dirname(stem), stdout=PIPE, stderr=PIPE, universal_newlines=True) 16 | (o,e)=p.communicate() 17 | if e: raise Exception(e) 18 | o = re.sub(r'(=.*) 0x[0-9a-f]+', r'\1 0xXXXXX', o) 19 | o = re.sub(r'Temporary breakpoint 1 at .*\n', '', o) 20 | o = re.sub(r'\n.*/lib.*\n', '\n', o) 21 | with open(stem+'reject', 'w') as f: f.write(o) 22 | execlp('diff', 'diff', '-us', stem+'out', stem+'reject') 23 | -------------------------------------------------------------------------------- /tests/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int d_strcmp(const char *s1, const char *s2) 4 | { return strcmp(s1, s2); } 5 | 6 | int d_strncmp(const char *s1, const char *s2, size_t n) 7 | { return strncmp(s1, s2, n); } 8 | 9 | int d_strcasecmp(const char *s1, const char *s2) 10 | { return strcasecmp(s1, s2); } 11 | 12 | int foo = 1; 13 | float bar = 2.0; 14 | const char *s = "s1"; 15 | long arr[] = {5, 10, 15, 20, 0}; 16 | struct {unsigned i; double _r;} st = {123, 3.1415}; 17 | struct t { int v; struct t *left, *right; }; 18 | struct t t0={ 0, 0, 0}, t1={ 1, 0, 0}, t2={ 2, 0, 0}, t3={ 3, 0, 0}, 19 | t4={ 4, 0, 0}, t5={ 5, 0, 0}, t6={ 6, 0, 0}, t7={ 7, 0, 0}, 20 | t8={ 8,&t0,&t1}, t9={ 9,&t2,&t3}, ta={10,&t4,&t5}, tb={11,&t6,&t7}, 21 | tc={12,&t8,&t9}, td={13,&ta,&tb}, te={14,&tc,&td}, *tree = &te; 22 | 23 | 24 | int main() 25 | { 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Sergei Golubchik 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /duel/help.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | INTRO = """\ 4 | Supported DUEL commands: 5 | duel help - give basic help (shortcut: dl ?) 6 | duel longhelp - give a longer help (dl ??) 7 | duel examples - show useful usage examples 8 | duel operators - operators summary 9 | duel aliases - show current aliases 10 | duel clear - clear all aliases 11 | """ 12 | 13 | with open(__file__.rstrip("pyc") + "md") as f: LONGHELP = ''.join(f) 14 | 15 | HELP = """\ 16 | Duel - Debugging U (might) Even Like -- A high level data exploration language 17 | 18 | Duel was designed to overcome problems with traditional debuggers' print 19 | statement. It supports the C operators, many C constructs, and many new 20 | operators for easy exploration of the program's space, e.g. 21 | x[..100] >? 0 show positive x[i] for i=0 to 99 22 | y[10..20].code !=? 0 show non-zero y[i].code for i=10 to 20 23 | h-->next->code expand linked list h->next, h->next->next ... 24 | head-->next.if(code>0) name show name for each element with code>0 25 | x[i:=..100]=y[i]; array copy. i is an alias to vals 0..99 26 | head-->next[[10..15]] the 10th to 15th element of a linked list 27 | #/(head-->next->val==?4) count elements with val==4 28 | head-->next->if(next) val >? next->val check if list is sorted by val 29 | 30 | Duel was created by Michael Golan at Princeton University. 31 | Duel.py is a pure-python Duel implementation by Sergei Golubchik. 32 | 33 | Try "dl operators" or "dl longhelp" 34 | """ 35 | 36 | OPERATORS = re.sub(r'(?s)^.*\nOperators\n---------\n\n*(.*?\n)[^\n]+\n-----+\n.*$', r'\1', LONGHELP) 37 | EXAMPLES = re.sub(r'(?s)^.*\nExamples\n--------\n\n*(.*?\n)[^\n]+\n-----+\n.*$', r'\1', LONGHELP) 38 | EXAMPLES = re.sub(r'\n\n', r'\n', EXAMPLES) 39 | -------------------------------------------------------------------------------- /duel/__init__.py: -------------------------------------------------------------------------------- 1 | import gdb 2 | import sys 3 | import traceback 4 | 5 | from duel.help import * 6 | from duel import parser, expr 7 | 8 | VERSION='1.6' 9 | 10 | class duel (gdb.Command): 11 | """Evaluate Duel expressions. 12 | 13 | Duel is a high level data exploration language. 14 | Type "dl" for help""" 15 | 16 | debug = False 17 | 18 | def __init__ (self): 19 | super (duel, self).__init__ ("duel", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION, False) 20 | gdb.execute('alias -a dl = duel') 21 | gdb.write("Loaded DUEL.py " + VERSION + ", high level data exploration language\n") 22 | 23 | def invoke (self, arg, from_tty): 24 | if arg == "": 25 | gdb.write(INTRO) 26 | elif arg in [ '?', 'help' ]: 27 | gdb.write(HELP) 28 | elif arg in [ '??', 'longhelp' ]: 29 | gdb.write(LONGHELP) 30 | elif arg in [ 'examples' ]: 31 | gdb.write(EXAMPLES) 32 | elif arg in [ 'operators' ]: 33 | gdb.write(OPERATORS) 34 | elif arg in [ 'debug' ]: 35 | self.debug = not self.debug 36 | gdb.write('Duel debug is ' + ['dis', 'en'][self.debug] + 'abled\n') 37 | elif arg in [ 'aliases' ]: 38 | if len(expr.aliases) > 0: 39 | gdb.write("Aliases table:\n") 40 | for k in sorted(expr.aliases.keys()): 41 | n,v=expr.aliases[k] 42 | gdb.write("{0}: {1} = {2}\n".format(k, n, expr.val2str(v))) 43 | else: 44 | gdb.write("Aliases table empty\n") 45 | elif arg == 'clear': 46 | expr.aliases.clear() 47 | gdb.write("Aliases table cleared\n") 48 | else: 49 | try: 50 | parser.eval(arg) 51 | except Exception as e: 52 | gdb.write(str(e)+'\n') 53 | if self.debug: 54 | traceback.print_exc() 55 | 56 | duel() 57 | -------------------------------------------------------------------------------- /pretty_printer/__init__.py: -------------------------------------------------------------------------------- 1 | """a helper for easy creation of gdb pretty-printers""" 2 | 3 | import gdb.printing 4 | 5 | # in python2 gdb.Value can only be converted to long(), python3 only has int() 6 | try: a=long(1) 7 | except: long=int 8 | 9 | pp_registry=dict(); 10 | 11 | class PPWrapper: 12 | def __init__(self, prefix, val, cb): 13 | self.prefix = prefix 14 | self.val = val 15 | self.cb = cb 16 | def to_string(self): 17 | return self.prefix + self.cb(self.val) 18 | 19 | class PPDispatcher(gdb.printing.PrettyPrinter): 20 | def __init__(self): 21 | super(PPDispatcher, self).__init__('gdb-tools') 22 | def __call__(self, val): 23 | prefix = '' 24 | if val.type.code == gdb.TYPE_CODE_PTR: 25 | if long(val) == 0: 26 | return None 27 | prefix = '({0}) {1:#08x} '.format(str(val.type), long(val)) 28 | try: val = val.dereference() 29 | except: return None 30 | valtype=val.type.unqualified() 31 | try: cb=pp_registry[valtype.name] 32 | except: 33 | try: 34 | n=valtype.strip_typedefs().name 35 | cb=pp_registry[valtype.strip_typedefs().name] 36 | except: 37 | try: cb=pp_registry[n[0:n.index('<')]+'<>'] 38 | except: return None 39 | return PPWrapper(prefix, val, cb) 40 | 41 | gdb.printing.register_pretty_printer(None, PPDispatcher(), True) 42 | 43 | def PrettyPrinter(arg): 44 | """@PrettyPrinter decorator. 45 | 46 | With a @PrettyPrinter decorator one only needs to write a function 47 | that takes gdb.Value as an argument and returns a string to be 48 | shown by gdb. 49 | 50 | Typical usage: 51 | 52 | @PrettyPrinter 53 | def some_typename(val): 54 | 55 | 56 | This creates all necessary classes and register a pretty-printer 57 | for the type "some_typename", be it a typedef'ed type name or 58 | the real underlying type with all typedef's resolved. It also 59 | creates a pretty-printer for a pointer to some_typename. 60 | 61 | When a type name is not a valid Python identifier, one can use 62 | 63 | @PrettyPrinter("real complex type name") 64 | def does_not_matter(val): 65 | 66 | """ 67 | name = getattr(arg, '__name__', arg) 68 | 69 | def register(func): 70 | pp_registry[name]=func 71 | return func 72 | 73 | if callable(arg): 74 | return register(arg) 75 | return register 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gdb-tools 2 | 3 | This repository contains various tools used to make the time spent in gdb more 4 | comfortable. 5 | 6 | To install these tools, first install the modules with 7 | 8 | pip install gdb-tools 9 | 10 | Then you need to import corresponding modules into your gdb. Add, for example, 11 | 12 | py import duel 13 | 14 | into your `~/.gdbinit`. If you plan to use `pretty_printer` module, I'd 15 | recommend to put all your python gdb enhancements in `~/.gdb.py` and source it 16 | from `~/.gdbinit`. 17 | 18 | ## pretty_printer.py 19 | 20 | A convenience helper to write **gdb pretty-printers**. Import this module and 21 | write new pretty printers as easy as 22 | ```python 23 | from pretty_printer import PrettyPrinter 24 | 25 | @PrettyPrinter 26 | def st_bitmap(val): 27 | s='' 28 | for i in range((val['n_bits']+31)//32): 29 | s = format(int(val['bitmap'][i]), '032b') + s 30 | return "b'" + s[-int(val['n_bits']):] + "'" 31 | ``` 32 | Here `val` is a `gdb.Value` object to print, and `st_bitmap` is the type to 33 | pretty-print (alternatively, a type can be passed to the decorator as an 34 | argument, useful for types that aren't valid Python identifiers). If the type 35 | has a name, either typedef'ed name or the underlying actual type can be used in 36 | the pretty printer definition (useful, for types like 37 | `typedef int set_of_flags`). Pointers are resolved automatically: 38 | ``` 39 | (gdb) p map 40 | $1 = b'001010111' 41 | (gdb) p &map 42 | $1 = (st_bitmap *) 0x7fff8610 b'001010111' 43 | ``` 44 | 45 | Import this module into your `~/.gdb.py` and create your own pretty printers 46 | there. 47 | 48 | ## DUEL — Debugging U (might) Even Like 49 | 50 | A high level language for exploring various data structures. Created by 51 | Michael Golan in 1993, who implemented it for gdb 4.x. "Insanely cool", 52 | according to gdb developers. This is **DUEL.py** — a pure python implementation 53 | that uses gdb Python API and the [Arpeggio](https://github.com/igordejanovic/Arpeggio) 54 | parser. Install arpeggio (or copy it into `duel/` — it's only 20K) and 55 | `import duel` into your `~/.gdb.py`. Few examples of what DUEL can do: 56 | 57 | Command | Explanation 58 | ------------ | ------------- 59 | `dl ?` | short help 60 | `dl x[10..20,22,24,40..60]` | display `x[i]` for the selected indexes 61 | `dl x[9..0]` | display `x[i]` backwards 62 | `dl x[..100] >? 5 ? x[i+1]` | check whether `x[i]` is sorted 65 | `dl (x[..100] >? 0)[[2]]` | return the 3rd positive `x[i]` 66 | `dl argv[0..]@0` | `argv[0]`, `argv[1]`, etc until first null 67 | `dl emp[0..]@(code==0)` | `emp[0]`, `emp[1]`, etc until `emp[n].code==0` 68 | `dl head-->next->val` | `val` of each element in a linked list 69 | `dl head-->(left,right)->val` | `val` of each element in a binary tree 70 | `dl head-->next[[20]]` | element 20 of list 71 | `dl #/head-->next` | count elements on a linked list 72 | `dl #/(head-->next-val>?5)` | count those over 5 73 | `dl head-->(next!=?head)` | expand cyclic linked list 74 | 75 | Or read the [manual](https://github.com/vuvova/gdb-tools/blob/arpeggio/duel/help.md). 76 | -------------------------------------------------------------------------------- /duel/expr.py: -------------------------------------------------------------------------------- 1 | import gdb 2 | import sys 3 | 4 | try: a=xrange # Python 3 compatibility 5 | except: 6 | def xrange(f,t,s=1): return range(int(f),int(t),s) 7 | 8 | aliases = dict() 9 | scopes = list() 10 | 11 | def val2str(v): 12 | try: v = v.referenced_value() if v.type.code==gdb.TYPE_CODE_REF else v 13 | except: pass 14 | return str(v) 15 | 16 | # this uses gdb convenience variables to avoid convering all arguments to strings 17 | def parse_and_call(func, *args): 18 | s = func + '(' 19 | for i, a in enumerate(args): 20 | n = 'duel_eval_func_call_'+str(i) 21 | gdb.set_convenience_variable(n, a) 22 | s += '$' + n + ',' 23 | return gdb.parse_and_eval(s[0:-1]+')') 24 | 25 | class Expr(object): 26 | def name(self): return self.name_ 27 | def value(self): return self.value_ 28 | def eval(self): yield self.name(), self.value() 29 | def no_parens(self): return False 30 | def scoped_eval(self, v): 31 | g = self.eval() 32 | while True: 33 | scopes.append(v) 34 | try: 35 | p = next(g) 36 | except StopIteration: 37 | return 38 | finally: 39 | scopes.pop() 40 | yield p 41 | 42 | class Literal(Expr): 43 | def __init__(self, n, v): self.name_, self.value_ = n, v 44 | def no_parens(self): return True 45 | 46 | class Ident(Expr): 47 | def __init__(self, n): self.name_, self.scope, self.sym = n, None, None 48 | def no_parens(self): return True 49 | def symval(self, s): return s.value(gdb.selected_frame()) if s.needs_frame else s.value() 50 | def value(self): 51 | if self.scope: return scopes[self.scope][self.name_] 52 | if self.sym: return self.symval(self.sym) 53 | if self.name_ in aliases: return aliases[self.name_][1] 54 | for self.scope in range(len(scopes)-1,-1,-1): 55 | try: return scopes[self.scope][self.name_] 56 | except gdb.error: self.scope = None 57 | try: self.sym = gdb.lookup_symbol(self.name_)[0] 58 | except gdb.error: self.sym = gdb.lookup_global_symbol(self.name_) 59 | if self.sym: return self.symval(self.sym) 60 | return gdb.parse_and_eval(self.name_) 61 | 62 | class Underscore(Expr): 63 | def __init__(self, n): self.name_ = n 64 | def eval(self): 65 | v = scopes[-len(self.name_)] 66 | yield val2str(v), v 67 | 68 | class UnaryBase(Expr): 69 | def __init__(self, a): self.arg1_ = a 70 | def name(self): return self.name_.format(self.arg1_.name()) 71 | def eval(self): 72 | for n,v in self.arg1_.eval(): 73 | yield self.name_.format(n), self.value(v) 74 | 75 | class Unary(UnaryBase): 76 | def __init__(self, n, a, v): 77 | super (Unary, self).__init__ (a) 78 | self.name_ = n if '{' in n else n + '{0}' 79 | self.value = v 80 | 81 | class Parens(UnaryBase): 82 | name_ = "({0})" 83 | def no_parens(self): return True 84 | def eval(self): 85 | for n,v in self.arg1_.eval(): 86 | yield n if self.arg1_.no_parens() else '('+n+')', v 87 | 88 | class Curlies(UnaryBase): 89 | name_ = "({0})" 90 | def eval(self): 91 | for n,v in self.arg1_.eval(): 92 | yield val2str(v), v 93 | 94 | class BinaryBase(Expr): 95 | def __init__(self, a1, a2): self.arg1_, self.arg2_ = a1, a2 96 | def name(self): return self.name_.format(self.arg1_.name(), self.arg2_.name()) 97 | def eval(self): 98 | for n1,v1 in self.arg1_.eval(): 99 | for n2,v2 in self.arg2_.eval(): 100 | yield self.name_.format(n1, n2), self.value(v1, v2) 101 | 102 | class Binary(BinaryBase): 103 | def __init__(self, a1, n, a2, v): 104 | super (Binary, self).__init__ (a1, a2) 105 | self.name_ = n if '{' in n else '{0} ' + n + ' {1}' 106 | self.value = v 107 | 108 | class Filter(Binary): 109 | def eval(self): 110 | for n1,v1 in self.arg1_.eval(): 111 | for n2,v2 in self.arg2_.eval(): 112 | if self.value(v1, v2): 113 | yield self.name_.format(n1, n2), v1 114 | 115 | class Struct(BinaryBase): 116 | def __init__(self, a1, n, a2): 117 | super (Struct, self).__init__ (a1, a2) 118 | self.name_ = n 119 | def eval(self): 120 | for n1,v1 in self.arg1_.eval(): 121 | for n2,v2 in self.arg2_.scoped_eval(v1): 122 | yield self.name_.format(n1, n2), v2 123 | 124 | class StructWalk(BinaryBase): 125 | name_ = '{0}-->{1}' 126 | def path2str(self, path): 127 | if len(path) == 1: return path[0] 128 | s, prev, cnt = path[0], path[1], 1 129 | for m in path[2:] + [None]: 130 | if m == prev: cnt += 1 131 | else: 132 | if cnt == 1: s += '->{0}'.format(prev) 133 | else: s += '-->{0}[[{1}]]'.format(prev, cnt) 134 | prev, cnt = m, 1 135 | return s 136 | def eval(self): 137 | for n1,v1 in self.arg1_.eval(): 138 | queue = [ ([n1], v1) ] 139 | while queue: 140 | n1, v1 = queue.pop() 141 | if not v1: continue 142 | yield self.path2str(n1), v1 143 | l = len(queue) 144 | for n2,v2 in self.arg2_.scoped_eval(v1.dereference()): 145 | queue.insert(l, (n1+[n2], v2)) 146 | 147 | class TakeNth(BinaryBase): 148 | name_ = '{0}[[{1}]]' 149 | def eval(self): 150 | l = None 151 | for n2,v2 in self.arg2_.eval(): 152 | if v2 < 0: 153 | if l is None: l = sum(1 for i in self.arg1_.eval()) 154 | v2 += l 155 | if isinstance(self.arg2_, Curlies): n2 = str(v2) 156 | if v2 < 0: raise StopIteration 157 | val = self.arg1_.eval() 158 | for i in xrange(0,v2): next(val) 159 | n1, v1 = next(val) 160 | yield self.name_.format(self.arg1_.name(), n2), v1 161 | 162 | class Until(BinaryBase): 163 | name_ = '{0}@{1}' 164 | def eval(self): 165 | if isinstance(self.arg2_, Literal): f = lambda x,y: x == y 166 | else: f= lambda x,y: y 167 | for n1,v1 in self.arg1_.eval(): 168 | stop, output = False, False 169 | for n2,v2 in self.arg2_.scoped_eval(v1): 170 | if f(v1, v2): stop = True 171 | else: output = True 172 | if stop and output: break 173 | if output: yield n1, v1 174 | if stop: break 175 | 176 | class URange(UnaryBase): 177 | def __init__(self, n, a1, to): 178 | super (URange, self).__init__ (a1) 179 | self.name_, self.to = n, to 180 | def no_parens(self): return True 181 | def eval(self): 182 | for n1,v1 in self.arg1_.eval(): 183 | for i in xrange(0 if self.to else v1, v1 if self.to else sys.maxsize): 184 | v = gdb.Value(i).cast(v1.type) 185 | yield val2str(v), v 186 | 187 | class BiRange(BinaryBase): 188 | name_ = '{0}..{1}' 189 | def no_parens(self): return True 190 | def eval(self): 191 | for n1,v1 in self.arg1_.eval(): 192 | for n2,v2 in self.arg2_.eval(): 193 | step = 1 if v1 < v2 else -1 194 | for i in xrange(v1, v2 + step, step): 195 | v = gdb.Value(i).cast(v1.type) 196 | yield val2str(v), v 197 | 198 | class EagerGrouping(UnaryBase): 199 | def __init__(self, n, a, v): 200 | super (EagerGrouping, self).__init__ (a) 201 | self.name_, self.add = n + '{0}', v 202 | def eval(self): 203 | i = 0 204 | for n,v in self.arg1_.eval(): i = self.add(i, v) 205 | yield self.name(), gdb.Value(i) 206 | 207 | class LazyGrouping(UnaryBase): 208 | def __init__(self, n, a, v0, v): 209 | super (LazyGrouping, self).__init__ (a) 210 | self.name_, self.init_val, self.add = n + '{0}', v0, v 211 | def eval(self): 212 | i = self.init_val 213 | for n,v in self.arg1_.eval(): 214 | i = self.add(i, v) 215 | if i != self.init_val: break 216 | yield self.name(), gdb.Value(i) 217 | 218 | class Ternary(Expr): 219 | def __init__(self, n, a1, a2, a3): 220 | self.name_, self.arg1_, self.arg2_, self.arg3_= n, a1, a2, a3 221 | def name(self): 222 | return self.name_.format(self.arg1_.name(), self.arg2_.name(), 223 | self.arg3_ and self.arg3_.name()) 224 | def eval(self): 225 | for n1,v1 in self.arg1_.eval(): 226 | for n2,v2 in self.arg2_.eval(): 227 | if self.arg3_: 228 | for n3,v3 in self.arg3_.eval(): 229 | yield self.name_.format(n1, n2, n3), v2 if v1 else v3 230 | else: 231 | if v1: yield self.name_.format(n1, n2), v2 232 | 233 | class Alias(BinaryBase): 234 | name_ = '{0} := {1}' 235 | def no_parens(self): return True 236 | def eval(self): 237 | for n2,v2 in self.arg2_.eval(): 238 | try: v2 = v2.reference_value() 239 | except: pass 240 | aliases[self.arg1_.name()] = (n2, v2) 241 | yield self.arg1_.name(), v2 242 | 243 | class Enumerate(BinaryBase): 244 | name_ = '{0}#{1}' 245 | def eval(self): 246 | for i, nv1 in enumerate(self.arg1_.eval()): 247 | aliases[self.arg2_.name()] = (str(i), i) 248 | yield nv1 249 | 250 | class List(Expr): 251 | def __init__(self, args): self.args_ = args 252 | def name(self): return ','.join([e.name() for e in self.args_]) 253 | def no_parens(self): return self.cur.no_parens() 254 | def eval(self): 255 | for self.cur in self.args_: 256 | for n2,v2 in self.cur.eval(): 257 | yield n2, v2 258 | 259 | class Statement(Expr): 260 | def __init__(self, args): self.args_ = args 261 | def name(self): return '; '.join([e.name() for e in self.args_]) 262 | def eval(self): 263 | for v in self.args_[:-1]: 264 | for n2,v2 in v.eval(): pass 265 | for n2,v2 in self.args_[-1].eval(): 266 | yield n2, v2 267 | 268 | class Foreach(BinaryBase): 269 | name_ = '{0} => {1}' 270 | def eval(self): 271 | for n1,v1 in self.arg1_.eval(): 272 | for n2,v2 in self.arg2_.scoped_eval(v1): 273 | yield n2, v2 274 | 275 | class Call(BinaryBase): 276 | name_ = '{0}({1})' 277 | def eval(self): 278 | for n1,v1 in self.arg1_.eval(): 279 | args = self.arg2_.args_ 280 | gens = [] + args 281 | nams = [] + args 282 | vals = [] + args 283 | cur = -1 284 | if v1.type.code == gdb.TYPE_CODE_INTERNAL_FUNCTION: 285 | v1=lambda *args: parse_and_call(n1, *args) 286 | while True: 287 | while cur < len(args)-1: 288 | cur += 1 289 | gens[cur] = args[cur].eval() 290 | nams[cur], vals[cur] = next(gens[cur]) 291 | yield self.name_.format(n1, ','.join(nams)), v1(*vals) 292 | repeat = True 293 | while repeat and cur >= 0: 294 | repeat = False 295 | try: nams[cur], vals[cur] = next(gens[cur]) 296 | except StopIteration: 297 | cur -= 1 298 | repeat = True 299 | if cur < 0: break 300 | -------------------------------------------------------------------------------- /tests/test.gdb: -------------------------------------------------------------------------------- 1 | (gdb) shell g++ test.cc -O0 -ggdb3 2 | (gdb) file a.out 3 | (gdb) py sys.path.append('..') 4 | (gdb) py import duel 5 | Loaded DUEL.py 1.6, high level data exploration language 6 | (gdb) help duel 7 | duel, dl 8 | Evaluate Duel expressions. 9 | 10 | Duel is a high level data exploration language. 11 | Type "dl" for help 12 | (gdb) dl 13 | Supported DUEL commands: 14 | duel help - give basic help (shortcut: dl ?) 15 | duel longhelp - give a longer help (dl ??) 16 | duel examples - show useful usage examples 17 | duel operators - operators summary 18 | duel aliases - show current aliases 19 | duel clear - clear all aliases 20 | (gdb) dl ? 21 | Duel - Debugging U (might) Even Like -- A high level data exploration language 22 | 23 | Duel was designed to overcome problems with traditional debuggers' print 24 | statement. It supports the C operators, many C constructs, and many new 25 | operators for easy exploration of the program's space, e.g. 26 | x[..100] >? 0 show positive x[i] for i=0 to 99 27 | y[10..20].code !=? 0 show non-zero y[i].code for i=10 to 20 28 | h-->next->code expand linked list h->next, h->next->next ... 29 | head-->next.if(code>0) name show name for each element with code>0 30 | x[i:=..100]=y[i]; array copy. i is an alias to vals 0..99 31 | head-->next[[10..15]] the 10th to 15th element of a linked list 32 | #/(head-->next->val==?4) count elements with val==4 33 | head-->next->if(next) val >? next->val check if list is sorted by val 34 | 35 | Duel was created by Michael Golan at Princeton University. 36 | Duel.py is a pure-python Duel implementation by Sergei Golubchik. 37 | 38 | Try "dl operators" or "dl longhelp" 39 | (gdb) dl 1 40 | = 1 41 | (gdb) dl 2.0 42 | 2.0 = 2 43 | (gdb) dl 2e1 44 | 2e1 = 20 45 | (gdb) dl 1 46 | = 1 47 | (gdb) dl 2.0 48 | 2.0 = 2 49 | (gdb) dl 2e1 50 | 2e1 = 20 51 | (gdb) dl 2.0e1 52 | 2.0e1 = 20 53 | (gdb) dl 020 54 | 020 = 16 55 | (gdb) dl 0x20 56 | 0x20 = 32 57 | (gdb) dl 019 58 | Expected 'if' or ident or '&&/' or '||/' or '#/' or '+/' or '-' or '*' or '&' or '!' or '~' or '(cast)' or real or hexadecimal or decimal or octal or char or string or underscores or gdbvar or '(' or '{' or '..' at position (1, 1) => '*019'. 59 | (gdb) dl foo 60 | foo = 1 61 | (gdb) dl bar 62 | bar = 2 63 | (gdb) dl s 64 | s = 0xXXXXX "s1" 65 | (gdb) dl 'a' 66 | 'a' = 97 'a' 67 | (gdb) dl '\r' 68 | '\r' = 13 '\r' 69 | (gdb) dl '\x20' 70 | '\x20' = 32 ' ' 71 | (gdb) dl "foo\7bar" 72 | "foo\7bar" = "foo\abar" 73 | (gdb) dl "\a\r\n\v\t"[2..3] 74 | "\a\r\n\v\t"[2] = 10 '\n' 75 | "\a\r\n\v\t"[3] = 11 '\v' 76 | (gdb) dl "reverse"[6..2] 77 | "reverse"[6] = 101 'e' 78 | "reverse"[5] = 115 's' 79 | "reverse"[4] = 114 'r' 80 | "reverse"[3] = 101 'e' 81 | "reverse"[2] = 118 'v' 82 | (gdb) dl unknown_var 83 | No symbol "unknown_var" in current context. 84 | (gdb) dl (2.0) 85 | 2.0 = 2 86 | (gdb) dl {2.0} 87 | = 2 88 | (gdb) dl {(2.0)} 89 | = 2 90 | (gdb) dl (foo) 91 | foo = 1 92 | (gdb) dl {foo} 93 | = 1 94 | (gdb) dl 5[1] 95 | Cannot subscript requested type. 96 | (gdb) dl s[1] 97 | s[1] = 49 '1' 98 | (gdb) dl arr[1] 99 | arr[1] = 10 100 | (gdb) dl arr[foo] 101 | arr[foo] = 10 102 | (gdb) dl arr[{foo}] 103 | arr[1] = 10 104 | (gdb) dl st.i 105 | st.i = 123 106 | (gdb) dl st.(_r) 107 | st._r = 3.1415000000000002 108 | (gdb) dl st.{_r} 109 | st.3.1415000000000002 = 3.1415000000000002 110 | (gdb) dl tree->v 111 | tree->v = 14 112 | (gdb) dl tree->u 113 | No symbol "u" in current context. 114 | (gdb) dl tree.val 115 | No symbol "val" in current context. 116 | (gdb) dl tree->left->v 117 | tree->left->v = 12 118 | (gdb) dl tree-->left->v 119 | tree->v = 14 120 | tree->left->v = 12 121 | tree-->left[[2]]->v = 8 122 | tree-->left[[3]]->v = 0 123 | (gdb) dl tree-->left->v[[1]] 124 | tree-->left->v[[1]] = 12 125 | (gdb) dl tree-->left->v[[2]] 126 | tree-->left->v[[2]] = 8 127 | (gdb) dl tree-->left->v[[5]] 128 | generator raised StopIteration 129 | (gdb) dl tree-->left->v[[-1]] 130 | tree-->left->v[[-1]] = 0 131 | (gdb) dl tree-->left->v[[-2]] 132 | tree-->left->v[[-2]] = 8 133 | (gdb) dl tree-->left->v[[{-3}]] 134 | tree-->left->v[[1]] = 12 135 | (gdb) dl tree-->left->v[[-5]] 136 | generator raised StopIteration 137 | (gdb) dl tree-->left->v + v 138 | No symbol "v" in current context. 139 | (gdb) dl (*tree).v 140 | (*tree).v = 14 141 | (gdb) dl *tree.v 142 | Attempt to take contents of a non-pointer value. 143 | (gdb) dl *1 144 | Attempt to take contents of a non-pointer value. 145 | (gdb) dl *&foo 146 | *&foo = 1 147 | (gdb) dl #/foo 148 | #/foo = 1 149 | (gdb) dl #/{foo} 150 | #/(foo) = 1 151 | (gdb) dl #/tree-->left 152 | #/tree-->left = 4 153 | (gdb) dl #/arr[..2] 154 | #/arr[..2] = 2 155 | (gdb) dl #/(arr[..2] left->v 162 | &&/tree-->left->v = 0 163 | (gdb) dl ||/arr[..2] 164 | ||/arr[..2] = 1 165 | (gdb) dl &&/arr[0..] 166 | &&/arr[0..] = 0 167 | (gdb) dl -5 168 | = -5 169 | (gdb) dl --10 170 | --10 = 10 171 | (gdb) dl -foo 172 | -foo = -1 173 | (gdb) dl !0 174 | !0 = true 175 | (gdb) dl !foo 176 | !foo = false 177 | (gdb) dl !{foo} 178 | !1 = false 179 | (gdb) dl foo + 1 180 | foo + 1 = 2 181 | (gdb) dl ~5 182 | ~5 = -6 183 | (gdb) dl ~ 0xFF77 184 | ~0xFF77 = -65400 185 | (gdb) dl 10 * 3.14 186 | 10 * 3.14 = 31.400000000000002 187 | (gdb) dl arr[foo] / {st._r} 188 | arr[foo] / 3.1415000000000002 = 3.1831927423205473 189 | (gdb) dl st.i % 10 190 | st.i % 10 = 3 191 | (gdb) dl 1 + 2 192 | 1 + 2 = 3 193 | (gdb) dl 100 - 9*8 194 | 100 - 9 * 8 = 28 195 | (gdb) dl 1 << 8/2 196 | 1 << 8 / 2 = 16 197 | (gdb) dl 1024 >> 2*3 198 | 1024 >> 2 * 3 = 16 199 | (gdb) dl foo ^ 0x0BEC 200 | foo ^ 0x0BEC = 3053 201 | (gdb) dl 1234 | 0xBABE 202 | 1234 | 0xBABE = 48894 203 | (gdb) dl 0xDEAD & 0xBEEF 204 | 0xDEAD & 0xBEEF = 40621 205 | (gdb) dl 0xDEAD && 0xBEEF 206 | 0xDEAD && 0xBEEF = 1 207 | (gdb) dl foo && 0 208 | foo && 0 = 0 209 | (gdb) dl foo || 0 210 | foo || 0 = 1 211 | (gdb) dl tree->left->left->left->v || 0 212 | tree->left->left->left->v || 0 = 0 213 | (gdb) dl tree-->right->(v @ (v <= 11)) 214 | tree->(v) = 14 215 | tree->right->(v) = 13 216 | (gdb) dl tree-->right @ (v <= 11, 0) 217 | tree = 0xXXXXX 218 | tree->right = 0xXXXXX 219 | tree-->right[[2]] = 0xXXXXX 220 | (gdb) dl tree-->right @ (v <= 11, 0) + v 221 | No symbol "v" in current context. 222 | (gdb) dl 1..5 223 | = 1 224 | = 2 225 | = 3 226 | = 4 227 | = 5 228 | (gdb) dl 'x'..'z' 229 | = 120 'x' 230 | = 121 'y' 231 | = 122 'z' 232 | (gdb) dl ..3 233 | = 0 234 | = 1 235 | = 2 236 | (gdb) dl foo..4 237 | = 1 238 | = 2 239 | = 3 240 | = 4 241 | (gdb) dl #/(..15) 242 | #/(..15) = 15 243 | (gdb) dl #/1..5 244 | = 1 245 | = 2 246 | = 3 247 | = 4 248 | = 5 249 | (gdb) dl (..3) + (10..11) 250 | 0 + 10 = 10 251 | 0 + 11 = 11 252 | 1 + 10 = 11 253 | 1 + 11 = 12 254 | 2 + 10 = 12 255 | 2 + 11 = 13 256 | (gdb) dl arr[..2] 257 | arr[0] = 5 258 | arr[1] = 10 259 | (gdb) dl arr[0..]@0 260 | arr[0] = 5 261 | arr[1] = 10 262 | arr[2] = 15 263 | arr[3] = 20 264 | (gdb) dl #/arr[0..]@0 265 | #/arr[0..]@0 = 4 266 | (gdb) dl 1..(2..3) 267 | = 1 268 | = 2 269 | = 1 270 | = 2 271 | = 3 272 | (gdb) dl 2 < 5 273 | 2 < 5 = true 274 | (gdb) dl foo > arr[foo] 275 | foo > arr[foo] = false 276 | (gdb) dl 1 >= 2 277 | 1 >= 2 = false 278 | (gdb) dl foo == 10 279 | foo == 10 = false 280 | (gdb) dl st.i != 20 281 | st.i != 20 = true 282 | (gdb) dl 1 ? 2 : 3 283 | 1 ? 2 : 3 = 2 284 | (gdb) dl foo ? 2 ? 3 : 4 : 5 285 | foo ? 2 ? 3 : 4 : 5 = 3 286 | (gdb) dl !foo ? foo : 2 ? 3 : 4 287 | !foo ? foo : 2 ? 3 : 4 = 3 288 | (gdb) dl ..10 >? 4 289 | 5 >? 4 = 5 290 | 6 >? 4 = 6 291 | 7 >? 4 = 7 292 | 8 >? 4 = 8 293 | 9 >? 4 = 9 294 | (gdb) dl arr[..2] <=? 6 295 | arr[0] <=? 6 = 5 296 | (gdb) dl ((3..6) * (1..3)) %3 ==? 2 297 | (4 * 2) % 3 ==? 2 = 2 298 | (5 * 1) % 3 ==? 2 = 2 299 | (gdb) dl arr[..2] !=? 5 300 | arr[1] !=? 5 = 10 301 | (gdb) dl 1 >? 0 302 | 1 >? 0 = 1 303 | (gdb) dl 1 >? 2 304 | (gdb) dl (i:=5)+9 305 | i + 9 = 14 306 | (gdb) dl arr[i:=..2] 307 | arr[i] = 5 308 | arr[i] = 10 309 | (gdb) dl arr[i:=..2] + i 310 | arr[i] + i = 5 311 | arr[i] + i = 11 312 | (gdb) dl arr[i:=..2] + {i} 313 | arr[i] + 0 = 5 314 | arr[i] + 1 = 11 315 | (gdb) dl x:= "string" 316 | x = "string" 317 | (gdb) dl y:=&foo, *y 318 | y = 0xXXXXX 319 | *y = 1 320 | (gdb) dl z:=foo 321 | z = 1 322 | (gdb) dl &z 323 | &z = 0xXXXXX 324 | (gdb) dl &i 325 | Not addressable 326 | (gdb) dl aliases 327 | Aliases table: 328 | i: 1 = 1 329 | x: "string" = "string" 330 | y: &foo = 0xXXXXX 331 | z: foo = 1 332 | (gdb) dl clear 333 | Aliases table cleared 334 | (gdb) dl aliases 335 | Aliases table empty 336 | (gdb) dl 1..3 => _+5 337 | 1 + 5 = 6 338 | 2 + 5 = 7 339 | 3 + 5 = 8 340 | (gdb) dl ..2 => arr[_] 341 | arr[0] = 5 342 | arr[1] = 10 343 | (gdb) dl (..2 => arr[_]) + _ 344 | list index out of range 345 | (gdb) dl 1..3 => .._ => __ + _ 346 | 1 + 0 = 1 347 | 2 + 0 = 2 348 | 2 + 1 = 3 349 | 3 + 0 = 3 350 | 3 + 1 = 4 351 | 3 + 2 = 5 352 | (gdb) dl 1,2,3 353 | = 1 354 | = 2 355 | = 3 356 | (gdb) dl #/(1,4,8,16) 357 | #/(1,4,8,16) = 4 358 | (gdb) dl +/(1,4,8,16) 359 | +/(1,4,8,16) = 29 360 | (gdb) dl arr[0,1] 361 | arr[0] = 5 362 | arr[1] = 10 363 | (gdb) dl 1..3,2..4,3..5 364 | = 1 365 | = 2 366 | = 3 367 | = 2 368 | = 3 369 | = 4 370 | = 3 371 | = 4 372 | = 5 373 | (gdb) dl st.(i,_r) 374 | st.i = 123 375 | st._r = 3.1415000000000002 376 | (gdb) dl st.(i + _r) 377 | st.(i + _r) = 126.14149999999999 378 | (gdb) dl (st.i) + _r 379 | No symbol "_r" in current context. 380 | (gdb) dl st.i + _r 381 | No symbol "_r" in current context. 382 | (gdb) dl tree-->(left,right)->v 383 | tree->v = 14 384 | tree->left->v = 12 385 | tree-->left[[2]]->v = 8 386 | tree-->left[[3]]->v = 0 387 | tree-->left[[2]]->right->v = 1 388 | tree->left->right->v = 9 389 | tree->left->right->left->v = 2 390 | tree->left-->right[[2]]->v = 3 391 | tree->right->v = 13 392 | tree->right->left->v = 10 393 | tree->right-->left[[2]]->v = 4 394 | tree->right->left->right->v = 5 395 | tree-->right[[2]]->v = 11 396 | tree-->right[[2]]->left->v = 6 397 | tree-->right[[3]]->v = 7 398 | (gdb) dl (100..)#i@(i > 10) 399 | = 100 400 | = 101 401 | = 102 402 | = 103 403 | = 104 404 | = 105 405 | = 106 406 | = 107 407 | = 108 408 | = 109 409 | = 110 410 | (gdb) dl tree-->left->(if(v>10)v else v+100) 411 | tree->(if(v > 10) v else v + 100) = 14 412 | tree->left->(if(v > 10) v else v + 100) = 12 413 | tree-->left[[2]]->(if(v > 10) v else v + 100) = 108 414 | tree-->left[[3]]->(if(v > 10) v else v + 100) = 100 415 | (gdb) dl #/(100..)#i@(i > 10) 416 | #/(100..)#i@(i > 10) = 11 417 | (gdb) dl tree-->right->(if(v%2)v*v) 418 | tree->right->(if(v % 2) v * v) = 169 419 | tree-->right[[2]]->(if(v % 2) v * v) = 121 420 | tree-->right[[3]]->(if(v % 2) v * v) = 49 421 | (gdb) dl (100..=>if(&&/(2,3..(_-1)=>__%_ )) _)[[..5]] 422 | (100.. => if(&&/(2,3..(_ - 1) => __ % _)) _)[[0]] = 101 423 | (100.. => if(&&/(2,3..(_ - 1) => __ % _)) _)[[1]] = 103 424 | (100.. => if(&&/(2,3..(_ - 1) => __ % _)) _)[[2]] = 107 425 | (100.. => if(&&/(2,3..(_ - 1) => __ % _)) _)[[3]] = 109 426 | (100.. => if(&&/(2,3..(_ - 1) => __ % _)) _)[[4]] = 113 427 | (gdb) dl (int)3.5, (int)3.5 * 10 428 | (int)3.5 = 3 429 | (int)3.5 * 10 = 30 430 | (gdb) dl 1..2;3;x:=4;5;6;x+7 431 | x + 7 = 11 432 | (gdb) dl #/(1..4;x:=5;x+6) 433 | #/(1..4; x := 5; x + 6) = 1 434 | (gdb) start 435 | [Thread debugging using libthread_db enabled] 436 | 437 | Temporary breakpoint 1, main () at test.cc:26 438 | 26 return 0; 439 | (gdb) dl d_strncmp("foo", "bar", 1..3) 440 | d_strncmp("foo","bar",1) = 4 441 | d_strncmp("foo","bar",2) = 4 442 | d_strncmp("foo","bar",3) = 4 443 | (gdb) dl (d_strcmp, d_strcasecmp)("FOO", "foo") 444 | d_strcmp("FOO","foo") = -32 445 | d_strcasecmp("FOO","foo") = 0 446 | (gdb) dl (((1.5))+(((2)))) 447 | (1.5 + 2) = 3.5 448 | (gdb) dl ("f-o-o")[((1..3))] 449 | "f-o-o"[1] = 45 '-' 450 | "f-o-o"[2] = 111 'o' 451 | "f-o-o"[3] = 45 '-' 452 | (gdb) dl (1,2+3)*10 453 | 1 * 10 = 10 454 | (2 + 3) * 10 = 50 455 | (gdb) set $a=5 456 | (gdb) p $a 457 | $1 = 5 458 | (gdb) dl $a + $1 459 | $a + $1 = 10 460 | (gdb) dl *(char *)s 461 | *(char *)s = 115 's' 462 | (gdb) dl $_strlen(s) 463 | $_strlen(s) = 2 464 | (gdb) dl $_regex(s, ".*[0-9]") 465 | $_regex(s,".*[0-9]") = true 466 | -------------------------------------------------------------------------------- /duel/help.md: -------------------------------------------------------------------------------- 1 | DUEL - A high level data exploration language for gdb 2 | ===================================================== 3 | 4 | Duel is a special purpose language designed for concise state 5 | exploration of debugged C programs, currently implemented for the GNU 6 | gdb. Duel is invoked by entering the gdb command `duel` (or `dl`) 7 | instead of `print`: 8 | 9 | (gdb) dl x[1..10] >? 5 10 | x[3] = 14 11 | x[8] = 6 12 | 13 | prints the array elements `x[1]` to `x[10]` that are greater than 5. 14 | The output includes the values 14 and 6, as well as their symbolic 15 | representation "x[3]" and "x[8]". 16 | 17 | Note that some gdb concepts (such as the value history) do not work 18 | with the `dl` command, and Duel expressions are not understood by 19 | other gdb command. 20 | 21 | Quick Start 22 | ----------- 23 | 24 | Duel is based on expressions which return multiple values. The `x..y` 25 | operator returns all integers from `x` to `y`; the `x,y` operator 26 | returns `x` and then `y`, e.g. 27 | 28 | (gdb) dl (1,9,12..15,22) 29 | 30 | prints 1, 9, 12, 13, 14, 15 and 22. Such expressions can be used 31 | wherever a single value is used, e.g. 32 | 33 | (gdb) dl x[0..99] 34 | 35 | prints first 100 elements of array `x`. 36 | 37 | Aliases are defined with `x:=y`: 38 | 39 | (gdb) dl if(x[i:=0..99]<0) x[i] 40 | x[i] = -4 41 | 42 | The symbolic output "x[i]" can be fixed by surrounding `i` with curly 43 | braces, i.e. 44 | 45 | (gdb) dl if(x[i:=0..99]<0) x[{i}] 46 | x[7] = -4 47 | 48 | The curly braces are like parentheses, but they replace the symbolic 49 | representation of the argument with its value. You can usually avoid 50 | this altogether with direct Duel operators: 51 | 52 | (gdb) dl x[..100] =?y`, etc., operators 57 | compare their left side operand to their right side operand as in C, 58 | but return the left side value if the comparison result is true. 59 | Otherwise, they look for the next values to compare, without returning 60 | anything. 61 | 62 | Operators `x.y` and `x->y` allow an expression `y`, evaluated under 63 | `x` scope: 64 | 65 | (gdb) dl emp[..100].(if(code>400) (code,name)) 66 | emp[46].code = 682 67 | emp[46].name = "Ela" 68 | 69 | The `if()` expression is evaluated under the scope of each element of 70 | `emp[]`, an array of structures. 71 | 72 | A useful alternative to loops is the `x=>y` operator. It returns `y` 73 | for each value of `x`, setting `_` to reference the value of `x`, e.g. 74 | 75 | (gdb) ..100 => if(emp[_].code>400) emp[_].code,emp[_].name 76 | 77 | Using `_` instead of `i` also avoids the need for curly braces. When `=>` 78 | operators are nested, `__` refers to the parent scope value, `___` to the 79 | great-parent, and so on. 80 | 81 | Finally, the `x-->y` operator expands lists and other data structures. 82 | If `head` points to a linked list threaded through the `next` field, then: 83 | 84 | (gdb) dl head-->next->data 85 | head->data = 12 86 | head->next->data = 14 87 | head-->next[[2]]->data = 20 88 | head-->next[[3]]->data = 26 89 | 90 | produce the data field for each node in the list. `x-->y` returns `x`, 91 | `x->y`, `x->y->y`, `x->y->y->y`, etc. until a NULL is found. The 92 | symbolic output "x-->y[[n]]" indicates that `->y` was applied n times. 93 | `x[[y]]` is also the selection operator: 94 | 95 | (gdb) head-->next[[3]]->data 96 | head-->next[[3]]->data = 26 97 | 98 | For example, 99 | 100 | (gdb) dl head-->next[[50..60]]->data 101 | 102 | return the 50th through the 60th elements in the list. By specifying a 103 | negative number one can count from the end: 104 | 105 | (gdb) head-->next[[-1]]->data 106 | head-->next[[-1]]->data = 123 107 | 108 | And curly braces evaluate the offset, as usual: 109 | 110 | (gdb) head-->next[[{-1}]]->data 111 | head-->next[[73]]->data = 123 112 | 113 | The `#/x` operator counts the number of values, so 114 | 115 | (gdb) dl #/( head-->next->data >? 50 ) 116 | 117 | counts the number of data elements over 50 on the list. 118 | 119 | Operator `x@y` stops `x` generating values, as soon as `y` becomes 120 | true. It is mainly used with the unbounded `x..` range: 121 | 122 | (gdb) dl argv[0..]@(_ == 0) 123 | argv[0] = 0x7fffffffe2bb "./a.out" 124 | argv[1] = 0x7fffffffe2dd "--foo" 125 | argv[2] = 0x7fffffffe300 "--bar" 126 | argv[3] = 0x7fffffffe306 "42" 127 | 128 | The `@(_ == const)` is so common, that it can be abbreviated to 129 | `@const`: 130 | 131 | (gdb) dl argv[0..]@0 132 | 133 | Semantics 134 | --------- 135 | 136 | Duel's semantics are modeled after the Icon programming language. The 137 | input consists of expressions which return sequences of values. C 138 | statements are expressions, too. Typically binary operators evaluate 139 | its operands and produce one result value for every combination of 140 | values from the left and right operands. 141 | 142 | For example, in `(5,3)+(6..8)`, the evaluation of `+` first retrieves 143 | the operands 5 and 6, to compute and return 5+6. Then 7, the next 144 | right operand is retrieved and 5+7 is returned, followed by 5+8. Since 145 | there are no other right operand value, the next left operand, 3 is 146 | fetched. The right operand's computation is restarted returning 6, 147 | and 3+6 is returned. The final return values are 3+7 and 3+8: 148 | 149 | (gdb) dl (5,3)+(6..8) 150 | (5) + (6) = 11 151 | (5) + (7) = 12 152 | (5) + (8) = 13 153 | (3) + (6) = 9 154 | (3) + (7) = 10 155 | (3) + (8) = 11 156 | 157 | The computation for operators like `x>?y` is similar, but when the 158 | condition is false, the next values are fetched instead of returning a 159 | value, reducing the sequence of `x` values to those that satisfy the 160 | condition. Operators like `..` return a sequence of values for each 161 | pair of operands. 162 | 163 | Duel types also follow the C semantics, with some important 164 | differences. C types are checked statically; Duel types are checked 165 | when operators are applied, e.g., `(1,1.0)/2` returns 0 (int) and 0.5 166 | (double); `(x,y).z` returns `x.z` and `y.z` even if `x` and `y` are of 167 | different types, as long as they both have a field `z`. 168 | 169 | Commands 170 | --------- 171 | 172 | Command | Description 173 | -------------- | ------------- 174 | `duel` | prints the list of commands 175 | `dl` | alias for `duel` 176 | `dl help` | prints short help 177 | `dl ?` | alias for `dl help` 178 | `dl longhelp` | prints this help file 179 | `dl ??` | alias for `dl longhelp` 180 | `dl examples` | prints **Examples** section of this file 181 | `dl operators` | prints **Operators** section of this file 182 | `dl aliases` | prints the list of aliases, see `x:=y` operator below 183 | `dl clear` | clears the list of aliases 184 | 185 | Operators 186 | --------- 187 | 188 | Variables and functions of the inferior (program being debugged) can be read 189 | and called normally and work as expected. Convenience gdb variables 190 | (like `$a`) and references to results (like `$1`) can be used too. 191 | 192 | The complete list of operators, in the precedence order: 193 | 194 | * `(x)`, `{x}` - curly braces work as parentheses, but print the value 195 | of `x`, not its symbolic representation. 196 | * `x#y` - for every value of `x`, set `y` to the index of this `x` 197 | value (0, 1, ...). 198 | * `x.y`, `x->y`, `x-->y`, `x[y]`, `x[[y]]`, `x@y` - the first two 199 | operators are *scope* operators, they evaluate `y` in the scope of 200 | `x`. The third one walks the linked list, evaluating `x`, `x->y`, 201 | `x->y->y`, etc until NULL. The fourth is a familiar C array element 202 | access, the fifth takes the `y`-th value in the sequence of values 203 | of `x` (counting from the end, if `y` is negative). The last one 204 | stops `x` from generating values as soon as `y` (evaluated in the 205 | scope of `x`) becomes true. If `y` is a literal, stops as soon as 206 | `x` value becomes equal to `y`. If `y` is a sequence of values, 207 | stops when at least one value in the sequence is true, and prints 208 | `x` if at least one value in the sequence is false. 209 | * `(cast)x`, `-x`, `*x`, `&x`, `!x`, `~x`, `#/x`, `+/x`, `&&/x`, `||/x` - 210 | first six are conventional unary C operators, the last four are 211 | *grouping* operators. The first one counts the numbers of values of 212 | `x`, the second sums them, the third returns a boolean AND of all 213 | values of `x`, the fourth - boolean OR. Just like in C, AND and OR 214 | operators are lazy and stop as soon as the result value is known. 215 | * `x/y`, `x*y`, `x%y` - conventional C operators. 216 | * `x-y`, `x+y` - conventional C operators. 217 | * `x<>y` - conventional C operators. 218 | * `x..y`, `..x`, `x..` - ranges. First returns integers from `x` to 219 | `y`, inclusive (`x` can be greater than `y`, for counting 220 | backwards). Second returns first `x` integers starting from 0 (in 221 | other words, it works like `0..x-1`). Last returns an unlimited list 222 | of integers starting from `x`, and is normally used with `x@y`. 223 | * `x<=y`, `x>=y`, `xy`, `x<=?y`, `x>=?y`, `x?y` - 224 | first four are conventional C operators, the second four return `x` 225 | if the condition is true, otherwise they return nothing (so, they 226 | work like a filter of the `x` value sequence). 227 | * `x==y`, `x!=y`, `x==?y`, `x!=?y` - same. First two are conventional 228 | C operators, the other two are filters. 229 | * `x&y` - conventional C operator. 230 | * `x^y` - conventional C operator. 231 | * `x|y` - conventional C operator. 232 | * `x&&y` - conventional C operator. 233 | * `x||y` - conventional C operator. 234 | * `x?y:z` - conventional C operator. 235 | * `x:=y` - create an alias `x` for the value of `y`. Note, that it is 236 | an *alias* to `y`, not a copy of `y` value. If `y` value changes, 237 | the value of the alias will change too. Use `dl aliases` and `dl 238 | clear` commands to see and, respectively, clear the list of aliases. 239 | * `x,y` - creates a sequence of two values. 240 | * `x=>y` - for every value of `x`, store it in `_` and evaluate `y`. 241 | * `if (x) y else z`, `if (x) y` - C statement turned into an operator. 242 | In the first form it's equivalent to `x?y:z`, in the second form it 243 | has a filter semantics, returning `y` for every non-zero `x` 244 | (equivalent to `x !=? 0 => y`). 245 | * `x;y` - evaluate x, ignoring all the results, then evaluate y. 246 | 247 | Examples 248 | -------- 249 | 250 | Compute simple expression: 251 | 252 | dl (0xff-0x12)*3 253 | 254 | Display multiplication table: 255 | 256 | dl (1..10)*(1..10) 257 | 258 | Display `x[i]` for the selected indexes: 259 | 260 | dl x[10..20,22,24,40..60] 261 | 262 | Display `x[i]` backwards: 263 | 264 | dl x[9..0] 265 | 266 | Display `x[i]` elements that are greater than 5: 267 | 268 | dl x[..100] >? 5 269 | 270 | Display `x[i]` elements that are between 5 and 10: 271 | 272 | dl x[..100] >? 5 if(_>5 && _<10) _ 277 | 278 | Same, if `x[i]` elements are integers: 279 | 280 | dl x[..100] ==? 6..9 281 | 282 | Display `y[x[i]]` for each non-zero `x[i]`: 283 | 284 | dl y[x[..100] !=? 0] 285 | 286 | Display both `emp[i].code` and `emp[i].name` for first 50 `emp[]` elements 287 | 288 | dl emp[..50].(code,name) 289 | 290 | Display `val[i].singl` or `val[i].doubl` depending on `val[i].is_doubl`: 291 | 292 | dl val[..50].(is_doubl?doubl:singl) 293 | 294 | Display `hash[i].scope` for non-null `hash[i]`: 295 | 296 | dl (hash[..1024]!=?0)->scope 297 | 298 | Check if `x[i]` array is sorted: 299 | 300 | dl x[i:=..100] >? x[i+1] 301 | 302 | Check if x has non-unique elements: 303 | 304 | dl x[i:=..100] ==? x[j:=..100] => if(i? 0)[[0]] 313 | 314 | Find first five positive `x[i]` 315 | 316 | dl (x[..100] >? 0)[[..5]] 317 | 318 | Return the first `x[i]` greater than 6 (note, no limit on i): 319 | 320 | dl (x[0..] >? 6)[[0]] 321 | 322 | Display `argv[]` array until the first NULL: 323 | 324 | dl argv[0..]@0 325 | 326 | Display `emp[]` values until `emp[i].code` is 0: 327 | 328 | dl emp[0..]@(code==0) 329 | 330 | Walk the linked list by the `next` pointer, starting from `head`, print `val` of each list element: 331 | 332 | dl head-->next->val 333 | 334 | Display the 21st element of a linked list: 335 | 336 | dl head-->next[[20]] 337 | 338 | Count elements on a linked list: 339 | 340 | dl #/head-->next 341 | 342 | Find the last element in a linked list: 343 | 344 | dl x-->y[[#/x-->y - 1]] 345 | 346 | Or just 347 | 348 | dl x-->y[[-1]] 349 | 350 | Walk the cyclic linked list (start from `head`, walk until seeing `head` again): 351 | 352 | dl head-->(next!=?head) 353 | 354 | Walk the binary tree: 355 | 356 | dl root-->(left,right)->key 357 | 358 | Select matching strings from the array: 359 | 360 | dl $_regex(items[i:=0..20], "foo") ==? 1 => items[i] 361 | 362 | Find first 10 primes greater than 1000: 363 | 364 | dl (1000..=>if(&&/(2,3.._-1=>__%_ )) _)[[..10]] 365 | 366 | To Do 367 | ----- 368 | 369 | Features of the original Duel, that are not implemented in the 370 | Duel.py yet: 371 | 372 | * Builtins: `frame()`, `sizeof()`, `frames_no`, `func.x` 373 | * Variables: `int i; i=5; i++` 374 | * Assignments: `x[..10]=0` 375 | * `while` and `for` operators 376 | 377 | Features that were not in the original Duel: 378 | * gdb scope specification: `file.c::var` 379 | * gdb convenience variables (`$a`), references to results (`$1`) and 380 | functions (`$_streq`) 381 | * negative indexes in `[[ ]]` operator 382 | 383 | 384 | Author 385 | ------ 386 | 387 | Duel was designed by Michael Golan as part of a PhD thesis in the 388 | Computer Science Department of Princeton University. He also 389 | implemented Duel in C as a patch for gdb 4.x. 390 | 391 | Duel.py is a pure-python implementation written by Sergei Golubchik 392 | 393 | Duel stands for Debugging U (might) Even Like, or Don't Use this 394 | Exotic Language. Judge for yourself! 395 | -------------------------------------------------------------------------------- /duel/parser.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from arpeggio import ZeroOrMore, Optional, EOF, RegExMatch, Match, Terminal, \ 3 | ParserPython, PTNodeVisitor, visit_parse_tree, OneOrMore 4 | import re 5 | import gdb 6 | from duel import expr 7 | 8 | try: a=unichr # Python 3 compatibility 9 | except: unichr=chr 10 | 11 | escapes=r"""\\(?:[abefnrtv"'?]|[0-7]{1,3}|x[0-9a-fA-F]+|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})""" 12 | 13 | # typespec parser 14 | types = dict() 15 | def make_typespec_parser(): 16 | self = Match('(cast)') 17 | def chars(): return RegExMatch(r'[0-9a-zA-Z_*& :,]+') 18 | def ts(): return OneOrMore([chars,('(',ts,')'),('[',ts,']'),('<',ts,'>')]) 19 | def cast(): return '(',ts,')' 20 | parser=ParserPython(cast, autokwd=True, debug=False) 21 | def parse_ts(s): 22 | try: 23 | parse_tree=parser.parse(s) 24 | tstr = s[parse_tree[1].position:parse_tree[2].position] 25 | if tstr not in types: 26 | t = gdb.parse_and_eval('('+tstr+' *)0').type.target() 27 | types[tstr] = t 28 | return (parser.position, tstr) 29 | except: 30 | return (0, None) 31 | def parse(parser): 32 | c_pos = parser.position 33 | (matchlen, t) = parse_ts(parser.input[c_pos:]) 34 | if t: 35 | matched = parser.input[c_pos:c_pos + matchlen] 36 | if parser.debug: 37 | parser.dprint( 38 | "++ Match '%s' at %d => '%s'" % 39 | (matched, c_pos, parser.context(matchlen))) 40 | parser.position += matchlen 41 | return Terminal(self, c_pos, t) 42 | else: 43 | if parser.debug: 44 | parser.dprint("-- NoMatch at {0}".format(c_pos)) 45 | parser._nm_raise(self, c_pos, parser) 46 | self.to_match=self.rule_name 47 | self._parse = parse 48 | return self 49 | 50 | cast = make_typespec_parser() 51 | 52 | def real(): return RegExMatch(r'\d+(([eE][+-]?\d+)|\.\d+([eE][+-]?\d+)?)') 53 | def hexadecimal(): return RegExMatch(r'0[xX][0-9A-Fa-f]*\b') 54 | def decimal(): return RegExMatch(r'[1-9][0-9]*\b') 55 | def octal(): return RegExMatch(r'0[0-7]*\b') 56 | def char(): return RegExMatch(r"'([^'\\]|"+escapes+")'") 57 | def string(): return RegExMatch(r'"([^\\"]|'+escapes+')*"') 58 | def ident(): return RegExMatch(r'[A-Za-z_]\w*') 59 | def gdbvar(): return RegExMatch(r'\$\w+') 60 | def underscores(): return RegExMatch(r'_+\b') 61 | def parens(): return [('(', expression, ')'), ('{', expression, '}')] 62 | def term21(): return [real, hexadecimal, decimal, octal, char, string, 63 | underscores, ident, gdbvar] 64 | def term20(): return [ term21, parens ] 65 | def term19a(): return term20, Optional('#', ident) 66 | def term19(): return term19a, ZeroOrMore([ 67 | (['.', '->', '-->', '@'], term19a), 68 | ('[', expression, ']'), 69 | ('[[', expression, ']]'), 70 | ]) 71 | def term18a(): return term19, ZeroOrMore('(', Optional(expression), ')') 72 | def term18(): return ZeroOrMore(['&&/', '||/', '#/', '+/', '-', '*', '&', '!', '~', cast]), term18a, 73 | def term17(): return term18, ZeroOrMore(['/', '*', '%'], term18) 74 | def term16(): return term17, ZeroOrMore(['-', '+'], term17) 75 | def term15(): return term16, ZeroOrMore(['<<', '>>'], term16) 76 | def term14(): return [(term15, Optional('..', Optional(term15))), ('..', term15)] 77 | def term13(): return term14, ZeroOrMore(['<=?', '>=?', '?','<=', '>=', '<', '>' ], term14) 78 | def term12(): return term13, ZeroOrMore(['==?', '!=?', '==', '!='], term13) 79 | def term11(): return term12, ZeroOrMore(RegExMatch(r'&(?!&)', str_repr='&'), term12) 80 | def term10(): return term11, ZeroOrMore('^', term11) 81 | def term9(): return term10, ZeroOrMore('|', term10) 82 | def term8(): return term9, ZeroOrMore('&&', term9) 83 | def term7(): return term8, ZeroOrMore('||', term8) 84 | def term6(): return term7, Optional('?', term6, ':', term6) 85 | #def term5(): return term6, ZeroOrMore(['=', '+=', '-=', '*=', '/='], term6) 86 | def term4(): return Optional(ident, ':='), term6 87 | def term3(): return term4, ZeroOrMore(',', term4) 88 | def term2(): return term3, ZeroOrMore('=>', term1) 89 | def ifterm(): return 'if', '(', expression , ')', term1, Optional('else', term1) 90 | #def whileterm(): return 'while', '(', expression , ')', term1 91 | #def forterm(): return 'for', '(', term2, ';', term2, ';', term2, ')', term1 92 | def term1(): return [ ifterm, term2 ] 93 | def term0(): return term1, ZeroOrMore(';', term1) 94 | def expression(): return term0, 95 | def input(): return expression, EOF 96 | 97 | parser=ParserPython(input, autokwd=True, debug=False) 98 | 99 | def type_error(s): raise TypeError(s) 100 | def not_implemented(): raise NotImplementedError("Not implemented yet") 101 | 102 | def getchar(s): 103 | escmap = {'a':'\a', 'b':'\b', 'e':'\033', 'f':'\f', 'n':'\n', 104 | 'r':'\r', 't':'\t', 'v':'\v', '"':'"', "'":"'", '?':'?' } 105 | if s[0] != '\\': return s[0], s[1:] 106 | if s[1] in escmap: return escmap[s[1]], s[2:] 107 | if s[1] == 'u': return unichr(int(s[2:6], 16)), s[6:] 108 | if s[1] == 'U': return unichr(int(s[2:10], 16)), s[10:] 109 | if s[1] == 'x': 110 | m = re.match('([0-9a-fA-F]+)(.*)', s[2:]) 111 | return unichr(int(m.group(1), 16)), m.group(2) 112 | m = re.match('([0-7]{1,3})(.*)', s[1:]) 113 | return unichr(int(m.group(1), 8)), m.group(2) 114 | 115 | class DuelVisitor(PTNodeVisitor): 116 | def visit__default__(self, node, ch): 117 | node.suppress=False 118 | return super(DuelVisitor, self).visit__default__(node, ch) 119 | def visit_real(self, node, ch): 120 | return expr.Literal(node.value, gdb.Value(float(node.value))) 121 | def visit_decimal(self, node, ch): 122 | return expr.Literal(node.value, gdb.Value(int(node.value, 10))) 123 | def visit_octal(self, node, ch): 124 | return expr.Literal(node.value, gdb.Value(int(node.value, 8))) 125 | def visit_hexadecimal(self, node, ch): 126 | return expr.Literal(node.value, gdb.Value(int(node.value, 16))) 127 | def visit_string(self, node, ch): 128 | s, tail = '', node.value[1:-1] 129 | while tail: 130 | head, tail = getchar(tail) 131 | s += head 132 | return expr.Literal(node.value, gdb.Value(s)) 133 | def visit_char(self, node, ch): 134 | c, _ = getchar(node.value[1:-1]) 135 | c = gdb.Value(ord(c)).cast(gdb.lookup_type('char')) 136 | return expr.Literal(node.value, c) 137 | def visit_ident(self, node, ch): 138 | return expr.Ident(node.value) 139 | def visit_gdbvar(self, node, ch): 140 | return expr.Ident(node.value) 141 | def visit_underscores(self, node, ch): 142 | return expr.Underscore(node.value) 143 | def visit_parens(self, node, ch): 144 | op, arg = ch[0], ch[1] 145 | if op == '(': return expr.Parens(arg) 146 | if op == '{': return expr.Curlies(arg) 147 | def visit_term19a(self, node, ch): 148 | if len(ch) == 1: return ch[0] 149 | return expr.Enumerate(ch[0], ch[2]) 150 | def visit_term19(self, node, ch): 151 | l = ch.pop(0) 152 | while len(ch): 153 | op, r = ch.pop(0), ch.pop(0) 154 | if op == '[': l, _ = expr.Binary(l, '{0}[{1}]', r, lambda x,y: x[int(y)]), ch.pop(0) 155 | elif op == '.': l = expr.Struct(l, '{0}.{1}', r) 156 | elif op == '->': l = expr.Struct(l, '{0}->{1}', r) 157 | elif op == '-->': l = expr.StructWalk(l, r) 158 | elif op == '[[': l, _ = expr.TakeNth(l, r), ch.pop(0) 159 | elif op == '@': l = expr.Until(l, r) 160 | return l 161 | def visit_term18a(self, node, ch): 162 | l = ch.pop(0) 163 | while len(ch): 164 | ch.pop(0) # '(' 165 | r = ch.pop(0) 166 | if r == ')': r = expr.List([]) 167 | else: 168 | ch.pop(0) # ')' 169 | if not isinstance(r, expr.List): r = expr.List([r]) 170 | l = expr.Call(l, r) 171 | return l 172 | def visit_term18(self, node, ch): 173 | r = ch.pop() 174 | while (len(ch)): 175 | op = ch.pop() 176 | if op == '#/': r = expr.EagerGrouping(op, r, lambda i, x: i + 1) 177 | elif op == '+/': r = expr.EagerGrouping(op, r, lambda i, x: i + x) 178 | elif op == '&&/': r = expr.LazyGrouping(op, r, 1, lambda i, x: 1 if i and x else 0) 179 | elif op == '||/': r = expr.LazyGrouping(op, r, 0, lambda i, x: 1 if i or x else 0) 180 | elif op == '-': r = expr.Unary(op, r, lambda x: -x) 181 | elif op == '*': r = expr.Unary(op, r, lambda x: x.dereference()) 182 | elif op == '&': r = expr.Unary(op, r, lambda x: x.address or type_error("Not addressable")) 183 | elif op == '!': r = expr.Unary(op, r, lambda x: gdb.Value(not x)) 184 | elif op == '~': r = expr.Unary(op, r, lambda x: ~x) 185 | elif op == '++': not_implemented() 186 | elif op == '--': not_implemented() 187 | else: 188 | t = types[op] 189 | r = expr.Unary('('+op+')', r, lambda x: x.cast(t)) 190 | return r 191 | def visit_term17(self, node, ch): 192 | l = ch.pop(0) 193 | while len(ch): 194 | op, r = ch.pop(0), ch.pop(0) 195 | if op == '*': l = expr.Binary(l, op, r, lambda x,y: x*y) 196 | elif op == '/': l = expr.Binary(l, op, r, lambda x,y: x/y) 197 | elif op == '%': l = expr.Binary(l, op, r, lambda x,y: x%y) 198 | return l 199 | def visit_term16(self, node, ch): 200 | l = ch.pop(0) 201 | while len(ch): 202 | op, r = ch.pop(0), ch.pop(0) 203 | if op == '+': l = expr.Binary(l, op, r, lambda x,y: x+y) 204 | elif op == '-': l = expr.Binary(l, op, r, lambda x,y: x-y) 205 | return l 206 | def visit_term15(self, node, ch): 207 | l = ch.pop(0) 208 | while len(ch): 209 | op, r = ch.pop(0), ch.pop(0) 210 | if op == '<<': l = expr.Binary(l, op, r, lambda x,y: x<>': l = expr.Binary(l, op, r, lambda x,y: x>>y) 212 | return l 213 | def visit_term14(self, node, ch): 214 | if len(ch) == 1: return ch[0] 215 | if len(ch) == 3: return expr.BiRange(ch[0], ch[2]) 216 | if ch[0] == '..': return expr.URange('..{0}', ch[1], True) 217 | return expr.URange('{0}..', ch[0], False) 218 | def visit_term13(self, node, ch): 219 | l = ch.pop(0) 220 | while len(ch): 221 | op, r = ch.pop(0), ch.pop(0) 222 | if op == '<': l = expr.Binary(l, op, r, lambda x,y: gdb.Value(x': l = expr.Binary(l, op, r, lambda x,y: gdb.Value(x>y)) 224 | elif op == '<=': l = expr.Binary(l, op, r, lambda x,y: gdb.Value(x<=y)) 225 | elif op == '>=': l = expr.Binary(l, op, r, lambda x,y: gdb.Value(x>=y)) 226 | elif op == '?': l = expr.Filter(l, op, r, lambda x,y: gdb.Value(x>y)) 228 | elif op == '<=?': l = expr.Filter(l, op, r, lambda x,y: gdb.Value(x<=y)) 229 | elif op == '>=?': l = expr.Filter(l, op, r, lambda x,y: gdb.Value(x>=y)) 230 | return l 231 | def visit_term12(self, node, ch): 232 | l = ch.pop(0) 233 | while len(ch): 234 | op, r = ch.pop(0), ch.pop(0) 235 | if op == '==': l = expr.Binary(l, op, r, lambda x,y: gdb.Value(x==y)) 236 | elif op == '!=': l = expr.Binary(l, op, r, lambda x,y: gdb.Value(x!=y)) 237 | elif op == '==?': l = expr.Filter(l, op, r, lambda x,y: gdb.Value(x==y)) 238 | elif op == '!=?': l = expr.Filter(l, op, r, lambda x,y: gdb.Value(x!=y)) 239 | return l 240 | def visit_term11(self, node, ch): 241 | l = ch.pop(0) 242 | while len(ch): 243 | op, r = ch.pop(0), ch.pop(0) 244 | if op == '&': l = expr.Binary(l, op, r, lambda x,y: x&y) 245 | return l 246 | def visit_term10(self, node, ch): 247 | l = ch.pop(0) 248 | while len(ch): 249 | op, r = ch.pop(0), ch.pop(0) 250 | if op == '^': l = expr.Binary(l, op, r, lambda x,y: x^y) 251 | return l 252 | def visit_term9(self, node, ch): 253 | l = ch.pop(0) 254 | while len(ch): 255 | op, r = ch.pop(0), ch.pop(0) 256 | if op == '|': l = expr.Binary(l, op, r, lambda x,y: x|y) 257 | return l 258 | def visit_term8(self, node, ch): 259 | l = ch.pop(0) 260 | while len(ch): 261 | op, r = ch.pop(0), ch.pop(0) 262 | if op == '&&': l = expr.Binary(l, op, r, lambda x,y: 1 if x and y else 0) 263 | return l 264 | def visit_term7(self, node, ch): 265 | l = ch.pop(0) 266 | while len(ch): 267 | op, r = ch.pop(0), ch.pop(0) 268 | if op == '||': l = expr.Binary(l, op, r, lambda x,y: 1 if x or y else 0) 269 | return l 270 | def visit_term6(self, node, ch): 271 | if len(ch) == 1: return ch[0] 272 | return expr.Ternary('{0} ? {1} : {2}', ch[0], ch[2], ch[4]) 273 | def visit_term5(self, node, ch): 274 | l = ch.pop(0) 275 | while len(ch): 276 | op, r = ch.pop(0), ch.pop(0) 277 | if op == '=': not_implemented() 278 | elif op == '+=': not_implemented() 279 | elif op == '-=': not_implemented() 280 | elif op == '*=': not_implemented() 281 | elif op == '/=': not_implemented() 282 | return l 283 | def visit_term4(self, node, ch): 284 | if len(ch) == 1: return ch[0] 285 | return expr.Alias(ch[0], ch[2]) 286 | def visit_term3(self, node, ch): 287 | if len(ch) == 1: return ch[0] 288 | return expr.List(ch[::2]) 289 | def visit_term2(self, node, ch): 290 | l = ch.pop(0) 291 | while len(ch): 292 | op, r = ch.pop(0), ch.pop(0) 293 | if op == '=>': l = expr.Foreach(l, r) 294 | return l 295 | def visit_ifterm(self, node, ch): 296 | if len(ch) == 1: return ch[0] 297 | if len(ch) == 5: return expr.Ternary('if({0}) {1}', ch[2], ch[4], None) 298 | return expr.Ternary('if({0}) {1} else {2}', ch[2], ch[4], ch[6]) 299 | def visit_whileterm(self, node, ch): 300 | if len(ch) == 1: return ch[0] 301 | not_implemented() 302 | def visit_forterm(self, node, ch): 303 | if len(ch) == 1: return ch[0] 304 | not_implemented() 305 | def visit_term0(self, node, ch): 306 | if len(ch) == 1: return ch[0] 307 | return expr.Statement(ch[::2]) 308 | 309 | def eval(arg): 310 | parse_tree=parser.parse(arg) 311 | 312 | #from arpeggio.export import PTDOTExporter 313 | #PTDOTExporter().exportFile(tree, "tree.dot") 314 | #import pprint 315 | #pprint.PrettyPrinter(indent=4).pprint(parse_tree) 316 | 317 | expr.scopes = list() 318 | expr.underscores = list() 319 | expr_tree = visit_parse_tree(parse_tree, DuelVisitor(debug=False)) 320 | assert len(expr.scopes) == 0 321 | assert len(expr.underscores) == 0 322 | 323 | for name, val in expr_tree.eval(): 324 | val = expr.val2str(val) 325 | if name == val: 326 | gdb.write('= {0}\n'.format(val)) 327 | else: 328 | gdb.write('{0} = {1}\n'.format(name, val)) 329 | --------------------------------------------------------------------------------