cwg 0.1.0__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.
- cwg-0.1.0/PKG-INFO +322 -0
- cwg-0.1.0/core/__init__.py +16 -0
- cwg-0.1.0/core/cli.py +26 -0
- cwg-0.1.0/core/gpScraper.py +414 -0
- cwg-0.1.0/core/interpreter.py +370 -0
- cwg-0.1.0/core/runner.py +163 -0
- cwg-0.1.0/cwg.egg-info/PKG-INFO +322 -0
- cwg-0.1.0/cwg.egg-info/SOURCES.txt +18 -0
- cwg-0.1.0/cwg.egg-info/dependency_links.txt +1 -0
- cwg-0.1.0/cwg.egg-info/entry_points.txt +2 -0
- cwg-0.1.0/cwg.egg-info/requires.txt +4 -0
- cwg-0.1.0/cwg.egg-info/top_level.txt +1 -0
- cwg-0.1.0/docs/LICENSE +674 -0
- cwg-0.1.0/docs/README.md +309 -0
- cwg-0.1.0/pyproject.toml +49 -0
- cwg-0.1.0/setup.cfg +4 -0
- cwg-0.1.0/tests/test_cli.py +159 -0
- cwg-0.1.0/tests/test_gpScraper.py +465 -0
- cwg-0.1.0/tests/test_interpreter.py +785 -0
- cwg-0.1.0/tests/test_runner.py +343 -0
cwg-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cwg
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CWG — a Git-based programming language interpreter
|
|
5
|
+
License-Expression: GPL-3.0-only
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: docs/LICENSE
|
|
9
|
+
Requires-Dist: gitpython<4,>=3
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest; extra == "dev"
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
<img width="1622" height="669" alt="image" src="https://github.com/user-attachments/assets/1c015b26-30bf-4f6b-9023-d2ba5ccc7714" />
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Coding With Git
|
|
18
|
+
|
|
19
|
+
> **Work in progress.** This project is in early draft stage. Syntax and semantics are subject to change.
|
|
20
|
+
|
|
21
|
+
CWG is a programming language where the source code *is* the git history. Commit messages are statements, branches are control flow blocks, and merges close those blocks. The interpreter walks the commit DAG and executes it as a program.
|
|
22
|
+
|
|
23
|
+
The goal is to use git as a genuine programming medium. Readable syntax, real execution, built entirely on top of version control primitives.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Concept
|
|
28
|
+
|
|
29
|
+
| Git construct | Language construct |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `git commit -m "..."` | Statement / instruction |
|
|
32
|
+
| Branch | Conditional block or loop |
|
|
33
|
+
| Merge | Close a block, return to parent scope |
|
|
34
|
+
| Tag | Function definition |
|
|
35
|
+
| `cherry-pick` | Function call |
|
|
36
|
+
| `revert` | Exception handler / undo |
|
|
37
|
+
| `git stash` | Push to memory stack |
|
|
38
|
+
| `git stash pop` | Pop from memory stack |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Syntax
|
|
43
|
+
|
|
44
|
+
CWG executes Python syntax inside commit messages. Commit messages must be valid Python statements for the interpreter to recognize and run them.
|
|
45
|
+
|
|
46
|
+
### Variables, int, string, bool, int operator
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git commit -m "x = 5"
|
|
50
|
+
git commit -m "name = 'world'"
|
|
51
|
+
git commit -m "active = True"
|
|
52
|
+
git commit -m "x = x + 1"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### If / Else
|
|
56
|
+
|
|
57
|
+
Branches named `if/<name>` and `else/<name>` define conditional blocks. The condition is the first commit of the `if/` branch.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git branch if/large
|
|
61
|
+
git commit -m "if x > 10:"
|
|
62
|
+
git commit -m " print('x is large')"
|
|
63
|
+
git checkout main
|
|
64
|
+
git branch else/small
|
|
65
|
+
git commit -m " print('x is small')"
|
|
66
|
+
git checkout main
|
|
67
|
+
git merge if/large
|
|
68
|
+
git merge else/small
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### While Loops
|
|
72
|
+
|
|
73
|
+
Branches named `while/<name>` define while loops. The first commit in the branch is the `while` condition.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git commit -m "i = 10"
|
|
77
|
+
git branch while/countdown
|
|
78
|
+
git commit -m "while i > 0:"
|
|
79
|
+
git commit -m " print(i)"
|
|
80
|
+
git commit -m " i = i - 1"
|
|
81
|
+
git checkout main
|
|
82
|
+
git merge while/countdown
|
|
83
|
+
git commit -m "print('blastoff')"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### For Loops
|
|
87
|
+
|
|
88
|
+
Branches named `for/<name>` define for loops. The first commit in the branch is the `for` header — both `for x in iterable:` and tuple-unpacking forms like `for x, y in pairs:` are supported.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git commit -m "total = 0"
|
|
92
|
+
git branch for/sum
|
|
93
|
+
git commit -m "for i in [1, 2, 3, 4, 5]:"
|
|
94
|
+
git commit -m " total = total + i"
|
|
95
|
+
git checkout main
|
|
96
|
+
git merge for/sum -m "return total"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Functions
|
|
100
|
+
|
|
101
|
+
Functions are defined as tagged commit ranges and called via `cherry-pick`.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git tag -a "fn/greet"
|
|
105
|
+
git commit -m " print('hello ' + name)"
|
|
106
|
+
git commit -m " return"
|
|
107
|
+
git tag -a "end-fn/greet"
|
|
108
|
+
|
|
109
|
+
# call the function
|
|
110
|
+
git cherry-pick fn/greet
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Branch Naming Conventions
|
|
116
|
+
|
|
117
|
+
| Prefix | Purpose |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `main` / `master` | Global scope |
|
|
120
|
+
| `if/<name>` | Conditional true block |
|
|
121
|
+
| `else/<name>` | Conditional false block |
|
|
122
|
+
| `while/<name>` | While loop |
|
|
123
|
+
| `for/<name>` | For loop |
|
|
124
|
+
| `fn/<name>` | Function definition |
|
|
125
|
+
| `check/<name>` | Inline conditional (if/elif/else one-liners) |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Scoping
|
|
130
|
+
|
|
131
|
+
- `main` holds global scope
|
|
132
|
+
- `if/`, `else/`, `while/`, and `for/` branches each receive a copy of the parent scope at the moment they start
|
|
133
|
+
- `else/` always starts from the parent scope — never from the `if/` branch's scope
|
|
134
|
+
- `check/` branches run directly in the parent scope with no isolation
|
|
135
|
+
- Variables modified inside a branch are discarded on merge unless explicitly returned
|
|
136
|
+
- Returned values are promoted back into the parent scope
|
|
137
|
+
|
|
138
|
+
## Execution Model
|
|
139
|
+
|
|
140
|
+
CWG uses a **first-parent walk** to traverse the commit DAG. When `cwg run` is called, the interpreter walks `main` from the first commit to HEAD, oldest to newest, applying these rules at every level:
|
|
141
|
+
|
|
142
|
+
1. **Regular commit** — execute the message as a Python statement, save a state snapshot
|
|
143
|
+
2. **Merge commit** — pause, walk the branch's commits as a self-contained block (oldest to newest), execute the block, apply any returned values to parent scope, continue on `main`
|
|
144
|
+
3. **Revert commit** — restore the state snapshot from before the reverted commit, continue
|
|
145
|
+
|
|
146
|
+
Because branches can contain branches, rule 2 is recursive. The same three rules apply at every level of nesting.
|
|
147
|
+
|
|
148
|
+
Nothing executes as commits are written. The full history is read first, then executed in one pass.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Merging
|
|
153
|
+
|
|
154
|
+
When a branch merges back into `main`, any variables modified inside the branch are discarded unless explicitly returned via the merge commit message.
|
|
155
|
+
|
|
156
|
+
Multiple variables can be returned comma-separated:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git merge while/countdown -m "return i"
|
|
160
|
+
git merge while/multi -m "return i, j, k"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Only the variables named in the return are promoted back into the parent scope. Everything else is dropped.
|
|
164
|
+
|
|
165
|
+
If no return is specified, the branch is treated as side-effects only — prints and other output still happen, but no state is promoted back. This is valid and intentional, not an error.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Revert and Exception Handling
|
|
170
|
+
|
|
171
|
+
`git revert` serves two purposes in CWG: undoing a coding mistake, and handling exceptions.
|
|
172
|
+
|
|
173
|
+
### Pure undo
|
|
174
|
+
|
|
175
|
+
A revert with no added message restores the scope to what it was just before the target commit ran. This is how you "edit" a mistake without rewriting history.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
git commit -m "x = 1"
|
|
179
|
+
git commit -m "x = 99" # oops
|
|
180
|
+
git revert HEAD # x is now back to 1
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The target can be any commit by SHA, not just the previous one:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git revert abc1234 # rolls back state to before abc1234 ran
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Exception handler
|
|
190
|
+
|
|
191
|
+
If you add code to the revert commit message (via `git revert <sha> --edit`), that code runs only if the target commit raised an error. If the target succeeded, the revert is a no-op.
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
git commit -m "result = 1 / x" # might fail if x == 0
|
|
195
|
+
git revert HEAD --edit
|
|
196
|
+
# message becomes:
|
|
197
|
+
# result = -1
|
|
198
|
+
#
|
|
199
|
+
# This reverts commit abc1234.
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
When `x == 0` the original commit raises `ZeroDivisionError`, the handler runs, and `result = -1`. When `x != 0` the original succeeds and the handler is skipped.
|
|
203
|
+
|
|
204
|
+
### Detection
|
|
205
|
+
|
|
206
|
+
CWG detects reverts by the auto-generated `This reverts commit <sha>.` line that `git revert` produces. Manually writing a commit with a message starting with "revert" does not trigger revert behaviour — you must use the `git revert` command.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Sample Programs
|
|
211
|
+
|
|
212
|
+
### Hello World
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git init hello-world
|
|
216
|
+
git commit -m "message = 'hello world'"
|
|
217
|
+
git commit -m "print(message)"
|
|
218
|
+
# output: hello world
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Countdown
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
git init countdown
|
|
225
|
+
git commit -m "i = 10"
|
|
226
|
+
git branch while/countdown
|
|
227
|
+
git commit -m "while i > 0:"
|
|
228
|
+
git commit -m " print(i)"
|
|
229
|
+
git commit -m " i = i - 1"
|
|
230
|
+
git checkout main
|
|
231
|
+
git merge while/countdown
|
|
232
|
+
git commit -m "print('blastoff')"
|
|
233
|
+
# output: 10 9 8 7 6 5 4 3 2 1 blastoff
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### FizzBuzz
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
git init fizzbuzz
|
|
240
|
+
git commit -m "i = 1"
|
|
241
|
+
git branch while/fizzbuzz
|
|
242
|
+
git commit -m "while i <= 20:"
|
|
243
|
+
git branch check/fizzbuzz
|
|
244
|
+
git commit -m " if i % 15 == 0: print('FizzBuzz')"
|
|
245
|
+
git checkout while/fizzbuzz
|
|
246
|
+
git merge check/fizzbuzz
|
|
247
|
+
git branch check/fizz
|
|
248
|
+
git commit -m " elif i % 3 == 0: print('Fizz')"
|
|
249
|
+
git checkout while/fizzbuzz
|
|
250
|
+
git merge check/fizz
|
|
251
|
+
git branch check/buzz
|
|
252
|
+
git commit -m " elif i % 5 == 0: print('Buzz')"
|
|
253
|
+
git checkout while/fizzbuzz
|
|
254
|
+
git merge check/buzz
|
|
255
|
+
git branch check/default
|
|
256
|
+
git commit -m " else: print(i)"
|
|
257
|
+
git checkout while/fizzbuzz
|
|
258
|
+
git merge check/default
|
|
259
|
+
git commit -m " i = i + 1"
|
|
260
|
+
git checkout main
|
|
261
|
+
git merge while/fizzbuzz
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Data Types
|
|
267
|
+
|
|
268
|
+
| Type | Example |
|
|
269
|
+
|---|---|
|
|
270
|
+
| int | `x = 5` |
|
|
271
|
+
| float | `pi = 3.14` |
|
|
272
|
+
| string | `name = 'alice'` |
|
|
273
|
+
| bool | `flag = True` |
|
|
274
|
+
| list | `nums = [1, 2, 3]` |
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Installation
|
|
279
|
+
|
|
280
|
+
### Prerequisites
|
|
281
|
+
|
|
282
|
+
- Python 3.10 or newer
|
|
283
|
+
- Git
|
|
284
|
+
|
|
285
|
+
### Setup
|
|
286
|
+
|
|
287
|
+
Clone the repo, create a virtual environment, and install CWG in editable mode:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
git clone <repo-url>
|
|
291
|
+
cd CWG-Coding_with_Git
|
|
292
|
+
python3 -m venv .venv
|
|
293
|
+
source .venv/bin/activate # macOS / Linux
|
|
294
|
+
# .venv\Scripts\activate # Windows
|
|
295
|
+
pip install -e .
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The `pip install -e .` step installs CWG itself, makes the `cwg` command available on your `PATH`, and pulls in the runtime dependency (GitPython).
|
|
299
|
+
|
|
300
|
+
To also run the test suite:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
pip install pytest pytest-cov
|
|
304
|
+
pytest
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Hello world
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
mkdir hello && cd hello
|
|
311
|
+
git init
|
|
312
|
+
git commit --allow-empty -m "message = 'hello world'"
|
|
313
|
+
git commit --allow-empty -m "print(message)"
|
|
314
|
+
cwg run .
|
|
315
|
+
# output: hello world
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Status
|
|
321
|
+
|
|
322
|
+
This project is a work in progress. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to get involved.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .gpScraper import scrape, GpScrapeResult, CommitNode, FunctionDef, StashEntry
|
|
2
|
+
from .interpreter import run, build_exec_tree, StatementNode, IfNode, WhileNode, ForNode
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"scrape",
|
|
6
|
+
"GpScrapeResult",
|
|
7
|
+
"CommitNode",
|
|
8
|
+
"FunctionDef",
|
|
9
|
+
"StashEntry",
|
|
10
|
+
"run",
|
|
11
|
+
"build_exec_tree",
|
|
12
|
+
"StatementNode",
|
|
13
|
+
"IfNode",
|
|
14
|
+
"WhileNode",
|
|
15
|
+
"ForNode",
|
|
16
|
+
]
|
cwg-0.1.0/core/cli.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CWG command-line dispatcher.
|
|
3
|
+
|
|
4
|
+
cwg run [target] — execute a CWG program
|
|
5
|
+
target = GitHub URL or .cwg file; empty = current repo
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
_USAGE = (
|
|
11
|
+
"usage: cwg run [target]\n"
|
|
12
|
+
" target GitHub repo URL or .cwg file; omit to run the current repo's "
|
|
13
|
+
"git history"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> None:
|
|
18
|
+
args = sys.argv[1:]
|
|
19
|
+
|
|
20
|
+
if not args or args[0] != "run":
|
|
21
|
+
print(_USAGE, file=sys.stderr)
|
|
22
|
+
sys.exit(0 if not args else 2)
|
|
23
|
+
|
|
24
|
+
sys.argv = [sys.argv[0]] + args[1:]
|
|
25
|
+
from .runner import main as run_main
|
|
26
|
+
run_main()
|