sysmlpy 0.74.0__tar.gz → 0.76.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/PKG-INFO +1 -1
  2. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/pyproject.toml +1 -1
  3. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/__init__.py +1 -1
  4. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr_parser.py +11 -4
  5. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/project.py +66 -10
  6. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/semantic.py +362 -1
  7. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/LICENSE +0 -0
  8. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/README.md +0 -0
  9. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/__main__.py +0 -0
  10. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/README.md +0 -0
  11. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2Lexer.interp +0 -0
  12. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2Lexer.py +0 -0
  13. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2Lexer.tokens +0 -0
  14. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2Parser.interp +0 -0
  15. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2Parser.py +0 -0
  16. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2Parser.tokens +0 -0
  17. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2ParserListener.py +0 -0
  18. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/SysMLv2ParserVisitor.py +0 -0
  19. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
  20. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
  21. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
  22. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/antlr_visitor.py +0 -0
  23. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/boxes_view.py +0 -0
  24. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/definition.py +0 -0
  25. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/diff.py +0 -0
  26. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/evaluator.py +0 -0
  27. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/examples/attribute_values.py +0 -0
  28. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/examples/part_attributes.py +0 -0
  29. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/examples/tuples_sequences.py +0 -0
  30. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/formatting.py +0 -0
  31. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/README.md +0 -0
  32. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +0 -0
  33. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
  34. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
  35. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
  36. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +0 -0
  37. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.interp +0 -0
  38. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.py +0 -0
  39. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +0 -0
  40. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +0 -0
  41. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +0 -0
  42. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/desc.xml +0 -0
  43. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/examples/camera.sysml +0 -0
  44. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/examples/toaster-system.sysml +0 -0
  45. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +0 -0
  46. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/antlr4/pom.xml +0 -0
  47. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/grammar/classes.py +0 -0
  48. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/interchange.py +0 -0
  49. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/LICENSE +0 -0
  50. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/README.md +0 -0
  51. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/__init__.py +0 -0
  52. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +0 -0
  53. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Analysis/SampledFunctions.sysml +0 -0
  54. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +0 -0
  55. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Analysis/TradeStudies.sysml +0 -0
  56. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +0 -0
  57. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +0 -0
  58. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Geometry/ShapeItems.sysml +0 -0
  59. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Geometry/SpatialItems.sysml +0 -0
  60. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Metadata/ImageMetadata.sysml +0 -0
  61. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +0 -0
  62. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +0 -0
  63. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Metadata/RiskMetadata.sysml +0 -0
  64. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQ.sysml +0 -0
  65. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +0 -0
  66. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +0 -0
  67. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +0 -0
  68. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +0 -0
  69. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +0 -0
  70. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +0 -0
  71. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +0 -0
  72. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +0 -0
  73. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +0 -0
  74. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +0 -0
  75. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +0 -0
  76. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +0 -0
  77. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +0 -0
  78. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +0 -0
  79. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/Quantities.sysml +0 -0
  80. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +0 -0
  81. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/SI.sysml +0 -0
  82. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +0 -0
  83. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +0 -0
  84. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/Time.sysml +0 -0
  85. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +0 -0
  86. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +0 -0
  87. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +0 -0
  88. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +0 -0
  89. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Base.kerml +0 -0
  90. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/BaseFunctions.kerml +0 -0
  91. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/BooleanFunctions.kerml +0 -0
  92. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Clocks.kerml +0 -0
  93. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/CollectionFunctions.kerml +0 -0
  94. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Collections.kerml +0 -0
  95. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/ComplexFunctions.kerml +0 -0
  96. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/ControlFunctions.kerml +0 -0
  97. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/ControlPerformances.kerml +0 -0
  98. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/DataFunctions.kerml +0 -0
  99. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +0 -0
  100. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/IntegerFunctions.kerml +0 -0
  101. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/KerML.kerml +0 -0
  102. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Links.kerml +0 -0
  103. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Metaobjects.kerml +0 -0
  104. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/NaturalFunctions.kerml +0 -0
  105. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/NumericalFunctions.kerml +0 -0
  106. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Objects.kerml +0 -0
  107. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Observation.kerml +0 -0
  108. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/OccurrenceFunctions.kerml +0 -0
  109. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Occurrences.kerml +0 -0
  110. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Performances.kerml +0 -0
  111. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/RationalFunctions.kerml +0 -0
  112. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/RealFunctions.kerml +0 -0
  113. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/ScalarFunctions.kerml +0 -0
  114. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/ScalarValues.kerml +0 -0
  115. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/SequenceFunctions.kerml +0 -0
  116. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/SpatialFrames.kerml +0 -0
  117. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/StatePerformances.kerml +0 -0
  118. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/StringFunctions.kerml +0 -0
  119. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Transfers.kerml +0 -0
  120. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/TransitionPerformances.kerml +0 -0
  121. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/TrigFunctions.kerml +0 -0
  122. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/Triggers.kerml +0 -0
  123. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/VectorFunctions.kerml +0 -0
  124. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/kernel/VectorValues.kerml +0 -0
  125. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Actions.sysml +0 -0
  126. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Allocations.sysml +0 -0
  127. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/AnalysisCases.sysml +0 -0
  128. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Attributes.sysml +0 -0
  129. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Calculations.sysml +0 -0
  130. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Cases.sysml +0 -0
  131. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Connections.sysml +0 -0
  132. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Constraints.sysml +0 -0
  133. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Flows.sysml +0 -0
  134. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Interfaces.sysml +0 -0
  135. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Items.sysml +0 -0
  136. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Metadata.sysml +0 -0
  137. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Parts.sysml +0 -0
  138. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Ports.sysml +0 -0
  139. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Requirements.sysml +0 -0
  140. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/StandardViewDefinitions.sysml +0 -0
  141. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/States.sysml +0 -0
  142. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/SysML.sysml +0 -0
  143. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/UseCases.sysml +0 -0
  144. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/VerificationCases.sysml +0 -0
  145. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/library/systems/Views.sysml +0 -0
  146. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/lsp/__init__.py +0 -0
  147. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/lsp/__main__.py +0 -0
  148. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/lsp/protocol.py +0 -0
  149. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/lsp/server.py +0 -0
  150. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/lsp/stdio.py +0 -0
  151. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/navigate.py +0 -0
  152. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/plantuml.py +0 -0
  153. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/sim.py +0 -0
  154. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/spreadsheet.py +0 -0
  155. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/store.py +0 -0
  156. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/traceability.py +0 -0
  157. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/us_customary_units.txt +0 -0
  158. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/usage.py +0 -0
  159. {sysmlpy-0.74.0 → sysmlpy-0.76.0}/src/sysmlpy/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sysmlpy
3
- Version: 0.74.0
3
+ Version: 0.76.0
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: Jon R. Fox (mycr0ft)
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
6
6
 
7
7
  [project]
8
8
  name = "sysmlpy"
9
- version = "0.74.0"
9
+ version = "0.76.0"
10
10
  description = ""
11
11
  authors = [{ name = "Jon R. Fox (mycr0ft)", email = "jon.fox@drfox.com" }]
12
12
  readme = "README.md"
@@ -34,7 +34,7 @@ __all__ = [
34
34
  "render_state_transition_view_svg", "boxes_view",
35
35
  ]
36
36
  __author__ = "Jon Fox"
37
- __version__ = "0.74.0"
37
+ __version__ = "0.76.0"
38
38
 
39
39
  from sysmlpy.usage import (
40
40
  Item, Attribute, Part, Port, Action, Reference, UseCase, Requirement, Interface, Message,
@@ -74,8 +74,9 @@ def parse(source, library=None, recover=False, prediction_mode="sll"):
74
74
  prediction_mode : {"sll", "ll"}, default "sll"
75
75
  ``"sll"`` (default) runs the fast SLL pass with LL fallback.
76
76
  ``"ll"`` forces the full-context pass directly (slower; useful
77
- for debugging). ``"sll"`` without fallback can be forced with
78
- ``prediction_mode="sll_only"``.
77
+ for debugging). ``"sll_only"`` keeps every pass in SLL
78
+ prediction — including the fallback pass — so diagnostics
79
+ reflect SLL behaviour (error parity with the fast path).
79
80
 
80
81
  Returns
81
82
  -------
@@ -127,14 +128,20 @@ def parse(source, library=None, recover=False, prediction_mode="sll"):
127
128
  # errors but re-run without bail to build the partial tree.
128
129
  pass
129
130
 
130
- # ── Stage 2: full LL parse (fallback) ─────────────────────────────
131
+ # ── Stage 2: full parse (fallback / forced mode) ──────────────────
131
132
  lexer, token_stream, parser = _make_parser(content)
132
133
  error_listener = ANTLRErrorListener()
133
134
  lexer.addErrorListener(error_listener)
134
135
  parser.addErrorListener(error_listener)
135
136
  from antlr4.error.ErrorStrategy import DefaultErrorStrategy
136
137
  parser._errHandler = DefaultErrorStrategy()
137
- parser._interp.predictionMode = PredictionMode.LL
138
+ # SLL error parity (Goal 10): an explicit "sll_only" request stays
139
+ # in SLL prediction so its diagnostics match the fast pass; every
140
+ # other fallback (sll -> ll, forced ll) uses full LL prediction.
141
+ if prediction_mode == "sll_only":
142
+ parser._interp.predictionMode = PredictionMode.SLL
143
+ else:
144
+ parser._interp.predictionMode = PredictionMode.LL
138
145
  tree = parser.rootNamespace()
139
146
 
140
147
  # Check for errors
@@ -233,17 +233,73 @@ def _extract_imports(content: str) -> list[str]:
233
233
  Returns a list of qualified names like ``['ScalarValues', 'ISQ']``.
234
234
  Handles quoted package names (e.g., ``'Some Package'``) and the
235
235
  ``all`` keyword (e.g., ``import all Types::*``).
236
+
237
+ Since v0.76.0 this scans the SysML v2 lexer token stream instead
238
+ of a regex over raw text (Goal 10 "regex to parser" item), which
239
+ fixes three defects of the old regex:
240
+
241
+ - a bare ``import X::Y;`` (no visibility keyword) was missed —
242
+ the visibility prefix is optional per the SysML grammar and is
243
+ now correctly treated as such
244
+ - imports inside comments produced false positives
245
+ - imports inside string literals (e.g. ``doc about "..."``)
246
+ produced false positives
247
+
248
+ Syntax-error tolerance: the lexer keeps producing tokens for
249
+ partially broken sources (dependency scanning must not require a
250
+ parseable file), so scanning continues past stray tokens; an
251
+ import with a stray token in its name run is skipped.
236
252
  """
237
- imports = []
238
- # Match: [private|public|protected] import [all] <QualifiedName> [::*] [::**];
239
- # Qualified name parts can be identifiers or quoted strings
240
- pattern = _re.compile(
241
- r'(?:private|public|protected)\s+import\s+(?:all\s+)?'
242
- r"((?:[A-Za-z_][A-Za-z0-9_]*|'[^']*')(?:::(?:[A-Za-z_][A-Za-z0-9_]*|'[^']*'))*)"
243
- r'(?:\:\:\*)?(?:\:\:\*\*)?\s*;'
244
- )
245
- for match in pattern.finditer(content):
246
- imports.append(match.group(1))
253
+ imports: list[str] = []
254
+ try:
255
+ from sysmlpy.antlr_parser import _make_parser
256
+ from sysmlpy.antlr.SysMLv2Lexer import SysMLv2Lexer
257
+ _lexer, token_stream, _parser = _make_parser(content)
258
+ token_stream.fill()
259
+ tokens = token_stream.tokens
260
+ except Exception:
261
+ return imports
262
+
263
+ from antlr4.Token import Token
264
+
265
+ visible = [t for t in tokens
266
+ if t.type != Token.EOF
267
+ and t.channel == Token.DEFAULT_CHANNEL]
268
+
269
+ NAME_TYPES = frozenset((SysMLv2Lexer.IDENTIFIER, SysMLv2Lexer.STRING))
270
+ COLON_COLON = SysMLv2Lexer.COLON_COLON
271
+ STAR = SysMLv2Lexer.STAR
272
+ STAR_STAR = SysMLv2Lexer.STAR_STAR
273
+ SEMI = SysMLv2Lexer.SEMI
274
+ ALL = SysMLv2Lexer.ALL
275
+
276
+ i = 0
277
+ n = len(visible)
278
+ while i < n:
279
+ tok = visible[i]
280
+ if tok.type != SysMLv2Lexer.IMPORT:
281
+ i += 1
282
+ continue
283
+ # Collect the import run up to the terminating ';'
284
+ j = i + 1
285
+ parts: list[str] = []
286
+ aborted = False
287
+ while j < n and visible[j].type != SEMI:
288
+ t = visible[j]
289
+ if t.type in NAME_TYPES:
290
+ parts.append(t.text)
291
+ elif t.type in (ALL, COLON_COLON, STAR, STAR_STAR):
292
+ pass # 'all' keyword, namespace separators, wildcards
293
+ else:
294
+ aborted = True # stray token — not a well-formed import
295
+ break
296
+ j += 1
297
+ if parts and not aborted:
298
+ imports.append("::".join(parts))
299
+ # advance past the run (or the aborted token) and its ';' if present
300
+ i = j
301
+ if i < n and visible[i].type == SEMI:
302
+ i += 1
247
303
  return imports
248
304
 
249
305
 
@@ -1533,6 +1533,22 @@ def _parse_dimension(dim: Optional[str]) -> Optional[dict[str, int]]:
1533
1533
  return dims
1534
1534
 
1535
1535
 
1536
+ def _dims_mul(a: dict[str, int], b: dict[str, int]) -> dict[str, int]:
1537
+ """Multiply two dimension dicts (add exponents, drop zeros)."""
1538
+ out = dict(a)
1539
+ for k, v in b.items():
1540
+ out[k] = out.get(k, 0) + v
1541
+ return {k: v for k, v in out.items() if v != 0}
1542
+
1543
+
1544
+ def _dims_div(a: dict[str, int], b: dict[str, int]) -> dict[str, int]:
1545
+ """Divide two dimension dicts (subtract exponents, drop zeros)."""
1546
+ out = dict(a)
1547
+ for k, v in b.items():
1548
+ out[k] = out.get(k, 0) - v
1549
+ return {k: v for k, v in out.items() if v != 0}
1550
+
1551
+
1536
1552
  def _dimension_to_pint(dims: Optional[dict[str, int]]) -> Optional[Any]:
1537
1553
  """Map a SysML dimension dict to a pint dimensionality.
1538
1554
 
@@ -2107,6 +2123,263 @@ class ExpressionTypeChecker:
2107
2123
  if lhs_cat in ("boolean", "string") or rhs_cat in ("boolean", "string"):
2108
2124
  self._emit_mismatch(op, lhs_cat, rhs_cat, element, issues)
2109
2125
 
2126
+ # -- unit-dimension derivation (Goal 10) ---------------------------------
2127
+
2128
+ def check_derivations(self, model: Any) -> list[SemanticIssue]:
2129
+ """Derive ``*`` / ``/`` dimension algebra and compare with typing.
2130
+
2131
+ For every expression owner whose declared type carries a known
2132
+ quantity dimension (library ``*Value`` / ``*Unit`` definitions),
2133
+ the initializer's dimension is derived algebraically:
2134
+
2135
+ - ``a * b`` adds exponents, ``a / b`` subtracts them
2136
+ - ``a ** n`` (literal integer ``n``) multiplies them
2137
+ - ``+`` / ``-`` chains require equal operand dimensions
2138
+ - dimensionless literals are the multiplicative identity
2139
+
2140
+ A mismatch between the derived dimension and the declared
2141
+ typing is reported as ``UNIT_DIMENSION_DERIVATION_MISMATCH``.
2142
+ Conservative skips: any operand with unknown dimension, non-
2143
+ literal exponents, ``%``, boolean/string/relational levels,
2144
+ and initializers with no quantity-typed operand at all (a bare
2145
+ literal like ``= 70`` cannot reveal its intended unit).
2146
+ """
2147
+ issues: list[SemanticIssue] = []
2148
+ self._walk_derivation_owners(model, issues, [])
2149
+ return issues
2150
+
2151
+ def _walk_derivation_owners(
2152
+ self,
2153
+ element: Any,
2154
+ issues: list[SemanticIssue],
2155
+ scope_path: list[str],
2156
+ ) -> None:
2157
+ if element is None:
2158
+ return
2159
+ name = getattr(element, "name", None)
2160
+ elem_type = type(element).__name__
2161
+ is_container = getattr(element, "is_definition", False) or elem_type == "Package"
2162
+ child_scope = scope_path
2163
+ if is_container and name is not None and elem_type != "Model":
2164
+ child_scope = scope_path + [name]
2165
+
2166
+ if elem_type in _EXPRESSION_OWNER_TYPES:
2167
+ grammar = getattr(element, "grammar", None)
2168
+ if grammar is not None:
2169
+ typed = getattr(element, "typed_by_name", None)
2170
+ if typed:
2171
+ declared = self._declared_dimension(str(typed))
2172
+ if declared is not None:
2173
+ try:
2174
+ grammar_def = grammar.get_definition()
2175
+ except Exception:
2176
+ grammar_def = None
2177
+ if grammar_def is not None:
2178
+ for expr in _find_owned_expressions(grammar_def):
2179
+ self._check_derivation(
2180
+ expr, element, str(typed), declared,
2181
+ scope_path, issues)
2182
+
2183
+ for child in getattr(element, "children", []):
2184
+ self._walk_derivation_owners(child, issues, child_scope)
2185
+
2186
+ def _declared_dimension(self, type_name: str) -> Optional[dict[str, int]]:
2187
+ """Dimension dict for a declared type name, or None when unknown."""
2188
+ dim_str = self._dimensions.get(type_name)
2189
+ if not dim_str:
2190
+ simple = type_name.rsplit("::", 1)[-1]
2191
+ dim_str = self._dimensions.get(simple)
2192
+ if not dim_str:
2193
+ return None
2194
+ return _parse_dimension(dim_str)
2195
+
2196
+ def _check_derivation(
2197
+ self,
2198
+ expr_dict: dict,
2199
+ element: Any,
2200
+ type_name: str,
2201
+ declared: dict[str, int],
2202
+ scope_path: list[str],
2203
+ issues: list[SemanticIssue],
2204
+ ) -> None:
2205
+ node = expr_dict.get("expression")
2206
+ if not isinstance(node, dict):
2207
+ return
2208
+ derived, contributed = self._derive_dimension(node, scope_path)
2209
+ if derived is None or not contributed:
2210
+ return
2211
+ if derived != declared:
2212
+ issues.append(SemanticIssue(
2213
+ severity="error",
2214
+ code="UNIT_DIMENSION_DERIVATION_MISMATCH",
2215
+ message=(
2216
+ f"Initializer of '{getattr(element, 'name', '?')}' derives "
2217
+ f"dimension '{_format_dimension(derived)}' but declared type "
2218
+ f"'{type_name}' has dimension '{_format_dimension(declared)}'"
2219
+ ),
2220
+ element=element,
2221
+ reference=_format_dimension(declared),
2222
+ ))
2223
+
2224
+ def _derive_dimension(
2225
+ self,
2226
+ node: Any,
2227
+ scope_path: list[str],
2228
+ ) -> tuple:
2229
+ """Algebraically derive the dimension of an expression dict.
2230
+
2231
+ Returns ``(dims, contributed)`` where ``dims`` is ``None`` when
2232
+ the expression is not statically derivable and ``contributed``
2233
+ is True when at least one quantity-typed operand was seen
2234
+ (bare-literal initializers stay silent).
2235
+ """
2236
+ if not isinstance(node, dict):
2237
+ return (None, False)
2238
+ name = node.get("name")
2239
+
2240
+ if name == "OwnedExpression":
2241
+ return self._derive_dimension(node.get("expression"), scope_path)
2242
+ if name == "ConditionalExpression":
2243
+ operands = node.get("operand", []) or []
2244
+ if len(operands) != 1:
2245
+ return (None, False)
2246
+ return self._derive_dimension(operands[0], scope_path)
2247
+
2248
+ # Operator-less levels are pure wrappers (the visitor always
2249
+ # emits the full NullCoalescing → Implies → … chain); with an
2250
+ # operator present the level is boolean-valued — not derivable.
2251
+ if name == "NullCoalescingExpression":
2252
+ if node.get("operator"):
2253
+ return (None, False)
2254
+ return self._derive_dimension(node.get("implies"), scope_path)
2255
+ if name in ("ImpliesExpression", "OrExpression", "XorExpression",
2256
+ "AndExpression"):
2257
+ if node.get("operator") or node.get("operand") \
2258
+ or node.get("operation"):
2259
+ return (None, False)
2260
+ child_key = {"ImpliesExpression": "or",
2261
+ "OrExpression": "xor",
2262
+ "XorExpression": "and",
2263
+ "AndExpression": "equality"}[name]
2264
+ return self._derive_dimension(node.get(child_key), scope_path)
2265
+ if name == "EqualityExpression":
2266
+ if node.get("operation"):
2267
+ return (None, False)
2268
+ return self._derive_dimension(
2269
+ node.get("classification"), scope_path)
2270
+ if name == "ClassificationExpression":
2271
+ if node.get("operator"):
2272
+ return (None, False)
2273
+ return self._derive_dimension(
2274
+ node.get("relational"), scope_path)
2275
+ if name == "RelationalExpression":
2276
+ if node.get("operation"):
2277
+ return (None, False)
2278
+ return self._derive_dimension(node.get("range"), scope_path)
2279
+
2280
+ if name == "RangeExpression":
2281
+ if node.get("operator"):
2282
+ return (None, False) # a .. b — bounds may differ
2283
+ return self._derive_dimension(node.get("additive"), scope_path)
2284
+
2285
+ if name == "AdditiveExpression":
2286
+ dims, contributed = self._derive_dimension(
2287
+ node.get("multiplicitive"), scope_path)
2288
+ for op_dict in node.get("operation", []) or []:
2289
+ if not isinstance(op_dict, dict):
2290
+ continue
2291
+ op = op_dict.get("operator")
2292
+ rhs, c = self._derive_dimension(
2293
+ op_dict.get("operand"), scope_path)
2294
+ if op not in ("+", "-") or dims is None or rhs is None \
2295
+ or dims != rhs:
2296
+ return (None, False)
2297
+ contributed = contributed or c
2298
+ return (dims, contributed)
2299
+
2300
+ if name == "MultiplicativeExpression":
2301
+ dims, contributed = self._derive_dimension(
2302
+ node.get("exponential"), scope_path)
2303
+ for op_dict in node.get("operation", []) or []:
2304
+ if not isinstance(op_dict, dict):
2305
+ continue
2306
+ op = op_dict.get("operator")
2307
+ rhs, c = self._derive_dimension(
2308
+ op_dict.get("operand"), scope_path)
2309
+ if dims is None or rhs is None:
2310
+ return (None, False)
2311
+ contributed = contributed or c
2312
+ if op == "*":
2313
+ dims = _dims_mul(dims, rhs)
2314
+ elif op == "/":
2315
+ dims = _dims_div(dims, rhs)
2316
+ else: # '%' and anything else — not derivable
2317
+ return (None, False)
2318
+ return (dims, contributed)
2319
+
2320
+ if name == "ExponentiationExpression":
2321
+ dims, contributed = self._derive_dimension(
2322
+ node.get("unary"), scope_path)
2323
+ if dims is None:
2324
+ return (None, False)
2325
+ # Two shapes: parallel operator/operand lists (visitor form)
2326
+ # or operation entries with nested operator/operand dicts.
2327
+ operator_list = node.get("operator")
2328
+ if isinstance(operator_list, list) and operator_list:
2329
+ operand_list = node.get("operand", []) or []
2330
+ for op, rhs_node in zip(operator_list, operand_list):
2331
+ if op not in ("**", "^"):
2332
+ return (None, False)
2333
+ exponent = const_fold(rhs_node)
2334
+ if not isinstance(exponent, int) \
2335
+ or isinstance(exponent, bool):
2336
+ return (None, False)
2337
+ dims = {k: v * exponent for k, v in dims.items()}
2338
+ return (dims, contributed)
2339
+ for op_dict in node.get("operation", []) or []:
2340
+ if not isinstance(op_dict, dict):
2341
+ continue
2342
+ op = op_dict.get("operator")
2343
+ rhs_node = op_dict.get("operand")
2344
+ if op not in ("**", "^"):
2345
+ return (None, False)
2346
+ exponent = const_fold(rhs_node)
2347
+ if not isinstance(exponent, int) or isinstance(exponent, bool):
2348
+ return (None, False)
2349
+ dims = {k: v * exponent for k, v in dims.items()}
2350
+ return (dims, contributed)
2351
+
2352
+ if name == "UnaryExpression":
2353
+ op = node.get("operator")
2354
+ if op and op not in ("-", "+"):
2355
+ return (None, False) # 'not' etc. — boolean
2356
+ return self._derive_leaf(node.get("extent"), scope_path)
2357
+
2358
+ if name in ("ExtentExpression", "PrimaryExpression", "BaseExpression"):
2359
+ return self._derive_leaf(node, scope_path)
2360
+
2361
+ # Leaf nodes (FeatureReferenceExpression etc.)
2362
+ return self._derive_leaf(node, scope_path)
2363
+
2364
+ def _derive_leaf(self, node: Any, scope_path: list[str]) -> tuple:
2365
+ """Dimension of a leaf operand: literal, typed reference or unwrap."""
2366
+ if not isinstance(node, dict):
2367
+ return (None, False)
2368
+ if node.get("name") in ("LiteralInteger", "LiteralReal",
2369
+ "LiteralInfinity"):
2370
+ return ({}, False)
2371
+ dims = self._dimension_of_operand(node, scope_path)
2372
+ if dims is not None:
2373
+ return (dims, True)
2374
+ # Parenthesized / wrapped operands: descend through wrapper keys
2375
+ for key in ("primary", "base", "expression", "ownedRelationship"):
2376
+ child = node.get(key)
2377
+ if isinstance(child, dict):
2378
+ result = self._derive_dimension(child, scope_path)
2379
+ if result[0] is not None:
2380
+ return result
2381
+ return (None, False)
2382
+
2110
2383
  # -- unit dimension safety ---------------------------------------------
2111
2384
 
2112
2385
  def _check_unit_dimensions(self, op, lhs, rhs, element, scope_path, issues) -> None:
@@ -2500,6 +2773,8 @@ class SemanticAnalyzer:
2500
2773
  # Step 4c: Expression type checking & unit-dimension safety
2501
2774
  # (v0.55.0 Phase C).
2502
2775
  issues.extend(self._check_expression_types(model, symtab, lib_roots))
2776
+ issues.extend(
2777
+ self._check_expression_derivations(model, symtab, lib_roots))
2503
2778
 
2504
2779
  # Step 5: OCL well-formedness constraints
2505
2780
  issues.extend(self._check_duplicate_names(symtab))
@@ -3100,6 +3375,16 @@ class SemanticAnalyzer:
3100
3375
  checker = ExpressionTypeChecker(self, symtab, lib_roots)
3101
3376
  return checker.check_units(model)
3102
3377
 
3378
+ def _check_expression_derivations(
3379
+ self,
3380
+ model: Any,
3381
+ symtab: SymbolTable,
3382
+ lib_roots: list[Path] | None = None,
3383
+ ) -> list[SemanticIssue]:
3384
+ """Derive ``*``/``/`` dimension algebra vs declared typing."""
3385
+ checker = ExpressionTypeChecker(self, symtab, lib_roots)
3386
+ return checker.check_derivations(model)
3387
+
3103
3388
  # -- OCL well-formedness constraints ------------------------------------
3104
3389
 
3105
3390
  def _check_trigger_payloads(
@@ -3560,7 +3845,7 @@ class SemanticAnalyzer:
3560
3845
  return issues
3561
3846
 
3562
3847
  def _check_connector_directions(self, model: Any) -> list[SemanticIssue]:
3563
- """Check connection-end port direction compatibility (Goal 9).
3848
+ """Check connection ends: directions (Goal 9) + types (Goal 10).
3564
3849
 
3565
3850
  ``connection c connect a.p1 to b.p2;`` — when both end ports
3566
3851
  carry explicit directions and neither is ``inout``, wiring
@@ -3570,6 +3855,14 @@ class SemanticAnalyzer:
3570
3855
  error). Ends without a direction keyword, ``inout`` ends,
3571
3856
  chains deeper than two segments and unresolvable parts are
3572
3857
  skipped.
3858
+
3859
+ Goal 10 (connector-end compatibility depth): when both ends
3860
+ resolve to typings that are local ``port def`` names and
3861
+ neither is a (transitive) specialization of the other, the
3862
+ connection flags CONNECTOR_END_TYPE_MISMATCH (warning) —
3863
+ conjugation only makes ports of the *same* (or related) port
3864
+ definition compatible. Ends typed by library/external port
3865
+ definitions are skipped (no local subclass data).
3573
3866
  """
3574
3867
  issues: list[SemanticIssue] = []
3575
3868
  try:
@@ -3582,6 +3875,7 @@ class SemanticAnalyzer:
3582
3875
  part_typing: dict = {} # part usage name -> typed-by name
3583
3876
  member_typing: dict = {} # (container name, member) -> typed-by
3584
3877
  supertypes: dict = {} # def name -> {superclassifier names}
3878
+ port_defs: set = set() # local port def names (Goal 10)
3585
3879
  connections: list = [] # (name, scope_stack, [chain, ...])
3586
3880
 
3587
3881
  def walk2(node, scope_stack):
@@ -3612,6 +3906,8 @@ class SemanticAnalyzer:
3612
3906
  if sups:
3613
3907
  supertypes.setdefault(declared, set()).update(
3614
3908
  sups)
3909
+ if nm == "PortDefinition":
3910
+ port_defs.add(declared)
3615
3911
  if nm == "PortUsage":
3616
3912
  d = _port_direction(node)
3617
3913
  if scope_stack:
@@ -3715,6 +4011,55 @@ class SemanticAnalyzer:
3715
4011
  return None, False # known container, no such port
3716
4012
  return None, True # external/library type: skip
3717
4013
 
4014
+ def end_typing(chain, scope_stack):
4015
+ """Typed-by name of an end chain's final segment, or None."""
4016
+ if not chain:
4017
+ return None
4018
+ if len(chain) == 1:
4019
+ name = chain[0]
4020
+ for scope in reversed(scope_stack):
4021
+ t = member_typing.get((scope, name))
4022
+ if t:
4023
+ return t
4024
+ return part_typing.get(name)
4025
+ first = chain[0]
4026
+ cur = part_typing.get(first)
4027
+ if cur is None:
4028
+ for scope in reversed(scope_stack):
4029
+ t = member_typing.get((scope, first))
4030
+ if t:
4031
+ cur = t
4032
+ break
4033
+ if cur is None:
4034
+ return None
4035
+ for seg in chain[1:-1]:
4036
+ t = member_typing.get((cur, seg))
4037
+ if t is None:
4038
+ t = member_typing.get((first, seg))
4039
+ if t is None:
4040
+ return None
4041
+ cur = t
4042
+ t = member_typing.get((cur, chain[-1]))
4043
+ if t is None:
4044
+ t = member_typing.get((first, chain[-1]))
4045
+ return t
4046
+
4047
+ def _sub(a, b):
4048
+ """True when a is b or a (transitively) specializes b."""
4049
+ if a == b:
4050
+ return True
4051
+ seen = set()
4052
+ stack = [a]
4053
+ while stack:
4054
+ cur = stack.pop()
4055
+ if cur == b:
4056
+ return True
4057
+ if cur in seen:
4058
+ continue
4059
+ seen.add(cur)
4060
+ stack.extend(supertypes.get(cur, ()))
4061
+ return False
4062
+
3718
4063
  for cname, cstack, chains in connections:
3719
4064
  d1, r1 = end_resolve(chains[0], cstack)
3720
4065
  d2, r2 = end_resolve(chains[1], cstack)
@@ -3736,6 +4081,22 @@ class SemanticAnalyzer:
3736
4081
  f"'{'.'.join(chains[1])}' does not resolve"),
3737
4082
  reference=cname,
3738
4083
  ))
4084
+ # Goal 10: end-type compatibility (port defs, local only)
4085
+ t1 = end_typing(chains[0], cstack)
4086
+ t2 = end_typing(chains[1], cstack)
4087
+ if (t1 and t2 and t1 in port_defs and t2 in port_defs
4088
+ and not _sub(t1, t2) and not _sub(t2, t1)):
4089
+ issues.append(SemanticIssue(
4090
+ severity="warning",
4091
+ code="CONNECTOR_END_TYPE_MISMATCH",
4092
+ message=(
4093
+ f"Connection '{cname}' binds "
4094
+ f"'{'.'.join(chains[0])}' (typed '{t1}') to "
4095
+ f"'{'.'.join(chains[1])}' (typed '{t2}'); "
4096
+ "connected port definitions are unrelated"
4097
+ ),
4098
+ reference=cname,
4099
+ ))
3739
4100
  if not d1 or not d2:
3740
4101
  continue
3741
4102
  if d1 == "inout" or d2 == "inout":
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes