sonolus.py 0.3.3__tar.gz → 0.3.4__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.

Potentially problematic release.


This version of sonolus.py might be problematic. Click here for more details.

Files changed (178) hide show
  1. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/PKG-INFO +1 -1
  2. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/doc_stubs/math.pyi +33 -0
  3. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/builtins.md +3 -0
  4. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/cli.md +1 -1
  5. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/constructs.md +3 -1
  6. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/types.md +5 -1
  7. sonolus_py-0.3.4/docs/index.md +27 -0
  8. sonolus_py-0.3.4/docs/overview.md +174 -0
  9. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/mkdocs.yml +1 -0
  10. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/pyproject.toml +1 -1
  11. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/archetype.py +15 -15
  12. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/array.py +3 -2
  13. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/array_like.py +20 -20
  14. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/debug.py +4 -4
  15. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/engine.py +2 -2
  16. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/instruction.py +1 -1
  17. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/math_impls.py +17 -0
  18. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/native.py +3 -3
  19. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/range.py +6 -6
  20. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/value.py +1 -1
  21. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/interval.py +2 -2
  22. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/num.py +13 -13
  23. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/pointer.py +1 -1
  24. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/record.py +5 -0
  25. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/stream.py +3 -3
  26. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/vec.py +6 -6
  27. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_interval.py +3 -1
  28. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_vec.py +20 -0
  29. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/uv.lock +1 -1
  30. sonolus_py-0.3.3/docs/index.md +0 -22
  31. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/.github/workflows/publish.yaml +0 -0
  32. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/.gitignore +0 -0
  33. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/.python-version +0 -0
  34. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/.run/Python tests in tests.run.xml +0 -0
  35. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/LICENSE +0 -0
  36. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/README.md +0 -0
  37. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/doc_stubs/__init__.py +0 -0
  38. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/doc_stubs/builtins.pyi +0 -0
  39. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/doc_stubs/num.pyi +0 -0
  40. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/doc_stubs/random.pyi +0 -0
  41. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/CNAME +0 -0
  42. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/index.md +0 -0
  43. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/project.md +0 -0
  44. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/concepts/resources.md +0 -0
  45. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/builtins.md +0 -0
  46. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/index.md +0 -0
  47. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/math.md +0 -0
  48. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/random.md +0 -0
  49. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.archetype.md +0 -0
  50. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.array.md +0 -0
  51. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.array_like.md +0 -0
  52. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.bucket.md +0 -0
  53. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.containers.md +0 -0
  54. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.debug.md +0 -0
  55. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.easing.md +0 -0
  56. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.effect.md +0 -0
  57. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.engine.md +0 -0
  58. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.globals.md +0 -0
  59. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.instruction.md +0 -0
  60. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.interval.md +0 -0
  61. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.iterator.md +0 -0
  62. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.level.md +0 -0
  63. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.metadata.md +0 -0
  64. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.num.md +0 -0
  65. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.options.md +0 -0
  66. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.particle.md +0 -0
  67. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.printing.md +0 -0
  68. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.project.md +0 -0
  69. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.quad.md +0 -0
  70. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.record.md +0 -0
  71. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.runtime.md +0 -0
  72. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.sprite.md +0 -0
  73. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.stream.md +0 -0
  74. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.text.md +0 -0
  75. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.timing.md +0 -0
  76. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.transform.md +0 -0
  77. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.ui.md +0 -0
  78. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.values.md +0 -0
  79. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/docs/reference/sonolus.script.vec.md +0 -0
  80. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/scripts/generate.py +0 -0
  81. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
  82. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/scripts/runtimes/Functions.json +0 -0
  83. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/scripts/runtimes/Level/Play/Blocks.json +0 -0
  84. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
  85. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
  86. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/__init__.py +0 -0
  87. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/__init__.py +0 -0
  88. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/blocks.py +0 -0
  89. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/excepthook.py +0 -0
  90. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/finalize.py +0 -0
  91. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/interpret.py +0 -0
  92. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/ir.py +0 -0
  93. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/mode.py +0 -0
  94. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/node.py +0 -0
  95. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/ops.py +0 -0
  96. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/__init__.py +0 -0
  97. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/allocate.py +0 -0
  98. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/constant_evaluation.py +0 -0
  99. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/copy_coalesce.py +0 -0
  100. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/dead_code.py +0 -0
  101. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/dominance.py +0 -0
  102. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/flow.py +0 -0
  103. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/inlining.py +0 -0
  104. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/liveness.py +0 -0
  105. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/optimize.py +0 -0
  106. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/passes.py +0 -0
  107. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/simplify.py +0 -0
  108. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/optimize/ssa.py +0 -0
  109. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/place.py +0 -0
  110. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/utils.py +0 -0
  111. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/backend/visitor.py +0 -0
  112. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/__init__.py +0 -0
  113. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/cli.py +0 -0
  114. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/collection.py +0 -0
  115. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/compile.py +0 -0
  116. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/engine.py +0 -0
  117. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/level.py +0 -0
  118. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/node.py +0 -0
  119. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/build/project.py +0 -0
  120. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/py.typed +0 -0
  121. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/__init__.py +0 -0
  122. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/bucket.py +0 -0
  123. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/containers.py +0 -0
  124. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/easing.py +0 -0
  125. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/effect.py +0 -0
  126. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/globals.py +0 -0
  127. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/__init__.py +0 -0
  128. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/builtin_impls.py +0 -0
  129. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/callbacks.py +0 -0
  130. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/constant.py +0 -0
  131. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/context.py +0 -0
  132. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/descriptor.py +0 -0
  133. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/dict_impl.py +0 -0
  134. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/error.py +0 -0
  135. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/generic.py +0 -0
  136. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/impl.py +0 -0
  137. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/introspection.py +0 -0
  138. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/random.py +0 -0
  139. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/simulation_context.py +0 -0
  140. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/transient.py +0 -0
  141. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/internal/tuple_impl.py +0 -0
  142. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/iterator.py +0 -0
  143. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/level.py +0 -0
  144. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/metadata.py +0 -0
  145. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/options.py +0 -0
  146. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/particle.py +0 -0
  147. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/printing.py +0 -0
  148. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/project.py +0 -0
  149. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/quad.py +0 -0
  150. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/runtime.py +0 -0
  151. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/sprite.py +0 -0
  152. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/text.py +0 -0
  153. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/timing.py +0 -0
  154. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/transform.py +0 -0
  155. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/ui.py +0 -0
  156. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/sonolus/script/values.py +0 -0
  157. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/__init__.py +0 -0
  158. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/__init__.py +0 -0
  159. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/conftest.py +0 -0
  160. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_array.py +0 -0
  161. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_array_map.py +0 -0
  162. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_array_set.py +0 -0
  163. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_assert.py +0 -0
  164. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_dict.py +0 -0
  165. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_flow.py +0 -0
  166. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_functions.py +0 -0
  167. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_helpers.py +0 -0
  168. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_match.py +0 -0
  169. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_num.py +0 -0
  170. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_operator.py +0 -0
  171. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_quad.py +0 -0
  172. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_random.py +0 -0
  173. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_range.py +0 -0
  174. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_record.py +0 -0
  175. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_transform.py +0 -0
  176. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_tuple.py +0 -0
  177. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_values.py +0 -0
  178. {sonolus_py-0.3.3 → sonolus_py-0.3.4}/tests/script/test_var_array.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sonolus.py
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Sonolus engine development in Python
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -154,3 +154,36 @@ def log(x: float, base: float = ...) -> float:
154
154
  The logarithm of x to the specified base.
155
155
  """
156
156
  ...
157
+
158
+ def sqrt(x: float) -> float:
159
+ """Compute the square root of x.
160
+
161
+ Args:
162
+ x: A non-negative numeric value.
163
+
164
+ Returns:
165
+ The square root of x.
166
+ """
167
+ ...
168
+
169
+ def degrees(x: float) -> float:
170
+ """Convert radians to degrees.
171
+
172
+ Args:
173
+ x: An angle in radians.
174
+
175
+ Returns:
176
+ The angle in degrees.
177
+ """
178
+ ...
179
+
180
+ def radians(x: float) -> float:
181
+ """Convert degrees to radians.
182
+
183
+ Args:
184
+ x: An angle in degrees.
185
+
186
+ Returns:
187
+ The angle in radians.
188
+ """
189
+ ...
@@ -38,6 +38,9 @@ Sonolus.py also comes with support for some standard library modules.
38
38
  - `ceil(x)`
39
39
  - `trunc(x)`
40
40
  - `log(x[, base])`
41
+ - `sqrt(x)`
42
+ - `degrees(x)`
43
+ - `radians(x)`
41
44
  - `pi`
42
45
  - `e`
43
46
  - `tau`
@@ -25,4 +25,4 @@ sonolus-py schema
25
25
  ## Programmatic usage
26
26
  The same functionality can be accessed programmatically as methods of a project.
27
27
 
28
- See [Project][sonolus.script.project.Project] for more information.
28
+ See [Project](../reference/sonolus.script.project.md) for more information.
@@ -1,6 +1,8 @@
1
1
  # Constructs
2
2
 
3
- Most standard Python constructs are supported in Sonolus.py.
3
+ Sonolus.py functions as a compiler from Python to Sonolus nodes. While most standard Python constructs are supported,
4
+ there are some limitations compared to standard Python. The following sections outline what Sonolus.py supports and
5
+ how it differs from standard Python.
4
6
 
5
7
  ## Key Differences
6
8
 
@@ -11,8 +11,12 @@ the constants `None`, `Ellipsis`, and `NotImplemented`.
11
11
  `Num` is the numeric and boolean type in Sonolus.py. It is interchangeable with `int`, `float`, and `bool`.
12
12
  Sonolus.py will treat any of these types as `Num`, but it's recommended to use what's appropriate for clarity.
13
13
 
14
+ Typically, `Num` isn't directly used in code since `int`, `float`, and `bool` are more specific and easier to read.
15
+ The main exception is for instance checking (`isinstance` or `match` patterns), where `Num` is the only supported
16
+ way to check for numeric or boolean values.
17
+
14
18
  The Sonolus app uses 32-bit floating-point numbers for all numeric values, so precision may be lower compared to Python
15
- when running on Sonolus.
19
+ when running within Sonolus.
16
20
 
17
21
  NaN and values outside the range of 32-bit floating-point numbers are not supported.
18
22
 
@@ -0,0 +1,27 @@
1
+ # Sonolus.py
2
+ Sonolus.py is a Python library for creating Sonolus engines.
3
+
4
+ ## Installation
5
+ Sonolus.py is available on PyPI and can be installed using a package manager like pip.
6
+
7
+ === "pip"
8
+ ```bash
9
+ pip install sonolus.py
10
+ ```
11
+ === "uv"
12
+ ```bash
13
+ uv add sonolus.py
14
+ ```
15
+
16
+ ## Getting Started
17
+ Example Projects:
18
+
19
+ - [pydori](https://github.com/qwewqa/pydori): A Bandori-like engine designed to be an example project for Sonolus.py.
20
+
21
+ New Project Template: [sonolus.py-template-project](https://github.com/qwewqa/sonolus.py-template-project)
22
+
23
+ ## Documentation
24
+
25
+ - [Overview](overview.md): High-level overview of Sonolus.py.
26
+ - [Concepts](concepts/index.md): Detailed information on concepts and usage.
27
+ - [Reference](reference/index.md): Detailed information on included classes and functions.
@@ -0,0 +1,174 @@
1
+ # Overview
2
+
3
+ Sonolus.py is a Python library for creating Sonolus engines. This page provides an overview of the key functionality
4
+ available in the library. For more detailed information, see the [Concepts](concepts/index.md) and
5
+ [Reference](reference/index.md) sections.
6
+
7
+ ## Features
8
+ Sonolus.py functions by compiling Python code into Sonolus nodes. As such, it supports a subset of Python including
9
+ most syntax and a portion of the standard library. Additionally, Sonolus.py provides its own library of types and
10
+ functions that are specifically designed for use in Sonolus engines.
11
+
12
+ ### Language
13
+
14
+ #### Syntax
15
+ Most Python syntax is supported, but there are a few limitations:
16
+
17
+ - Destructuring assignment with the `*` operator is unsupported.
18
+ - Sequence (list and array) `match` patterns with the `*` operator are unsupported.
19
+ - Mapping (dict) `match` patterns are unsupported.
20
+ - Within functions, `import` statements are unsupported.
21
+ - The `global` and `nonlocal` keywords are unsupported.
22
+ - Exception related statements (`try`, `except`, `finally`, `raise`) are unsupported.
23
+
24
+ #### Compile Time Evaluation
25
+ Sonolus.py will evaluate some expressions at compile time such as basic arithmetic operations on constants,
26
+ boolean logical operations (`and`, `or`, `not`) on constants, and type checks (`isinstance`, `issubclass`).
27
+
28
+ In control flow constructs like `if` and `match`, Sonolus.py may determine some branches to be unreachable at compile
29
+ and eliminate them without evaluating them. This allows code like the following to compile successfully:
30
+
31
+ ```python
32
+ a = 1
33
+ if isinstance(a, Vec2):
34
+ # This branch is eliminated at compile time.
35
+ # If it were not, compilation would fail because `a` has no attribute `x`.
36
+ debug_log(a.x)
37
+ else:
38
+ debug_log(a)
39
+ ```
40
+
41
+ #### Variables
42
+ Numeric (`int`, `float`, `bool`) variables are fully supported and can be freely assigned and modified.
43
+
44
+ All other variables have the restriction that if the compiler finds multiple possible values for a variable, it may
45
+ not be accessed. For example, the following code will not compile:
46
+
47
+ ```python
48
+ if random() < 0.5:
49
+ a = Vec2(1, 2)
50
+ else:
51
+ a = Vec2(3, 4)
52
+ # This will not compile because `a` could have been defined in either branch.
53
+ debug_log(a.x)
54
+ ```
55
+
56
+ #### Function Returns
57
+ Similar to variables, functions returning `int`, `float`, or `bool` can have any number of return statements. Functions
58
+ returning `None` may also have any number of `return` or `return None` statements.
59
+
60
+ Functions returning any other type must have exactly one `return` statement, and it must be the only exit point of the
61
+ function. It is ok, however, for a function to have other `return` statements that are eliminated at compile time.
62
+ For example, the following code will compile successfully:
63
+
64
+ ```python
65
+ def fn(a: int | Vec2):
66
+ if isinstance(a, Vec2):
67
+ return a.x + a.y
68
+ else:
69
+ return a * 2
70
+
71
+ fn(123)
72
+ ```
73
+
74
+ ### Types
75
+
76
+ #### Numbers
77
+ Sonolus.py supports `int`, `float`, and `bool` types and most of the standard operations such as mathematical operations
78
+ (`+`, `-`, `*`, `/`, `//`, `%`), comparisons (`<`, `<=`, `>`, `>=`, `==`, `!=`), and boolean operations
79
+ (`and`, `or`, `not`).
80
+
81
+ #### Record
82
+ [`Record`](reference/sonolus.script.record.md) is the main way to define custom types in Sonolus.py. It functions similarly to a data class and
83
+ provides a way to define a type with named fields:
84
+
85
+ ```python
86
+ class MyRecord(Record):
87
+ a: int
88
+ b: float
89
+
90
+ my_record = MyRecord(1, b=2.5)
91
+ my_zero_record = +MyRecord # Short for MyRecord(0, 0.0)
92
+ my_record_copy = +my_record # Create a copy of my_record with the same values
93
+ my_record.a = 123 # Modify a field of the record
94
+ ```
95
+
96
+ Records may also be generic:
97
+
98
+ ```python
99
+ class MyGenericRecord[T](Record):
100
+ value: T
101
+
102
+ my_generic_record = MyGenericRecord[int](42)
103
+ my_generic_record_2 = MyGenericRecord(MyRecord(1, 3.5)) # Type arguments are inferred
104
+ ```
105
+
106
+ #### Array
107
+ [`Array`](reference/sonolus.script.array.md) is a type that represents a fixed-size array of elements of a specific type.
108
+
109
+ ```python
110
+ my_array = Array[int, 3](1, 2, 3)
111
+ my_array_2 = Array(4, 5, 6) # Type arguments are inferred
112
+ my_zero_array = +Array[int, 3] # Short for Array[int, 3](0, 0, 0)
113
+ my_array_copy = +my_array # Create a copy of my_array with the same values
114
+ my_array[2] = 10 # Modify an element of the array
115
+ ```
116
+
117
+ #### Other Types
118
+ Sonolus.py has limited support for other types of values such as strings, tuples, and functions. These have restrictions
119
+ such as not being valid as Record field types or Array element types.
120
+
121
+ ### Modules
122
+ Sonolus.py provides a number of built-in modules that can be used in Sonolus engines. These include:
123
+
124
+ - Project
125
+ - [Project](reference/sonolus.script.project.md): Configuration for a Sonolus.py project.
126
+ - [Engine](reference/sonolus.script.engine.md): Configuration for a Sonolus.py engine.
127
+ - [Level](reference/sonolus.script.level.md): Configuration for a Sonolus.py level.
128
+ - [Archetype](reference/sonolus.script.archetype.md): Engine archetypes and their configuration.
129
+ - Core Types
130
+ - [Array](reference/sonolus.script.array.md): Fixed-size arrays.
131
+ - [Num](reference/sonolus.script.num.md): Numeric values (int, float, bool).
132
+ - [Record](reference/sonolus.script.record.md): User-defined types with named fields.
133
+ - Engine Resources
134
+ - [Bucket](reference/sonolus.script.bucket.md): Judgment buckets.
135
+ - [Effect](reference/sonolus.script.effect.md): Sound effects.
136
+ - [Instruction](reference/sonolus.script.instruction.md): Tutorial instructions.
137
+ - [Options](reference/sonolus.script.options.md): Engine options.
138
+ - [Particle](reference/sonolus.script.particle.md): Particle effects.
139
+ - [Sprite](reference/sonolus.script.sprite.md): Sprites and skins.
140
+ - [UI](reference/sonolus.script.ui.md): Engine ui configuration.
141
+ - Sonolus Runtime
142
+ - [Globals](reference/sonolus.script.globals.md): Level data and level memory definition.
143
+ - [Runtime](reference/sonolus.script.runtime.md): Runtime functions like time and ui configuration.
144
+ - [Stream](reference/sonolus.script.stream.md): Data streams recorded in play mode and used in watch mode.
145
+ - [Text](reference/sonolus.script.text.md): Standard Sonolus text constants.
146
+ - [Timing](reference/sonolus.script.timing.md): Beat and timescale related functions.
147
+ - Python Builtins
148
+ - [builtins](reference/builtins.md): Supported Python builtins.
149
+ - [math](reference/math.md): Supported math functions.
150
+ - [random](reference/random.md): Supported random functions.
151
+ - Utilities
152
+ - [ArrayLike](reference/sonolus.script.array_like.md): Mixin for array functionality.
153
+ - [Containers](reference/sonolus.script.containers.md): Additional container types like `VarArray` and `ArrayMap`.
154
+ - [Debug](reference/sonolus.script.debug.md): Debugging utilities.
155
+ - [Easing](reference/sonolus.script.easing.md): Easing functions for animations.
156
+ - [Interval](reference/sonolus.script.interval.md): Mathematical intervals.
157
+ - [Iterator](reference/sonolus.script.iterator.md): Iterators over collections.
158
+ - [Printing](reference/sonolus.script.printing.md): Preview mode number printing.
159
+ - [Quad](reference/sonolus.script.quad.md): Quadrilaterals.
160
+ - [Transform](reference/sonolus.script.transform.md): Transformations like translation, rotation, and scaling.
161
+ - [Values](reference/sonolus.script.values.md): Generic utilities for working with values.
162
+ - [Vec](reference/sonolus.script.vec.md): The Vec2 type and related functions.
163
+
164
+ For more details, see the [Reference](reference/index.md) section.
165
+
166
+ ## Getting Started
167
+
168
+ Before making a new Sonolus.py engine, it may be helpful to look at some existing projects to see how they use
169
+ the library:
170
+
171
+ - [pydori](https://github.com/qwewqa/pydori): A Bandori-like engine designed to be an example project for Sonolus.py.
172
+
173
+ If you're starting a new project, you'll probably want to use the
174
+ [new project template](https://github.com/qwewqa/sonolus.py-template-project).
@@ -70,6 +70,7 @@ markdown_extensions:
70
70
  alternate_style: true
71
71
  nav:
72
72
  - index.md
73
+ - Overview: overview.md
73
74
  - Concepts:
74
75
  - concepts/index.md
75
76
  - concepts/types.md
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sonolus.py"
3
- version = "0.3.3"
3
+ version = "0.3.4"
4
4
  description = "Sonolus engine development in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -312,7 +312,7 @@ class StandardImport:
312
312
  def callback[T: Callable](*, order: int = 0) -> Callable[[T], T]:
313
313
  """Annotate a callback with its order.
314
314
 
315
- Callbacks are execute from lowest to highest order. By default, callbacks have an order of 0.
315
+ Callbacks are executed from lowest to highest order. By default, callbacks have an order of 0.
316
316
 
317
317
  Usage:
318
318
  ```python
@@ -338,9 +338,9 @@ class _ArchetypeSelfData:
338
338
 
339
339
 
340
340
  class _ArchetypeReferenceData:
341
- index: Num
341
+ index: int
342
342
 
343
- def __init__(self, index: Num):
343
+ def __init__(self, index: int):
344
344
  self.index = index
345
345
 
346
346
 
@@ -416,21 +416,21 @@ class _BaseArchetype:
416
416
 
417
417
  @classmethod
418
418
  @meta_fn
419
- def at(cls, index: Num) -> Self:
419
+ def at(cls, index: int) -> Self:
420
420
  result = cls._new()
421
421
  result._data_ = _ArchetypeReferenceData(index=Num._accept_(index))
422
422
  return result
423
423
 
424
424
  @classmethod
425
425
  @meta_fn
426
- def is_at(cls, index: Num) -> bool:
426
+ def is_at(cls, index: int) -> bool:
427
427
  if not ctx():
428
428
  raise RuntimeError("is_at is only available during compilation")
429
429
  return entity_info_at(index).archetype_id == cls.id()
430
430
 
431
431
  @classmethod
432
432
  @meta_fn
433
- def id(cls):
433
+ def id(cls) -> int:
434
434
  if not ctx():
435
435
  raise RuntimeError("Archetype id is only available during compilation")
436
436
  result = ctx().global_state.archetypes.get(cls)
@@ -992,7 +992,7 @@ class PreviewArchetype(_BaseArchetype):
992
992
 
993
993
 
994
994
  @meta_fn
995
- def entity_info_at(index: Num) -> PlayEntityInfo | WatchEntityInfo | PreviewEntityInfo:
995
+ def entity_info_at(index: int) -> PlayEntityInfo | WatchEntityInfo | PreviewEntityInfo:
996
996
  """Retrieve entity info of the entity at the given index.
997
997
 
998
998
  Available in play, watch, and preview mode.
@@ -1047,24 +1047,24 @@ class PreviewEntityInfo(Record):
1047
1047
  class ArchetypeLife(Record):
1048
1048
  """How an entity contributes to life."""
1049
1049
 
1050
- perfect_increment: Num
1050
+ perfect_increment: int
1051
1051
  """Life increment for a perfect judgment."""
1052
1052
 
1053
- great_increment: Num
1053
+ great_increment: int
1054
1054
  """Life increment for a great judgment."""
1055
1055
 
1056
- good_increment: Num
1056
+ good_increment: int
1057
1057
  """Life increment for a good judgment."""
1058
1058
 
1059
- miss_increment: Num
1059
+ miss_increment: int
1060
1060
  """Life increment for a miss judgment."""
1061
1061
 
1062
1062
  def update(
1063
1063
  self,
1064
- perfect_increment: Num | None = None,
1065
- great_increment: Num | None = None,
1066
- good_increment: Num | None = None,
1067
- miss_increment: Num | None = None,
1064
+ perfect_increment: int | None = None,
1065
+ great_increment: int | None = None,
1066
+ good_increment: int | None = None,
1067
+ miss_increment: int | None = None,
1068
1068
  ):
1069
1069
  """Update the life increments."""
1070
1070
  if perfect_increment is not None:
@@ -31,6 +31,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
31
31
  array_1 = Array(1, 2, 3)
32
32
  array_2 = Array[int, 0]()
33
33
  array_3 = +Array[int, 3] # Create a zero-initialized array
34
+ array_4 = +array_1 # Create a copy of array_1
34
35
  ```
35
36
  """
36
37
 
@@ -186,7 +187,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
186
187
  return self.size()
187
188
 
188
189
  @meta_fn
189
- def __getitem__(self, index: Num) -> T:
190
+ def __getitem__(self, index: int) -> T:
190
191
  index: Num = Num._accept_(get_positive_index(index, self.size()))
191
192
  if index._is_py_() and 0 <= index._as_py_() < self.size():
192
193
  const_index = index._as_py_()
@@ -230,7 +231,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
230
231
  raise InternalError("Unexpected array value")
231
232
 
232
233
  @meta_fn
233
- def __setitem__(self, index: Num, value: T):
234
+ def __setitem__(self, index: int, value: T):
234
235
  index: Num = Num._accept_(get_positive_index(index, self.size()))
235
236
  value = self.element_type()._accept_(value)
236
237
  if ctx():
@@ -26,10 +26,10 @@ class ArrayLike[T]:
26
26
  def __len__(self) -> int:
27
27
  ...
28
28
 
29
- def __getitem__(self, index: Num) -> T:
29
+ def __getitem__(self, index: int) -> T:
30
30
  ...
31
31
 
32
- def __setitem__(self, index: Num, value: T):
32
+ def __setitem__(self, index: int, value: T):
33
33
  ...
34
34
  ```
35
35
  """
@@ -41,7 +41,7 @@ class ArrayLike[T]:
41
41
  """Return the length of the array."""
42
42
 
43
43
  @abstractmethod
44
- def __getitem__(self, index: Num) -> T:
44
+ def __getitem__(self, index: int) -> T:
45
45
  """Return the item at the given index.
46
46
 
47
47
  Args:
@@ -49,7 +49,7 @@ class ArrayLike[T]:
49
49
  """
50
50
 
51
51
  @abstractmethod
52
- def __setitem__(self, index: Num, value: T):
52
+ def __setitem__(self, index: int, value: T):
53
53
  """Set the value of the item at the given index.
54
54
 
55
55
  Args:
@@ -78,10 +78,10 @@ class ArrayLike[T]:
78
78
  """Return a reversed view of the array."""
79
79
  return _ArrayReverser(self)
80
80
 
81
- def _enumerate_(self, start: Num = 0) -> SonolusIterator[T]:
81
+ def _enumerate_(self, start: int = 0) -> SonolusIterator[T]:
82
82
  return _ArrayEnumerator(0, start, self)
83
83
 
84
- def index(self, value: T, start: Num = 0, stop: Num | None = None) -> Num:
84
+ def index(self, value: T, start: int = 0, stop: int | None = None) -> int:
85
85
  """Return the index of the value in the array equal to the given value.
86
86
 
87
87
  Args:
@@ -98,7 +98,7 @@ class ArrayLike[T]:
98
98
  i += 1
99
99
  return -1
100
100
 
101
- def count(self, value: T) -> Num:
101
+ def count(self, value: T) -> int:
102
102
  """Return the number of elements in the array equal to the given value.
103
103
 
104
104
  Args:
@@ -112,7 +112,7 @@ class ArrayLike[T]:
112
112
  i += 1
113
113
  return count
114
114
 
115
- def last_index(self, value: T) -> Num:
115
+ def last_index(self, value: T) -> int:
116
116
  """Return the last index of the value in the array equal to the given value.
117
117
 
118
118
  Args:
@@ -125,7 +125,7 @@ class ArrayLike[T]:
125
125
  i -= 1
126
126
  return -1
127
127
 
128
- def index_of_max(self, *, key: Callable[T, Any] | None = None) -> Num:
128
+ def index_of_max(self, *, key: Callable[[T], Any] | None = None) -> int:
129
129
  """Return the index of the maximum value in the array.
130
130
 
131
131
  Args:
@@ -143,7 +143,7 @@ class ArrayLike[T]:
143
143
  i += 1
144
144
  return max_index
145
145
 
146
- def index_of_min(self, *, key: Callable[T, Any] | None = None) -> Num:
146
+ def index_of_min(self, *, key: Callable[[T], Any] | None = None) -> int:
147
147
  """Return the index of the minimum value in the array.
148
148
 
149
149
  Args:
@@ -161,17 +161,17 @@ class ArrayLike[T]:
161
161
  i += 1
162
162
  return min_index
163
163
 
164
- def _max_(self, key: Callable[T, Any] | None = None) -> T:
164
+ def _max_(self, key: Callable[[T], Any] | None = None) -> T:
165
165
  index = self.index_of_max(key=key)
166
166
  assert index != -1
167
167
  return self[index]
168
168
 
169
- def _min_(self, key: Callable[T, Any] | None = None) -> T:
169
+ def _min_(self, key: Callable[[T], Any] | None = None) -> T:
170
170
  index = self.index_of_min(key=key)
171
171
  assert index != -1
172
172
  return self[index]
173
173
 
174
- def swap(self, i: Num, j: Num, /):
174
+ def swap(self, i: int, j: int, /):
175
175
  """Swap the values at the given indices.
176
176
 
177
177
  Args:
@@ -182,7 +182,7 @@ class ArrayLike[T]:
182
182
  self[i] = self[j]
183
183
  self[j] = temp
184
184
 
185
- def sort(self, *, key: Callable[T, Any] | None = None, reverse: bool = False):
185
+ def sort(self, *, key: Callable[[T], Any] | None = None, reverse: bool = False):
186
186
  """Sort the values in the array in place.
187
187
 
188
188
  Args:
@@ -216,7 +216,7 @@ def _identity[T](value: T) -> T:
216
216
  return value
217
217
 
218
218
 
219
- def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, key: Callable[T, Any], reverse: bool):
219
+ def _insertion_sort[T](array: ArrayLike[T], start: int, end: int, key: Callable[[T], Any], reverse: bool):
220
220
  i = start + 1
221
221
  if reverse:
222
222
  while i < end:
@@ -234,7 +234,7 @@ def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, key: Callable[
234
234
  i += 1
235
235
 
236
236
 
237
- def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
237
+ def _heapify[T](array: ArrayLike[T], end: int, index: int, reverse: bool):
238
238
  while True:
239
239
  left = index * 2 + 1
240
240
  right = left + 1
@@ -249,7 +249,7 @@ def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
249
249
  index = largest
250
250
 
251
251
 
252
- def _heap_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
252
+ def _heap_sort[T](array: ArrayLike[T], start: int, end: int, reverse: bool):
253
253
  i = end // 2 - 1
254
254
  while i >= start:
255
255
  _heapify(array, end, i, reverse)
@@ -281,10 +281,10 @@ class _ArrayReverser[V: ArrayLike](Record, ArrayLike):
281
281
  def __len__(self) -> int:
282
282
  return len(self.array)
283
283
 
284
- def __getitem__(self, index: Num) -> V:
284
+ def __getitem__(self, index: int) -> V:
285
285
  return self.array[len(self) - 1 - index]
286
286
 
287
- def __setitem__(self, index: Num, value: V):
287
+ def __setitem__(self, index: int, value: V):
288
288
  self.array[len(self) - 1 - index] = value
289
289
 
290
290
  def reversed(self) -> ArrayLike[V]:
@@ -307,7 +307,7 @@ class _ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
307
307
 
308
308
 
309
309
  @meta_fn
310
- def get_positive_index(index: Num, length: Num) -> Num:
310
+ def get_positive_index(index: int, length: int) -> int:
311
311
  """Get the positive index for the given index in the array of the given length.
312
312
 
313
313
  This is used to convert negative indixes relative to the end of the array to positive indices.
@@ -57,7 +57,7 @@ def static_error(message: str | None = None) -> Never:
57
57
 
58
58
 
59
59
  @meta_fn
60
- def debug_log(value: Num):
60
+ def debug_log(value: int | float | bool):
61
61
  """Log a value in debug mode."""
62
62
  if debug_log_callback.get(None):
63
63
  return debug_log_callback.get()(value)
@@ -66,7 +66,7 @@ def debug_log(value: Num):
66
66
 
67
67
 
68
68
  @native_function(Op.DebugLog)
69
- def _debug_log(value: Num):
69
+ def _debug_log(value: int | float | bool):
70
70
  print(f"[DEBUG] {value}")
71
71
  return 0
72
72
 
@@ -77,13 +77,13 @@ def debug_pause():
77
77
  input("[DEBUG] Paused")
78
78
 
79
79
 
80
- def assert_true(value: Num, message: str | None = None):
80
+ def assert_true(value: int | float | bool, message: str | None = None):
81
81
  message = message if message is not None else "Assertion failed"
82
82
  if not value:
83
83
  error(message)
84
84
 
85
85
 
86
- def assert_false(value: Num, message: str | None = None):
86
+ def assert_false(value: int | float | bool, message: str | None = None):
87
87
  message = message if message is not None else "Assertion failed"
88
88
  if value:
89
89
  error(message)
@@ -217,7 +217,7 @@ class WatchMode:
217
217
 
218
218
  for archetype in self.archetypes:
219
219
  if not issubclass(archetype, WatchArchetype):
220
- raise ValueError(f"archetype {archetype} is not a PlayArchetype")
220
+ raise ValueError(f"archetype {archetype} is not a WatchArchetype")
221
221
 
222
222
 
223
223
  class PreviewMode:
@@ -239,7 +239,7 @@ class PreviewMode:
239
239
 
240
240
  for archetype in self.archetypes:
241
241
  if not issubclass(archetype, PreviewArchetype):
242
- raise ValueError(f"archetype {archetype} is not a BaseArchetype")
242
+ raise ValueError(f"archetype {archetype} is not a PreviewArchetype")
243
243
 
244
244
 
245
245
  class TutorialMode:
@@ -97,7 +97,7 @@ def instructions[T](cls: type[T]) -> T | TutorialInstructions:
97
97
  annotation_values = annotation.__metadata__
98
98
  if annotation_type is not Instruction:
99
99
  raise TypeError(
100
- f"Invalid annotation for instruction: {annotation}, expected annotation of type InstructionText"
100
+ f"Invalid annotation for instruction: {annotation}, expected annotation of type Instruction"
101
101
  )
102
102
  if len(annotation_values) != 1 or not isinstance(annotation_values[0], _InstructionTextInfo):
103
103
  raise TypeError(f"Invalid annotation for instruction: {annotation}, expected a single annotation value")
@@ -97,6 +97,22 @@ def _log(x: float, base: float | None = None) -> float:
97
97
  return _ln(x) / _ln(base)
98
98
 
99
99
 
100
+ def _sqrt(x: float) -> float:
101
+ return x**0.5
102
+
103
+
104
+ @native_function(Op.Degree)
105
+ def _degrees(x: float) -> float:
106
+ """Convert radians to degrees."""
107
+ return math.degrees(x)
108
+
109
+
110
+ @native_function(Op.Radian)
111
+ def _radians(x: float) -> float:
112
+ """Convert degrees to radians."""
113
+ return math.radians(x)
114
+
115
+
100
116
  @native_function(Op.Rem)
101
117
  def _remainder(x: float, y: float) -> float:
102
118
  # This is different from math.remainder in Python's math package, which could be confusing
@@ -119,4 +135,5 @@ MATH_BUILTIN_IMPLS = {
119
135
  id(math.trunc): _trunc,
120
136
  id(round): _round,
121
137
  id(math.log): _log,
138
+ id(math.sqrt): _sqrt,
122
139
  }