sysmlpy 0.95.0__tar.gz → 0.96.1__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 (177) hide show
  1. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/PKG-INFO +111 -3
  2. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/README.md +109 -2
  3. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/pyproject.toml +1 -1
  4. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/__init__.py +89 -2
  5. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr_visitor.py +27 -0
  6. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/definition.py +39 -0
  7. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/dfa_cache.py +14 -1
  8. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/classes.py +27 -2
  9. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLLexer.g4 +11 -0
  10. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLLexer.interp +482 -0
  11. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLLexer.py +751 -0
  12. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLLexer.tokens +302 -0
  13. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLParser.g4 +16 -0
  14. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLParser.interp +519 -0
  15. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLParser.py +15048 -0
  16. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLParser.tokens +305 -0
  17. sysmlpy-0.96.1/src/sysmlpy/kerml/KerMLParserVisitor.py +973 -0
  18. sysmlpy-0.96.1/src/sysmlpy/kerml/README.md +45 -0
  19. sysmlpy-0.96.1/src/sysmlpy/kerml/__init__.py +18 -0
  20. sysmlpy-0.96.1/src/sysmlpy/kerml/kerml.py +117 -0
  21. sysmlpy-0.96.1/src/sysmlpy/kerml/kerml_visitor.py +482 -0
  22. sysmlpy-0.96.1/src/sysmlpy/mdtables.py +243 -0
  23. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/plantuml.py +249 -15
  24. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/semantic.py +90 -1
  25. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/traceability.py +4 -2
  26. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/usage.py +312 -5
  27. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/LICENSE +0 -0
  28. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/__main__.py +0 -0
  29. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/README.md +0 -0
  30. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2Lexer.interp +0 -0
  31. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2Lexer.py +0 -0
  32. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2Lexer.tokens +0 -0
  33. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2Parser.interp +0 -0
  34. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2Parser.py +0 -0
  35. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2Parser.tokens +0 -0
  36. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2ParserListener.py +0 -0
  37. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/SysMLv2ParserVisitor.py +0 -0
  38. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
  39. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
  40. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
  41. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/antlr_parser.py +0 -0
  42. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/boxes_view.py +0 -0
  43. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/diff.py +0 -0
  44. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/evaluator.py +0 -0
  45. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/examples/attribute_values.py +0 -0
  46. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/examples/part_attributes.py +0 -0
  47. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/examples/tuples_sequences.py +0 -0
  48. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/formatting.py +0 -0
  49. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/README.md +0 -0
  50. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.g4 +0 -0
  51. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.interp +0 -0
  52. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.py +0 -0
  53. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Lexer.tokens +0 -0
  54. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.g4 +0 -0
  55. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.interp +0 -0
  56. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.py +0 -0
  57. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2Parser.tokens +0 -0
  58. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2ParserListener.py +0 -0
  59. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/SysMLv2ParserVisitor.py +0 -0
  60. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/desc.xml +0 -0
  61. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/examples/camera.sysml +0 -0
  62. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/examples/toaster-system.sysml +0 -0
  63. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/examples/vehicle-model.sysml +0 -0
  64. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/grammar/antlr4/pom.xml +0 -0
  65. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/interchange.py +0 -0
  66. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/ipython_magic.py +0 -0
  67. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/LICENSE +0 -0
  68. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/README.md +0 -0
  69. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/__init__.py +0 -0
  70. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Analysis/AnalysisTooling.sysml +0 -0
  71. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Analysis/SampledFunctions.sysml +0 -0
  72. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Analysis/StateSpaceRepresentation.sysml +0 -0
  73. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Analysis/TradeStudies.sysml +0 -0
  74. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Cause and Effect/CausationConnections.sysml +0 -0
  75. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Cause and Effect/CauseAndEffect.sysml +0 -0
  76. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Geometry/ShapeItems.sysml +0 -0
  77. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Geometry/SpatialItems.sysml +0 -0
  78. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Metadata/ImageMetadata.sysml +0 -0
  79. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Metadata/ModelingMetadata.sysml +0 -0
  80. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Metadata/ParametersOfInterestMetadata.sysml +0 -0
  81. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Metadata/RiskMetadata.sysml +0 -0
  82. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQ.sysml +0 -0
  83. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQAcoustics.sysml +0 -0
  84. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQAtomicNuclear.sysml +0 -0
  85. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQBase.sysml +0 -0
  86. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQCharacteristicNumbers.sysml +0 -0
  87. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQChemistryMolecular.sysml +0 -0
  88. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQCondensedMatter.sysml +0 -0
  89. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQElectromagnetism.sysml +0 -0
  90. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQInformation.sysml +0 -0
  91. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQLight.sysml +0 -0
  92. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQMechanics.sysml +0 -0
  93. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQSpaceTime.sysml +0 -0
  94. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/ISQThermodynamics.sysml +0 -0
  95. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/MeasurementRefCalculations.sysml +0 -0
  96. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/MeasurementReferences.sysml +0 -0
  97. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/Quantities.sysml +0 -0
  98. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/QuantityCalculations.sysml +0 -0
  99. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/SI.sysml +0 -0
  100. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/SIPrefixes.sysml +0 -0
  101. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/TensorCalculations.sysml +0 -0
  102. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/Time.sysml +0 -0
  103. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/USCustomaryUnits.sysml +0 -0
  104. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Quantities and Units/VectorCalculations.sysml +0 -0
  105. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Requirement Derivation/DerivationConnections.sysml +0 -0
  106. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/domain/Requirement Derivation/RequirementDerivation.sysml +0 -0
  107. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Base.kerml +0 -0
  108. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/BaseFunctions.kerml +0 -0
  109. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/BooleanFunctions.kerml +0 -0
  110. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Clocks.kerml +0 -0
  111. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/CollectionFunctions.kerml +0 -0
  112. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Collections.kerml +0 -0
  113. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/ComplexFunctions.kerml +0 -0
  114. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/ControlFunctions.kerml +0 -0
  115. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/ControlPerformances.kerml +0 -0
  116. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/DataFunctions.kerml +0 -0
  117. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/FeatureReferencingPerformances.kerml +0 -0
  118. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/IntegerFunctions.kerml +0 -0
  119. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/KerML.kerml +0 -0
  120. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Links.kerml +0 -0
  121. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Metaobjects.kerml +0 -0
  122. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/NaturalFunctions.kerml +0 -0
  123. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/NumericalFunctions.kerml +0 -0
  124. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Objects.kerml +0 -0
  125. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Observation.kerml +0 -0
  126. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/OccurrenceFunctions.kerml +0 -0
  127. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Occurrences.kerml +0 -0
  128. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Performances.kerml +0 -0
  129. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/RationalFunctions.kerml +0 -0
  130. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/RealFunctions.kerml +0 -0
  131. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/ScalarFunctions.kerml +0 -0
  132. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/ScalarValues.kerml +0 -0
  133. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/SequenceFunctions.kerml +0 -0
  134. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/SpatialFrames.kerml +0 -0
  135. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/StatePerformances.kerml +0 -0
  136. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/StringFunctions.kerml +0 -0
  137. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Transfers.kerml +0 -0
  138. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/TransitionPerformances.kerml +0 -0
  139. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/TrigFunctions.kerml +0 -0
  140. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/Triggers.kerml +0 -0
  141. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/VectorFunctions.kerml +0 -0
  142. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/kernel/VectorValues.kerml +0 -0
  143. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Actions.sysml +0 -0
  144. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Allocations.sysml +0 -0
  145. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/AnalysisCases.sysml +0 -0
  146. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Attributes.sysml +0 -0
  147. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Calculations.sysml +0 -0
  148. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Cases.sysml +0 -0
  149. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Connections.sysml +0 -0
  150. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Constraints.sysml +0 -0
  151. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Flows.sysml +0 -0
  152. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Interfaces.sysml +0 -0
  153. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Items.sysml +0 -0
  154. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Metadata.sysml +0 -0
  155. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Parts.sysml +0 -0
  156. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Ports.sysml +0 -0
  157. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Requirements.sysml +0 -0
  158. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/StandardViewDefinitions.sysml +0 -0
  159. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/States.sysml +0 -0
  160. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/SysML.sysml +0 -0
  161. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/UseCases.sysml +0 -0
  162. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/VerificationCases.sysml +0 -0
  163. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/library/systems/Views.sysml +0 -0
  164. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/lsp/__init__.py +0 -0
  165. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/lsp/__main__.py +0 -0
  166. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/lsp/protocol.py +0 -0
  167. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/lsp/server.py +0 -0
  168. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/lsp/stdio.py +0 -0
  169. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/navigate.py +0 -0
  170. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/project.py +0 -0
  171. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/repl.py +0 -0
  172. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/reqif_io.py +0 -0
  173. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/sim.py +0 -0
  174. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/spreadsheet.py +0 -0
  175. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/store.py +0 -0
  176. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/us_customary_units.txt +0 -0
  177. {sysmlpy-0.95.0 → sysmlpy-0.96.1}/src/sysmlpy/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sysmlpy
3
- Version: 0.95.0
3
+ Version: 0.96.1
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: Jon R. Fox (mycr0ft)
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: Python :: 3.15
16
17
  Provides-Extra: cayley
17
18
  Provides-Extra: graph
18
19
  Provides-Extra: jupyter
@@ -136,6 +137,40 @@ and the mapping to this implementation — is in the
136
137
  [sysml-copier](https://github.com/mycr0ft/sysml-copier) template's
137
138
  `docs/sysml-magics.md`.
138
139
 
140
+ ## Interactive REPL
141
+
142
+ `sysmlpy repl [FILE ...]` is a terminal session in the same spirit: each
143
+ submission is a SysML declaration, an expression, or a `%command`, and
144
+ declarations accumulate into a session model with the same
145
+ member-granularity merge as the Jupyter magics (a re-declared member
146
+ replaces its prior definition, sibling members are kept, and a
147
+ `note:` line says so). Submissions continue onto a `...>` line while
148
+ brackets stay open; a line that is not a declaration evaluates as an
149
+ expression. Ctrl-D or `%quit` exits; `%reset` discards the session.
150
+
151
+ | Command | Purpose |
152
+ |---------|---------|
153
+ | `%list [NAME]` | Packages (with member counts), or elements matching NAME with qualified names |
154
+ | `%show NAME` | `repr()` of the first element named NAME |
155
+ | `%dump` | Round-tripped SysML text of the session model |
156
+ | `%eval EXPR` | Evaluate an expression against the session model |
157
+ | `%set NAME=VALUE` | Bind a what-if value (number / bool / string / unit) |
158
+ | `%bindings` | Show current what-if bindings |
159
+ | `%calc NAME a, b` | Invoke a calc def with positional literal arguments |
160
+ | `%check` | Check the model's constraints (pass/fail report) |
161
+ | `%values` | Collected attribute values |
162
+ | `%sim [FOCUS]` | Start a simulator session on a state machine |
163
+ | `%send TRIGGER` / `%step` / `%state` | Drive / advance / inspect that session |
164
+ | `%view NAME [KIND]` | Render a view: `gv` `pkg` `afv` `iv` `stv` `tab` `dvt` `matrix` |
165
+ | `%load PATH` / `%save PATH` | Merge a file into the session / write the session out |
166
+ | `%reset` / `%quit` | Discard everything / leave the REPL |
167
+ | `%help` | Command summary |
168
+
169
+ Files given on the command line (`sysmlpy repl model.sysml`) are loaded
170
+ into the session before the first prompt. The session core is the same
171
+ Python API surface used throughout the library, so anything you build in
172
+ a REPL session maps one-to-one onto a `ReplSession` scripted from Python.
173
+
139
174
  ## Command-Line Tools
140
175
 
141
176
  sysmlpy ships a CLI (`sysmlpy`, plus `sysmlpy-lsp` for editor support).
@@ -404,7 +439,79 @@ tree = classtree(model)
404
439
  print(tree.dump())
405
440
  ```
406
441
 
407
- **All 143 grammar round-trip tests pass** (100%). Covered categories: packages, parts, items, ports, interfaces, binding connectors, flow connections, all action forms (definition, shorthand, succession, decomposition), expressions, calculations, constraints, state definitions, requirements, analysis cases, control flow (if/else, while, loop, fork, join, decision, send, accept, terminate), trade studies, views, viewpoints, render states, portion usages, and annotations.
442
+ **All 168 grammar round-trip tests pass** (100%). Covered categories: packages, parts, items, ports, interfaces, binding connectors, flow connections, all action forms (definition, shorthand, succession, decomposition), expressions, calculations, constraints, state definitions, requirements, analysis cases, control flow (if/else, while, loop, fork, join, decision, send, accept, terminate), trade studies, views, viewpoints, render states, portion usages, and annotations.
443
+
444
+ ### Doc comments, interface ends, and flows (v0.96.1)
445
+
446
+ These constructs are easy to lose between the parser and the API — v0.96.1 makes them first-class and round-trip-safe:
447
+
448
+ - **`doc /* ... */` comments** — on a package, on any part/item/port/action/... usage or definition, and inside interface bodies — are captured as a `.doc` attribute (a plain string) and re-emitted by `dump()`/`classtree()`, even when the comment shares its body with ports, attributes, or other members.
449
+ - **Interface ends** — `end <name>;` and `end <name> ::> part.port;` members of an interface body — are captured as `Interface.ends` (name, type, multiplicity tuples) and `Interface.iface_connections` (`::>` target paths), and survive a dump round-trip.
450
+ - **`flow <source> to <target>;`** — parses into a `Flow` child of the owning part, re-emits in the dump, and renders as a flow edge in the Interconnection View.
451
+
452
+ ```python
453
+ from sysmlpy import loads, as_interconnection_view
454
+ from sysmlpy.formatting import classtree
455
+
456
+ text = """package WaterSystem {
457
+ doc /* package doc: the coffee water loop */
458
+
459
+ part def WaterTank {
460
+ doc /* stores the water */
461
+ port waterOut;
462
+ }
463
+
464
+ part def Brewer {
465
+ doc /* heats and brews */
466
+ port waterInlet;
467
+ }
468
+
469
+ interface def WaterSupply {
470
+ doc /* the supply contract */
471
+ end supplierPort;
472
+ end consumerPort;
473
+ }
474
+
475
+ part def CoffeeMachine {
476
+ doc /* makes coffee */
477
+ part waterTank : WaterTank;
478
+ part brewer : Brewer;
479
+
480
+ interface waterLine : WaterSupply {
481
+ doc /* internal plumbing */
482
+ end supplierPort ::> waterTank.waterOut;
483
+ end consumerPort ::> brewer.waterInlet;
484
+ }
485
+
486
+ flow waterTank.waterOut to brewer.waterInlet;
487
+ }
488
+ }"""
489
+
490
+ model = loads(text)
491
+ package = model.children[0]
492
+
493
+ # doc text on every element
494
+ assert package.doc == "package doc: the coffee water loop"
495
+ tank = package.find("WaterTank")[0]
496
+ assert tank.doc == "stores the water"
497
+
498
+ # interface ends: names and ::> targets
499
+ machine = package.find("CoffeeMachine")[0]
500
+ water_line = next(c for c in machine.children if type(c).__name__ == "Interface")
501
+ assert [e[0] for e in water_line.ends] == ["supplierPort", "consumerPort"]
502
+ assert water_line.iface_connections == [
503
+ ("supplierPort", "waterTank.waterOut"),
504
+ ("consumerPort", "brewer.waterInlet"),
505
+ ]
506
+
507
+ # flows show up in the Interconnection View
508
+ assert "flow" in as_interconnection_view(model)
509
+
510
+ # and everything survives the dump round-trip (docs included)
511
+ assert "stores the water" in classtree(model).dump()
512
+ ```
513
+
514
+ See `examples/doc_ends_flows.py` for the full walk through the parse → grammar → API layers.
408
515
 
409
516
  ## Semantic Analysis
410
517
 
@@ -458,7 +565,8 @@ result = analyze(model, strict=True)
458
565
  - **Undefined symbol detection** — catches references to non-existent types, features, and packages
459
566
  - **Qualified name resolution** — `P::A` and `Outer::Inner::DeepPart` resolve through scope chains
460
567
  - **Inheritance resolution** — subsetting/redefinition references resolve through supertype chains
461
- - **Library symbol index** — scans 88 `.kerml`/`.sysml` files (~1,417 symbols) from the bundled standard library
568
+ - **KerML parser** — OMG-KEBNF-generated ANTLR4 grammar for the Kernel Modeling Language (`sysmlpy.kerml`): `parse` / `parse_file` / `parse_to_dict`; all 130 `.kerml` corpus files (OMG examples + standard libraries) parse, and the library symbol index is backed by real parses (2,986 symbols)
569
+ - **Library symbol index** — scans 94 files (36 parsed `.kerml` + 58 `.sysml`) from the bundled standard library (~2,986 symbols)
462
570
 
463
571
  ### Import Resolution
464
572
 
@@ -101,6 +101,40 @@ and the mapping to this implementation — is in the
101
101
  [sysml-copier](https://github.com/mycr0ft/sysml-copier) template's
102
102
  `docs/sysml-magics.md`.
103
103
 
104
+ ## Interactive REPL
105
+
106
+ `sysmlpy repl [FILE ...]` is a terminal session in the same spirit: each
107
+ submission is a SysML declaration, an expression, or a `%command`, and
108
+ declarations accumulate into a session model with the same
109
+ member-granularity merge as the Jupyter magics (a re-declared member
110
+ replaces its prior definition, sibling members are kept, and a
111
+ `note:` line says so). Submissions continue onto a `...>` line while
112
+ brackets stay open; a line that is not a declaration evaluates as an
113
+ expression. Ctrl-D or `%quit` exits; `%reset` discards the session.
114
+
115
+ | Command | Purpose |
116
+ |---------|---------|
117
+ | `%list [NAME]` | Packages (with member counts), or elements matching NAME with qualified names |
118
+ | `%show NAME` | `repr()` of the first element named NAME |
119
+ | `%dump` | Round-tripped SysML text of the session model |
120
+ | `%eval EXPR` | Evaluate an expression against the session model |
121
+ | `%set NAME=VALUE` | Bind a what-if value (number / bool / string / unit) |
122
+ | `%bindings` | Show current what-if bindings |
123
+ | `%calc NAME a, b` | Invoke a calc def with positional literal arguments |
124
+ | `%check` | Check the model's constraints (pass/fail report) |
125
+ | `%values` | Collected attribute values |
126
+ | `%sim [FOCUS]` | Start a simulator session on a state machine |
127
+ | `%send TRIGGER` / `%step` / `%state` | Drive / advance / inspect that session |
128
+ | `%view NAME [KIND]` | Render a view: `gv` `pkg` `afv` `iv` `stv` `tab` `dvt` `matrix` |
129
+ | `%load PATH` / `%save PATH` | Merge a file into the session / write the session out |
130
+ | `%reset` / `%quit` | Discard everything / leave the REPL |
131
+ | `%help` | Command summary |
132
+
133
+ Files given on the command line (`sysmlpy repl model.sysml`) are loaded
134
+ into the session before the first prompt. The session core is the same
135
+ Python API surface used throughout the library, so anything you build in
136
+ a REPL session maps one-to-one onto a `ReplSession` scripted from Python.
137
+
104
138
  ## Command-Line Tools
105
139
 
106
140
  sysmlpy ships a CLI (`sysmlpy`, plus `sysmlpy-lsp` for editor support).
@@ -369,7 +403,79 @@ tree = classtree(model)
369
403
  print(tree.dump())
370
404
  ```
371
405
 
372
- **All 143 grammar round-trip tests pass** (100%). Covered categories: packages, parts, items, ports, interfaces, binding connectors, flow connections, all action forms (definition, shorthand, succession, decomposition), expressions, calculations, constraints, state definitions, requirements, analysis cases, control flow (if/else, while, loop, fork, join, decision, send, accept, terminate), trade studies, views, viewpoints, render states, portion usages, and annotations.
406
+ **All 168 grammar round-trip tests pass** (100%). Covered categories: packages, parts, items, ports, interfaces, binding connectors, flow connections, all action forms (definition, shorthand, succession, decomposition), expressions, calculations, constraints, state definitions, requirements, analysis cases, control flow (if/else, while, loop, fork, join, decision, send, accept, terminate), trade studies, views, viewpoints, render states, portion usages, and annotations.
407
+
408
+ ### Doc comments, interface ends, and flows (v0.96.1)
409
+
410
+ These constructs are easy to lose between the parser and the API — v0.96.1 makes them first-class and round-trip-safe:
411
+
412
+ - **`doc /* ... */` comments** — on a package, on any part/item/port/action/... usage or definition, and inside interface bodies — are captured as a `.doc` attribute (a plain string) and re-emitted by `dump()`/`classtree()`, even when the comment shares its body with ports, attributes, or other members.
413
+ - **Interface ends** — `end <name>;` and `end <name> ::> part.port;` members of an interface body — are captured as `Interface.ends` (name, type, multiplicity tuples) and `Interface.iface_connections` (`::>` target paths), and survive a dump round-trip.
414
+ - **`flow <source> to <target>;`** — parses into a `Flow` child of the owning part, re-emits in the dump, and renders as a flow edge in the Interconnection View.
415
+
416
+ ```python
417
+ from sysmlpy import loads, as_interconnection_view
418
+ from sysmlpy.formatting import classtree
419
+
420
+ text = """package WaterSystem {
421
+ doc /* package doc: the coffee water loop */
422
+
423
+ part def WaterTank {
424
+ doc /* stores the water */
425
+ port waterOut;
426
+ }
427
+
428
+ part def Brewer {
429
+ doc /* heats and brews */
430
+ port waterInlet;
431
+ }
432
+
433
+ interface def WaterSupply {
434
+ doc /* the supply contract */
435
+ end supplierPort;
436
+ end consumerPort;
437
+ }
438
+
439
+ part def CoffeeMachine {
440
+ doc /* makes coffee */
441
+ part waterTank : WaterTank;
442
+ part brewer : Brewer;
443
+
444
+ interface waterLine : WaterSupply {
445
+ doc /* internal plumbing */
446
+ end supplierPort ::> waterTank.waterOut;
447
+ end consumerPort ::> brewer.waterInlet;
448
+ }
449
+
450
+ flow waterTank.waterOut to brewer.waterInlet;
451
+ }
452
+ }"""
453
+
454
+ model = loads(text)
455
+ package = model.children[0]
456
+
457
+ # doc text on every element
458
+ assert package.doc == "package doc: the coffee water loop"
459
+ tank = package.find("WaterTank")[0]
460
+ assert tank.doc == "stores the water"
461
+
462
+ # interface ends: names and ::> targets
463
+ machine = package.find("CoffeeMachine")[0]
464
+ water_line = next(c for c in machine.children if type(c).__name__ == "Interface")
465
+ assert [e[0] for e in water_line.ends] == ["supplierPort", "consumerPort"]
466
+ assert water_line.iface_connections == [
467
+ ("supplierPort", "waterTank.waterOut"),
468
+ ("consumerPort", "brewer.waterInlet"),
469
+ ]
470
+
471
+ # flows show up in the Interconnection View
472
+ assert "flow" in as_interconnection_view(model)
473
+
474
+ # and everything survives the dump round-trip (docs included)
475
+ assert "stores the water" in classtree(model).dump()
476
+ ```
477
+
478
+ See `examples/doc_ends_flows.py` for the full walk through the parse → grammar → API layers.
373
479
 
374
480
  ## Semantic Analysis
375
481
 
@@ -423,7 +529,8 @@ result = analyze(model, strict=True)
423
529
  - **Undefined symbol detection** — catches references to non-existent types, features, and packages
424
530
  - **Qualified name resolution** — `P::A` and `Outer::Inner::DeepPart` resolve through scope chains
425
531
  - **Inheritance resolution** — subsetting/redefinition references resolve through supertype chains
426
- - **Library symbol index** — scans 88 `.kerml`/`.sysml` files (~1,417 symbols) from the bundled standard library
532
+ - **KerML parser** — OMG-KEBNF-generated ANTLR4 grammar for the Kernel Modeling Language (`sysmlpy.kerml`): `parse` / `parse_file` / `parse_to_dict`; all 130 `.kerml` corpus files (OMG examples + standard libraries) parse, and the library symbol index is backed by real parses (2,986 symbols)
533
+ - **Library symbol index** — scans 94 files (36 parsed `.kerml` + 58 `.sysml`) from the bundled standard library (~2,986 symbols)
427
534
 
428
535
  ### Import Resolution
429
536
 
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
6
6
 
7
7
  [project]
8
8
  name = "sysmlpy"
9
- version = "0.95.0"
9
+ version = "0.96.1"
10
10
  description = ""
11
11
  authors = [{ name = "Jon R. Fox (mycr0ft)", email = "jon.fox@drfox.com" }]
12
12
  readme = "README.md"
@@ -8,7 +8,7 @@ Uses the ANTLR4 parser for full SysML v2 grammar support.
8
8
  """
9
9
 
10
10
  __all__ = [
11
- "load", "loads", "parse", "load_grammar", "load_antlr", "load_grammar_antlr",
11
+ "load", "loads", "load_wrapped", "loads_wrapped", "parse", "load_grammar", "load_antlr", "load_grammar_antlr",
12
12
  "load_files", "load_project", "load_with_dependencies",
13
13
  "Searchable",
14
14
  "Store", "InMemoryStore", "NetworkXStore", "KuzuStore", "CayleyStore", "create_store", "new_id",
@@ -35,7 +35,7 @@ __all__ = [
35
35
  "render_state_transition_view_svg", "boxes_view",
36
36
  ]
37
37
  __author__ = "Jon Fox"
38
- __version__ = "0.95.0"
38
+ __version__ = "0.96.1"
39
39
 
40
40
  from sysmlpy.usage import (
41
41
  Item, Attribute, Part, Port, Action, Reference, UseCase, Requirement, Interface, Message,
@@ -311,6 +311,93 @@ def loads(s: str, library=None, rescue_language="English") -> Model:
311
311
  rescue_language=rescue_language)
312
312
 
313
313
 
314
+ def _is_bare_top_level(s: str) -> bool:
315
+ """True when *s* starts with a top-level definition/usage keyword
316
+ rather than a (library) package — the same textual probe
317
+ :meth:`sysmlpy.definition.Model.load` uses for its strict guard."""
318
+ s_stripped = s.strip() if isinstance(s, str) else s
319
+ if not isinstance(s_stripped, str) or not s_stripped.split():
320
+ return False
321
+ invalid_starts = ['item', 'part', 'port', 'attribute', 'action', 'state',
322
+ 'connection', 'interface', 'requirement', 'case', 'use',
323
+ 'calc', 'constraint', 'concern', 'ref', 'flow', 'allocation',
324
+ 'view', 'viewpoint', 'rendering', 'metadata', 'individual']
325
+ return s_stripped.split(None, 1)[0] in invalid_starts
326
+
327
+
328
+ def loads_wrapped(s: str, package_name="Snippets", library=None,
329
+ rescue_language="English") -> Model:
330
+ """Load bare top-level SysML (no enclosing package) into a Model by
331
+ wrapping it in a synthetic package.
332
+
333
+ OMG example and training snippets frequently open directly with
334
+ ``part def Camera { ... }`` or ``action def DecisionTest { ... }``.
335
+ :func:`loads` / ``Model.load`` deliberately reject that form ("Base
336
+ Model must be encapsulated by a package"). This loader accepts it:
337
+ the source is wrapped as ``package <package_name> { ... }`` and then
338
+ parsed exactly like :func:`loads`. Content already carrying a
339
+ top-level package (or ``standard library package``) passes through
340
+ untouched, so the function is safe to call unconditionally on file
341
+ text.
342
+
343
+ Parameters
344
+ ----------
345
+ s : str
346
+ The SysML v2 source code to parse.
347
+ package_name : str, optional
348
+ Name of the synthetic package. Defaults to ``"Snippets"``.
349
+ library : str or Path, optional
350
+ Path to SysML v2 library files for resolving imports.
351
+
352
+ Returns
353
+ -------
354
+ Model
355
+ Model instance with one child: the synthetic package.
356
+
357
+ .. versionadded:: 0.96.1
358
+ """
359
+ if _is_bare_top_level(s):
360
+ s = "package %s {\n%s\n}\n" % (package_name, s.rstrip("\n"))
361
+ return loads(s, library=library, rescue_language=rescue_language)
362
+
363
+
364
+ def load_wrapped(fp, package_name="Snippets", library=None) -> Model:
365
+ """File-pointer twin of :func:`loads_wrapped`.
366
+
367
+ Deserialize ``fp`` (a ``.read()``-supporting file-like object) —
368
+ wrapping bare top-level definitions in a synthetic package when
369
+ needed — to a Model object. Content with a top-level package passes
370
+ through untouched.
371
+
372
+ Parameters
373
+ ----------
374
+ fp : _io.TextIOWrapper
375
+ File pointer to a SysML v2.0 document.
376
+ package_name : str, optional
377
+ Name of the synthetic package. Defaults to ``"Snippets"``.
378
+ library : str or Path, optional
379
+ Path to SysML v2 library files for resolving imports.
380
+
381
+ Returns
382
+ -------
383
+ Model
384
+ Model instance structured utilizing SysML v2.0 grammar.
385
+
386
+ Raises
387
+ ------
388
+ TypeError
389
+ Input was not a ``.read()``-supporting file-like object.
390
+
391
+ .. versionadded:: 0.96.1
392
+ """
393
+ if not hasattr(fp, "read"):
394
+ raise TypeError(
395
+ f"the SysML object must be a file-like object, "
396
+ f"not {fp.__class__.__name__}"
397
+ )
398
+ return loads_wrapped(fp.read(), package_name=package_name, library=library)
399
+
400
+
314
401
  def parse(s: str, library=None):
315
402
  """Parse SysML source, returning (model, errors) rather than raising.
316
403
 
@@ -7321,6 +7321,20 @@ def _visit_multiplicity(mult_ctx):
7321
7321
  if mult_ctx is None:
7322
7322
  return result
7323
7323
 
7324
+ # OwnedCrossMultiplicityMember carries an OwnedCrossMultiplicity
7325
+ # wrapper (KerML OwnedCrossMultiplicity → OwnedMultiplicity); read
7326
+ # through it when present (interface ends: `connect [1] a to b`)
7327
+ if hasattr(mult_ctx, 'ownedCrossMultiplicity'):
7328
+ ocm = mult_ctx.ownedCrossMultiplicity()
7329
+ if ocm:
7330
+ om = ocm.ownedMultiplicity()
7331
+ if isinstance(om, list):
7332
+ om = om[0] if om else None
7333
+ if om:
7334
+ built = _extract_multiplicity_from_ctx(om)
7335
+ if built:
7336
+ return built
7337
+
7324
7338
  if hasattr(mult_ctx, 'ownedRelatedElement') and mult_ctx.ownedRelatedElement():
7325
7339
  related = mult_ctx.ownedRelatedElement()
7326
7340
  if isinstance(related, list):
@@ -10208,6 +10222,19 @@ def _visit_definition_body_item_dict(item_ctx, is_interface=False):
10208
10222
  inner_element = _visit_nested_non_occurrence_usage(non_occ)
10209
10223
  wrapper = "NonOccurrenceUsageMember"
10210
10224
 
10225
+ # v0.96.1: a bare ``doc /* ... */`` member parses as
10226
+ # DefinitionMemberContext (MemberPrefix + DefinitionElement ->
10227
+ # AnnotatingElement -> Documentation) with NO
10228
+ # definitionBodyItemContent — handle it directly (previously the
10229
+ # doc member was dropped when it shared its body with siblings).
10230
+ if not inner_element and hasattr(item_ctx, 'definitionMember') and item_ctx.definitionMember():
10231
+ member_ctx = item_ctx.definitionMember()
10232
+ element_ctx = member_ctx.definitionElement()
10233
+ if element_ctx is not None:
10234
+ ann_dict = _visit_nested_definition_element(element_ctx)
10235
+ if ann_dict is not None and ann_dict.get("name") == "PackageMember":
10236
+ inner_element = ann_dict.get("ownedRelatedElement", {})
10237
+ wrapper = "DefinitionMember"
10211
10238
  # Check for definitionBodyItemContent
10212
10239
  if not inner_element and hasattr(item_ctx, 'definitionBodyItemContent') and item_ctx.definitionBodyItemContent():
10213
10240
  content = item_ctx.definitionBodyItemContent()
@@ -26,6 +26,7 @@ from sysmlpy.usage import (
26
26
  Allocation, Metadata, Rendering, Individual, FlowDef,
27
27
  View, Viewpoint, Concern, Reference,
28
28
  )
29
+ from sysmlpy.usage import _comment_body_to_text as _comment_body_to_text_package
29
30
 
30
31
  ModelType = TypeVar("Model", bound="Model")
31
32
 
@@ -565,6 +566,8 @@ class Package(Searchable):
565
566
  self.typedby = None
566
567
  self.grammar = PackageGrammar()
567
568
  self.parent = None
569
+ # v0.96.1: doc text from a package-level ``doc /* ... */`` comment.
570
+ self.doc = None
568
571
 
569
572
  if name is not None:
570
573
  self._set_name(name)
@@ -708,6 +711,33 @@ class Package(Searchable):
708
711
  else:
709
712
  child_defs.append(PackageMember(v).get_definition())
710
713
 
714
+ # v0.96.1: keep the package-level doc comment in the rebuilt
715
+ # body — captured as ``self.doc`` at load time (the doc member
716
+ # has no public-API child to re-serialize, so emit it directly).
717
+ if getattr(self, 'doc', None):
718
+ doc_dict = {
719
+ "name": "Documentation",
720
+ "body": "/* %s */" % self.doc,
721
+ "identification": None,
722
+ "ownedRelationship": [],
723
+ }
724
+ child_defs.append({
725
+ "name": "PackageMember",
726
+ "prefix": None,
727
+ "ownedRelatedElement": {
728
+ "name": "DefinitionElement",
729
+ "ownedRelatedElement": {
730
+ "name": "AnnotatingElement",
731
+ "ownedRelatedElement": {
732
+ "name": "Documentation",
733
+ "body": "/* %s */" % self.doc,
734
+ "identification": None,
735
+ "ownedRelationship": [],
736
+ },
737
+ },
738
+ },
739
+ })
740
+
711
741
  body_children = getattr(
712
742
  getattr(getattr(self, 'grammar', None), 'body', None), 'children', None
713
743
  )
@@ -1311,6 +1341,15 @@ class Package(Searchable):
1311
1341
  mf = getattr(inner_element, 'children', None)
1312
1342
  if mf is not None and mf.__class__.__name__ == "MetadataFeature":
1313
1343
  self._add_metadata_child(mf)
1344
+ elif mf is not None and mf.__class__.__name__ in (
1345
+ "Documentation", "CommentSysML"):
1346
+ # v0.96.1: package-level ``doc /* ... */`` — capture
1347
+ # the text as ``self.doc`` (previously silently
1348
+ # dropped).
1349
+ text = _comment_body_to_text_package(
1350
+ getattr(mf, 'body', ''))
1351
+ if text:
1352
+ self.doc = text
1314
1353
  else:
1315
1354
  print(f"[Package.load_from_grammar] Unknown class: {inner_class} - skipping") # pragma: no cover
1316
1355
  elif inner_class == "NonOccurrenceUsageElement":
@@ -50,6 +50,7 @@ _state: Dict[str, Any] = {
50
50
  "directory": None, # None = default (env/XDG/~/.cache/sysmlpy)
51
51
  "load_attempted": False,
52
52
  "loaded": False,
53
+ "file_states": 0, # DFA states in the cache file at load time
53
54
  "save_attempted": False,
54
55
  "saved": False,
55
56
  "saved_states": 0,
@@ -290,9 +291,12 @@ def load_dfa_cache(path: Optional[str] = None) -> bool:
290
291
  payload = pickle.load(f)
291
292
  if not isinstance(payload, tuple) or len(payload) != 5:
292
293
  raise ValueError("unexpected cache payload shape")
294
+ file_states = sum(len(dfa.states) for dfa in payload[1])
295
+ file_states += sum(len(dfa.states) for dfa in payload[4])
293
296
  with _lock:
294
297
  _install(payload)
295
298
  _state["loaded"] = True
299
+ _state["file_states"] = file_states
296
300
  return True
297
301
  except Exception as exc:
298
302
  warnings.warn(
@@ -315,10 +319,18 @@ def save_dfa_cache(path: Optional[str] = None) -> bool:
315
319
  return False
316
320
  path = path or cache_file()
317
321
  try:
318
- if os.path.isfile(path):
322
+ warm_states = sum(len(dfa.states) for dfa in _payload()[1])
323
+ warm_states += sum(len(dfa.states) for dfa in _payload()[4])
324
+ with _lock:
325
+ file_states = _state["file_states"]
326
+ if os.path.isfile(path) and warm_states <= file_states:
327
+ # Nothing learned beyond what the file already holds.
319
328
  with _lock:
320
329
  _state["saved"] = False
321
330
  return False
331
+ # Fresh write, or a strictly warmer in-process cache: supersede
332
+ # the file so DFA states learned since its last write (e.g. new
333
+ # grammar paths like the short-name `<...>` forms) persist.
322
334
  P, L = _classes()
323
335
  payload = _payload()
324
336
  blob = pickle.dumps(payload, protocol=_CACHE_PROTOCOL)
@@ -355,6 +367,7 @@ def reset_for_tests() -> None:
355
367
  for key in ("load_attempted", "loaded", "save_attempted",
356
368
  "saved", "saved_states"):
357
369
  _state[key] = False if key != "saved_states" else 0
370
+ _state["file_states"] = 0
358
371
 
359
372
 
360
373
  def stats() -> Dict[str, Any]:
@@ -4067,7 +4067,14 @@ class InterfaceDefinition:
4067
4067
  if isinstance(def_dict, dict) and def_dict.get("body") is not None:
4068
4068
  body_dict = def_dict["body"]
4069
4069
  if isinstance(body_dict, dict) and body_dict.get("name") in ("DefinitionBody", "InterfaceBody"):
4070
- self.body = InterfaceBody({"name": "InterfaceBody", "item": body_dict.get("ownedRelatedElement", [])})
4070
+ # v0.96.1: a rebuilt DefinitionBody dict carries
4071
+ # its items in "ownedRelatedElement" (the
4072
+ # standard relationship name); a native
4073
+ # InterfaceBody dict carries them in "item".
4074
+ items = body_dict.get("item",
4075
+ body_dict.get("ownedRelatedElement", []))
4076
+ self.body = InterfaceBody(
4077
+ {"name": "InterfaceBody", "item": items})
4071
4078
  else:
4072
4079
  self.body = InterfaceBody(body_dict)
4073
4080
  else:
@@ -5105,6 +5112,13 @@ class DefinitionBodyItem:
5105
5112
  self.children.append(NonOccurrenceUsageMember(item))
5106
5113
  elif item["name"] == "DefinitionMember":
5107
5114
  self.children.append(DefinitionMember(item))
5115
+ elif item["name"] == "InterfaceOccurrenceUsageMember":
5116
+ # v0.96.1: interface ends inside a rebuilt
5117
+ # definition body (the ends have no public-API
5118
+ # child to re-serialize).
5119
+ self.children.append(
5120
+ InterfaceOccurrenceUsageMember(item)
5121
+ )
5108
5122
  elif item["name"] == "Import":
5109
5123
  # Imports are legal in usage bodies too
5110
5124
  # (``part p1 { private import Q::*; }``) —
@@ -7551,7 +7565,18 @@ class InterfaceUsageDeclaration:
7551
7565
  if definition["part1"] is not None:
7552
7566
  self.part = InterfacePart(definition["part1"])
7553
7567
  # The connect usage was optional and it was used here.
7554
- self.keyword = "connect\n"
7568
+ # v0.96.1: only when the binary part actually carries the
7569
+ # CONNECT-form ends — the visitor also emits an EMPTY
7570
+ # part1 for plain ``interface x : T { ... }`` (body-form
7571
+ # ends), and marking those "connect" produced a
7572
+ # reparse-breaking ``interface x: T connect ...`` dump.
7573
+ binpart = getattr(self.part, 'children', None)
7574
+ has_end_members = False
7575
+ if binpart is not None and binpart.__class__.__name__ == "BinaryInterfacePart":
7576
+ has_end_members = bool(
7577
+ getattr(binpart, 'children', []) or [])
7578
+ if has_end_members:
7579
+ self.keyword = "connect\n"
7555
7580
  elif definition["part2"] is not None:
7556
7581
  self.part = InterfacePart(definition["part2"])
7557
7582
 
@@ -0,0 +1,11 @@
1
+ /*
2
+ * KerML v1.0 ANTLR4 grammar (lexer).
3
+ *
4
+ * Generated from the OMG KerML textual-notation KEBNF
5
+ * (Systems-Modeling/SysML-v2-Release, bnf/KerML-textual-bnf.kebnf,
6
+ * 2026-08 checkout / Release 2026-07 edition) using the daltskin
7
+ * sysml-v2-grammar generator in KerML-only mode. MIT license, same as
8
+ * the generator upstream.
9
+ */
10
+
11
+ lexer grammar KerMLLexer;