Zack Williams | 28f1e49 | 2019-02-01 10:02:56 -0700 | [diff] [blame] | 1 | # Copyright 2017-present Open Networking Foundation and others |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
| 15 | from __future__ import print_function |
| 16 | from six.moves import range |
Scott Baker | 20997cf | 2019-04-04 10:10:06 -0700 | [diff] [blame^] | 17 | import sys |
Zack Williams | 28f1e49 | 2019-02-01 10:02:56 -0700 | [diff] [blame] | 18 | |
| 19 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 20 | class LexHelper: |
| 21 | offset = 0 |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 22 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 23 | def get_max_linespan(self, p): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 24 | defSpan = [1e60, -1] |
| 25 | mSpan = [1e60, -1] |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 26 | for sp in range(0, len(p)): |
| 27 | csp = p.linespan(sp) |
| 28 | if csp[0] == 0 and csp[1] == 0: |
| 29 | if hasattr(p[sp], "linespan"): |
| 30 | csp = p[sp].linespan |
| 31 | else: |
| 32 | continue |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 33 | if csp is None or len(csp) != 2: |
| 34 | continue |
| 35 | if csp[0] == 0 and csp[1] == 0: |
| 36 | continue |
| 37 | if csp[0] < mSpan[0]: |
| 38 | mSpan[0] = csp[0] |
| 39 | if csp[1] > mSpan[1]: |
| 40 | mSpan[1] = csp[1] |
| 41 | if defSpan == mSpan: |
| 42 | return (0, 0) |
| 43 | return tuple([mSpan[0] - self.offset, mSpan[1] - self.offset]) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 44 | |
| 45 | def get_max_lexspan(self, p): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 46 | defSpan = [1e60, -1] |
| 47 | mSpan = [1e60, -1] |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 48 | for sp in range(0, len(p)): |
| 49 | csp = p.lexspan(sp) |
| 50 | if csp[0] == 0 and csp[1] == 0: |
| 51 | if hasattr(p[sp], "lexspan"): |
| 52 | csp = p[sp].lexspan |
| 53 | else: |
| 54 | continue |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 55 | if csp is None or len(csp) != 2: |
| 56 | continue |
| 57 | if csp[0] == 0 and csp[1] == 0: |
| 58 | continue |
| 59 | if csp[0] < mSpan[0]: |
| 60 | mSpan[0] = csp[0] |
| 61 | if csp[1] > mSpan[1]: |
| 62 | mSpan[1] = csp[1] |
| 63 | if defSpan == mSpan: |
| 64 | return (0, 0) |
| 65 | return tuple([mSpan[0] - self.offset, mSpan[1] - self.offset]) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 66 | |
| 67 | def set_parse_object(self, dst, p): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 68 | dst.setLexData( |
| 69 | linespan=self.get_max_linespan(p), |
| 70 | lexspan=self.get_max_lexspan(p)) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 71 | dst.setLexObj(p) |
| 72 | |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 73 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 74 | class Base(object): |
| 75 | parent = None |
| 76 | lexspan = None |
| 77 | linespan = None |
| 78 | |
| 79 | def v(self, obj, visitor): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 80 | if obj is None: |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 81 | return |
| 82 | elif hasattr(obj, "accept"): |
| 83 | obj.accept(visitor) |
| 84 | elif isinstance(obj, list): |
| 85 | for s in obj: |
| 86 | self.v(s, visitor) |
| 87 | pass |
| 88 | pass |
| 89 | |
| 90 | @staticmethod |
| 91 | def p(obj, parent): |
| 92 | if isinstance(obj, list): |
| 93 | for s in obj: |
| 94 | Base.p(s, parent) |
| 95 | |
| 96 | if hasattr(obj, "parent"): |
| 97 | obj.parent = parent |
| 98 | |
| 99 | # Lexical unit - contains lexspan and linespan for later analysis. |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 100 | |
| 101 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 102 | class LU(Base): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 103 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 104 | def __init__(self, p, idx): |
| 105 | self.p = p |
| 106 | self.idx = idx |
| 107 | self.pval = p[idx] |
| 108 | self.lexspan = p.lexspan(idx) |
| 109 | self.linespan = p.linespan(idx) |
| 110 | |
| 111 | # If string is in the value (raw value) and start and stop lexspan is the same, add real span |
| 112 | # obtained by string length. |
| 113 | if isinstance(self.pval, str) \ |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 114 | and self.lexspan is not None \ |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 115 | and self.lexspan[0] == self.lexspan[1] \ |
| 116 | and self.lexspan[0] != 0: |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 117 | self.lexspan = tuple( |
| 118 | [self.lexspan[0], self.lexspan[0] + len(self.pval)]) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 119 | super(LU, self).__init__() |
| 120 | |
| 121 | @staticmethod |
| 122 | def i(p, idx): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 123 | if isinstance(p[idx], LU): |
| 124 | return p[idx] |
| 125 | if isinstance(p[idx], str): |
| 126 | return LU(p, idx) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 127 | return p[idx] |
| 128 | |
| 129 | def describe(self): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 130 | return "LU(%s,%s)" % (self.pval, self.lexspan) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 131 | |
| 132 | def __str__(self): |
| 133 | return self.pval |
| 134 | |
| 135 | def __repr__(self): |
| 136 | return self.describe() |
| 137 | |
| 138 | def accept(self, visitor): |
| 139 | self.v(self.pval, visitor) |
| 140 | |
| 141 | def __iter__(self): |
| 142 | for x in self.pval: |
| 143 | yield x |
| 144 | |
| 145 | # Base node |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 146 | |
| 147 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 148 | class SourceElement(Base): |
| 149 | ''' |
| 150 | A SourceElement is the base class for all elements that occur in a Protocol Buffers |
| 151 | file parsed by plyproto. |
| 152 | ''' |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 153 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 154 | def __init__(self, linespan=[], lexspan=[], p=None): |
| 155 | super(SourceElement, self).__init__() |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 156 | self._fields = [] # ['linespan', 'lexspan'] |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 157 | self.linespan = linespan |
| 158 | self.lexspan = lexspan |
| 159 | self.p = p |
| 160 | |
| 161 | def __repr__(self): |
| 162 | equals = ("{0}={1!r}".format(k, getattr(self, k)) |
| 163 | for k in self._fields) |
| 164 | args = ", ".join(equals) |
| 165 | return "{0}({1})".format(self.__class__.__name__, args) |
| 166 | |
| 167 | def __eq__(self, other): |
| 168 | try: |
| 169 | return self.__dict__ == other.__dict__ |
| 170 | except AttributeError: |
| 171 | return False |
| 172 | |
| 173 | def __ne__(self, other): |
| 174 | return not self == other |
| 175 | |
| 176 | def setLexData(self, linespan, lexspan): |
| 177 | self.linespan = linespan |
| 178 | self.lexspan = lexspan |
| 179 | |
| 180 | def setLexObj(self, p): |
| 181 | self.p = p |
| 182 | |
| 183 | def accept(self, visitor): |
| 184 | pass |
| 185 | |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 186 | |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 187 | class Visitor(object): |
| 188 | |
| 189 | def __init__(self, verbose=False): |
| 190 | self.verbose = verbose |
| 191 | |
| 192 | def __getattr__(self, name): |
| 193 | if not name.startswith('visit_'): |
Zack Williams | be7f36d | 2018-02-02 11:37:11 -0700 | [diff] [blame] | 194 | raise AttributeError( |
| 195 | 'name must start with visit_ but was {}'.format(name)) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 196 | |
| 197 | def f(element): |
| 198 | if self.verbose: |
| 199 | msg = 'unimplemented call to {}; ignoring ({})' |
Scott Baker | 20997cf | 2019-04-04 10:10:06 -0700 | [diff] [blame^] | 200 | print((msg.format(name, element)), file=sys.stderr) |
Sapan Bhatia | 64c7251 | 2017-06-23 02:32:45 -0700 | [diff] [blame] | 201 | return True |
| 202 | return f |
| 203 | |
| 204 | # visitor.visit_PackageStatement(self) |
| 205 | # visitor.visit_ImportStatement(self) |
| 206 | # visitor.visit_OptionStatement(self) |
| 207 | # visitor.visit_FieldDirective(self) |
| 208 | # visitor.visit_FieldType(self) |
| 209 | # visitor.visit_FieldDefinition(self) |
| 210 | # visitor.visit_EnumFieldDefinition(self) |
| 211 | # visitor.visit_EnumDefinition(self) |
| 212 | # visitor.visit_MessageDefinition(self) |
| 213 | # visitor.visit_MessageExtension(self) |
| 214 | # visitor.visit_MethodDefinition(self) |
| 215 | # visitor.visit_ServiceDefinition(self) |
| 216 | # visitor.visit_ExtensionsDirective(self) |
| 217 | # visitor.visit_Literal(self) |
| 218 | # visitor.visit_Name(self) |
| 219 | # visitor.visit_Proto(self) |
| 220 | # visitor.visit_LU(self) |