shellsim 0.1.8__tar.gz → 0.1.10__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 (184) hide show
  1. {shellsim-0.1.8 → shellsim-0.1.10}/Cargo.lock +1 -1
  2. {shellsim-0.1.8 → shellsim-0.1.10}/Cargo.toml +1 -1
  3. {shellsim-0.1.8 → shellsim-0.1.10}/PKG-INFO +1 -1
  4. {shellsim-0.1.8 → shellsim-0.1.10}/python/native/Cargo.lock +2 -2
  5. {shellsim-0.1.8 → shellsim-0.1.10}/python/native/Cargo.toml +1 -1
  6. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/ast.rs +17 -3
  7. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/bytecode.rs +13 -0
  8. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/compiler.rs +223 -10
  9. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/heap.rs +8 -1
  10. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/lexer.rs +2 -0
  11. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/mod.rs +444 -28
  12. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/native.rs +10 -0
  13. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/object_model.rs +4 -0
  14. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/parser.rs +47 -9
  15. shellsim-0.1.10/src/python/stdlib/asyncio.rs +47 -0
  16. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/core.rs +45 -2
  17. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/frozen.rs +2 -0
  18. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/mod.rs +3 -1
  19. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/pytest.rs +4 -4
  20. shellsim-0.1.10/src/python/stdlib/source/asyncio.py +364 -0
  21. shellsim-0.1.10/src/python/stdlib/source/pytest.py +36 -0
  22. shellsim-0.1.10/src/python/stdlib/typing.rs +136 -0
  23. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/token.rs +2 -0
  24. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/calls.rs +2 -1
  25. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/dispatch.rs +65 -0
  26. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/iteration.rs +27 -4
  27. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/namespace.rs +10 -0
  28. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/native_runtime.rs +99 -1
  29. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/objects.rs +26 -0
  30. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm.rs +4 -0
  31. shellsim-0.1.8/src/python/stdlib/typing.rs +0 -45
  32. {shellsim-0.1.8 → shellsim-0.1.10}/LICENSE +0 -0
  33. {shellsim-0.1.8 → shellsim-0.1.10}/README.md +0 -0
  34. {shellsim-0.1.8 → shellsim-0.1.10}/pyproject.toml +0 -0
  35. {shellsim-0.1.8 → shellsim-0.1.10}/python/native/src/lib.rs +0 -0
  36. {shellsim-0.1.8 → shellsim-0.1.10}/python/shellsim/__init__.py +0 -0
  37. {shellsim-0.1.8 → shellsim-0.1.10}/python/shellsim/__main__.py +0 -0
  38. {shellsim-0.1.8 → shellsim-0.1.10}/python/shellsim/_api.py +0 -0
  39. {shellsim-0.1.8 → shellsim-0.1.10}/python/shellsim/_cli.py +0 -0
  40. {shellsim-0.1.8 → shellsim-0.1.10}/python/shellsim/py.typed +0 -0
  41. {shellsim-0.1.8 → shellsim-0.1.10}/python/shellsim/python.py +0 -0
  42. {shellsim-0.1.8 → shellsim-0.1.10}/rust-toolchain.toml +0 -0
  43. {shellsim-0.1.8 → shellsim-0.1.10}/src/bin/shellsim-python.rs +0 -0
  44. {shellsim-0.1.8 → shellsim-0.1.10}/src/clock.rs +0 -0
  45. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/archives.rs +0 -0
  46. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/awk/ast.rs +0 -0
  47. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/awk/eval.rs +0 -0
  48. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/awk/lexer.rs +0 -0
  49. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/awk/parser.rs +0 -0
  50. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/awk.rs +0 -0
  51. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/builtins.rs +0 -0
  52. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/dd.rs +0 -0
  53. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/echo.rs +0 -0
  54. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/find.rs +0 -0
  55. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/fs.rs +0 -0
  56. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/apply.rs +0 -0
  57. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/commit.rs +0 -0
  58. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/compare.rs +0 -0
  59. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/config.rs +0 -0
  60. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/conflict.rs +0 -0
  61. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/diff.rs +0 -0
  62. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/ignore.rs +0 -0
  63. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/import.rs +0 -0
  64. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/log.rs +0 -0
  65. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/merge.rs +0 -0
  66. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/mod.rs +0 -0
  67. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/plumbing.rs +0 -0
  68. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/rebase.rs +0 -0
  69. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/refs.rs +0 -0
  70. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/repo.rs +0 -0
  71. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/stash.rs +0 -0
  72. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/switch.rs +0 -0
  73. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/git/worktree.rs +0 -0
  74. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/hashing.rs +0 -0
  75. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/makecmd.rs +0 -0
  76. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/mod.rs +0 -0
  77. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/net.rs +0 -0
  78. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/options.rs +0 -0
  79. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/patch.rs +0 -0
  80. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/pkg.rs +0 -0
  81. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/printf.rs +0 -0
  82. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/proc.rs +0 -0
  83. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/regex_compat.rs +0 -0
  84. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/rgcmd.rs +0 -0
  85. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/sed.rs +0 -0
  86. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/sort.rs +0 -0
  87. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/system.rs +0 -0
  88. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/tarcmd.rs +0 -0
  89. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/text.rs +0 -0
  90. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/unavailable.rs +0 -0
  91. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/util.rs +0 -0
  92. {shellsim-0.1.8 → shellsim-0.1.10}/src/commands/zipcmd.rs +0 -0
  93. {shellsim-0.1.8 → shellsim-0.1.10}/src/descriptors.rs +0 -0
  94. {shellsim-0.1.8 → shellsim-0.1.10}/src/exec.rs +0 -0
  95. {shellsim-0.1.8 → shellsim-0.1.10}/src/expand.rs +0 -0
  96. {shellsim-0.1.8 → shellsim-0.1.10}/src/harness.rs +0 -0
  97. {shellsim-0.1.8 → shellsim-0.1.10}/src/harness_manager.rs +0 -0
  98. {shellsim-0.1.8 → shellsim-0.1.10}/src/hashes.rs +0 -0
  99. {shellsim-0.1.8 → shellsim-0.1.10}/src/host_ingest.rs +0 -0
  100. {shellsim-0.1.8 → shellsim-0.1.10}/src/interp.rs +0 -0
  101. {shellsim-0.1.8 → shellsim-0.1.10}/src/jqcmd.rs +0 -0
  102. {shellsim-0.1.8 → shellsim-0.1.10}/src/lib.rs +0 -0
  103. {shellsim-0.1.8 → shellsim-0.1.10}/src/main.rs +0 -0
  104. {shellsim-0.1.8 → shellsim-0.1.10}/src/mcp.rs +0 -0
  105. {shellsim-0.1.8 → shellsim-0.1.10}/src/net.rs +0 -0
  106. {shellsim-0.1.8 → shellsim-0.1.10}/src/netcmd.rs +0 -0
  107. {shellsim-0.1.8 → shellsim-0.1.10}/src/process.rs +0 -0
  108. {shellsim-0.1.8 → shellsim-0.1.10}/src/pseudo_fs.rs +0 -0
  109. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/filesystem.rs +0 -0
  110. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/http.rs +0 -0
  111. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/mapping.rs +0 -0
  112. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/number.rs +0 -0
  113. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/process.rs +0 -0
  114. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/protocol.rs +0 -0
  115. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/slice.rs +0 -0
  116. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/source.rs +0 -0
  117. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/argparse.rs +0 -0
  118. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/base64.rs +0 -0
  119. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/bisect.rs +0 -0
  120. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/collections.rs +0 -0
  121. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/dataclasses.rs +0 -0
  122. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/enum.rs +0 -0
  123. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/functools.rs +0 -0
  124. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/hashlib.rs +0 -0
  125. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/heapq.rs +0 -0
  126. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/http.rs +0 -0
  127. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/importlib.rs +0 -0
  128. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/itertools.rs +0 -0
  129. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/json.rs +0 -0
  130. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/math.rs +0 -0
  131. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/numpy.rs +0 -0
  132. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/os.rs +0 -0
  133. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/re.rs +0 -0
  134. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/abc.py +0 -0
  135. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/base64.py +0 -0
  136. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/cmath.py +0 -0
  137. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/codecs.py +0 -0
  138. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/collections.py +0 -0
  139. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/complex.py +0 -0
  140. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/copy.py +0 -0
  141. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/csv.py +0 -0
  142. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/datetime.py +0 -0
  143. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/glob.py +0 -0
  144. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/hashlib.py +0 -0
  145. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/http.py +0 -0
  146. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/http_client.py +0 -0
  147. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/importlib.py +0 -0
  148. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/io.py +0 -0
  149. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/json.py +0 -0
  150. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/logging.py +0 -0
  151. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/numpy_random.py +0 -0
  152. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/os.py +0 -0
  153. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/pathlib.py +0 -0
  154. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/random.py +0 -0
  155. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/shutil.py +0 -0
  156. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/statistics.py +0 -0
  157. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/struct.py +0 -0
  158. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/subprocess.py +0 -0
  159. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/tempfile.py +0 -0
  160. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/textwrap.py +0 -0
  161. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/urllib.py +0 -0
  162. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/urllib_error.py +0 -0
  163. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/urllib_request.py +0 -0
  164. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/uuid.py +0 -0
  165. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/zipfile.py +0 -0
  166. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/source/zlib.py +0 -0
  167. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/string.rs +0 -0
  168. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/struct.rs +0 -0
  169. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/subprocess.rs +0 -0
  170. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/sys.rs +0 -0
  171. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/time.rs +0 -0
  172. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/unittest.rs +0 -0
  173. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/vfs.rs +0 -0
  174. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/stdlib/zlib.rs +0 -0
  175. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/string.rs +0 -0
  176. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/host.rs +0 -0
  177. {shellsim-0.1.8 → shellsim-0.1.10}/src/python/vm/operations.rs +0 -0
  178. {shellsim-0.1.8 → shellsim-0.1.10}/src/resources.rs +0 -0
  179. {shellsim-0.1.8 → shellsim-0.1.10}/src/sandbox.rs +0 -0
  180. {shellsim-0.1.8 → shellsim-0.1.10}/src/scenario.rs +0 -0
  181. {shellsim-0.1.8 → shellsim-0.1.10}/src/scheduler.rs +0 -0
  182. {shellsim-0.1.8 → shellsim-0.1.10}/src/shell.rs +0 -0
  183. {shellsim-0.1.8 → shellsim-0.1.10}/src/telemetry.rs +0 -0
  184. {shellsim-0.1.8 → shellsim-0.1.10}/src/vfs.rs +0 -0
@@ -330,7 +330,7 @@ dependencies = [
330
330
 
331
331
  [[package]]
332
332
  name = "shellsim"
333
- version = "0.1.8"
333
+ version = "0.1.10"
334
334
  dependencies = [
335
335
  "base64",
336
336
  "crc32fast",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "shellsim"
3
- version = "0.1.8"
3
+ version = "0.1.10"
4
4
  edition = "2021"
5
5
  description = "In-process BusyBox-like environment for RL rollouts and agent tests"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellsim
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: License :: OSI Approved :: Apache Software License
6
6
  Classifier: Operating System :: MacOS :: MacOS X
@@ -406,7 +406,7 @@ dependencies = [
406
406
 
407
407
  [[package]]
408
408
  name = "shellsim"
409
- version = "0.1.8"
409
+ version = "0.1.10"
410
410
  dependencies = [
411
411
  "base64",
412
412
  "crc32fast",
@@ -427,7 +427,7 @@ dependencies = [
427
427
 
428
428
  [[package]]
429
429
  name = "shellsim-python-bindings"
430
- version = "0.1.8"
430
+ version = "0.1.10"
431
431
  dependencies = [
432
432
  "pyo3",
433
433
  "serde",
@@ -2,7 +2,7 @@
2
2
 
3
3
  [package]
4
4
  name = "shellsim-python-bindings"
5
- version = "0.1.8"
5
+ version = "0.1.10"
6
6
  edition = "2021"
7
7
  description = "Python bindings for shellsim"
8
8
  license = "Apache-2.0"
@@ -50,10 +50,17 @@ pub enum StatementKind {
50
50
  body: Vec<Statement>,
51
51
  otherwise: Vec<Statement>,
52
52
  },
53
+ AsyncFor {
54
+ target: AssignmentTarget,
55
+ iterable: Expression,
56
+ body: Vec<Statement>,
57
+ otherwise: Vec<Statement>,
58
+ },
53
59
  Function {
54
60
  name: String,
55
61
  parameters: Vec<Parameter>,
56
62
  body: Vec<Statement>,
63
+ is_async: bool,
57
64
  },
58
65
  Class {
59
66
  name: String,
@@ -91,6 +98,11 @@ pub enum StatementKind {
91
98
  target: Option<AssignmentTarget>,
92
99
  body: Vec<Statement>,
93
100
  },
101
+ AsyncWith {
102
+ context: Expression,
103
+ target: Option<AssignmentTarget>,
104
+ body: Vec<Statement>,
105
+ },
94
106
  }
95
107
 
96
108
  #[derive(Clone, Debug, PartialEq)]
@@ -122,11 +134,13 @@ impl StatementKind {
122
134
  Self::If { .. }
123
135
  | Self::While { .. }
124
136
  | Self::For { .. }
137
+ | Self::AsyncFor { .. }
125
138
  | Self::Function { .. }
126
139
  | Self::Class { .. }
127
140
  | Self::Decorated { .. }
128
141
  | Self::Try { .. }
129
142
  | Self::With { .. }
143
+ | Self::AsyncWith { .. }
130
144
  )
131
145
  }
132
146
  }
@@ -173,14 +187,14 @@ pub enum ExpressionKind {
173
187
  value: Box<Expression>,
174
188
  clauses: Vec<ComprehensionClause>,
175
189
  },
176
- /// The VM deliberately materializes this as a list for now. This gives
177
- /// generator expressions an ordinary iterable and isolated comprehension
178
- /// scope while keeping the bounded-resource contract simple and explicit.
190
+ /// A lazy comprehension evaluated in its own generator frame.
179
191
  GeneratorExpression {
180
192
  element: Box<Expression>,
181
193
  clauses: Vec<ComprehensionClause>,
182
194
  },
183
195
  Yield(Option<Box<Expression>>),
196
+ YieldFrom(Box<Expression>),
197
+ Await(Box<Expression>),
184
198
  Attribute {
185
199
  value: Box<Expression>,
186
200
  name: String,
@@ -65,6 +65,7 @@ pub struct Code {
65
65
  #[derive(Clone, Debug, PartialEq, Eq)]
66
66
  pub struct CallSignature {
67
67
  pub is_generator: bool,
68
+ pub is_coroutine: bool,
68
69
  pub positional_count: usize,
69
70
  pub variadic_slot: Option<usize>,
70
71
  pub default_slots: Box<[usize]>,
@@ -223,9 +224,12 @@ pub enum Opcode {
223
224
  Reraise,
224
225
  Raise(bool),
225
226
  Yield,
227
+ AwaitResult,
226
228
  WithEnter,
227
229
  WithExit,
228
230
  WithExitException,
231
+ AsyncWithExitException,
232
+ AsyncWithFinishException,
229
233
  PopExpression,
230
234
  Halt,
231
235
  }
@@ -321,9 +325,13 @@ pub enum Operation {
321
325
  Raise(bool),
322
326
  /// Suspend a generator frame and return the value on top of the stack.
323
327
  Yield,
328
+ /// Unwrap the scheduler outcome sent into a suspended coroutine.
329
+ AwaitResult,
324
330
  WithEnter,
325
331
  WithExit,
326
332
  WithExitException,
333
+ AsyncWithExitException,
334
+ AsyncWithFinishException,
327
335
  PopExpression,
328
336
  Halt,
329
337
  }
@@ -489,9 +497,12 @@ impl CodeBuilder {
489
497
  Operation::Reraise => Opcode::Reraise,
490
498
  Operation::Raise(cause) => Opcode::Raise(cause),
491
499
  Operation::Yield => Opcode::Yield,
500
+ Operation::AwaitResult => Opcode::AwaitResult,
492
501
  Operation::WithEnter => Opcode::WithEnter,
493
502
  Operation::WithExit => Opcode::WithExit,
494
503
  Operation::WithExitException => Opcode::WithExitException,
504
+ Operation::AsyncWithExitException => Opcode::AsyncWithExitException,
505
+ Operation::AsyncWithFinishException => Opcode::AsyncWithFinishException,
495
506
  Operation::PopExpression => Opcode::PopExpression,
496
507
  Operation::Halt => Opcode::Halt,
497
508
  }
@@ -503,6 +514,7 @@ impl CodeBuilder {
503
514
  spans: Vec<Span>,
504
515
  parameters: Vec<Parameter>,
505
516
  local_names: Vec<String>,
517
+ is_coroutine: bool,
506
518
  ) -> CodeRef {
507
519
  let positional_count = parameters
508
520
  .iter()
@@ -512,6 +524,7 @@ impl CodeBuilder {
512
524
  is_generator: instructions
513
525
  .iter()
514
526
  .any(|instruction| matches!(instruction.opcode, Opcode::Yield)),
527
+ is_coroutine,
515
528
  positional_count,
516
529
  variadic_slot: parameters.iter().position(|parameter| parameter.variadic),
517
530
  default_slots: parameters
@@ -15,6 +15,7 @@ pub fn compile(program: Program) -> CodeRef {
15
15
  finalizers: Vec::new(),
16
16
  protected_regions: Vec::new(),
17
17
  in_function: false,
18
+ is_coroutine: false,
18
19
  globals: HashSet::new(),
19
20
  nonlocals: HashSet::new(),
20
21
  named_expression: NamedExpressionContext::local(),
@@ -38,6 +39,7 @@ struct Compiler {
38
39
  /// at compile time rather than losing that state in a suspended generator.
39
40
  protected_regions: Vec<&'static str>,
40
41
  in_function: bool,
42
+ is_coroutine: bool,
41
43
  globals: HashSet<String>,
42
44
  nonlocals: HashSet<String>,
43
45
  named_expression: NamedExpressionContext,
@@ -155,7 +157,7 @@ impl Compiler {
155
157
  });
156
158
  spans.push(instruction.span);
157
159
  }
158
- builder.finish(bytecode, spans, parameters, local_names)
160
+ builder.finish(bytecode, spans, parameters, local_names, self.is_coroutine)
159
161
  }
160
162
 
161
163
  fn statements(&mut self, statements: Vec<Statement>) {
@@ -320,10 +322,70 @@ impl Compiler {
320
322
  let finished = self.instructions.len();
321
323
  self.finish_loop(finished);
322
324
  }
325
+ StatementKind::AsyncFor {
326
+ target,
327
+ iterable,
328
+ body,
329
+ otherwise,
330
+ } => {
331
+ let iterator_name =
332
+ format!("$__shellsim_async_iterator_{}", self.instructions.len());
333
+ self.expression(iterable);
334
+ self.emit(Operation::LoadAttribute("__aiter__".into()), span);
335
+ self.emit(
336
+ Operation::Call {
337
+ positional: 0,
338
+ keywords: Vec::new(),
339
+ starred: Vec::new(),
340
+ },
341
+ span,
342
+ );
343
+ self.emit(Operation::StoreName(iterator_name.clone()), span);
344
+ let next = self.instructions.len();
345
+ let begin = self.emit(Operation::TryBegin(usize::MAX), span);
346
+ self.emit(Operation::LoadName(iterator_name), span);
347
+ self.emit(Operation::LoadAttribute("__anext__".into()), span);
348
+ self.emit(
349
+ Operation::Call {
350
+ positional: 0,
351
+ keywords: Vec::new(),
352
+ starred: Vec::new(),
353
+ },
354
+ span,
355
+ );
356
+ self.emit(Operation::Yield, span);
357
+ self.emit(Operation::AwaitResult, span);
358
+ self.emit(Operation::TryEnd, span);
359
+ self.store_target(target, span);
360
+ self.loops.push(LoopContext {
361
+ continue_target: next,
362
+ iterator_on_stack: false,
363
+ finalizer_depth: self.finalizers.len(),
364
+ breaks: Vec::new(),
365
+ });
366
+ self.statements(body);
367
+ self.emit(Operation::Jump(next), span);
368
+ let handler = self.instructions.len();
369
+ self.patch_try_begin(begin, handler);
370
+ self.emit(Operation::LoadName("StopAsyncIteration".into()), span);
371
+ self.emit(Operation::MatchException { typed: true }, span);
372
+ let rethrow = self.emit(Operation::PopJumpIfFalse(usize::MAX), span);
373
+ self.emit(Operation::PopTop, span);
374
+ self.emit(Operation::ClearException, span);
375
+ self.statements(otherwise);
376
+ let done = self.emit(Operation::Jump(usize::MAX), span);
377
+ self.patch_jump(rethrow, self.instructions.len());
378
+ self.emit(Operation::PopTop, span);
379
+ self.emit(Operation::Reraise, span);
380
+ let finished = self.instructions.len();
381
+ self.patch_jump(done, finished);
382
+ self.finish_loop(finished);
383
+ }
323
384
  StatementKind::Function {
324
385
  name,
325
386
  parameters,
326
387
  body,
388
+ is_async,
327
389
  } => {
328
390
  let defaults = parameters
329
391
  .iter()
@@ -338,6 +400,7 @@ impl Compiler {
338
400
  finalizers: Vec::new(),
339
401
  protected_regions: Vec::new(),
340
402
  in_function: true,
403
+ is_coroutine: is_async,
341
404
  globals: HashSet::new(),
342
405
  nonlocals: HashSet::new(),
343
406
  named_expression: NamedExpressionContext::local(),
@@ -391,6 +454,7 @@ impl Compiler {
391
454
  finalizers: Vec::new(),
392
455
  protected_regions: Vec::new(),
393
456
  in_function: false,
457
+ is_coroutine: false,
394
458
  globals: HashSet::new(),
395
459
  nonlocals: HashSet::new(),
396
460
  named_expression: NamedExpressionContext::local(),
@@ -660,6 +724,60 @@ impl Compiler {
660
724
  self.emit(Operation::WithExitException, span);
661
725
  self.patch_jump(done, self.instructions.len());
662
726
  }
727
+ StatementKind::AsyncWith {
728
+ context,
729
+ target,
730
+ body,
731
+ } => {
732
+ let context_name = format!("$__shellsim_async_context_{}", self.instructions.len());
733
+ self.expression(context);
734
+ self.emit(Operation::StoreName(context_name.clone()), span);
735
+ self.emit(Operation::LoadName(context_name.clone()), span);
736
+ self.emit(Operation::LoadAttribute("__aenter__".into()), span);
737
+ self.emit(
738
+ Operation::Call {
739
+ positional: 0,
740
+ keywords: Vec::new(),
741
+ starred: Vec::new(),
742
+ },
743
+ span,
744
+ );
745
+ self.emit(Operation::Yield, span);
746
+ self.emit(Operation::AwaitResult, span);
747
+ if let Some(target) = target {
748
+ self.store_target(target, span);
749
+ } else {
750
+ self.emit(Operation::PopTop, span);
751
+ }
752
+ let begin = self.emit(Operation::TryBegin(usize::MAX), span);
753
+ self.statements(body);
754
+ self.emit(Operation::TryEnd, span);
755
+ self.emit(Operation::LoadName(context_name.clone()), span);
756
+ self.emit(Operation::LoadAttribute("__aexit__".into()), span);
757
+ for _ in 0..3 {
758
+ self.emit(Operation::LoadConstant(Constant::None), span);
759
+ }
760
+ self.emit(
761
+ Operation::Call {
762
+ positional: 3,
763
+ keywords: Vec::new(),
764
+ starred: vec![false; 3],
765
+ },
766
+ span,
767
+ );
768
+ self.emit(Operation::Yield, span);
769
+ self.emit(Operation::AwaitResult, span);
770
+ self.emit(Operation::PopTop, span);
771
+ let done = self.emit(Operation::Jump(usize::MAX), span);
772
+ let handler = self.instructions.len();
773
+ self.patch_try_begin(begin, handler);
774
+ self.emit(Operation::LoadName(context_name), span);
775
+ self.emit(Operation::AsyncWithExitException, span);
776
+ self.emit(Operation::Yield, span);
777
+ self.emit(Operation::AwaitResult, span);
778
+ self.emit(Operation::AsyncWithFinishException, span);
779
+ self.patch_jump(done, self.instructions.len());
780
+ }
663
781
  }
664
782
  }
665
783
 
@@ -936,10 +1054,7 @@ impl Compiler {
936
1054
  self.emit_dict_comprehension(*key, *value, clauses, span);
937
1055
  }
938
1056
  ExpressionKind::GeneratorExpression { element, clauses } => {
939
- // The first implementation intentionally materializes generator expressions.
940
- // It is still a separate function scope (like CPython's generator frame), and
941
- // all iteration remains metered by the ordinary bytecode loop.
942
- self.emit_comprehension(ComprehensionKind::List, *element, clauses, span);
1057
+ self.emit_generator_expression(*element, clauses, span);
943
1058
  }
944
1059
  ExpressionKind::Yield(value) => {
945
1060
  if !self.in_function {
@@ -947,11 +1062,6 @@ impl Compiler {
947
1062
  Operation::RuntimeError("'yield' outside function".into()),
948
1063
  span,
949
1064
  );
950
- } else if let Some(region) = self.protected_regions.last() {
951
- self.emit(
952
- Operation::RuntimeError(format!("yield is not supported inside a {region} cleanup region; suspending here would lose exception or context-manager state")),
953
- span,
954
- );
955
1065
  } else {
956
1066
  if let Some(value) = value {
957
1067
  self.expression(*value);
@@ -961,6 +1071,36 @@ impl Compiler {
961
1071
  self.emit(Operation::Yield, span);
962
1072
  }
963
1073
  }
1074
+ ExpressionKind::YieldFrom(value) => {
1075
+ if !self.in_function {
1076
+ self.emit(
1077
+ Operation::RuntimeError("'yield from' outside function".into()),
1078
+ span,
1079
+ );
1080
+ } else {
1081
+ self.expression(*value);
1082
+ self.emit(Operation::GetIterator, span);
1083
+ let next = self.emit(Operation::ForIterator(usize::MAX), span);
1084
+ self.emit(Operation::Yield, span);
1085
+ self.emit(Operation::PopTop, span);
1086
+ self.emit(Operation::Jump(next), span);
1087
+ let exhausted = self.instructions.len();
1088
+ self.patch_jump(next, exhausted);
1089
+ self.emit(Operation::LoadConstant(Constant::None), span);
1090
+ }
1091
+ }
1092
+ ExpressionKind::Await(value) => {
1093
+ if !self.is_coroutine {
1094
+ self.emit(
1095
+ Operation::RuntimeError("'await' outside async function".into()),
1096
+ span,
1097
+ );
1098
+ } else {
1099
+ self.expression(*value);
1100
+ self.emit(Operation::Yield, span);
1101
+ self.emit(Operation::AwaitResult, span);
1102
+ }
1103
+ }
964
1104
  ExpressionKind::Attribute { value, name } => {
965
1105
  self.expression(*value);
966
1106
  self.emit(Operation::LoadAttribute(name), span);
@@ -1032,6 +1172,7 @@ impl Compiler {
1032
1172
  finalizers: Vec::new(),
1033
1173
  protected_regions: Vec::new(),
1034
1174
  in_function: true,
1175
+ is_coroutine: false,
1035
1176
  globals: HashSet::new(),
1036
1177
  nonlocals: HashSet::new(),
1037
1178
  named_expression: NamedExpressionContext::local(),
@@ -1180,6 +1321,7 @@ impl Compiler {
1180
1321
  finalizers: Vec::new(),
1181
1322
  protected_regions: Vec::new(),
1182
1323
  in_function: true,
1324
+ is_coroutine: false,
1183
1325
  globals: HashSet::new(),
1184
1326
  nonlocals: HashSet::new(),
1185
1327
  named_expression,
@@ -1216,6 +1358,47 @@ impl Compiler {
1216
1358
  );
1217
1359
  }
1218
1360
 
1361
+ fn emit_generator_expression(
1362
+ &mut self,
1363
+ element: Expression,
1364
+ clauses: Vec<ComprehensionClause>,
1365
+ span: Span,
1366
+ ) {
1367
+ let named_expression = self.comprehension_named_expression_context();
1368
+ let mut nested = Compiler {
1369
+ instructions: Vec::new(),
1370
+ loops: Vec::new(),
1371
+ finalizers: Vec::new(),
1372
+ protected_regions: Vec::new(),
1373
+ in_function: true,
1374
+ is_coroutine: false,
1375
+ globals: HashSet::new(),
1376
+ nonlocals: HashSet::new(),
1377
+ named_expression,
1378
+ is_class_scope: false,
1379
+ structural_depth: self.structural_depth,
1380
+ };
1381
+ nested.emit_generator_comprehension_body(&clauses, 0, &element, span);
1382
+ nested.emit(Operation::LoadConstant(Constant::None), span);
1383
+ nested.emit(Operation::Return, span);
1384
+ self.emit(
1385
+ Operation::MakeFunction {
1386
+ name: "<genexpr>".into(),
1387
+ code: nested.finish(Vec::new()),
1388
+ defaults: 0,
1389
+ },
1390
+ span,
1391
+ );
1392
+ self.emit(
1393
+ Operation::Call {
1394
+ positional: 0,
1395
+ keywords: Vec::new(),
1396
+ starred: Vec::new(),
1397
+ },
1398
+ span,
1399
+ );
1400
+ }
1401
+
1219
1402
  fn emit_dict_comprehension(
1220
1403
  &mut self,
1221
1404
  key: Expression,
@@ -1230,6 +1413,7 @@ impl Compiler {
1230
1413
  finalizers: Vec::new(),
1231
1414
  protected_regions: Vec::new(),
1232
1415
  in_function: true,
1416
+ is_coroutine: false,
1233
1417
  globals: HashSet::new(),
1234
1418
  nonlocals: HashSet::new(),
1235
1419
  named_expression,
@@ -1307,6 +1491,35 @@ impl Compiler {
1307
1491
  self.patch_jump(next, exhausted);
1308
1492
  }
1309
1493
 
1494
+ fn emit_generator_comprehension_body(
1495
+ &mut self,
1496
+ clauses: &[ComprehensionClause],
1497
+ index: usize,
1498
+ element: &Expression,
1499
+ span: Span,
1500
+ ) {
1501
+ let clause = &clauses[index];
1502
+ self.expression(clause.iterable.clone());
1503
+ self.emit(Operation::GetIterator, span);
1504
+ let next = self.emit(Operation::ForIterator(usize::MAX), span);
1505
+ self.store_target(clause.target.clone(), span);
1506
+ for condition in &clause.conditions {
1507
+ self.expression(condition.clone());
1508
+ let skip = self.emit(Operation::PopJumpIfFalse(usize::MAX), span);
1509
+ self.patch_jump(skip, next);
1510
+ }
1511
+ if index + 1 < clauses.len() {
1512
+ self.emit_generator_comprehension_body(clauses, index + 1, element, span);
1513
+ } else {
1514
+ self.expression(element.clone());
1515
+ self.emit(Operation::Yield, span);
1516
+ self.emit(Operation::PopTop, span);
1517
+ }
1518
+ self.emit(Operation::Jump(next), span);
1519
+ let exhausted = self.instructions.len();
1520
+ self.patch_jump(next, exhausted);
1521
+ }
1522
+
1310
1523
  fn emit_dict_comprehension_body(
1311
1524
  &mut self,
1312
1525
  clauses: &[ComprehensionClause],
@@ -212,6 +212,7 @@ pub enum Object {
212
212
  stack: Vec<Value>,
213
213
  exhausted: bool,
214
214
  running: bool,
215
+ return_value: Value,
215
216
  },
216
217
  Module {
217
218
  name: String,
@@ -1430,9 +1431,15 @@ fn trace_object(
1430
1431
  trace_value(*callable, object_work);
1431
1432
  trace_value(*sentinel, object_work);
1432
1433
  }
1433
- Object::Generator { scope, stack, .. } => {
1434
+ Object::Generator {
1435
+ scope,
1436
+ stack,
1437
+ return_value,
1438
+ ..
1439
+ } => {
1434
1440
  scope_work.push(*scope);
1435
1441
  trace_values(stack.iter().copied(), object_work);
1442
+ trace_value(*return_value, object_work);
1436
1443
  }
1437
1444
  Object::Module { scope, .. } => scope_work.push(*scope),
1438
1445
  Object::Array { storage, .. } => object_work.push(*storage),
@@ -280,6 +280,8 @@ impl<'a> Lexer<'a> {
280
280
  "raise" => TokenKind::Raise,
281
281
  "with" => TokenKind::With,
282
282
  "yield" => TokenKind::Yield,
283
+ "async" => TokenKind::Async,
284
+ "await" => TokenKind::Await,
283
285
  name => TokenKind::Name((*name).to_string()),
284
286
  }
285
287
  }