| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- # Copyright (c) 2017 by Rocky Bernstein
- from __future__ import print_function
- from gdbloc.parser import (
- parse_bp_location, parse_range, parse_arange
- )
- from gdbloc.parser import LocationError as PLocationError
- from gdbloc.scanner import ScannerError
- from spark_parser import GenericASTTraversal # , DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
- from collections import namedtuple
- Location = namedtuple("Location", "path line_number is_address method")
- BPLocation = namedtuple("BPLocation", "location condition")
- ListRange = namedtuple("ListRange", "first last")
- class LocationError(Exception):
- def __init__(self, errmsg):
- self.errmsg = errmsg
- def __str__(self):
- return self.errmsg
- class RangeError(Exception):
- def __init__(self, errmsg):
- self.errmsg = errmsg
- def __str__(self):
- return self.errmsg
- class LocationGrok(GenericASTTraversal, object):
- def __init__(self, text):
- GenericASTTraversal.__init__(self, None)
- self.text = text
- self.result = None
- return
- def n_addr_location(self, node):
- # addr_location ::= location
- # addr_location ::= ADDRESS
- path, line_number, method = None, None, None
- if node[0] == 'ADDRESS':
- assert node[0].value[0] == '*'
- line_number = int(node[0].value[1:])
- self.result = Location(path, line_number, True, method)
- node.location = Location(path, line_number, True, method)
- self.prune()
- else:
- assert node[0] == 'location'
- self.preorder(node[0])
- node.location = node[0].location
- def n_location(self, node):
- path, line_number, method = None, None, None
- if node[0] == 'FILENAME':
- path = node[0].value
- # If there is a line number, it is the last token of a location
- if len(node) > 1 and node[-1] == 'NUMBER':
- line_number = node[-1].value
- elif node[0] == 'FUNCNAME':
- method = node[0].value[:-2]
- elif node[0] == 'NUMBER':
- line_number = node[0].value
- else:
- assert True, "n_location: Something's is wrong; node[0] is %s" % node[0]
- self.result = Location(path, line_number, False, method)
- node.location = Location(path, line_number, False, method)
- self.prune()
- def n_NUMBER(self, node):
- self.result = Location(None, node.value, False, None)
- def n_FUNCNAME(self, node):
- self.result = Location(None, None, False, node.value[:-2])
- def n_location_if(self, node):
- location = None
- if node[0] == 'location':
- self.preorder(node[0])
- location = node[0].location
- if len(node) == 1:
- return
- if node[1] == 'IF':
- if_node = node[1]
- elif node[2] == 'IF':
- if_node = node[2]
- elif node[3] == 'IF':
- if_node = node[3]
- else:
- assert False, 'location_if: Something is wrong; cannot find "if"'
- condition = self.text[if_node.offset:]
- # Pick out condition from string and location inside "IF" token
- self.result = BPLocation(location, condition)
- self.prune()
- # FIXME: DRY with range
- def n_arange(self, arange_node):
- if arange_node[0] == 'range':
- arange_node = arange_node[0]
- length = len(arange_node)
- if 1 <= length <= 2:
- # arange ::= location
- # arange ::= DIRECTION
- # arange ::= FUNCNAME
- # arange ::= NUMBER
- # arange ::= OFFSET
- # arange ::= ADDRESS
- last_node = arange_node[-1]
- if last_node == 'location':
- self.preorder(arange_node[-1])
- self.result = ListRange(last_node.location, None)
- elif last_node == 'FUNCNAME':
- self.result = ListRange(Location(None, None, False, last_node.value[:-2]),
- None)
- elif last_node in ('NUMBER', 'OFFSET', 'ADDRESS'):
- if last_node == 'ADDRESS':
- assert last_node.value[0] == '*'
- is_address = True
- value = int(last_node.value[1:])
- else:
- is_address = False
- value = last_node.value
- self.result = ListRange(Location(None, value, is_address, None),
- None)
- else:
- assert last_node == 'DIRECTION'
- self.result = ListRange(None, last_node.value)
- pass
- self.prune()
- elif length == 3:
- # arange ::= COMMA opt_space location
- # arange ::= location opt_space COMMA
- if arange_node[0] == 'COMMA':
- assert arange_node[-1] == 'location'
- self.preorder(arange_node[-1])
- self.result = ListRange(None, self.result)
- self.prune()
- else:
- assert arange_node[-1] == 'COMMA'
- assert arange_node[0] == 'location'
- self.preorder(arange_node[0])
- self.result = ListRange(arange_node[0].location, None)
- self.prune()
- pass
- elif length == 5:
- # arrange ::= location opt_space COMMA opt_space {NUMBER | OFFSET | ADDRESS}
- assert arange_node[2] == 'COMMA'
- assert arange_node[-1] in ('NUMBER', 'OFFSET', 'ADDRESS')
- self.preorder(arange_node[0])
- self.result = ListRange(arange_node[0].location, arange_node[-1].value)
- self.prune()
- else:
- raise RangeError("Something is wrong")
- return
- def n_range(self, range_node):
- length = len(range_node)
- if 1 <= length <= 2:
- # range ::= location
- # range ::= DIRECTION
- # range ::= FUNCNAME
- # range ::= NUMBER
- # range ::= OFFSET
- last_node = range_node[-1]
- if last_node == 'location':
- self.preorder(range_node[-1])
- self.result = ListRange(last_node.location, None)
- elif last_node == 'FUNCNAME':
- self.result = ListRange(Location(None, None, False, last_node.value[:-2]),
- None)
- elif last_node in ('NUMBER', 'OFFSET'):
- self.result = ListRange(Location(None, last_node.value, False, None),
- None)
- else:
- assert last_node == 'DIRECTION'
- self.result = ListRange(None, last_node.value)
- pass
- self.prune()
- elif length == 3:
- # range ::= COMMA opt_space location
- # range ::= location opt_space COMMA
- if range_node[0] == 'COMMA':
- assert range_node[-1] == 'location'
- self.preorder(range_node[-1])
- self.result = ListRange(None, self.result)
- self.prune()
- else:
- assert range_node[-1] == 'COMMA'
- assert range_node[0] == 'location'
- self.preorder(range_node[0])
- self.result = ListRange(range_node[0].location, None)
- self.prune()
- pass
- elif length == 5:
- # range ::= location opt_space COMMA opt_space {NUMBER|OFFSET}
- assert range_node[2] == 'COMMA'
- assert range_node[-1] in ('NUMBER', 'OFFSET')
- self.preorder(range_node[0])
- self.result = ListRange(range_node[0].location, range_node[-1].value)
- self.prune()
- else:
- raise RangeError("Something is wrong")
- return
- def default(self, node):
- if node not in frozenset(("""opt_space tokens token bp_start range_start arange_start
- IF FILENAME COLON COMMA SPACE DIRECTION""".split())):
- assert False, ("Something's wrong: you missed a rule for %s" % node.kind)
- def traverse(self, node, ):
- return self.preorder(node)
- def build_bp_expr(string, show_tokens=False, show_ast=False, show_grammar=False):
- parser_debug = {'rules': False, 'transition': False,
- 'reduce': show_grammar,
- 'errorstack': True, 'context': True, 'dups': True }
- parsed = parse_bp_location(string, show_tokens=show_tokens,
- parser_debug=parser_debug)
- assert parsed == 'bp_start'
- if show_ast:
- print(parsed)
- walker = LocationGrok(string)
- walker.traverse(parsed)
- bp_expr = walker.result
- if isinstance(bp_expr, Location):
- bp_expr = BPLocation(bp_expr, None)
- location = bp_expr.location
- assert location.line_number is not None or location.method
- return bp_expr
- def build_range(string, show_tokens=True, show_ast=False, show_grammar=True):
- parser_debug = {'rules': False, 'transition': False,
- 'reduce': show_grammar,
- 'errorstack': True, 'context': True, 'dups': True }
- parsed = parse_range(string, show_tokens=show_tokens,
- parser_debug=parser_debug)
- if show_ast:
- print(parsed)
- assert parsed == 'range_start'
- walker = LocationGrok(string)
- walker.traverse(parsed)
- list_range = walker.result
- return list_range
- # FIXME: DRY with build_range
- def build_arange(string, show_tokens=False, show_ast=False, show_grammar=False):
- parser_debug = {'rules': False, 'transition': False,
- 'reduce': show_grammar,
- 'errorstack': None,
- 'context': True, 'dups': True
- }
- parsed = parse_arange(string, show_tokens=show_tokens,
- parser_debug=parser_debug)
- if show_ast:
- print(parsed)
- assert parsed == 'arange_start'
- walker = LocationGrok(string)
- walker.traverse(parsed)
- list_range = walker.result
- return list_range
- if __name__ == '__main__':
- # FIXME: make sure the below is in a test
- def doit(fn, line):
- print("=" * 30)
- print(line)
- print("+" * 30)
- try:
- result = fn(line)
- print(result)
- except ScannerError as e:
- print("Scanner error")
- print(e.text)
- print(e.text_cursor)
- except PLocationError as e:
- print("Parser error at or near")
- print(e.text)
- print(e.text_cursor)
- # lines = """
- # /tmp/foo.py:12
- # /tmp/foo.py line 12
- # 12
- # ../foo.py:5
- # gcd()
- # foo.py line 5 if x > 1
- # """.splitlines()
- # for line in lines:
- # if not line.strip():
- # continue
- # print("=" * 30)
- # print(line)
- # print("+" * 30)
- # bp_expr = build_bp_expr(line)
- # print(bp_expr)
- # lines = (
- # "/tmp/foo.py:12 , 5",
- # "-"
- # "+"
- # "../foo.py:5",
- # "../foo.py:5 ",
- # ", 5",
- # "6 , +2",
- # ", /foo.py:5",
- # )
- lines = (
- "*10",
- "/tmp/foo.py:1, *5",
- "../foo.py:0, +5",
- "../foo.py:5, *30",
- "*0, *10",
- ", /foo.py:5",
- )
- for line in lines:
- line = line.strip()
- if not line:
- continue
- doit(build_arange, line)
|