nockasm 1.0.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.
- nockasm-1.0.0/.claude/settings.local.json +10 -0
- nockasm-1.0.0/PKG-INFO +204 -0
- nockasm-1.0.0/README.md +195 -0
- nockasm-1.0.0/benchmarks/ackermann.nasm +57 -0
- nockasm-1.0.0/benchmarks/add.nasm +48 -0
- nockasm-1.0.0/benchmarks/dec.nasm +41 -0
- nockasm-1.0.0/benchmarks/factorial.nasm +96 -0
- nockasm-1.0.0/benchmarks/fibonacci.nasm +79 -0
- nockasm-1.0.0/benchmarks/tests.json +32 -0
- nockasm-1.0.0/img/hero.jpg +0 -0
- nockasm-1.0.0/nockasm.py +623 -0
- nockasm-1.0.0/pyproject.toml +18 -0
- nockasm-1.0.0/test_benchmarks.py +73 -0
- nockasm-1.0.0/test_e2e.py +161 -0
- nockasm-1.0.0/test_nockasm.py +247 -0
- nockasm-1.0.0/tour.ipynb +282 -0
nockasm-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nockasm
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A thin macro expander from Nock Assembly to canonical Nock 4K
|
|
5
|
+
Author-email: Sigilante <davisneale@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# Nock Assembly
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
Nock Assembly is a thin macro over [Nock ISA](https://nock.is) designed to make the language more legible for pedagogical purposes.
|
|
15
|
+
|
|
16
|
+
## Design
|
|
17
|
+
|
|
18
|
+
| | |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Named opcodes | `(%inc .x)` instead of `[4 0 2]`. Pure lexical. |
|
|
21
|
+
| Axis schemas | `:subject {.a .b .c}` resolves `.a` `.b` `.c` to axes 2, 6, 7. Right-leaning by Hoon convention. |
|
|
22
|
+
| `#let .name = E in B`| Opcode-8 push. Tracks subject shift via `+peg(3, n)` so old names still resolve in body. |
|
|
23
|
+
| `#match E { ... }` | Scrutinee lifted once via opcode 8. Nested opcode-6 dispatch on literal patterns. Required `_ =>` default. |
|
|
24
|
+
| `; comments` | And whitespace. |
|
|
25
|
+
|
|
26
|
+
## Install / use
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from nockasm import expand
|
|
30
|
+
print(expand("(%inc (%self))"))
|
|
31
|
+
# [4 0 1]
|
|
32
|
+
|
|
33
|
+
print(expand("""
|
|
34
|
+
:subject {.tag .data}
|
|
35
|
+
#match .tag {
|
|
36
|
+
1 => (%inc .data)
|
|
37
|
+
2 => .data
|
|
38
|
+
_ => 0
|
|
39
|
+
}
|
|
40
|
+
"""))
|
|
41
|
+
# [8 [0 2] 6 [5 [1 1] 0 2] [4 0 7] 6 [5 [1 2] 0 2] [0 7] 1 0]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
End-to-end with [`pinochle`](https://github.com/sigilante/pinochle):
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from pinochle import nock, parse_noun
|
|
48
|
+
from nockasm import expand
|
|
49
|
+
|
|
50
|
+
src = """
|
|
51
|
+
:subject {.before .target .after}
|
|
52
|
+
#let .next = (%inc .target) in
|
|
53
|
+
[.before .next .after]
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
formula = parse_noun(expand(src))
|
|
57
|
+
result = nock(parse_noun("[10 41 99]"), formula)
|
|
58
|
+
# result == [10 42 99]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
At the CLI:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python -m nockasm program.nasm # canonical flat
|
|
65
|
+
python -m nockasm --pretty program.nasm # explicit binary cells
|
|
66
|
+
echo "(%inc (%self))" | python -m nockasm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Integration with the Nock kernel
|
|
70
|
+
|
|
71
|
+
Pinochle ships `nock-kernel` for Jupyter (`Nock 4K` kernel). It accepts
|
|
72
|
+
canonical Nock in `:formula` cells. Workflow today:
|
|
73
|
+
|
|
74
|
+
1. Write `.nasm` in a regular Python cell (or text editor).
|
|
75
|
+
2. Run `expand(src)` in a Python notebook to get canonical Nock.
|
|
76
|
+
3. Paste the result into a `:formula` cell in a Nock notebook.
|
|
77
|
+
|
|
78
|
+
A `:asm` cell magic for the Nock kernel that does this in one step is the
|
|
79
|
+
obvious next step. Roughly:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# in pinochle/packages/nock_kernel/kernel.py
|
|
83
|
+
if cell.startswith(':asm'):
|
|
84
|
+
from nockasm import expand
|
|
85
|
+
formula_src = expand(cell[len(':asm'):])
|
|
86
|
+
# then dispatch as if user had typed ':formula <formula_src>'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Structural macros
|
|
90
|
+
|
|
91
|
+
### `#let .name = VALUE in BODY`
|
|
92
|
+
|
|
93
|
+
Pushes `VALUE` onto the subject via opcode 8 and binds `.name` to axis 2 in
|
|
94
|
+
`BODY`. Any axes that were already in scope are shifted rightward via
|
|
95
|
+
`+peg(3, axis)`, so the old names still resolve in the body.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
:subject {.before .target .after}
|
|
99
|
+
#let .next = (%inc .target) in
|
|
100
|
+
[.before .next .after]
|
|
101
|
+
; -> [8 [4 0 6] [0 6] [0 2] 0 15]
|
|
102
|
+
; against [10 41 99] -> [10 42 99]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`VALUE` and `BODY` are both formula positions (bare atoms lift). Shadowing
|
|
106
|
+
an existing schema name is a compile error.
|
|
107
|
+
|
|
108
|
+
### `#match EXPR { PAT => BODY ... _ => DEFAULT }`
|
|
109
|
+
|
|
110
|
+
Pattern match on the value of `EXPR`. The scrutinee is evaluated once via
|
|
111
|
+
opcode 8 — i.e. lifted onto the subject — then each `PAT` is compared
|
|
112
|
+
against the lifted value via opcode 5 (eq), with opcode 6 (if) dispatching
|
|
113
|
+
to the matching `BODY`. The `_ =>` default is required.
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
:subject {.tag .data}
|
|
117
|
+
#match .tag {
|
|
118
|
+
1 => (%inc .data)
|
|
119
|
+
2 => .data
|
|
120
|
+
_ => 0
|
|
121
|
+
}
|
|
122
|
+
; -> [8 [0 2] 6 [5 [1 1] 0 2] [4 0 7] 6 [5 [1 2] 0 2] [0 7] 1 0]
|
|
123
|
+
; against [1 41] -> 42
|
|
124
|
+
; against [2 41] -> 41
|
|
125
|
+
; against [9 41] -> 0
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`EXPR` and each `BODY` are formula positions. `PAT`s are *noun literals* —
|
|
129
|
+
they're compared against the scrutinee's runtime value, not against a
|
|
130
|
+
formula. Bare atoms in `PAT` position are not lifted: writing `1 => ...`
|
|
131
|
+
matches the atom `1`, not the formula `[1 1]`.
|
|
132
|
+
|
|
133
|
+
In the body of each arm (and the default), the scrutinee is at axis 2, and
|
|
134
|
+
the original schema axes are shifted rightward via `+peg(3, axis)` — same
|
|
135
|
+
shift rule as `#let`. That's why `.data` resolves to `[0 7]` (not `[0 3]`)
|
|
136
|
+
in the example above.
|
|
137
|
+
|
|
138
|
+
## What lifts and what doesn't
|
|
139
|
+
|
|
140
|
+
Bare atoms get lifted to `[1 atom]` in formula positions. Not in noun-literal
|
|
141
|
+
positions (`%const` arg, hint tag) or axis positions (`%slot` arg, `%call`
|
|
142
|
+
arity arg, etc.). The per-opcode kinds:
|
|
143
|
+
|
|
144
|
+
| Opcode | Kinds | Notes |
|
|
145
|
+
|-----------|-------|-------|
|
|
146
|
+
| `%slot N` | a | axis literal |
|
|
147
|
+
| `%const X`| n | any noun, no lift |
|
|
148
|
+
| `%arm X` | n | synonym for `%const`; intent: callable formula |
|
|
149
|
+
| `%crash` | — | `[0 0]` — Nock crash idiom |
|
|
150
|
+
| `%self` | — | `[0 1]` — whole subject |
|
|
151
|
+
| `%battery`| — | `[0 2]` — standard core battery |
|
|
152
|
+
| `%payload`| — | `[0 3]` — standard core payload |
|
|
153
|
+
| `%sample` | — | `[0 6]` — standard gate sample |
|
|
154
|
+
| `%context`| — | `[0 7]` — standard gate context |
|
|
155
|
+
| `%eval` | ff | both formulas |
|
|
156
|
+
| `%isa` | f | |
|
|
157
|
+
| `%inc` | f | |
|
|
158
|
+
| `%eq` | ff | |
|
|
159
|
+
| `%if` | fff | branches lift |
|
|
160
|
+
| `%comp` | ff | |
|
|
161
|
+
| `%push` | ff | |
|
|
162
|
+
| `%call N F`| af | |
|
|
163
|
+
| `%edit N V F`| aff | |
|
|
164
|
+
| `%hint T F` | nf | tag is a noun literal |
|
|
165
|
+
| `%hintd T C F` | nff | clue is a formula — per 4K spec it's evaluated |
|
|
166
|
+
|
|
167
|
+
The intent-marking opcodes (`%arm`, `%crash`, and the axis aliases) all lower
|
|
168
|
+
to the same cells as their `%const` / `%slot` equivalents — they exist purely
|
|
169
|
+
to surface meaning at the source level. `%arm X` is `%const X` for cases
|
|
170
|
+
where `X` is a formula that will later be invoked via `%call`; `%self`
|
|
171
|
+
through `%context` name the standard Hoon core/gate axes.
|
|
172
|
+
|
|
173
|
+
`#let` value and body are formulas. `#match` scrutinee and arm bodies are
|
|
174
|
+
formulas. Match *patterns* are noun literals (compared against the
|
|
175
|
+
scrutinee's evaluated value).
|
|
176
|
+
|
|
177
|
+
Raw cells `[...]` are taken structurally: their elements are *not* lifted.
|
|
178
|
+
That gives you an escape hatch into raw Nock when you need it, and the
|
|
179
|
+
cons-formula distribution pattern works as expected:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
:subject {.a .b}
|
|
183
|
+
[(%inc .a) (%inc .b)]
|
|
184
|
+
; -> [[4 0 2] [4 0 3]]
|
|
185
|
+
; against [3 5] -> [4 6] via Nock distribution
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Tests
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
python test_nockasm.py # unit tests, 55 cases
|
|
192
|
+
python test_e2e.py # end-to-end: expand -> pinochle -> verify, 19 cases
|
|
193
|
+
python test_benchmarks.py # urbit/benchmark equivalents, 5 cases (loaded from disk)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`test_benchmarks.py` reads `benchmarks/tests.json` and `benchmarks/<name>.nasm`
|
|
197
|
+
from disk and runs each through pinochle. The five benchmarks present
|
|
198
|
+
(`dec`, `add`, `factorial`, `fibonacci`, `ackermann`) are faithful
|
|
199
|
+
transcriptions of `urbit/benchmark/desk/bar/<name>.nock` — each `.nasm`
|
|
200
|
+
expands to a noun bit-identical to the corresponding `.nock` formula.
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT.
|
nockasm-1.0.0/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Nock Assembly
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Nock Assembly is a thin macro over [Nock ISA](https://nock.is) designed to make the language more legible for pedagogical purposes.
|
|
6
|
+
|
|
7
|
+
## Design
|
|
8
|
+
|
|
9
|
+
| | |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Named opcodes | `(%inc .x)` instead of `[4 0 2]`. Pure lexical. |
|
|
12
|
+
| Axis schemas | `:subject {.a .b .c}` resolves `.a` `.b` `.c` to axes 2, 6, 7. Right-leaning by Hoon convention. |
|
|
13
|
+
| `#let .name = E in B`| Opcode-8 push. Tracks subject shift via `+peg(3, n)` so old names still resolve in body. |
|
|
14
|
+
| `#match E { ... }` | Scrutinee lifted once via opcode 8. Nested opcode-6 dispatch on literal patterns. Required `_ =>` default. |
|
|
15
|
+
| `; comments` | And whitespace. |
|
|
16
|
+
|
|
17
|
+
## Install / use
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from nockasm import expand
|
|
21
|
+
print(expand("(%inc (%self))"))
|
|
22
|
+
# [4 0 1]
|
|
23
|
+
|
|
24
|
+
print(expand("""
|
|
25
|
+
:subject {.tag .data}
|
|
26
|
+
#match .tag {
|
|
27
|
+
1 => (%inc .data)
|
|
28
|
+
2 => .data
|
|
29
|
+
_ => 0
|
|
30
|
+
}
|
|
31
|
+
"""))
|
|
32
|
+
# [8 [0 2] 6 [5 [1 1] 0 2] [4 0 7] 6 [5 [1 2] 0 2] [0 7] 1 0]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
End-to-end with [`pinochle`](https://github.com/sigilante/pinochle):
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from pinochle import nock, parse_noun
|
|
39
|
+
from nockasm import expand
|
|
40
|
+
|
|
41
|
+
src = """
|
|
42
|
+
:subject {.before .target .after}
|
|
43
|
+
#let .next = (%inc .target) in
|
|
44
|
+
[.before .next .after]
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
formula = parse_noun(expand(src))
|
|
48
|
+
result = nock(parse_noun("[10 41 99]"), formula)
|
|
49
|
+
# result == [10 42 99]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
At the CLI:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python -m nockasm program.nasm # canonical flat
|
|
56
|
+
python -m nockasm --pretty program.nasm # explicit binary cells
|
|
57
|
+
echo "(%inc (%self))" | python -m nockasm
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Integration with the Nock kernel
|
|
61
|
+
|
|
62
|
+
Pinochle ships `nock-kernel` for Jupyter (`Nock 4K` kernel). It accepts
|
|
63
|
+
canonical Nock in `:formula` cells. Workflow today:
|
|
64
|
+
|
|
65
|
+
1. Write `.nasm` in a regular Python cell (or text editor).
|
|
66
|
+
2. Run `expand(src)` in a Python notebook to get canonical Nock.
|
|
67
|
+
3. Paste the result into a `:formula` cell in a Nock notebook.
|
|
68
|
+
|
|
69
|
+
A `:asm` cell magic for the Nock kernel that does this in one step is the
|
|
70
|
+
obvious next step. Roughly:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# in pinochle/packages/nock_kernel/kernel.py
|
|
74
|
+
if cell.startswith(':asm'):
|
|
75
|
+
from nockasm import expand
|
|
76
|
+
formula_src = expand(cell[len(':asm'):])
|
|
77
|
+
# then dispatch as if user had typed ':formula <formula_src>'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Structural macros
|
|
81
|
+
|
|
82
|
+
### `#let .name = VALUE in BODY`
|
|
83
|
+
|
|
84
|
+
Pushes `VALUE` onto the subject via opcode 8 and binds `.name` to axis 2 in
|
|
85
|
+
`BODY`. Any axes that were already in scope are shifted rightward via
|
|
86
|
+
`+peg(3, axis)`, so the old names still resolve in the body.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
:subject {.before .target .after}
|
|
90
|
+
#let .next = (%inc .target) in
|
|
91
|
+
[.before .next .after]
|
|
92
|
+
; -> [8 [4 0 6] [0 6] [0 2] 0 15]
|
|
93
|
+
; against [10 41 99] -> [10 42 99]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`VALUE` and `BODY` are both formula positions (bare atoms lift). Shadowing
|
|
97
|
+
an existing schema name is a compile error.
|
|
98
|
+
|
|
99
|
+
### `#match EXPR { PAT => BODY ... _ => DEFAULT }`
|
|
100
|
+
|
|
101
|
+
Pattern match on the value of `EXPR`. The scrutinee is evaluated once via
|
|
102
|
+
opcode 8 — i.e. lifted onto the subject — then each `PAT` is compared
|
|
103
|
+
against the lifted value via opcode 5 (eq), with opcode 6 (if) dispatching
|
|
104
|
+
to the matching `BODY`. The `_ =>` default is required.
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
:subject {.tag .data}
|
|
108
|
+
#match .tag {
|
|
109
|
+
1 => (%inc .data)
|
|
110
|
+
2 => .data
|
|
111
|
+
_ => 0
|
|
112
|
+
}
|
|
113
|
+
; -> [8 [0 2] 6 [5 [1 1] 0 2] [4 0 7] 6 [5 [1 2] 0 2] [0 7] 1 0]
|
|
114
|
+
; against [1 41] -> 42
|
|
115
|
+
; against [2 41] -> 41
|
|
116
|
+
; against [9 41] -> 0
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`EXPR` and each `BODY` are formula positions. `PAT`s are *noun literals* —
|
|
120
|
+
they're compared against the scrutinee's runtime value, not against a
|
|
121
|
+
formula. Bare atoms in `PAT` position are not lifted: writing `1 => ...`
|
|
122
|
+
matches the atom `1`, not the formula `[1 1]`.
|
|
123
|
+
|
|
124
|
+
In the body of each arm (and the default), the scrutinee is at axis 2, and
|
|
125
|
+
the original schema axes are shifted rightward via `+peg(3, axis)` — same
|
|
126
|
+
shift rule as `#let`. That's why `.data` resolves to `[0 7]` (not `[0 3]`)
|
|
127
|
+
in the example above.
|
|
128
|
+
|
|
129
|
+
## What lifts and what doesn't
|
|
130
|
+
|
|
131
|
+
Bare atoms get lifted to `[1 atom]` in formula positions. Not in noun-literal
|
|
132
|
+
positions (`%const` arg, hint tag) or axis positions (`%slot` arg, `%call`
|
|
133
|
+
arity arg, etc.). The per-opcode kinds:
|
|
134
|
+
|
|
135
|
+
| Opcode | Kinds | Notes |
|
|
136
|
+
|-----------|-------|-------|
|
|
137
|
+
| `%slot N` | a | axis literal |
|
|
138
|
+
| `%const X`| n | any noun, no lift |
|
|
139
|
+
| `%arm X` | n | synonym for `%const`; intent: callable formula |
|
|
140
|
+
| `%crash` | — | `[0 0]` — Nock crash idiom |
|
|
141
|
+
| `%self` | — | `[0 1]` — whole subject |
|
|
142
|
+
| `%battery`| — | `[0 2]` — standard core battery |
|
|
143
|
+
| `%payload`| — | `[0 3]` — standard core payload |
|
|
144
|
+
| `%sample` | — | `[0 6]` — standard gate sample |
|
|
145
|
+
| `%context`| — | `[0 7]` — standard gate context |
|
|
146
|
+
| `%eval` | ff | both formulas |
|
|
147
|
+
| `%isa` | f | |
|
|
148
|
+
| `%inc` | f | |
|
|
149
|
+
| `%eq` | ff | |
|
|
150
|
+
| `%if` | fff | branches lift |
|
|
151
|
+
| `%comp` | ff | |
|
|
152
|
+
| `%push` | ff | |
|
|
153
|
+
| `%call N F`| af | |
|
|
154
|
+
| `%edit N V F`| aff | |
|
|
155
|
+
| `%hint T F` | nf | tag is a noun literal |
|
|
156
|
+
| `%hintd T C F` | nff | clue is a formula — per 4K spec it's evaluated |
|
|
157
|
+
|
|
158
|
+
The intent-marking opcodes (`%arm`, `%crash`, and the axis aliases) all lower
|
|
159
|
+
to the same cells as their `%const` / `%slot` equivalents — they exist purely
|
|
160
|
+
to surface meaning at the source level. `%arm X` is `%const X` for cases
|
|
161
|
+
where `X` is a formula that will later be invoked via `%call`; `%self`
|
|
162
|
+
through `%context` name the standard Hoon core/gate axes.
|
|
163
|
+
|
|
164
|
+
`#let` value and body are formulas. `#match` scrutinee and arm bodies are
|
|
165
|
+
formulas. Match *patterns* are noun literals (compared against the
|
|
166
|
+
scrutinee's evaluated value).
|
|
167
|
+
|
|
168
|
+
Raw cells `[...]` are taken structurally: their elements are *not* lifted.
|
|
169
|
+
That gives you an escape hatch into raw Nock when you need it, and the
|
|
170
|
+
cons-formula distribution pattern works as expected:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
:subject {.a .b}
|
|
174
|
+
[(%inc .a) (%inc .b)]
|
|
175
|
+
; -> [[4 0 2] [4 0 3]]
|
|
176
|
+
; against [3 5] -> [4 6] via Nock distribution
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Tests
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
python test_nockasm.py # unit tests, 55 cases
|
|
183
|
+
python test_e2e.py # end-to-end: expand -> pinochle -> verify, 19 cases
|
|
184
|
+
python test_benchmarks.py # urbit/benchmark equivalents, 5 cases (loaded from disk)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`test_benchmarks.py` reads `benchmarks/tests.json` and `benchmarks/<name>.nasm`
|
|
188
|
+
from disk and runs each through pinochle. The five benchmarks present
|
|
189
|
+
(`dec`, `add`, `factorial`, `fibonacci`, `ackermann`) are faithful
|
|
190
|
+
transcriptions of `urbit/benchmark/desk/bar/<name>.nock` — each `.nasm`
|
|
191
|
+
expands to a noun bit-identical to the corresponding `.nock` formula.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
; ackermann: ack(m, n) on a [m n] subject.
|
|
2
|
+
;
|
|
3
|
+
; Faithful transcription of urbit/benchmark/desk/bar/ackermann.nock,
|
|
4
|
+
; drilled into named opcodes. The core has just two members: the
|
|
5
|
+
; main arm (axis 4) — which self-recurses via %call 4 — and the +dec
|
|
6
|
+
; arm wrapper (axis 5/10).
|
|
7
|
+
;
|
|
8
|
+
; Note: in this |^ trap the payload is [m n], so axis 6 = m and
|
|
9
|
+
; axis 7 = n. The %sample and %context aliases line up by axis but
|
|
10
|
+
; not by their usual gate-call semantics — they are just convenient
|
|
11
|
+
; names for [0 6] and [0 7] here.
|
|
12
|
+
;
|
|
13
|
+
; Subject: a cell [m n].
|
|
14
|
+
; Result : ack(m, n).
|
|
15
|
+
|
|
16
|
+
(%push
|
|
17
|
+
(%const
|
|
18
|
+
[ ; Main arm (axis 4 of [core m_n]). Standard Ackermann recursion.
|
|
19
|
+
(%if (%eq (%const 0) (%sample))
|
|
20
|
+
; m == 0 -> +(n)
|
|
21
|
+
(%inc (%context))
|
|
22
|
+
(%if (%eq (%const 0) (%context))
|
|
23
|
+
; n == 0 -> $(m (dec m), n 1)
|
|
24
|
+
(%call 4
|
|
25
|
+
(%edit 3
|
|
26
|
+
[ (%push (%call 5 (%self))
|
|
27
|
+
(%call 2 (%edit 6 (%slot 14) (%battery))))
|
|
28
|
+
(%const 1) ]
|
|
29
|
+
(%self)))
|
|
30
|
+
; else -> $(m (dec m), n $(n (dec n)))
|
|
31
|
+
(%call 4
|
|
32
|
+
(%edit 3
|
|
33
|
+
[ (%push (%call 5 (%self))
|
|
34
|
+
(%call 2 (%edit 6 (%slot 14) (%battery))))
|
|
35
|
+
(%call 4
|
|
36
|
+
(%edit 7
|
|
37
|
+
(%push (%call 5 (%self))
|
|
38
|
+
(%call 2 (%edit 6 (%slot 15) (%battery))))
|
|
39
|
+
(%self))) ]
|
|
40
|
+
(%self)))))
|
|
41
|
+
|
|
42
|
+
; +dec arm wrapper (axis 5 of [core m_n]). Same body as dec.nasm.
|
|
43
|
+
(%push (%const 0)
|
|
44
|
+
[ (%arm
|
|
45
|
+
(%if (%eq (%const 0) (%sample))
|
|
46
|
+
(%crash)
|
|
47
|
+
(%push (%const 0)
|
|
48
|
+
(%push (%arm
|
|
49
|
+
(%if (%eq (%slot 30) (%inc (%sample)))
|
|
50
|
+
(%sample)
|
|
51
|
+
(%call 2 (%edit 6
|
|
52
|
+
(%inc (%sample))
|
|
53
|
+
(%self)))))
|
|
54
|
+
(%call 2 (%self))))))
|
|
55
|
+
(%self) ]) ])
|
|
56
|
+
|
|
57
|
+
(%call 4 (%self)))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
; add: unsigned addition of [a b].
|
|
2
|
+
;
|
|
3
|
+
; Faithful transcription of urbit/benchmark/desk/bar/add.nock, drilled
|
|
4
|
+
; fully into named opcodes.
|
|
5
|
+
;
|
|
6
|
+
; Subject: a cell [a b].
|
|
7
|
+
; Result : a + b.
|
|
8
|
+
|
|
9
|
+
(%push
|
|
10
|
+
(%const
|
|
11
|
+
[ ; +add arm (axis 4 of [core a_b]). Pushes [0 0] as sample
|
|
12
|
+
; placeholder, then autocons [add-body subject] to make a gate.
|
|
13
|
+
(%push (%const [0 0])
|
|
14
|
+
[ (%arm
|
|
15
|
+
; +add body. Subject when entered: [body sample payload].
|
|
16
|
+
; axis 12 = a, axis 13 = b, axis 28 = (axis 14 of payload).
|
|
17
|
+
(%if (%eq (%const 0) (%slot 12))
|
|
18
|
+
(%slot 13)
|
|
19
|
+
(%call 2
|
|
20
|
+
(%edit 6
|
|
21
|
+
[ (%push (%call 10 (%context))
|
|
22
|
+
(%call 2 (%edit 6 (%slot 28) (%battery))))
|
|
23
|
+
(%inc (%slot 13)) ]
|
|
24
|
+
(%self)))))
|
|
25
|
+
(%self) ])
|
|
26
|
+
|
|
27
|
+
; +dec arm (axis 10 of [core a_b]). Same shape as dec.nasm.
|
|
28
|
+
(%push (%const 0)
|
|
29
|
+
[ (%arm
|
|
30
|
+
(%if (%eq (%const 0) (%sample))
|
|
31
|
+
(%crash)
|
|
32
|
+
(%push (%const 0)
|
|
33
|
+
(%push
|
|
34
|
+
(%arm
|
|
35
|
+
(%if (%eq (%slot 30) (%inc (%sample)))
|
|
36
|
+
(%sample)
|
|
37
|
+
(%call 2 (%edit 6
|
|
38
|
+
(%inc (%sample))
|
|
39
|
+
(%self)))))
|
|
40
|
+
(%call 2 (%self))))))
|
|
41
|
+
(%self) ])
|
|
42
|
+
|
|
43
|
+
; Main body: push +add core, then call its arm with [m n] sample.
|
|
44
|
+
(%push (%call 4 (%self))
|
|
45
|
+
(%call 2 (%edit 6 [(%slot 14) (%slot 15)] (%battery)))) ])
|
|
46
|
+
|
|
47
|
+
; Invoke main body at axis 11 of [core a_b].
|
|
48
|
+
(%call 11 (%self)))
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
; dec: naive unsigned decrement.
|
|
2
|
+
;
|
|
3
|
+
; Faithful transcription of urbit/benchmark/desk/bar/dec.nock, drilled
|
|
4
|
+
; fully into named opcodes. Uses %arm (intent-marked %const) for the
|
|
5
|
+
; formulas later invoked via %call, %crash for [0 0], and the standard
|
|
6
|
+
; axis aliases %self / %battery / %payload / %sample / %context.
|
|
7
|
+
;
|
|
8
|
+
; Subject: a single atom m (asserted nonzero).
|
|
9
|
+
; Result : m - 1.
|
|
10
|
+
|
|
11
|
+
(%push
|
|
12
|
+
; Push the |^ core onto the subject stack; new subject = [core m].
|
|
13
|
+
(%const
|
|
14
|
+
[ ; main arm at axis 4: build the dec sub-core via the core tail,
|
|
15
|
+
; then call its first arm with m installed at sample.
|
|
16
|
+
(%push (%call 5 (%self))
|
|
17
|
+
(%call 2 (%edit 6 (%context) (%battery))))
|
|
18
|
+
|
|
19
|
+
; core tail at axis 5: a formula that, when invoked, autocons
|
|
20
|
+
; [dec-arm subject] to assemble a gate-shaped sub-core.
|
|
21
|
+
(%push (%const 0)
|
|
22
|
+
[ (%arm
|
|
23
|
+
; +dec arm body. Subject when called is [arm payload]
|
|
24
|
+
; with the argument at sample, original at axis 30.
|
|
25
|
+
(%if (%eq (%const 0) (%sample))
|
|
26
|
+
(%crash)
|
|
27
|
+
(%push (%const 0)
|
|
28
|
+
(%push
|
|
29
|
+
(%arm
|
|
30
|
+
; inner |- loop on candidate b at sample:
|
|
31
|
+
; if a == +(b) return b, else recurse b+1
|
|
32
|
+
(%if (%eq (%slot 30) (%inc (%sample)))
|
|
33
|
+
(%sample)
|
|
34
|
+
(%call 2 (%edit 6
|
|
35
|
+
(%inc (%sample))
|
|
36
|
+
(%self)))))
|
|
37
|
+
(%call 2 (%self))))))
|
|
38
|
+
(%self) ]) ])
|
|
39
|
+
|
|
40
|
+
; Call the main arm at axis 4 of the new subject.
|
|
41
|
+
(%call 4 (%self)))
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
; factorial: tail-recursive factorial of a single-atom subject.
|
|
2
|
+
;
|
|
3
|
+
; Faithful transcription of urbit/benchmark/desk/bar/factorial.nock,
|
|
4
|
+
; drilled into named opcodes. The core has three explicit arms — +mul
|
|
5
|
+
; (axis 4), +add (axis 20), +dec (axis 21) — and a main body (axis 11)
|
|
6
|
+
; that builds an anonymous |= gate and invokes it on m.
|
|
7
|
+
;
|
|
8
|
+
; Subject: a single atom m.
|
|
9
|
+
; Result : m! (use small m; pure Nock multiplication is slow).
|
|
10
|
+
|
|
11
|
+
(%push
|
|
12
|
+
(%const
|
|
13
|
+
[ ; +mul arm (axis 4 of [core m]). Standard `|: [a=`@`1 b=`@`1]`
|
|
14
|
+
; pattern: push the [1 1] default sample, then autocons
|
|
15
|
+
; [mul-body subject].
|
|
16
|
+
(%push (%const [1 1])
|
|
17
|
+
[ (%arm
|
|
18
|
+
; +mul body. Push c=0 accumulator, then push the |- arm
|
|
19
|
+
; and call it.
|
|
20
|
+
(%push (%const 0)
|
|
21
|
+
(%push (%arm
|
|
22
|
+
; |- loop on a (axis 60) with c (axis 6).
|
|
23
|
+
; if a == 0 -> return c
|
|
24
|
+
; else recurse with a := dec(a), c := add(b, c)
|
|
25
|
+
(%if (%eq (%const 0) (%slot 60))
|
|
26
|
+
(%sample)
|
|
27
|
+
(%call 2
|
|
28
|
+
(%edit 60
|
|
29
|
+
(%push (%call 21 (%slot 31))
|
|
30
|
+
(%call 2 (%edit 6 (%slot 124) (%battery))))
|
|
31
|
+
(%edit 6
|
|
32
|
+
(%push (%call 20 (%slot 31))
|
|
33
|
+
(%call 2 (%edit 6 [(%slot 125) (%slot 14)] (%battery))))
|
|
34
|
+
(%self))))))
|
|
35
|
+
(%call 2 (%self)))))
|
|
36
|
+
(%self) ])
|
|
37
|
+
|
|
38
|
+
; Arms cell at axis 5 of [core m]:
|
|
39
|
+
; head (axis 10 of [core m]) = +add arm
|
|
40
|
+
; tail (axis 11 of [core m]) starts with `8 [1 0] ...` — the
|
|
41
|
+
; +dec arm wrapped as a noun-positioned formula.
|
|
42
|
+
; NOTE: axis 11 is also where the main body lives — it shares a
|
|
43
|
+
; prefix with the +dec wrapper, hence the structural mix here.
|
|
44
|
+
[ (%push (%const [0 0])
|
|
45
|
+
[ (%arm
|
|
46
|
+
; +add body — uses +dec at axis 21.
|
|
47
|
+
(%if (%eq (%const 0) (%slot 12))
|
|
48
|
+
(%slot 13)
|
|
49
|
+
(%call 2
|
|
50
|
+
(%edit 6
|
|
51
|
+
[ (%push (%call 21 (%context))
|
|
52
|
+
(%call 2 (%edit 6 (%slot 28) (%battery))))
|
|
53
|
+
(%inc (%slot 13)) ]
|
|
54
|
+
(%self)))))
|
|
55
|
+
(%self) ])
|
|
56
|
+
|
|
57
|
+
; +dec arm (axis 21 of [core m]) — identical body to dec.nasm.
|
|
58
|
+
(%push (%const 0)
|
|
59
|
+
[ (%arm
|
|
60
|
+
(%if (%eq (%const 0) (%sample))
|
|
61
|
+
(%crash)
|
|
62
|
+
(%push (%const 0)
|
|
63
|
+
(%push (%arm
|
|
64
|
+
(%if (%eq (%slot 30) (%inc (%sample)))
|
|
65
|
+
(%sample)
|
|
66
|
+
(%call 2 (%edit 6
|
|
67
|
+
(%inc (%sample))
|
|
68
|
+
(%self)))))
|
|
69
|
+
(%call 2 (%self))))))
|
|
70
|
+
(%self) ]) ]
|
|
71
|
+
|
|
72
|
+
; Main body (also at axis 11): build the anonymous |= gate, then
|
|
73
|
+
; call its battery with m installed at sample.
|
|
74
|
+
(%push (%push (%const 0)
|
|
75
|
+
[ (%arm
|
|
76
|
+
; |= gate body: =/ t=1; |- ...
|
|
77
|
+
(%push (%const 1)
|
|
78
|
+
(%push (%arm
|
|
79
|
+
; |- loop on n (axis 30), t (sample).
|
|
80
|
+
; if n == 1 -> t
|
|
81
|
+
; else recurse n := dec(n), t := mul(t, n)
|
|
82
|
+
(%if (%eq (%slot 30) (%const 1))
|
|
83
|
+
(%sample)
|
|
84
|
+
(%call 2
|
|
85
|
+
(%edit 30
|
|
86
|
+
(%push (%call 21 (%slot 31))
|
|
87
|
+
(%call 2 (%edit 6 (%slot 62) (%battery))))
|
|
88
|
+
(%edit 6
|
|
89
|
+
(%push (%call 4 (%slot 31))
|
|
90
|
+
(%call 2 (%edit 6 [(%slot 14) (%slot 62)] (%battery))))
|
|
91
|
+
(%self))))))
|
|
92
|
+
(%call 2 (%self)))))
|
|
93
|
+
(%self) ])
|
|
94
|
+
(%call 2 (%edit 6 (%context) (%battery)))) ])
|
|
95
|
+
|
|
96
|
+
(%call 11 (%self)))
|