Parse GML `lines` into a graph.
(lines, label, destringizer)
| 219 | |
| 220 | |
| 221 | def parse_gml_lines(lines, label, destringizer): |
| 222 | """Parse GML `lines` into a graph.""" |
| 223 | |
| 224 | def tokenize(): |
| 225 | patterns = [ |
| 226 | r"[A-Za-z][0-9A-Za-z_]*\b", # keys |
| 227 | # reals |
| 228 | r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*|INF)(?:[Ee][+-]?[0-9]+)?", |
| 229 | r"[+-]?[0-9]+", # ints |
| 230 | r'".*?"', # strings |
| 231 | r"\[", # dict start |
| 232 | r"\]", # dict end |
| 233 | r"#.*$|\s+", # comments and whitespaces |
| 234 | ] |
| 235 | tokens = re.compile("|".join(f"({pattern})" for pattern in patterns)) |
| 236 | lineno = 0 |
| 237 | for line in lines: |
| 238 | length = len(line) |
| 239 | pos = 0 |
| 240 | while pos < length: |
| 241 | match = tokens.match(line, pos) |
| 242 | if match is None: |
| 243 | m = f"cannot tokenize {line[pos:]} at ({lineno + 1}, {pos + 1})" |
| 244 | raise EasyGraphError(m) |
| 245 | for i in range(len(patterns)): |
| 246 | group = match.group(i + 1) |
| 247 | if group is not None: |
| 248 | if i == 0: # keys |
| 249 | value = group.rstrip() |
| 250 | elif i == 1: # reals |
| 251 | value = float(group) |
| 252 | elif i == 2: # ints |
| 253 | value = int(group) |
| 254 | else: |
| 255 | value = group |
| 256 | if i != 6: # comments and whitespaces |
| 257 | yield Token(Pattern(i), value, lineno + 1, pos + 1) |
| 258 | pos += len(group) |
| 259 | break |
| 260 | lineno += 1 |
| 261 | yield Token(None, None, lineno + 1, 1) # EOF |
| 262 | |
| 263 | def unexpected(curr_token, expected): |
| 264 | category, value, lineno, pos = curr_token |
| 265 | value = repr(value) if value is not None else "EOF" |
| 266 | raise EasyGraphError(f"expected {expected}, found {value} at ({lineno}, {pos})") |
| 267 | |
| 268 | def consume(curr_token, category, expected): |
| 269 | if curr_token.category == category: |
| 270 | return next(tokens) |
| 271 | unexpected(curr_token, expected) |
| 272 | |
| 273 | def parse_dict(curr_token): |
| 274 | # dict start |
| 275 | curr_token = consume(curr_token, Pattern.DICT_START, "'['") |
| 276 | # dict contents |
| 277 | curr_token, dct = parse_kv(curr_token) |
| 278 | # dict end |