Parse a specification markdown document. Returns (nodes, invalidEntries).
(specPath, stdlib, geompropNames)
| 503 | break |
| 504 | |
| 505 | def parseSpecDocument(specPath, stdlib, geompropNames): |
| 506 | '''Parse a specification markdown document. Returns (nodes, invalidEntries).''' |
| 507 | # Build type system data from stdlib |
| 508 | standardTypes = getStandardTypes(stdlib) |
| 509 | typeGroups = buildTypeGroups(stdlib) |
| 510 | typeGroupVariables = buildTypeGroupVariables(typeGroups) |
| 511 | |
| 512 | # Build derived values for validation and parsing |
| 513 | knownTypes = standardTypes | set(typeGroups.keys()) | set(typeGroupVariables.keys()) |
| 514 | specDefaultNotation = { |
| 515 | '__zero__': '0', |
| 516 | '__one__': '1', |
| 517 | '__half__': '0.5', |
| 518 | '__empty__': '', |
| 519 | } |
| 520 | for name in geompropNames: |
| 521 | specDefaultNotation[f'_{name}_'] = name |
| 522 | |
| 523 | nodes = {} |
| 524 | invalidEntries = [] |
| 525 | |
| 526 | with open(specPath, 'r', encoding='utf-8') as f: |
| 527 | content = f.read() |
| 528 | |
| 529 | lines = content.split('\n') |
| 530 | currentNode = None |
| 531 | currentTableInputs = {} |
| 532 | currentTableOutputs = {} |
| 533 | idx = 0 |
| 534 | |
| 535 | def finalizeCurrentTable(): |
| 536 | '''Expand current table to signatures and add to node.''' |
| 537 | nonlocal currentTableInputs, currentTableOutputs |
| 538 | if currentNode and (currentTableInputs or currentTableOutputs): |
| 539 | node = nodes[currentNode] |
| 540 | # Expand to signatures (do NOT pre-resolve typeRefs - expansion handles them) |
| 541 | tableSigs = expandSpecSignatures(currentTableInputs, currentTableOutputs, typeGroups, typeGroupVariables) |
| 542 | node.signatures.update(tableSigs) |
| 543 | # Merge input port info for default comparison (resolve types for defaults) |
| 544 | allPorts = {**currentTableInputs, **currentTableOutputs} |
| 545 | resolvePortTypeRefs(allPorts) |
| 546 | for name, port in currentTableInputs.items(): |
| 547 | if name not in node._specInputs: |
| 548 | node._specInputs[name] = port |
| 549 | else: |
| 550 | node._specInputs[name].types.update(port.types) |
| 551 | currentTableInputs = {} |
| 552 | currentTableOutputs = {} |
| 553 | |
| 554 | while idx < len(lines): |
| 555 | line = lines[idx] |
| 556 | |
| 557 | # Look for node headers (### `nodename`) |
| 558 | nodeMatch = re.match(r'^###\s+`([^`]+)`', line) |
| 559 | if nodeMatch: |
| 560 | # Finalize previous table before switching nodes |
| 561 | finalizeCurrentTable() |
| 562 | currentNode = nodeMatch.group(1) |
no test coverage detected