hoodscript 1.1.0__py3-none-any.whl

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.
hoodscript/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ from hoodscript.importer import install, uninstall
2
+ from hoodscript.transpiler import transpile
3
+
4
+ try:
5
+ from hoodscript._version import __version__
6
+ except ImportError:
7
+ __version__ = "1.1.0"
8
+
9
+ __all__ = ["transpile", "install", "uninstall", "__version__"]
hoodscript/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ import sys
2
+
3
+ from hoodscript.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ sys.exit(main())
hoodscript/_version.py ADDED
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.1.0'
22
+ __version_tuple__ = version_tuple = (1, 1, 0)
23
+
24
+ __commit_id__ = commit_id = None
hoodscript/cache.py ADDED
@@ -0,0 +1,36 @@
1
+ import importlib.util
2
+ import marshal
3
+ import os
4
+ import struct
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ FLAG_HASH_CHECKED = 0x03
9
+ HEADER_SIZE = 16
10
+
11
+ def get_cache_path(source_path):
12
+ p = Path(source_path).resolve()
13
+ tag = sys.implementation.cache_tag or f"cpython-{sys.version_info.major}{sys.version_info.minor}"
14
+ return p.parent / "__pycache__" / f"{p.stem}.{tag}.pyc"
15
+
16
+ def read_cache(source_path, source_bytes):
17
+ f = get_cache_path(source_path)
18
+ if not f.is_file(): return None
19
+ try:
20
+ d = f.read_bytes()
21
+ if len(d) < HEADER_SIZE or d[0:4] != importlib.util.MAGIC_NUMBER: return None
22
+ if d[8:16] != importlib.util.source_hash(source_bytes): return None
23
+ return marshal.loads(d[HEADER_SIZE:])
24
+ except Exception: return None
25
+
26
+ def write_cache(source_path, source_bytes, code_obj):
27
+ f = get_cache_path(source_path)
28
+ try:
29
+ f.parent.mkdir(parents=True, exist_ok=True)
30
+ hdr = (importlib.util.MAGIC_NUMBER + struct.pack("<I", FLAG_HASH_CHECKED)
31
+ + importlib.util.source_hash(source_bytes))
32
+ tmp = f.parent / f"{f.name}.tmp.{os.getpid()}"
33
+ tmp.write_bytes(hdr + marshal.dumps(code_obj))
34
+ tmp.replace(f)
35
+ return True
36
+ except Exception: return False
hoodscript/cli.py ADDED
@@ -0,0 +1,468 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from hoodscript import __version__, importer, sandbox, traceback_handler
7
+ from hoodscript.constants import (
8
+ HOOD_EXCEPTION_NAMES,
9
+ TIER_A,
10
+ TIER_B,
11
+ TIER_C,
12
+ )
13
+ from hoodscript.transpiler import transpile
14
+
15
+ TOUR_TEXT = f"""HoodScript v{__version__} — AAVE keywords mapped 1:1 to Python 3
16
+
17
+ Usage:
18
+ hoodscript <file.hs> Run a HoodScript program (or `hoodscript run <file.hs>`)
19
+ hoodscript repl [--mirror] Start interactive REPL (--mirror echoes Python)
20
+ hoodscript build <file.hs> Transpile HoodScript to Python (-c)
21
+ hoodscript check <file.hs> Check syntax without executing
22
+ hoodscript fmt <file.hs> Format source files
23
+ hoodscript lint <file.hs> Lint source files
24
+ hoodscript learn [topic] Interactive tutor (try: hoodscript learn basics)
25
+ hoodscript debug <file.hs> Debug program under debugpy / DAP
26
+ hoodscript tokens Show all keywords and mappings
27
+ hoodscript playground [--port] Launch Web Playground in browser
28
+ hoodscript lsp Start Language Server
29
+ hoodscript version Show version information
30
+ hoodscript completion [bash|zsh] Generate shell autocompletion
31
+
32
+ Topics for learn:
33
+ basics, flow, functions, classes, errors, async, mirror, all
34
+
35
+ Quick start:
36
+ $ hoodscript learn basics # Start the interactive curriculum
37
+ $ hoodscript repl --mirror # REPL echoing Python code line-by-line
38
+ $ hoodscript build app.hs # See transpiled Python
39
+ """
40
+
41
+
42
+ def _utf8_pipes() -> None:
43
+ """Crash reports carry non-ASCII (the header, em dashes). A real console handles
44
+ them; a pipe on Windows defaults to the legacy code page and mangles them."""
45
+ for stream in (sys.stdout, sys.stderr):
46
+ if not stream.isatty() and hasattr(stream, "reconfigure"):
47
+ stream.reconfigure(encoding="utf-8", errors="replace")
48
+
49
+
50
+ def print_tokens() -> None:
51
+ print(f"HoodScript v{__version__} Keyword and Exception Reference\n")
52
+ print(f"{'HoodScript':<18} {'Python':<18} {'Category':<15} {'Description'}")
53
+ print(f"{'-' * 18} {'-' * 18} {'-' * 15} {'-' * 30}")
54
+ for k, v in sorted(TIER_A.items()):
55
+ print(f"{k:<18} {v:<18} {'Tier A':<15} Core grammatical feature")
56
+ for k, v in sorted(TIER_B.items()):
57
+ print(f"{k:<18} {v:<18} {'Tier B':<15} High-frequency keyword")
58
+ for k, v in sorted(TIER_C.items()):
59
+ print(f"{k:<18} {v:<18} {'Tier C':<15} Control / statement")
60
+ for k, v in sorted(HOOD_EXCEPTION_NAMES.items()):
61
+ print(f"{k:<18} {v:<18} {'Exception':<15} Builtin exception")
62
+
63
+ print("\nMulti-token Patterns (normative R1–R12):")
64
+ patterns = [
65
+ ("ain't x", "not x", "R1 Negation"),
66
+ ("ain't nobody x", "not x", "R2 Emphatic negative concord"),
67
+ ("no cap", "True", "R3 Truth assertion"),
68
+ ("cap", "False", "R3 Falsehood"),
69
+ ("it's x", "(x is not None)", "R4 Existence test"),
70
+ ("steady:", "while True:", "R5 Persistent loop"),
71
+ ("a..b", "range(a, (b) + 1)", "R6 Inclusive range"),
72
+ ("BIN X = v", "X: Final = v", "R7 Constant declaration"),
73
+ ("else if", "elif", "R8 Chained conditional"),
74
+ ("holla x, y", "print(x, y)", "R9 Statement-form print"),
75
+ ("ask x", "input(x)", "R10 Statement-form input"),
76
+ ]
77
+ for src, py, desc in patterns:
78
+ print(f" {src:<20} -> {py:<25} ({desc})")
79
+
80
+
81
+ def print_completion(shell: str) -> int:
82
+ shell = shell.lower().strip()
83
+ if shell == "bash":
84
+ print("""# bash completion for hoodscript
85
+ _hoodscript_completion() {
86
+ local cur prev words cword
87
+ _init_completion || return
88
+
89
+ local commands="run build check fmt lint repl tokens learn lsp version completion make-stubs debug playground"
90
+ local topics="basics flow functions classes errors async mirror all"
91
+
92
+ if [[ $cword -eq 1 ]]; then
93
+ COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
94
+ return 0
95
+ fi
96
+
97
+ case "${words[1]}" in
98
+ learn)
99
+ COMPREPLY=( $(compgen -W "$topics" -- "$cur") )
100
+ return 0
101
+ ;;
102
+ completion)
103
+ COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
104
+ return 0
105
+ ;;
106
+ run|build|check|fmt|lint|debug)
107
+ COMPREPLY=( $(compgen -f -- "$cur") )
108
+ return 0
109
+ ;;
110
+ repl)
111
+ COMPREPLY=( $(compgen -W "--mirror" -- "$cur") )
112
+ return 0
113
+ ;;
114
+ esac
115
+ }
116
+ complete -F _hoodscript_completion hoodscript
117
+ """)
118
+ return 0
119
+ elif shell == "zsh":
120
+ print("""#compdef hoodscript
121
+
122
+ _hoodscript() {
123
+ local -a commands
124
+ commands=(
125
+ 'run:Run a HoodScript program'
126
+ 'build:Transpile HoodScript to Python stdout'
127
+ 'check:Check syntax without executing'
128
+ 'fmt:Format HoodScript source files'
129
+ 'lint:Lint HoodScript source files'
130
+ 'repl:Start interactive REPL'
131
+ 'tokens:Display language keywords and mappings'
132
+ 'learn:Interactive curriculum lessons'
133
+ 'debug:Debug program under debugpy / DAP'
134
+ 'lsp:Start Language Server'
135
+ 'version:Show version'
136
+ 'completion:Generate shell completion script'
137
+ )
138
+
139
+ _arguments -C \\
140
+ '1: :->command' \\
141
+ '*:: :->args'
142
+
143
+ case $state in
144
+ command)
145
+ _describe -t commands 'hoodscript command' commands
146
+ ;;
147
+ args)
148
+ case $line[1] in
149
+ learn)
150
+ _values 'topics' basics flow functions classes errors async mirror all
151
+ ;;
152
+ completion)
153
+ _values 'shell' bash zsh
154
+ ;;
155
+ run|build|check|fmt|lint)
156
+ _files
157
+ ;;
158
+ repl)
159
+ _arguments '--mirror[Echo Python equivalent under each line]'
160
+ ;;
161
+ esac
162
+ ;;
163
+ esac
164
+ }
165
+
166
+ _hoodscript "$@"
167
+ """)
168
+ return 0
169
+ else:
170
+ sys.stderr.write(f"Unknown shell '{shell}'. Supported: bash, zsh\n")
171
+ return 1
172
+
173
+
174
+ def check_command(args: list[str]) -> int:
175
+ parser = argparse.ArgumentParser(prog="hoodscript check")
176
+ parser.add_argument("file", help="HoodScript file to check")
177
+ pargs = parser.parse_args(args)
178
+
179
+ traceback_handler.install()
180
+ try:
181
+ with open(pargs.file, encoding="utf-8") as fh:
182
+ src = fh.read()
183
+ py = transpile(src, pargs.file)
184
+ compile(py, pargs.file, "exec")
185
+ print(f"Syntax OK: {pargs.file}")
186
+ return 0
187
+ except BaseException as e:
188
+ traceback_handler.hood_excepthook(type(e), e, e.__traceback__)
189
+ return 1
190
+
191
+
192
+ def fmt_command(args: list[str]) -> int:
193
+ parser = argparse.ArgumentParser(prog="hoodscript fmt")
194
+ parser.add_argument("files", nargs="+", help="Files or directories to format")
195
+ parser.add_argument("--check", action="store_true", help="Check if files are formatted without writing")
196
+ parser.add_argument("--diff", action="store_true", help="Show diff for changes")
197
+ pargs = parser.parse_args(args)
198
+
199
+ from hoodscript.formatter import format_file
200
+ from hoodscript.linter import ToolMissing
201
+
202
+ any_modified = False
203
+ for path_str in pargs.files:
204
+ p = Path(path_str)
205
+ targets = list(p.rglob("*.hs")) + list(p.rglob("*.hood")) if p.is_dir() else [p]
206
+
207
+ for target in targets:
208
+ try:
209
+ mod, msg = format_file(target, check_only=pargs.check, show_diff=pargs.diff, write=not pargs.check)
210
+ except ToolMissing as e:
211
+ sys.stderr.write(f"{e}\n"); return 2
212
+ if mod:
213
+ any_modified = True
214
+ if msg:
215
+ print(msg)
216
+
217
+ if pargs.check and any_modified:
218
+ return 1
219
+ return 0
220
+
221
+
222
+ def lint_command(args: list[str]) -> int:
223
+ parser = argparse.ArgumentParser(prog="hoodscript lint")
224
+ parser.add_argument("files", nargs="+", help="Files or directories to lint")
225
+ parser.add_argument("--types", action="store_true", help="Run mypy type checker (HL003)")
226
+ pargs = parser.parse_args(args)
227
+
228
+ from hoodscript.linter import ToolMissing, lint_file
229
+
230
+ all_findings = []
231
+ for path_str in pargs.files:
232
+ p = Path(path_str)
233
+ targets = list(p.rglob("*.hs")) + list(p.rglob("*.hood")) if p.is_dir() else [p]
234
+
235
+ for target in targets:
236
+ try:
237
+ findings = lint_file(target, check_types=pargs.types)
238
+ except ToolMissing as e:
239
+ sys.stderr.write(f"{e}\n"); return 2
240
+ all_findings.extend(findings)
241
+
242
+ for f in all_findings:
243
+ print(f.format_plain())
244
+
245
+ if all_findings:
246
+ print(f"\n{len(all_findings)} issue(s) found.")
247
+ return 1
248
+
249
+ print("No issues found.")
250
+ return 0
251
+
252
+
253
+ def repl_command(args: list[str]) -> int:
254
+ parser = argparse.ArgumentParser(prog="hoodscript repl")
255
+ parser.add_argument("--mirror", action="store_true", help="Echo Python equivalent under each input line")
256
+ pargs = parser.parse_args(args)
257
+
258
+ try:
259
+ from hoodscript.repl import HoodScriptREPL
260
+ except ImportError:
261
+ sys.stderr.write(
262
+ "The interactive REPL requires prompt-toolkit.\n"
263
+ "Install with: pip install 'hoodscript[repl]'\n"
264
+ )
265
+ return 1
266
+
267
+ HoodScriptREPL(mirror=pargs.mirror).start()
268
+ return 0
269
+
270
+
271
+ def learn_command(args: list[str]) -> int:
272
+ parser = argparse.ArgumentParser(prog="hoodscript learn")
273
+ parser.add_argument(
274
+ "topic",
275
+ nargs="?",
276
+ default=None,
277
+ help="Topic (basics, flow, functions, classes, errors, async, mirror) or lesson #",
278
+ )
279
+ parser.add_argument("--mirror", action="store_true", help="Show transpiled Python for each lesson")
280
+ parser.add_argument("--auto", action="store_true", help="Run lessons automatically without prompting")
281
+ pargs = parser.parse_args(args)
282
+
283
+ from hoodscript.tutor import run_curriculum
284
+
285
+ return run_curriculum(pargs.topic, mirror=pargs.mirror, auto_run=pargs.auto)
286
+
287
+
288
+ def debug_command(args: list[str]) -> int:
289
+ parser = argparse.ArgumentParser(prog="hoodscript debug")
290
+ parser.add_argument("file", help="HoodScript file to debug")
291
+ parser.add_argument("--port", type=int, default=5678, help="debugpy port (default 5678)")
292
+ parser.add_argument("--host", default="127.0.0.1", help="debugpy host (default 127.0.0.1)")
293
+ parser.add_argument("--wait", action="store_true", help="Wait for debugger client to attach")
294
+ parser.add_argument("--listen", action="store_true", help="Start debugpy listener")
295
+ pargs = parser.parse_args(args)
296
+
297
+ from hoodscript.debugger import run_debug
298
+
299
+ return run_debug(
300
+ pargs.file,
301
+ port=pargs.port,
302
+ host=pargs.host,
303
+ wait_for_client=pargs.wait,
304
+ listen=pargs.listen or pargs.wait,
305
+ )
306
+
307
+
308
+ def playground_command(args: list[str]) -> int:
309
+ import http.server
310
+ import socketserver
311
+ import webbrowser
312
+ from functools import partial
313
+
314
+ parser = argparse.ArgumentParser(prog="hoodscript playground", description="Launch the client-side Web Playground")
315
+ parser.add_argument("--port", type=int, default=8000, help="port to serve on (default 8000)")
316
+ parser.add_argument("--no-browser", action="store_true", help="do not open browser automatically")
317
+ pargs = parser.parse_args(args)
318
+
319
+ playground_dir = Path(__file__).resolve().parents[2] / "playground"
320
+ if not playground_dir.exists():
321
+ sys.stderr.write("The playground is a static site that lives in the repository, not in the installed package.\n"
322
+ "Clone https://github.com/khaoticdev62/hoodscript and run `hoodscript playground` "
323
+ "from the checkout.\n")
324
+ return 1
325
+
326
+ handler = partial(http.server.SimpleHTTPRequestHandler, directory=str(playground_dir))
327
+ port = pargs.port
328
+ url = f"http://localhost:{port}"
329
+
330
+ sys.stdout.write(f"Starting HoodScript Web Playground at {url}\n")
331
+ sys.stdout.write("Press Ctrl+C to stop.\n")
332
+ sys.stdout.flush()
333
+
334
+ if not pargs.no_browser:
335
+ import contextlib
336
+
337
+ with contextlib.suppress(Exception):
338
+ webbrowser.open(url)
339
+
340
+ try:
341
+ with socketserver.TCPServer(("", port), handler) as httpd:
342
+ httpd.serve_forever()
343
+ except KeyboardInterrupt:
344
+ sys.stdout.write("\nPlayground stopped.\n")
345
+ return 0
346
+ except Exception as exc:
347
+ sys.stderr.write(f"Playground server error: {exc}\n")
348
+ return 1
349
+
350
+
351
+ def main(argv=None) -> int:
352
+ if argv is None:
353
+ argv = sys.argv[1:]
354
+ _utf8_pipes()
355
+
356
+ # If no arguments are passed, show the tour (Sprint 7 item 1)
357
+ if not argv:
358
+ sys.stdout.write(TOUR_TEXT)
359
+ return 0
360
+
361
+ cmd = argv[0]
362
+
363
+ if cmd in ("version", "--version", "-v"):
364
+ print(f"HoodScript v{__version__}")
365
+ return 0
366
+ if cmd == "tokens":
367
+ print_tokens()
368
+ return 0
369
+ if cmd == "completion":
370
+ shell = argv[1] if len(argv) > 1 else "bash"
371
+ return print_completion(shell)
372
+ if cmd == "lsp":
373
+ try:
374
+ from hoodscript.lsp import run_lsp
375
+ except ImportError:
376
+ sys.stderr.write(
377
+ "The LSP server requires pygls and jedi.\n"
378
+ "Install with: pip install 'hoodscript[lsp]'\n"
379
+ )
380
+ return 1
381
+ run_lsp()
382
+ return 0
383
+ if cmd == "make-stubs":
384
+ from hoodscript.stubs import make_stubs_main
385
+ return make_stubs_main(argv[1:])
386
+ if cmd == "check":
387
+ return check_command(argv[1:])
388
+ if cmd == "fmt":
389
+ return fmt_command(argv[1:])
390
+ if cmd == "lint":
391
+ return lint_command(argv[1:])
392
+ if cmd == "repl":
393
+ return repl_command(argv[1:])
394
+ if cmd == "learn":
395
+ return learn_command(argv[1:])
396
+ if cmd == "debug":
397
+ return debug_command(argv[1:])
398
+ if cmd == "playground":
399
+ return playground_command(argv[1:])
400
+ if cmd == "build":
401
+ # build <file> transpiles to stdout (alias for -c)
402
+ if len(argv) < 2:
403
+ sys.stderr.write("Usage: hoodscript build <file.hs>\n")
404
+ return 1
405
+ filepath = argv[1]
406
+ with open(filepath, encoding="utf-8") as fh:
407
+ src = fh.read()
408
+ sys.stdout.write(transpile(src, filepath) + "\n")
409
+ return 0
410
+
411
+ # If user ran `hoodscript run ...`
412
+ if cmd == "run":
413
+ argv = argv[1:]
414
+
415
+ # Fallback to standard execution parser (supports `hoodscript <file>`, `-c`, `--sandbox`, etc.)
416
+ parser = argparse.ArgumentParser(prog="hoodscript")
417
+ parser.add_argument("file", nargs="?")
418
+ parser.add_argument("-c", "--compile", action="store_true")
419
+ parser.add_argument(
420
+ "--python-traceback",
421
+ action="store_true",
422
+ help="show raw Python tracebacks (for debugging the compiler)",
423
+ )
424
+ sb = parser.add_argument_group(
425
+ "sandbox",
426
+ "run with capabilities closed and budgets on (see SECURITY.md)",
427
+ )
428
+ sb.add_argument("--sandbox", action="store_true", help="enable the sandbox (also HOODSCRIPT_SANDBOX=1)")
429
+ sb.add_argument("--allow-fs", action="store_true", help="grant filesystem access inside the sandbox")
430
+ sb.add_argument("--allow-net", action="store_true", help="grant network access inside the sandbox")
431
+ sb.add_argument("--allow-clock", action="store_true", help="grant clock access inside the sandbox")
432
+ sb.add_argument("--max-seconds", type=float, default=None, help="wall-clock budget (default 10)")
433
+ sb.add_argument("--max-output", type=int, default=None, help="output budget in bytes (default 1,000,000)")
434
+ sb.add_argument("--max-depth", type=int, default=None, help="recursion budget (default 256)")
435
+ args = parser.parse_args(argv)
436
+
437
+ if args.python_traceback:
438
+ os.environ[traceback_handler.ENV_FLAG] = "1"
439
+ if not args.file:
440
+ sys.stdout.write(TOUR_TEXT)
441
+ return 0
442
+
443
+ with open(args.file, encoding="utf-8") as fh:
444
+ src = fh.read()
445
+ if args.compile:
446
+ sys.stdout.write(transpile(src, args.file) + "\n")
447
+ return 0
448
+
449
+ sys.path.insert(0, os.path.dirname(os.path.abspath(args.file)))
450
+ importer.install()
451
+ traceback_handler.install()
452
+ if args.sandbox or os.environ.get(sandbox.ENV_FLAG) or sandbox.SANDBOX_BY_DEFAULT:
453
+ caps = sandbox.Capabilities(filesystem=args.allow_fs, network=args.allow_net, clock=args.allow_clock)
454
+ defaults = sandbox.Budgets()
455
+ budgets = sandbox.Budgets(
456
+ max_recursion_depth=args.max_depth or defaults.max_recursion_depth,
457
+ max_wall_seconds=args.max_seconds or defaults.max_wall_seconds,
458
+ max_output_bytes=args.max_output or defaults.max_output_bytes,
459
+ )
460
+ sandbox.activate(sandbox.Sandbox(caps, budgets))
461
+
462
+ scope = {"__file__": os.path.abspath(args.file), "__name__": "__main__"}
463
+ exec(compile(transpile(src, args.file), args.file, "exec"), scope)
464
+ return 0
465
+
466
+
467
+ if __name__ == "__main__":
468
+ sys.exit(main())
@@ -0,0 +1,69 @@
1
+ """Keyword tables — the runtime truth. Specified by GRAMMAR.md v1.1; every entry
2
+ has a row in hoodscript/docs/linguistics.md. Do not add a word here without
3
+ the 7-step procedure in hoodscript/docs/lexical-sourcing.md §5.
4
+
5
+ Tier A: grammar (Yale GDP page each). Tier B: lexical (two dictionaries each;
6
+ `trip` on one, owner-approved). Tier C: plain English kept deliberately.
7
+ Multi-token forms (steady:, no cap, it's, ain't, BIN, a..b, else if, holla
8
+ "x") are the Sprint 3 pattern layer and are not in this table yet.
9
+ """
10
+
11
+ import builtins as _b
12
+
13
+ TIER_A = {
14
+ "be": "for", # habitual be -> ast.For
15
+ "finna": "async", # fixin' to -> ast.AsyncFunctionDef (with bet)
16
+ "done": "await", # perfective done -> ast.Await
17
+ "tryna": "try", # tryna -> ast.Try
18
+ }
19
+
20
+ TIER_B = {
21
+ "bet": "def", # commitment -> ast.FunctionDef
22
+ "fam": "class", # close group -> ast.ClassDef
23
+ "holla": "print", # call out -> ast.Call
24
+ "dip": "return", # leave -> ast.Return
25
+ "chill": "pass", # do nothing -> ast.Pass
26
+ "cap": "False", # a lie -> ast.Constant
27
+ }
28
+
29
+ TIER_C = {
30
+ "skip": "continue",
31
+ "catch": "except",
32
+ "regardless": "finally",
33
+ "throw": "raise",
34
+ "ask": "input",
35
+ }
36
+
37
+ # The *Trip family: Python exceptions under HoodScript names. Token-level, so
38
+ # generated Python reads `except ValueError` and py2hood gives `catch BadValueTrip`.
39
+ HOOD_EXCEPTION_NAMES = {
40
+ "Problem": "Exception",
41
+ "BaseProblem": "BaseException",
42
+ "BadValueTrip": "ValueError",
43
+ "WrongKindTrip": "TypeError",
44
+ "MissingKeyTrip": "KeyError",
45
+ "OutOfBoundsTrip": "IndexError",
46
+ "ZeroSplitTrip": "ZeroDivisionError",
47
+ "MissingFileTrip": "FileNotFoundError",
48
+ "AssertTrip": "AssertionError",
49
+ "UnknownNameTrip": "NameError",
50
+ "MissingAttrTrip": "AttributeError",
51
+ "BadSyntaxTrip": "SyntaxError",
52
+ "InfiniteLoopTrip": "RecursionError",
53
+ "StopRunTrip": "StopIteration",
54
+ "MissingModuleTrip": "ModuleNotFoundError",
55
+ "BadImportTrip": "ImportError",
56
+ "NoAccessTrip": "PermissionError",
57
+ }
58
+
59
+ HOOD_KEYWORD_MAP = {**TIER_A, **TIER_B, **TIER_C, **HOOD_EXCEPTION_NAMES}
60
+
61
+ # Python exception class -> HoodScript name, for crash reports.
62
+ HOOD_EXCEPTIONS = {getattr(_b, py): hood for hood, py in HOOD_EXCEPTION_NAMES.items()}
63
+
64
+ # Reserved: a HoodScript program cannot use these as variable names, exactly as
65
+ # Python cannot use `pass` or `return`. Attribute names (`future.done()`) are
66
+ # exempt — the transpiler never rewrites a NAME that follows a dot.
67
+ RESERVED = frozenset(HOOD_KEYWORD_MAP)
68
+
69
+ SUPPORTED_EXTENSIONS = (".hs", ".hood")