Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pycparser/plyparser.py: 93%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

56 statements  

1#----------------------------------------------------------------- 

2# plyparser.py 

3# 

4# PLYParser class and other utilities for simplifying programming 

5# parsers with PLY 

6# 

7# Eli Bendersky [https://eli.thegreenplace.net/] 

8# License: BSD 

9#----------------------------------------------------------------- 

10 

11import warnings 

12 

13class Coord(object): 

14 """ Coordinates of a syntactic element. Consists of: 

15 - File name 

16 - Line number 

17 - (optional) column number, for the Lexer 

18 """ 

19 __slots__ = ('file', 'line', 'column', '__weakref__') 

20 def __init__(self, file, line, column=None): 

21 self.file = file 

22 self.line = line 

23 self.column = column 

24 

25 def __str__(self): 

26 str = "%s:%s" % (self.file, self.line) 

27 if self.column: str += ":%s" % self.column 

28 return str 

29 

30 

31class ParseError(Exception): pass 

32 

33 

34class PLYParser(object): 

35 def _create_opt_rule(self, rulename): 

36 """ Given a rule name, creates an optional ply.yacc rule 

37 for it. The name of the optional rule is 

38 <rulename>_opt 

39 """ 

40 optname = rulename + '_opt' 

41 

42 def optrule(self, p): 

43 p[0] = p[1] 

44 

45 optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename) 

46 optrule.__name__ = 'p_%s' % optname 

47 setattr(self.__class__, optrule.__name__, optrule) 

48 

49 def _coord(self, lineno, column=None): 

50 return Coord( 

51 file=self.clex.filename, 

52 line=lineno, 

53 column=column) 

54 

55 def _token_coord(self, p, token_idx): 

56 """ Returns the coordinates for the YaccProduction object 'p' indexed 

57 with 'token_idx'. The coordinate includes the 'lineno' and 

58 'column'. Both follow the lex semantic, starting from 1. 

59 """ 

60 last_cr = p.lexer.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx)) 

61 if last_cr < 0: 

62 last_cr = -1 

63 column = (p.lexpos(token_idx) - (last_cr)) 

64 return self._coord(p.lineno(token_idx), column) 

65 

66 def _parse_error(self, msg, coord): 

67 raise ParseError("%s: %s" % (coord, msg)) 

68 

69 

70def parameterized(*params): 

71 """ Decorator to create parameterized rules. 

72 

73 Parameterized rule methods must be named starting with 'p_' and contain 

74 'xxx', and their docstrings may contain 'xxx' and 'yyy'. These will be 

75 replaced by the given parameter tuples. For example, ``p_xxx_rule()`` with 

76 docstring 'xxx_rule : yyy' when decorated with 

77 ``@parameterized(('id', 'ID'))`` produces ``p_id_rule()`` with the docstring 

78 'id_rule : ID'. Using multiple tuples produces multiple rules. 

79 """ 

80 def decorate(rule_func): 

81 rule_func._params = params 

82 return rule_func 

83 return decorate 

84 

85 

86def template(cls): 

87 """ Class decorator to generate rules from parameterized rule templates. 

88 

89 See `parameterized` for more information on parameterized rules. 

90 """ 

91 issued_nodoc_warning = False 

92 for attr_name in dir(cls): 

93 if attr_name.startswith('p_'): 

94 method = getattr(cls, attr_name) 

95 if hasattr(method, '_params'): 

96 # Remove the template method 

97 delattr(cls, attr_name) 

98 # Create parameterized rules from this method; only run this if 

99 # the method has a docstring. This is to address an issue when 

100 # pycparser's users are installed in -OO mode which strips 

101 # docstrings away. 

102 # See: https://github.com/eliben/pycparser/pull/198/ and 

103 # https://github.com/eliben/pycparser/issues/197 

104 # for discussion. 

105 if method.__doc__ is not None: 

106 _create_param_rules(cls, method) 

107 elif not issued_nodoc_warning: 

108 warnings.warn( 

109 'parsing methods must have __doc__ for pycparser to work properly', 

110 RuntimeWarning, 

111 stacklevel=2) 

112 issued_nodoc_warning = True 

113 return cls 

114 

115 

116def _create_param_rules(cls, func): 

117 """ Create ply.yacc rules based on a parameterized rule function 

118 

119 Generates new methods (one per each pair of parameters) based on the 

120 template rule function `func`, and attaches them to `cls`. The rule 

121 function's parameters must be accessible via its `_params` attribute. 

122 """ 

123 for xxx, yyy in func._params: 

124 # Use the template method's body for each new method 

125 def param_rule(self, p): 

126 func(self, p) 

127 

128 # Substitute in the params for the grammar rule and function name 

129 param_rule.__doc__ = func.__doc__.replace('xxx', xxx).replace('yyy', yyy) 

130 param_rule.__name__ = func.__name__.replace('xxx', xxx) 

131 

132 # Attach the new method to the class 

133 setattr(cls, param_rule.__name__, param_rule)