rosh-lang 0.9.3__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 (206) hide show
  1. rosh_lang-0.9.3/.github/workflows/ci.yml +69 -0
  2. rosh_lang-0.9.3/.gitignore +36 -0
  3. rosh_lang-0.9.3/CHANGELOG.md +91 -0
  4. rosh_lang-0.9.3/CLAUDE.md +263 -0
  5. rosh_lang-0.9.3/CONTRIBUTING.md +80 -0
  6. rosh_lang-0.9.3/LANGUAGE.md +819 -0
  7. rosh_lang-0.9.3/LICENSE +27 -0
  8. rosh_lang-0.9.3/PKG-INFO +630 -0
  9. rosh_lang-0.9.3/README.md +603 -0
  10. rosh_lang-0.9.3/SECURITY.md +14 -0
  11. rosh_lang-0.9.3/editor/vscode/.vscodeignore +4 -0
  12. rosh_lang-0.9.3/editor/vscode/LICENSE +21 -0
  13. rosh_lang-0.9.3/editor/vscode/README.md +31 -0
  14. rosh_lang-0.9.3/editor/vscode/install.sh +11 -0
  15. rosh_lang-0.9.3/editor/vscode/language-configuration.json +31 -0
  16. rosh_lang-0.9.3/editor/vscode/package.json +58 -0
  17. rosh_lang-0.9.3/editor/vscode/snippets/rosh.json +100 -0
  18. rosh_lang-0.9.3/editor/vscode/syntaxes/rosh.tmLanguage.json +104 -0
  19. rosh_lang-0.9.3/examples/breakout.rosh +105 -0
  20. rosh_lang-0.9.3/examples/checkpoint1.rosh +3 -0
  21. rosh_lang-0.9.3/examples/checkpoint2.rosh +5 -0
  22. rosh_lang-0.9.3/examples/click.rosh +18 -0
  23. rosh_lang-0.9.3/examples/composed.rosh +12 -0
  24. rosh_lang-0.9.3/examples/counter.rosh +4 -0
  25. rosh_lang-0.9.3/examples/dashboard.rosh +112 -0
  26. rosh_lang-0.9.3/examples/dodge.rosh +63 -0
  27. rosh_lang-0.9.3/examples/game.rosh +16 -0
  28. rosh_lang-0.9.3/examples/greeting.rosh +75 -0
  29. rosh_lang-0.9.3/examples/hello.rosh +1 -0
  30. rosh_lang-0.9.3/examples/player.rosh +6 -0
  31. rosh_lang-0.9.3/examples/pong.rosh +278 -0
  32. rosh_lang-0.9.3/examples/reference-game.rosh +77 -0
  33. rosh_lang-0.9.3/examples/scratch-ball.rosh +65 -0
  34. rosh_lang-0.9.3/examples/showcase/01-hello.rosh +22 -0
  35. rosh_lang-0.9.3/examples/showcase/02-click.rosh +20 -0
  36. rosh_lang-0.9.3/examples/showcase/03-keyboard.rosh +38 -0
  37. rosh_lang-0.9.3/examples/showcase/04-traffic.rosh +37 -0
  38. rosh_lang-0.9.3/examples/showcase/05-widgets.rosh +13 -0
  39. rosh_lang-0.9.3/examples/showcase/06-game.rosh +72 -0
  40. rosh_lang-0.9.3/examples/showcase/07-composed-game.rosh +37 -0
  41. rosh_lang-0.9.3/examples/showcase/08-sound-fx.rosh +88 -0
  42. rosh_lang-0.9.3/examples/showcase/09-enemies.rosh +62 -0
  43. rosh_lang-0.9.3/examples/showcase/10-scenes.rosh +74 -0
  44. rosh_lang-0.9.3/examples/showcase/11-animation.rosh +29 -0
  45. rosh_lang-0.9.3/examples/showcase/12-breakout.rosh +104 -0
  46. rosh_lang-0.9.3/examples/showcase/13-dodge.rosh +60 -0
  47. rosh_lang-0.9.3/examples/showcase/14-whack.rosh +146 -0
  48. rosh_lang-0.9.3/examples/showcase/15-pong.rosh +229 -0
  49. rosh_lang-0.9.3/examples/showcase/16-functions.rosh +90 -0
  50. rosh_lang-0.9.3/examples/showcase/17-repeat.rosh +29 -0
  51. rosh_lang-0.9.3/examples/showcase/18-reference-game.rosh +75 -0
  52. rosh_lang-0.9.3/examples/showcase/19-asset-defaults.rosh +14 -0
  53. rosh_lang-0.9.3/examples/showcase/20-museum-assets.rosh +184 -0
  54. rosh_lang-0.9.3/examples/showcase/player-sheet.png +0 -0
  55. rosh_lang-0.9.3/examples/showcase/threejs-hello.rosh +39 -0
  56. rosh_lang-0.9.3/examples/space-invaders.rosh +51 -0
  57. rosh_lang-0.9.3/examples/traffic-light.rosh +90 -0
  58. rosh_lang-0.9.3/examples/url-sprites-demo.rosh +78 -0
  59. rosh_lang-0.9.3/examples/whack.rosh +221 -0
  60. rosh_lang-0.9.3/examples/widgets/bullet.rosh +2 -0
  61. rosh_lang-0.9.3/examples/widgets/coin.rosh +2 -0
  62. rosh_lang-0.9.3/examples/widgets/counter.rosh +11 -0
  63. rosh_lang-0.9.3/examples/widgets/enemy-grid.rosh +2 -0
  64. rosh_lang-0.9.3/examples/widgets/explosion.rosh +2 -0
  65. rosh_lang-0.9.3/examples/widgets/grid.rosh +2 -0
  66. rosh_lang-0.9.3/examples/widgets/message.rosh +2 -0
  67. rosh_lang-0.9.3/examples/widgets/player.rosh +14 -0
  68. rosh_lang-0.9.3/examples/widgets/score.rosh +14 -0
  69. rosh_lang-0.9.3/examples/widgets/starfield.rosh +2 -0
  70. rosh_lang-0.9.3/examples/widgets/title-screen.rosh +2 -0
  71. rosh_lang-0.9.3/pyproject.toml +58 -0
  72. rosh_lang-0.9.3/src/rosh_lang/__init__.py +3 -0
  73. rosh_lang-0.9.3/src/rosh_lang/__main__.py +286 -0
  74. rosh_lang-0.9.3/src/rosh_lang/assets.py +4 -0
  75. rosh_lang-0.9.3/src/rosh_lang/cli/__init__.py +2 -0
  76. rosh_lang-0.9.3/src/rosh_lang/cli/assets_cli.py +412 -0
  77. rosh_lang-0.9.3/src/rosh_lang/cli/cloud.py +496 -0
  78. rosh_lang-0.9.3/src/rosh_lang/cli/library_cli.py +116 -0
  79. rosh_lang-0.9.3/src/rosh_lang/cli/scaffolder.py +213 -0
  80. rosh_lang-0.9.3/src/rosh_lang/cloud.py +4 -0
  81. rosh_lang-0.9.3/src/rosh_lang/core/__init__.py +2 -0
  82. rosh_lang-0.9.3/src/rosh_lang/core/model.py +355 -0
  83. rosh_lang-0.9.3/src/rosh_lang/core/normalise.py +295 -0
  84. rosh_lang-0.9.3/src/rosh_lang/core/parser.py +1112 -0
  85. rosh_lang-0.9.3/src/rosh_lang/core/runtime.py +1278 -0
  86. rosh_lang-0.9.3/src/rosh_lang/core/widgets.py +777 -0
  87. rosh_lang-0.9.3/src/rosh_lang/intent/__init__.py +6 -0
  88. rosh_lang-0.9.3/src/rosh_lang/intent/planner.py +252 -0
  89. rosh_lang-0.9.3/src/rosh_lang/intent/prompts.py +81 -0
  90. rosh_lang-0.9.3/src/rosh_lang/intent/providers.py +56 -0
  91. rosh_lang-0.9.3/src/rosh_lang/intent/settings.py +80 -0
  92. rosh_lang-0.9.3/src/rosh_lang/intent/validate.py +36 -0
  93. rosh_lang-0.9.3/src/rosh_lang/library/__init__.py +1 -0
  94. rosh_lang-0.9.3/src/rosh_lang/library/animation.py +40 -0
  95. rosh_lang-0.9.3/src/rosh_lang/library/ball.py +116 -0
  96. rosh_lang-0.9.3/src/rosh_lang/library/bullet.py +86 -0
  97. rosh_lang-0.9.3/src/rosh_lang/library/button.rosh +13 -0
  98. rosh_lang-0.9.3/src/rosh_lang/library/coin.rosh +15 -0
  99. rosh_lang-0.9.3/src/rosh_lang/library/controller.py +277 -0
  100. rosh_lang-0.9.3/src/rosh_lang/library/counter.rosh +13 -0
  101. rosh_lang-0.9.3/src/rosh_lang/library/enemy-grid.py +99 -0
  102. rosh_lang-0.9.3/src/rosh_lang/library/explosion.py +121 -0
  103. rosh_lang-0.9.3/src/rosh_lang/library/fps.py +49 -0
  104. rosh_lang-0.9.3/src/rosh_lang/library/game-lifecycle.rosh +76 -0
  105. rosh_lang-0.9.3/src/rosh_lang/library/grid.py +38 -0
  106. rosh_lang-0.9.3/src/rosh_lang/library/hazard.py +95 -0
  107. rosh_lang-0.9.3/src/rosh_lang/library/health-bar.py +55 -0
  108. rosh_lang-0.9.3/src/rosh_lang/library/label.rosh +16 -0
  109. rosh_lang-0.9.3/src/rosh_lang/library/lives.py +69 -0
  110. rosh_lang-0.9.3/src/rosh_lang/library/message.rosh +16 -0
  111. rosh_lang-0.9.3/src/rosh_lang/library/player.py +134 -0
  112. rosh_lang-0.9.3/src/rosh_lang/library/score.py +51 -0
  113. rosh_lang-0.9.3/src/rosh_lang/library/starfield.py +44 -0
  114. rosh_lang-0.9.3/src/rosh_lang/library/timer.py +62 -0
  115. rosh_lang-0.9.3/src/rosh_lang/library/title-screen.rosh +36 -0
  116. rosh_lang-0.9.3/src/rosh_lang/library_cli.py +4 -0
  117. rosh_lang-0.9.3/src/rosh_lang/media/__init__.py +2 -0
  118. rosh_lang-0.9.3/src/rosh_lang/media/asset_cache.py +57 -0
  119. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/amphora.json +61 -0
  120. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/ball.json +36 -0
  121. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/box.json +36 -0
  122. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/brooch.json +61 -0
  123. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/carved_relief.json +37 -0
  124. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/cauldron.json +61 -0
  125. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/coin.json +36 -0
  126. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/display_case.json +38 -0
  127. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/door.json +37 -0
  128. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/exhibit_label.json +37 -0
  129. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/key.json +36 -0
  130. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/necklace.json +62 -0
  131. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/painting.json +36 -0
  132. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/pedestal.json +37 -0
  133. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/person.json +36 -0
  134. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/platform.json +35 -0
  135. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/spotlight.json +37 -0
  136. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/stone.json +54 -0
  137. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/sword.json +62 -0
  138. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/torch.json +61 -0
  139. rosh_lang-0.9.3/src/rosh_lang/media/asset_manifests/tree.json +36 -0
  140. rosh_lang-0.9.3/src/rosh_lang/media/asset_providers.py +512 -0
  141. rosh_lang-0.9.3/src/rosh_lang/media/asset_registry.py +261 -0
  142. rosh_lang-0.9.3/src/rosh_lang/media/asset_requests.py +92 -0
  143. rosh_lang-0.9.3/src/rosh_lang/media/asset_reviews.py +318 -0
  144. rosh_lang-0.9.3/src/rosh_lang/media/assets/player-sheet.png +0 -0
  145. rosh_lang-0.9.3/src/rosh_lang/media/assets.py +55 -0
  146. rosh_lang-0.9.3/src/rosh_lang/media/sheets.py +235 -0
  147. rosh_lang-0.9.3/src/rosh_lang/media/sounds.py +221 -0
  148. rosh_lang-0.9.3/src/rosh_lang/media/sprites.py +309 -0
  149. rosh_lang-0.9.3/src/rosh_lang/model.py +4 -0
  150. rosh_lang-0.9.3/src/rosh_lang/parser.py +4 -0
  151. rosh_lang-0.9.3/src/rosh_lang/repl/__init__.py +5 -0
  152. rosh_lang-0.9.3/src/rosh_lang/repl/contracts.py +54 -0
  153. rosh_lang-0.9.3/src/rosh_lang/repl/kernel.py +490 -0
  154. rosh_lang-0.9.3/src/rosh_lang/repl/natural.py +288 -0
  155. rosh_lang-0.9.3/src/rosh_lang/repl/runtime_adapter.py +307 -0
  156. rosh_lang-0.9.3/src/rosh_lang/repl/shell.py +306 -0
  157. rosh_lang-0.9.3/src/rosh_lang/runtime.py +4 -0
  158. rosh_lang-0.9.3/src/rosh_lang/scaffolder.py +4 -0
  159. rosh_lang-0.9.3/src/rosh_lang/sheets.py +4 -0
  160. rosh_lang-0.9.3/src/rosh_lang/sounds.py +4 -0
  161. rosh_lang-0.9.3/src/rosh_lang/sprites.py +11 -0
  162. rosh_lang-0.9.3/src/rosh_lang/targets/__init__.py +1 -0
  163. rosh_lang-0.9.3/src/rosh_lang/targets/_js_codegen.py +733 -0
  164. rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime.py +1158 -0
  165. rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime_console.py +1042 -0
  166. rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime_phaser.py +410 -0
  167. rosh_lang-0.9.3/src/rosh_lang/targets/_js_runtime_threejs.py +530 -0
  168. rosh_lang-0.9.3/src/rosh_lang/targets/hytopia.py +226 -0
  169. rosh_lang-0.9.3/src/rosh_lang/targets/phaser.py +227 -0
  170. rosh_lang-0.9.3/src/rosh_lang/targets/rosh-natural.js +614 -0
  171. rosh_lang-0.9.3/src/rosh_lang/targets/scratch.py +1916 -0
  172. rosh_lang-0.9.3/src/rosh_lang/targets/terminal.py +13 -0
  173. rosh_lang-0.9.3/src/rosh_lang/targets/threejs.py +244 -0
  174. rosh_lang-0.9.3/src/rosh_lang/targets/web.py +554 -0
  175. rosh_lang-0.9.3/src/rosh_lang/targets/world.py +257 -0
  176. rosh_lang-0.9.3/src/rosh_lang/widgets.py +11 -0
  177. rosh_lang-0.9.3/tests/test_asset_cache.py +79 -0
  178. rosh_lang-0.9.3/tests/test_asset_providers.py +326 -0
  179. rosh_lang-0.9.3/tests/test_asset_registry.py +205 -0
  180. rosh_lang-0.9.3/tests/test_asset_requests.py +88 -0
  181. rosh_lang-0.9.3/tests/test_asset_reviews.py +343 -0
  182. rosh_lang-0.9.3/tests/test_cli.py +707 -0
  183. rosh_lang-0.9.3/tests/test_collections.py +383 -0
  184. rosh_lang-0.9.3/tests/test_conformance.py +428 -0
  185. rosh_lang-0.9.3/tests/test_controller.py +267 -0
  186. rosh_lang-0.9.3/tests/test_functions.py +381 -0
  187. rosh_lang-0.9.3/tests/test_intent.py +331 -0
  188. rosh_lang-0.9.3/tests/test_js_codegen.py +910 -0
  189. rosh_lang-0.9.3/tests/test_library_cli.py +64 -0
  190. rosh_lang-0.9.3/tests/test_normalise.py +342 -0
  191. rosh_lang-0.9.3/tests/test_parser.py +1067 -0
  192. rosh_lang-0.9.3/tests/test_phaser_target.py +246 -0
  193. rosh_lang-0.9.3/tests/test_public_release.py +42 -0
  194. rosh_lang-0.9.3/tests/test_repeat.py +140 -0
  195. rosh_lang-0.9.3/tests/test_runtime.py +1971 -0
  196. rosh_lang-0.9.3/tests/test_scaffolder.py +37 -0
  197. rosh_lang-0.9.3/tests/test_scratch_target.py +729 -0
  198. rosh_lang-0.9.3/tests/test_sheets.py +110 -0
  199. rosh_lang-0.9.3/tests/test_sounds.py +203 -0
  200. rosh_lang-0.9.3/tests/test_sprites.py +219 -0
  201. rosh_lang-0.9.3/tests/test_threejs_target.py +465 -0
  202. rosh_lang-0.9.3/tests/test_web_target.py +959 -0
  203. rosh_lang-0.9.3/tests/test_widgets.py +1446 -0
  204. rosh_lang-0.9.3/tests/test_world_target.py +294 -0
  205. rosh_lang-0.9.3/tools/build_showcase.py +376 -0
  206. rosh_lang-0.9.3/uv.lock +645 -0
@@ -0,0 +1,69 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ strategy:
10
+ matrix:
11
+ python-version: ["3.10", "3.12"]
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v6
16
+ with:
17
+ python-version: ${{ matrix.python-version }}
18
+ - run: uv sync --dev --frozen
19
+ - run: uv run pytest -q
20
+
21
+ release-artifacts:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: astral-sh/setup-uv@v6
26
+ with:
27
+ python-version: "3.12"
28
+ - run: uv build --no-sources
29
+ - name: Reject private or generated release files
30
+ run: |
31
+ if tar -tf dist/rosh_lang-*.tar.gz | grep -E '(\.uv-cache|\.venv|\.pytest_cache|test-verify|\.env|\.sb3|rosh-dev|rosh-corporate)'; then
32
+ echo "Unexpected file found in source distribution"
33
+ exit 1
34
+ fi
35
+ if python3 -m zipfile -l dist/rosh_lang-*.whl | grep -E '(\.uv-cache|\.venv|\.pytest_cache|test-verify|\.env|\.sb3|rosh-dev|rosh-corporate)'; then
36
+ echo "Unexpected file found in the wheel"
37
+ exit 1
38
+ fi
39
+ - run: uv tool run --from dist/rosh_lang-*.whl rosh --version
40
+ - uses: actions/upload-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ # Publishes to PyPI via Trusted Publishing (OIDC) — no stored token.
46
+ # Requires this workflow's environment to be registered as a trusted
47
+ # publisher at https://pypi.org/manage/project/rosh-lang/settings/publishing/
48
+ # (one-time setup, after the first manual `uv publish`).
49
+ publish-pypi:
50
+ needs: [test, release-artifacts]
51
+ if: startsWith(github.ref, 'refs/tags/v')
52
+ runs-on: ubuntu-latest
53
+ environment: pypi
54
+ permissions:
55
+ id-token: write
56
+ steps:
57
+ - uses: actions/download-artifact@v4
58
+ with:
59
+ name: dist
60
+ path: dist/
61
+ - name: Verify tag matches package version
62
+ run: |
63
+ tag_version="${GITHUB_REF_NAME#v}"
64
+ wheel_version=$(ls dist/*.whl | sed -E 's/.*rosh_lang-([0-9.]+)-.*/\1/')
65
+ if [ "$tag_version" != "$wheel_version" ]; then
66
+ echo "Tag v$tag_version does not match package version $wheel_version — refusing to publish." >&2
67
+ exit 1
68
+ fi
69
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,36 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .eggs/
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+
14
+ # Testing
15
+ .pytest_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # IDE
20
+ .vscode/
21
+ .idea/
22
+
23
+ # OS
24
+ .DS_Store
25
+ Thumbs.db
26
+
27
+ # Environment
28
+ .env
29
+
30
+ # UV
31
+ .uv-cache/
32
+
33
+ # Generated
34
+ *.rosh.html
35
+ *.sb3
36
+ test-verify.rosh
@@ -0,0 +1,91 @@
1
+ # Changelog
2
+
3
+ All notable changes to `rosh-lang` are documented here.
4
+
5
+ The core grammar (parser/runtime statement surface) is treated as stable
6
+ once documented in [LANGUAGE.md](LANGUAGE.md) — see CONTRIBUTING.md's rule
7
+ that new keywords require discussion first. Undocumented internals (e.g.
8
+ widget-specific properties like `.material`) may change without a version
9
+ bump until they're promoted into the public spec.
10
+
11
+ ## 0.9.3 - 2026-08-12
12
+
13
+ ### Fixed
14
+
15
+ - `normalise`: a quoted multi-word value passed to `set X.material to "..."`
16
+ or `turn X into '...'` left a stray trailing quote baked into the material
17
+ noun (e.g. `ivory"`). Quotes are now stripped before the material/desc
18
+ split.
19
+ - `normalise`: a trailing `# comment` on an otherwise-valid line (e.g.
20
+ `play laser # once`) was never stripped, so the comment text was parsed
21
+ as real arguments instead of being ignored. Inline comments are now
22
+ stripped, respecting quoted strings (so `background "#1a1a2e"` and
23
+ `print "score #1"` are unaffected).
24
+ - `after <seconds> send <event>`: payload support was documented
25
+ (`after 0.5 send scored value=5`) but never implemented — the model had
26
+ no payload field and any extra tokens after the event name were silently
27
+ dropped on every target. `AfterStatement` now carries a `payload` dict,
28
+ parsed the same way as `send`, and emitted correctly in compiled JS
29
+ (web/Phaser/Three.js targets).
30
+ - `normalise`: the inline-comment stripper above treated any bare apostrophe
31
+ as opening a quoted string, so a possessive or contraction with no actual
32
+ quotes (`say Rosh's world # note`, `say don't stop # note`) made the
33
+ scanner think it was still "inside a string" for the rest of the line —
34
+ the trailing comment was never stripped. A `'` now only opens a quote when
35
+ it's preceded by whitespace or starts the line, which a real quote always
36
+ is and a contraction never is.
37
+
38
+ ### Docs
39
+
40
+ - LANGUAGE.md's Scenes section documented `create scene ... end` as if it
41
+ scoped the statements inside it to that scene. It doesn't — `create scene`
42
+ only registers the scene, and everything between it and its `end` executes
43
+ immediately and unconditionally like any other top-level code. The section
44
+ now documents the real, tested pattern: register scenes, set properties
45
+ (`room_description`, `exits`), and gate per-scene behavior with a global
46
+ `when scene_enter` handler checked against the `scene` payload value.
47
+ - LANGUAGE.md documented `repeat items as item` for iterating a captured
48
+ list. `repeat` only accepts a numeric count (or `as <var>` for a counter);
49
+ it silently no-ops on a list. The working, tested mechanism is
50
+ `for each item in items`, which is now what's documented.
51
+ - The corrected `for each` example also fixed a second inaccuracy: captured
52
+ list items from `get all into items` have the shape
53
+ `{key, value, type}`, not `{name, ...}` — `{item.name}` never resolved.
54
+ The example now uses `{item.key}`.
55
+ - `send`/`after` payload examples used a bare positional value
56
+ (`send scored 10`) implying it binds to the event's declared payload
57
+ field. It doesn't — only `key=value` tokens are read into the payload; a
58
+ bare value is parsed but silently dropped. Examples now use
59
+ `send scored value=10`.
60
+ - The `event`/`when`/`send` example had the `send` before the matching
61
+ `when` block in program order. Handler registration happens as the
62
+ runtime scans forward through top-level statements, so a `send` that
63
+ fires before its `when` is reached has no handler to call — the example
64
+ now declares `when scored` before `send`ing it.
65
+ - The Web target section claimed `--target web` "generates a self-contained
66
+ HTML file... share it as a single `.html`". It doesn't — the CLI only
67
+ serves the rendered page from a local HTTP server; nothing is written to
68
+ disk. The section now describes the real behavior and points to
69
+ `rosh_lang.targets.web.render_html()` for anyone who wants the raw string
70
+ to save themselves.
71
+ - The Asset Registry section claimed `"ancient carved stone"`, `"carving"`,
72
+ and `"stone"` all fuzzy-match to the `stone` manifest. `"carving"` alone
73
+ actually resolves to the separate `carved_relief` manifest (it's a tag
74
+ there, not on `stone`). Swapped the example for `"pictish stone"`, which
75
+ does resolve to `stone`, and noted the near-miss.
76
+
77
+ ### Release process
78
+
79
+ - Added a `publish-pypi` job to `.github/workflows/ci.yml`: on a `v*` tag
80
+ push, it downloads the exact wheel/sdist the `release-artifacts` job
81
+ already built and verified (not a fresh rebuild), checks the tag matches
82
+ the wheel's version (refuses to publish on a mismatch), then publishes to
83
+ PyPI via Trusted Publishing (OIDC) — no stored token.
84
+ - The private/generated-file leak check now scans the built wheel as well
85
+ as the sdist; previously only the sdist was checked.
86
+ - Removed a stale, contradictory `uv.lock` entry from `.gitignore` — the
87
+ lockfile is (correctly) tracked in git, so ignoring it had no effect.
88
+
89
+ ## 0.9.2 and earlier
90
+
91
+ Not tracked retroactively in this file.
@@ -0,0 +1,263 @@
1
+ # Rosh Language — Agent Context
2
+
3
+ Everything an AI agent needs to write `.rosh` files and work on `rosh-lang/`.
4
+
5
+ ## What Rosh Is
6
+
7
+ A plain-English language: `print "hello"` runs on terminal, browser, and game engine. Same source, different targets. "One script, many worlds."
8
+
9
+ ## How to Run
10
+
11
+ ```bash
12
+ rosh hello.rosh # terminal
13
+ rosh hello.rosh --target web --run # browser
14
+ rosh hello.rosh --target phaser --run # Phaser game
15
+ rosh # REPL
16
+ rosh new game my-game # scaffold a starter file
17
+ ```
18
+
19
+ ## How to Test
20
+
21
+ ```bash
22
+ cd rosh-lang && uv run pytest -q # expect the full suite to pass
23
+ ```
24
+
25
+ After code changes, reinstall:
26
+ ```bash
27
+ uv tool install --from /path/to/rosh-lang rosh-lang --force
28
+ ```
29
+
30
+ ## Complete Syntax Forms
31
+
32
+ ```
33
+ print "text" # output ({var} interpolation)
34
+ print # blank line (no arguments)
35
+ create object player # create object/number/string/list
36
+ set player.x to 0.5 # assign value (arithmetic: score + 1)
37
+ set x to random # random 0.0–1.0 (or: random 0.1 0.9)
38
+ set x to clamp x 0.02 0.8 # constrain to [min, max]
39
+ get player # query state
40
+ get score into saved # capture result into state variable
41
+ get all into items # capture all visible state as structured list
42
+ get count of visitors into n # capture list length as integer
43
+ look # inspect scene
44
+ look player into info # capture inspection result into state
45
+ look programme into stmts # capture statement list (iterate with for each)
46
+ say hello # broadcast text
47
+ when click # event handler block
48
+ set count to count + 1
49
+ end # end block
50
+ on update set x to x + 0.01 # one-line reactive listener
51
+ on keydown when key == " " play sfx # conditional listener
52
+ event scored value # declare named event
53
+ send scored 10 # emit event
54
+ if score > 10 # branching
55
+ print "win!"
56
+ else if score > 5 # else-if chain (single end)
57
+ print "close!"
58
+ else
59
+ print "keep going"
60
+ end
61
+ use score label "Points" # compose widget with config
62
+ go level2 # scene navigation
63
+ connect server wss://... # register connection
64
+ destroy enemy # remove object
65
+ sprite ship "blue spaceship" # procedural pixel art
66
+ sprite ship "https://example.com/ship.png" # URL sprite (any image)
67
+ sound laser "laser shoot" # procedural sound
68
+ play laser # play sound (once/loop/stop)
69
+ animate obj sheet "run.png" frames 4 # spritesheet animation
70
+ after 3 send timeout # delayed event
71
+ background "#1a1a2e" # canvas/scene background (colour or image)
72
+ create scene level1 # scene definition
73
+ define fire_bullet # user-defined function
74
+ set bullet._fire to 1
75
+ end
76
+ do fire_bullet # call a function
77
+ on keydown when key == " " do fire_bullet # call from event
78
+ repeat 5 # counted loop
79
+ print "hello"
80
+ end
81
+ repeat 3 as i # loop with variable (1, 2, 3)
82
+ print "Round {i}"
83
+ end
84
+ ```
85
+
86
+ `into` capture for `get` and `look` is terminal/REPL-first. Browser, Phaser,
87
+ Three.js, Scratch, and world target capture semantics are deferred until those
88
+ targets have explicit `get`/`look` command-state paths.
89
+
90
+ ## Object Properties
91
+
92
+ | Property | Type | Description |
93
+ |----------|------|-------------|
94
+ | `x` | float | Position (0.0–1.0 = %, >1 = px) |
95
+ | `y` | float | Position (0.0–1.0 = %, >1 = px) |
96
+ | `width` | float | Size (default 0.1) |
97
+ | `height` | float | Size (default 0.1) |
98
+ | `color` | string | Hex or named color |
99
+ | `label` | string | Text on object (empty by default) |
100
+ | `sprite` | string | Procedural sprite description or image URL |
101
+ | `rotation` | float | Rotation in degrees (0=up, clockwise positive) |
102
+ | `visible` | int | 0 hides, any other shows (cascades to children) |
103
+ | `vx`, `vy` | float | Velocity (per second) |
104
+ | `text_color` | string | Text color (default #fff) |
105
+ | `font_size` | string | Font size (default 14px) |
106
+ | `_max_output` | int | Max console lines (excess trimmed from top) |
107
+
108
+ ## Events
109
+
110
+ `start`, `update` (dt), `click` (x,y), `click_<name>` (x,y), `keydown` (key), `keyup` (key), `collision` (a,b), `scene_enter` (scene), `scene_exit` (scene), `destroy` (name), `timer_done` (name), `game_start`, `game_over`, `game_restart`.
111
+
112
+ Key-hold: `_keys.ArrowLeft == 1` inside `when update`.
113
+
114
+ ## Game-Building Pattern
115
+
116
+ ```
117
+ # Minimal game: player moves, shoots, scores
118
+ set _max_output to 3
119
+ use score
120
+ use lives count 3
121
+ use player speed 0.03 move x
122
+ sprite player "green spaceship"
123
+
124
+ use bullet count 3 vy -0.5 color "#ffff00"
125
+
126
+ create object enemy
127
+ set enemy.x to 0.45
128
+ set enemy.y to 0.1
129
+ set enemy.width to 0.08
130
+ set enemy.height to 0.06
131
+ sprite enemy "red alien"
132
+
133
+ sound laser "laser shoot"
134
+ sound hit "explosion hit"
135
+
136
+ on keydown when key == " " set bullet._x to player.x
137
+ on keydown when key == " " set bullet._y to player.y
138
+ on keydown when key == " " set bullet._fire to 1
139
+ on keydown when key == " " play laser
140
+
141
+ when collision bullet.* enemy
142
+ set score.value to score.value + 1
143
+ play hit
144
+ end
145
+
146
+ # game_over auto-fires when lives.count hits 0
147
+ when game_over
148
+ print "GAME OVER — Score: {score.value}"
149
+ end
150
+ ```
151
+
152
+ Run: `rosh game.rosh --target web --run`
153
+
154
+ ## Widget Library (22 Widgets)
155
+
156
+ | Widget | Type | Config | Purpose |
157
+ |--------|------|--------|---------|
158
+ | `score` | .py | `anchor theme label x y bg text_color font_size` | Score display (HUD) |
159
+ | `player` | .py | `speed keys move x y width height color clamp_*` | Keyboard ship with auto-movement |
160
+ | `controller` | .py | `target keys touch touch_style speed move help fire fire_key fire_event clamp` | Universal input (keyboard + touch) |
161
+ | `counter` | .rosh | — | Click counter |
162
+ | `timer` | .py | `total running x y bg text_color font_size` | Auto-tick countdown (fires timer_done) |
163
+ | `health-bar` | .py | `max current x y bg text_color font_size` | Health display |
164
+ | `lives` | .py | `count auto_gameover x y bg text_color font_size` | Lives counter (auto game-over at 0) |
165
+ | `button` | .rosh | — | Clickable button |
166
+ | `label` | .rosh | `text x y bg text_color font_size` | Text label |
167
+ | `fps` | .py | `x y bg text_color font_size` | FPS counter |
168
+ | `message` | .rosh | `text x y bg text_color font_size` | Overlay message |
169
+ | `title-screen` | .rosh | `title subtitle bg text_color font_size` | Title screen |
170
+ | `coin` | .rosh | — | Collectible |
171
+ | `grid` | .py | `rows cols size gap color` | Cell grid |
172
+ | `enemy-grid` | .py | `rows cols size gap color` | Enemy formation |
173
+ | `starfield` | .py | `count` | Background stars |
174
+ | `bullet` | .py | `count vx vy color` | Pooled projectiles |
175
+ | `explosion` | .py | `count color` | Pooled explosions |
176
+ | `animation` | .py | `target sheet frames speed mode` | Spritesheet |
177
+ | `game-lifecycle` | .rosh | `title subtitle bg text_color font_size` | Title → playing → over flow |
178
+ | `ball` | .py | `x y size color vx vy walls` | Bouncing ball with wall bounce |
179
+ | `hazard` | .py | `count vx vy color width height sprite spawn_rate` | Auto-spawning obstacle pool |
180
+
181
+ Use: `use <widget> [key value ...]` — config pairs override defaults.
182
+
183
+ ## Architecture
184
+
185
+ ```
186
+ core/model.py → statement dataclasses + Programme
187
+ core/parser.py → text → Programme
188
+ core/runtime.py → execute Programme, state dict, events, if/else, scenes
189
+ core/widgets.py → widget loader: find, namespace-prefix, configure
190
+ media/sprites.py → name + description → data:image/png;base64 (7×9 grid)
191
+ media/sounds.py → name + description → Web Audio synthesis params
192
+ media/sheets.py → PNG spritesheet → list of frame data URIs
193
+ media/assets.py → resolve asset file paths (legacy)
194
+ media/asset_registry.py → semantic registry; find() by name/alias/tag, visibility-gated
195
+ media/asset_providers.py → provider connectors: MockAssetProvider + SketchfabAssetProvider (metadata-only)
196
+ media/asset_requests.py → load unknown-object request queue from generated _assetRequests state
197
+ media/asset_reviews.py → CandidateReview dataclass; validate_review(); review_to_draft_manifest(); candidates_to_review_template()
198
+ media/asset_manifests/ → bundled seed manifests (18 JSON files, MIT-licensed, review_status=featured)
199
+ cli/scaffolder.py → rosh new templates
200
+ cli/cloud.py → rosh.cloud login/create/publish commands
201
+ cli/library_cli.py → rosh library list/info (widget inspector)
202
+ cli/assets_cli.py → rosh assets search [--save review.json] | rosh assets register <review.json> [--output-dir DIR]
203
+ library/ → bundled widgets
204
+ targets/
205
+ terminal.py → print to stdout
206
+ web.py → self-contained HTML page (CSS divs + JS)
207
+ phaser.py → Phaser 3.70.0 game (canvas rendering)
208
+ threejs.py → Three.js 3D scene
209
+ scratch.py → Scratch 3 .sb3 export
210
+ _js_runtime.py → JS_RUNTIME_CORE (state, events) + JS_RUNTIME_DOM (CSS sync)
211
+ _js_runtime_phaser.py → JS_RUNTIME_PHASER (Phaser renderer, replaces DOM)
212
+ _js_runtime_threejs.py → JS_RUNTIME_THREEJS (Three.js renderer)
213
+ _js_codegen.py → compile Programme → JavaScript
214
+ ```
215
+
216
+ ## Rules
217
+
218
+ 1. **Don't add keywords** unless they are documented in the public syntax reference or agreed in an issue first
219
+ 2. **Test after every change**: `cd rosh-lang && uv run pytest -q`
220
+ 3. **Rebuild showcase** after completing build steps: `cd rosh-lang && uv run python tools/build_showcase.py`
221
+ 4. **Licence**: MIT. All widgets must declare `# licence: MIT`
222
+ 5. **Spec before code**: check the README and https://rosh.cloud/docs/syntax before implementing language behavior
223
+ 6. **Commits**: use `type: Short description`, with types like `feat`, `fix`, `docs`, `test`, `refactor`, or `chore`
224
+ 7. **Reinstall after changes**: `uv tool install --from .../rosh-lang rosh-lang --force`
225
+
226
+ ## Sprite Descriptions
227
+
228
+ Two modes:
229
+
230
+ 1. **Procedural** — description keywords generate 7×9 pixel art. Color keywords (red, blue, green, etc.) and shape keywords (spaceship, alien, ball, bullet, crystal, star). Example: `sprite ship "blue spaceship"`
231
+
232
+ 2. **URL** — any `http://` or `https://` URL loads the image directly. Works on all targets (web, phaser, threejs). Example: `sprite ship "https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f680.png"`
233
+
234
+ URL sprites and procedural sprites can be mixed in the same program. If a URL fails to load, the object falls back to a colored rectangle.
235
+
236
+ **Rendering differences:**
237
+ - Procedural sprites: fill the entire width×height box, pixelated upscaling (crisp edges)
238
+ - URL sprites: `contain` fit (preserves aspect ratio), smooth rendering
239
+ - `rotation` property: degrees, 0=up, clockwise positive (all targets)
240
+
241
+ ## Sound Families
242
+
243
+ 9 preset families matched by keyword: `laser`, `explosion`, `coin`, `jump`, `hit`, `powerup`, `gameover`, `click`, `win`.
244
+
245
+ ## Bullet Pool Pattern
246
+
247
+ ```
248
+ use bullet count 3 vy -0.5 color "#ffff00"
249
+
250
+ # Fire: set position then flag
251
+ on keydown when key == " " set bullet._x to player.x
252
+ on keydown when key == " " set bullet._y to player.y
253
+ on keydown when key == " " set bullet._fire to 1
254
+
255
+ # Collision: bullet.* matches all pool members
256
+ when collision bullet.* enemy
257
+ set score.value to score.value + 1
258
+ end
259
+ ```
260
+
261
+ ## Collision Wildcard
262
+
263
+ `when collision bullet.* enemy` — the `*` matches any suffix. Works in either position.
@@ -0,0 +1,80 @@
1
+ # Contributing to Rosh
2
+
3
+ Thanks for your interest in contributing to Rosh.
4
+
5
+ ## Licence
6
+
7
+ Rosh is released under the [MIT License](LICENSE). By contributing, you agree that your contributions will be licensed under MIT.
8
+
9
+ ## Getting Started
10
+
11
+ ### Prerequisites
12
+
13
+ - Python 3.10+
14
+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip
15
+
16
+ ### Setup
17
+
18
+ ```bash
19
+ git clone https://github.com/rosh-studio/rosh-lang.git
20
+ cd rosh-lang
21
+ uv sync --all-extras
22
+ ```
23
+
24
+ ### Running Tests
25
+
26
+ ```bash
27
+ uv run pytest -q
28
+ ```
29
+
30
+ All tests must pass before submitting a pull request.
31
+
32
+ The standalone public repository also builds wheel and source-distribution
33
+ artifacts in CI to prevent local caches or generated files entering a release.
34
+
35
+ ### Installing Locally
36
+
37
+ ```bash
38
+ uv tool install --from . rosh-lang --force
39
+ ```
40
+
41
+ Then run with `rosh`.
42
+
43
+ ## What You Can Contribute
44
+
45
+ - **Bug reports** — open an issue with reproduction steps
46
+ - **Example programs** — new `.rosh` files in `examples/`
47
+ - **Widget improvements** — enhancements to existing widgets in `src/rosh_lang/library/`
48
+ - **Documentation** — improvements to README, inline comments, or examples
49
+ - **Test coverage** — new test cases for edge cases or uncovered paths
50
+ - **Editor support** — syntax and snippet updates in `editor/vscode/`
51
+
52
+ ## What Requires Discussion First
53
+
54
+ - **New keywords** — language additions must be discussed and documented in the public syntax reference before implementation
55
+ - **New targets** — adding a new compilation target (e.g. Unity, Godot) is a significant change
56
+ - **Architecture changes** — changes to the parser, model, or code generation pipeline
57
+
58
+ Please open an issue to discuss before submitting a PR for these.
59
+
60
+ ## Code Style
61
+
62
+ - Use `pathlib.Path` over `os.path`
63
+ - Use f-strings over `.format()` or `%`
64
+ - Use type hints
65
+ - Keep it simple — no over-engineering
66
+
67
+ ## Commit Messages
68
+
69
+ ```text
70
+ type: Short description
71
+
72
+ Co-Authored-By: Your Name <your@email.com>
73
+ ```
74
+
75
+ Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`.
76
+
77
+ ## Questions?
78
+
79
+ - Discord: [discord.gg/gevBPucznD](https://discord.gg/gevBPucznD)
80
+ - Email: [info@rosh.cloud](mailto:info@rosh.cloud)