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.
@@ -0,0 +1,343 @@
1
+ # HoodScript 10-Lesson Curriculum
2
+
3
+ Each lesson is one runnable file. Type it in, run it with `hoodscript
4
+ lessonN.hs`, then run `hoodscript -c lessonN.hs` to see the Python it becomes.
5
+ That second step is the whole trick: you are learning Python with a handful
6
+ of different words, and the words are the only thing that changes.
7
+
8
+ Expected output is shown under each program. Every program here is executed
9
+ by the test suite, so if yours differs, the crash report will point at the
10
+ line.
11
+
12
+ Keyword reference: [`keywords.md`](keywords.md). The words this curriculum
13
+ teaches: `holla` `ask` `bet` `dip` `fam` `be` `skip` `chill` `tryna` `catch`
14
+ `regardless` `throw` `finna` `done` `cap` and the `*Trip` exception names.
15
+ Everything else is Python.
16
+
17
+ ---
18
+
19
+ ## Lesson 1 — Values and `holla`
20
+
21
+ **Keywords:** `holla` `cap` `no cap`
22
+
23
+ ```hood
24
+ name = "Khris"
25
+ age = 30
26
+ height = 1.8
27
+ ready = no cap
28
+ lying = cap
29
+ nothing = None
30
+ holla name, age, height, ready, lying, nothing
31
+ holla type(age), int("42") + 1, str(3.5)
32
+ ```
33
+
34
+ ```
35
+ Khris 30 1.8 True False None
36
+ <class 'int'> 43 3.5
37
+ ```
38
+
39
+ Variables don't need declaring. `holla` is `print` — with or without
40
+ parentheses. `cap` is `False` (a lie); `no cap` is `True`. `type`, `int`,
41
+ `str` and every other builtin keep their Python names.
42
+
43
+ ---
44
+
45
+ ## Lesson 2 — Decisions
46
+
47
+ **Keywords:** `else if` `ain't` `ain't nobody` `it's`
48
+
49
+ ```hood
50
+ score = 87
51
+ if score >= 90:
52
+ holla "A"
53
+ else if score >= 80:
54
+ holla "B"
55
+ else:
56
+ holla "C or below"
57
+ if ain't score < 50 and it's score:
58
+ holla "passing"
59
+ name = ""
60
+ if ain't nobody name:
61
+ holla "no name given"
62
+ ```
63
+
64
+ ```
65
+ B
66
+ passing
67
+ no name given
68
+ ```
69
+
70
+ `if`/`else` are Python's; `else if` is `elif`. `ain't` is `not`; `ain't
71
+ nobody` is the emphatic guard form (also `not`). `it's x` means `x` exists —
72
+ `(x is not None)` — so `it's 0` is true.
73
+
74
+ ---
75
+
76
+ ## Lesson 3 — Loops: `be` and `skip`
77
+
78
+ **Keywords:** `be` `steady` `skip` and inclusive ranges `a..b`
79
+
80
+ ```hood
81
+ be i in 1..3:
82
+ holla "lap", i
83
+ n = 0
84
+ steady:
85
+ n = n + 1
86
+ if n == 2:
87
+ skip
88
+ if n > 4:
89
+ break
90
+ holla "n is", n
91
+ ```
92
+
93
+ ```
94
+ lap 1
95
+ lap 2
96
+ lap 3
97
+ n is 1
98
+ n is 3
99
+ n is 4
100
+ ```
101
+
102
+ `be x in xs` is `for x in xs` — habitual *be*, the thing that happens over
103
+ and over. `1..3` is 1, 2, 3 — inclusive, unlike Python's `range`. `steady:`
104
+ is `while True:` — keep going until `break`. `skip` is `continue`.
105
+
106
+ ---
107
+
108
+ ## Lesson 4 — Functions: `bet` and `dip`
109
+
110
+ **Keywords:** `bet` `dip` `chill`
111
+
112
+ ```hood
113
+ bet greet(who, greeting="wassup"):
114
+ dip greeting + ", " + who
115
+ double = lambda x: x * 2
116
+ holla(greet("fam"), greet("Khris", "yo"), double(21))
117
+ bet countdown(n):
118
+ while n > 0:
119
+ yield n
120
+ n = n - 1
121
+ holla(list(countdown(3)))
122
+ bet nothing_yet():
123
+ chill
124
+ holla(nothing_yet())
125
+ ```
126
+
127
+ ```
128
+ wassup, fam yo, Khris 42
129
+ [3, 2, 1]
130
+ None
131
+ ```
132
+
133
+ `bet` defines a function — a commitment. `dip` returns — you leave with the
134
+ value. `chill` is `pass`. `lambda` and `yield` are Python's.
135
+
136
+ ---
137
+
138
+ ## Lesson 5 — Collections
139
+
140
+ **Keywords:** `BIN`
141
+
142
+ ```hood
143
+ nums = [3, 1, 2]
144
+ holla(len(nums), sum(nums), sorted(nums))
145
+ book = dict(a=1, b=2)
146
+ be k, v in book.items():
147
+ holla(k, v)
148
+ be i, x in enumerate(["x", "y"]):
149
+ holla(i, x)
150
+ holla(list(zip([1, 2], ["a", "b"])), set([1, 1, 2]), tuple([1, 2]))
151
+ holla([x for x in nums if x > 1], list(map(lambda x: x * 10, nums)))
152
+ holla(all([no cap, True]), any([cap, True]))
153
+ BIN LIMIT = 2
154
+ holla nums[:LIMIT]
155
+ ```
156
+
157
+ ```
158
+ 3 6 [1, 2, 3]
159
+ a 1
160
+ b 2
161
+ 0 x
162
+ 1 y
163
+ [(1, 'a'), (2, 'b')] {1, 2} (1, 2)
164
+ [3, 2] [30, 10, 20]
165
+ True True
166
+ [3, 1]
167
+ ```
168
+
169
+ Lists, dicts, sets, tuples, comprehensions, `len`, `sum`, `zip`, `map` —
170
+ all Python, all unchanged. There was no cited word for any of them, so
171
+ HoodScript doesn't pretend. `BIN X = v` is a constant (`X: Final = v`) —
172
+ stressed *BIN*: true for a long time, and still is.
173
+
174
+ ---
175
+
176
+ ## Lesson 6 — Classes: `fam`
177
+
178
+ **Keywords:** `fam`
179
+
180
+ ```hood
181
+ fam Animal:
182
+ bet __init__(self, name):
183
+ self.name = name
184
+ bet speak(self):
185
+ dip self.name + " makes a sound"
186
+ fam Dog(Animal):
187
+ bet speak(self):
188
+ dip super().speak() + " (woof)"
189
+ d = Dog("Rex")
190
+ holla(d.speak(), isinstance(d, Animal))
191
+ ```
192
+
193
+ ```
194
+ Rex makes a sound (woof) True
195
+ ```
196
+
197
+ `fam` is `class` — the close group an object belongs to. `self` and
198
+ `super()` are Python's.
199
+
200
+ ---
201
+
202
+ ## Lesson 7 — Errors: `tryna`, `catch`, `regardless`, `throw`
203
+
204
+ **Keywords:** `tryna` `catch` `regardless` `throw` · **Exceptions:** `ZeroSplitTrip` `BadValueTrip`
205
+
206
+ ```hood
207
+ bet divide(a, b):
208
+ tryna:
209
+ dip a / b
210
+ catch ZeroSplitTrip:
211
+ holla("can't split by zero")
212
+ dip None
213
+ regardless:
214
+ holla("done dividing")
215
+ holla(divide(6, 3))
216
+ holla(divide(1, 0))
217
+ bet check(x):
218
+ if x < 0:
219
+ throw BadValueTrip("x gotta be positive, got " + str(x))
220
+ dip x
221
+ tryna:
222
+ check(-1)
223
+ catch BadValueTrip as e:
224
+ holla("caught:", e)
225
+ assert check(1) == 1
226
+ ```
227
+
228
+ ```
229
+ done dividing
230
+ 2.0
231
+ can't split by zero
232
+ done dividing
233
+ None
234
+ caught: x gotta be positive, got -1
235
+ ```
236
+
237
+ `tryna` is `try`. `catch`/`regardless`/`throw` are `except`/`finally`/`raise`
238
+ — plain English kept deliberately. The `*Trip` names are Python's exception
239
+ classes: `ZeroSplitTrip` *is* `ZeroDivisionError` (run `-c` and see). Any
240
+ exception without a `*Trip` name is caught under its Python name.
241
+
242
+ ---
243
+
244
+ ## Lesson 8 — Modules and files
245
+
246
+ **Keywords:** none
247
+
248
+ ```hood
249
+ import json
250
+ from pathlib import Path
251
+ import math as m
252
+ holla(json.dumps({"ok": True}), m.sqrt(16), Path("."))
253
+ with open("out.txt", "w") as f:
254
+ f.write("written from HoodScript\n")
255
+ holla(open("out.txt").read().strip())
256
+ ```
257
+
258
+ ```
259
+ {"ok": true} 4.0 .
260
+ written from HoodScript
261
+ ```
262
+
263
+ The whole Python ecosystem is available, spelled the Python way. To import
264
+ your own `.hs` files, see the onboarding guide §6–7.
265
+
266
+ ---
267
+
268
+ ## Lesson 9 — Async: `finna` and `done`
269
+
270
+ **Keywords:** `finna` `done`
271
+
272
+ ```hood
273
+ import asyncio
274
+ finna bet fetch(n):
275
+ done asyncio.sleep(0)
276
+ dip n * 2
277
+ finna bet main():
278
+ results = done asyncio.gather(fetch(1), fetch(2))
279
+ holla(results)
280
+ asyncio.run(main())
281
+ ```
282
+
283
+ ```
284
+ [2, 4]
285
+ ```
286
+
287
+ `finna bet` is `async def` — *fixin' to*: prepared, not yet running, which
288
+ is exactly what a coroutine object is. `done` is `await` — perfective: the
289
+ thing, completed. Note that `fut.done()` — the *method* — is untouched;
290
+ keywords are never rewritten after a dot.
291
+
292
+ ---
293
+
294
+ ## Lesson 10 — Pattern matching, scope, and the escape hatch
295
+
296
+ **Keywords:** none
297
+
298
+ ```hood
299
+ bet describe(cmd):
300
+ match cmd:
301
+ case ["go", direction]:
302
+ dip "going " + direction
303
+ case ["stop"]:
304
+ dip "stopping"
305
+ case _:
306
+ dip "unknown"
307
+ holla(describe(["go", "north"]), describe(["stop"]), describe([]))
308
+ total = 0
309
+ bet bump():
310
+ global total
311
+ total = total + 1
312
+ bump(); bump()
313
+ holla(total)
314
+ bet outer():
315
+ x = 1
316
+ bet inner():
317
+ nonlocal x
318
+ x = x + 1
319
+ inner()
320
+ dip x
321
+ holla(outer())
322
+ ```
323
+
324
+ ```
325
+ going north stopping unknown
326
+ 2
327
+ 2
328
+ ```
329
+
330
+ `match`/`case`, `global`, `nonlocal` are Python's. And here is the escape
331
+ hatch: **every Python keyword still works in HoodScript.** `def` and `return`
332
+ are accepted alongside `bet` and `dip`. You can paste any Python file into a
333
+ `.hs` file and it runs. The HoodScript words are preferred, not required.
334
+
335
+ ---
336
+
337
+ ## Graduation
338
+
339
+ You now know every keyword in the language — fifteen words, eleven two-word
340
+ forms, plus the exception names. Run `hoodscript -c` on any of these files one last time and
341
+ read the Python: that is what you've been writing all along. From here, the
342
+ [Python documentation](https://docs.python.org/3/) is the HoodScript
343
+ documentation — substitute the words from [`keywords.md`](keywords.md).
hoodscript/debugger.py ADDED
@@ -0,0 +1,71 @@
1
+ """The HoodScript debugger shim.
2
+
3
+ Integrates with debugpy (DAP) and Python debugging infrastructure.
4
+ Because HoodScript transpilation preserves exact line numbers, debugpy
5
+ and Python debuggers operate directly on .hs and .hood files once the
6
+ import hook is installed and the entry point is transpiled.
7
+ """
8
+
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ from hoodscript import importer, traceback_handler
13
+ from hoodscript.transpiler import transpile
14
+
15
+
16
+ def run_debug(
17
+ filepath: str,
18
+ port: int = 5678,
19
+ host: str = "127.0.0.1",
20
+ wait_for_client: bool = False,
21
+ listen: bool = False,
22
+ ) -> int:
23
+ """Run a HoodScript program under debugpy or with debugging support."""
24
+ path = Path(filepath).resolve()
25
+ if not path.exists():
26
+ sys.stderr.write(f"File not found: {filepath}\n")
27
+ return 1
28
+
29
+ # Install import hook & traceback handler
30
+ importer.install()
31
+ traceback_handler.install()
32
+
33
+ # Put script directory on sys.path
34
+ file_dir = str(path.parent)
35
+ if file_dir not in sys.path:
36
+ sys.path.insert(0, file_dir)
37
+
38
+ # If debugpy listener is requested
39
+ if listen or wait_for_client:
40
+ try:
41
+ import debugpy
42
+
43
+ debugpy.listen((host, port))
44
+ sys.stderr.write(f"debugpy listening on {host}:{port}\n")
45
+ if wait_for_client:
46
+ sys.stderr.write("Waiting for debugger client to attach...\n")
47
+ debugpy.wait_for_client()
48
+ except ImportError:
49
+ sys.stderr.write("debugpy is not installed. Install with: pip install debugpy\n")
50
+ return 1
51
+
52
+ src = path.read_text(encoding="utf-8")
53
+ py_code = transpile(src, str(path))
54
+ code_obj = compile(py_code, str(path), "exec")
55
+
56
+ scope = {
57
+ "__file__": str(path),
58
+ "__name__": "__main__",
59
+ "__doc__": None,
60
+ }
61
+
62
+ try:
63
+ exec(code_obj, scope)
64
+ return 0
65
+ except SystemExit as e: # the program called exit(): honour it, don't report it
66
+ return int(e.code) if isinstance(e.code, int) else (0 if e.code is None else 1)
67
+ except KeyboardInterrupt:
68
+ return 130
69
+ except Exception as e:
70
+ traceback_handler.hood_excepthook(type(e), e, e.__traceback__)
71
+ return 1
@@ -0,0 +1,12 @@
1
+ """The JPE (Just Plain English) error engine.
2
+
3
+ diagnose(exc_type, exc, tb) -> Diagnostic # errors.translator
4
+ render(diagnostic, stream) # errors.renderer
5
+ CATALOG # errors.catalog
6
+ """
7
+
8
+ from hoodscript.errors.catalog import CATALOG, Entry, by_code, find
9
+ from hoodscript.errors.renderer import render, render_plain
10
+ from hoodscript.errors.translator import Diagnostic, diagnose
11
+
12
+ __all__ = ["CATALOG", "Entry", "Diagnostic", "by_code", "diagnose", "find", "render", "render_plain"]