iversonnb 0.2.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.
- iversonnb-0.2.0/LICENSE +201 -0
- iversonnb-0.2.0/MANIFEST.in +5 -0
- iversonnb-0.2.0/PKG-INFO +455 -0
- iversonnb-0.2.0/README.md +434 -0
- iversonnb-0.2.0/iversonnb/__init__.py +9 -0
- iversonnb-0.2.0/iversonnb/_modidx.py +67 -0
- iversonnb-0.2.0/iversonnb/core.py +253 -0
- iversonnb-0.2.0/iversonnb/j.py +204 -0
- iversonnb-0.2.0/iversonnb/jkernel.py +49 -0
- iversonnb-0.2.0/iversonnb/kernel.py +56 -0
- iversonnb-0.2.0/iversonnb.egg-info/PKG-INFO +455 -0
- iversonnb-0.2.0/iversonnb.egg-info/SOURCES.txt +18 -0
- iversonnb-0.2.0/iversonnb.egg-info/dependency_links.txt +1 -0
- iversonnb-0.2.0/iversonnb.egg-info/entry_points.txt +9 -0
- iversonnb-0.2.0/iversonnb.egg-info/requires.txt +2 -0
- iversonnb-0.2.0/iversonnb.egg-info/top_level.txt +1 -0
- iversonnb-0.2.0/pyproject.toml +44 -0
- iversonnb-0.2.0/setup.cfg +4 -0
- iversonnb-0.2.0/tests/test_jkernel.py +86 -0
- iversonnb-0.2.0/tests/test_kernel.py +163 -0
iversonnb-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
iversonnb-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iversonnb
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: APL and J in notebooks: Iverson-language magics and kernels for Jupyter, IPython, and LLM agents
|
|
5
|
+
Author-email: Jeremy Howard <info@answer.ai>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/answerdotai/iversonnb
|
|
8
|
+
Project-URL: Documentation, https://answerdotai.github.io/iversonnb
|
|
9
|
+
Keywords: nbdev,jupyter,notebook,python,apl,dyalog,j,jsoftware
|
|
10
|
+
Classifier: Natural Language :: English
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: fastcore
|
|
19
|
+
Requires-Dist: clikernel>=0.1.6
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# iversonnb
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
26
|
+
|
|
27
|
+
`iversonnb` brings the Iverson languages to notebooks. It adds `%%apl`
|
|
28
|
+
to Jupyter and IPython, which executes code in Dyalog APL, `%%j` to J,
|
|
29
|
+
plus persistent APL and J kernels for LLM agents (including MCP). APL
|
|
30
|
+
runs over the [RIDE
|
|
31
|
+
protocol](https://github.com/Dyalog/ride/blob/master/docs/protocol.md),
|
|
32
|
+
the same protocol Dyalog’s own IDE uses, so nothing needs to be loaded
|
|
33
|
+
into your workspace, output looks exactly like a Dyalog session, and
|
|
34
|
+
errors are detected reliably. J runs in-process through libj, the engine
|
|
35
|
+
library that ships with every J install.
|
|
36
|
+
|
|
37
|
+
See [`core`](https://answerdotai.github.io/iversonnb/core.html) and
|
|
38
|
+
[`j`](https://answerdotai.github.io/iversonnb/j.html) for step by step
|
|
39
|
+
walkthroughs of how each is built.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
Install the languages you plan to use; nothing starts until a magic
|
|
44
|
+
first runs, so neither is required.
|
|
45
|
+
|
|
46
|
+
- **APL**: install [Dyalog APL](https://www.dyalog.com/). Dyalog
|
|
47
|
+
provides a basic license for free. iversonnb is developed against
|
|
48
|
+
Dyalog 20.0; 18.2 and later may work but are untested.
|
|
49
|
+
- **J**: `pip install jlanguage`, or install it from
|
|
50
|
+
[jsoftware.com](https://www.jsoftware.com/) – iversonnb finds either.
|
|
51
|
+
|
|
52
|
+
Then:
|
|
53
|
+
|
|
54
|
+
pip install iversonnb
|
|
55
|
+
|
|
56
|
+
Once that’s complete, you can install the magics to all IPython and
|
|
57
|
+
Jupyter sessions automatically by running in your terminal:
|
|
58
|
+
|
|
59
|
+
iversonnb_install
|
|
60
|
+
|
|
61
|
+
An interpreter only starts the first time you run its magic, so having
|
|
62
|
+
iversonnb installed everywhere costs nothing when you don’t use it.
|
|
63
|
+
|
|
64
|
+
## The `apl` magics
|
|
65
|
+
|
|
66
|
+
After first running an `apl` magic in a notebook, the [APL language
|
|
67
|
+
bar](https://abrudz.github.io/lb/apl) by Adám Brudzewsky is
|
|
68
|
+
automatically added to the current page. (iversonnb bundles a modified
|
|
69
|
+
copy of Adám’s lb.js; the file header lists the changes. The most
|
|
70
|
+
visible ones: type a backtick twice in a row to enter triple backticks,
|
|
71
|
+
get a `⋄` glyph with backtick-q, and use the `▲`/`▼` button beside the
|
|
72
|
+
close button to choose whether the bar pushes the page down or overlays
|
|
73
|
+
it, remembered per site.)
|
|
74
|
+
|
|
75
|
+
The cell magic (`%%apl`) runs APL code and displays the session’s own
|
|
76
|
+
output, rendered in Adám’s [SAX2](https://github.com/abrudz/SAX2) APL
|
|
77
|
+
font, so results look exactly as they do in Dyalog:
|
|
78
|
+
|
|
79
|
+
``` python
|
|
80
|
+
%%apl
|
|
81
|
+
m←3 3⍴⍳9
|
|
82
|
+
m×10
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Javascript(// APL language bar by Adám Brudzewsky: https://abrudz.github.io/lb (source: https://github.com/abrudz/lb)
|
|
86
|
+
// MIT License, Copyright (c) 2011-2020 Nikolay G. Nikolov and Adam Brudzevski. This is a modified copy bundled with iversonnb.
|
|
87
|
+
// Changes from upstream: double backtick composes ```; insertion via insertText so undo and input events work;
|
|
88
|
+
// Monaco editor support (incl. EditContext mode); dark mode; overlay/push-down toggle persisted per site;
|
|
89
|
+
// idempotent injection; ResizeObserver-driven layout; @font-face with dead url() removed; skipped on quarto-rendered pages.
|
|
90
|
+
; (_ => {
|
|
91
|
+
if (document.querySelector('.ngn_lb')) return
|
|
92
|
+
if (document.querySelector('meta[name=generator][content^=quarto]')) return //no bar on rendered docs pages
|
|
93
|
+
let hc = { '<': '<', '&': '&', "'": ''', '"': '"' }, he = x => x.replace(/[<&'"]/g, c => hc[c]) //html chars and escape fn
|
|
94
|
+
, tcs = '<-←xx×/\\×:-÷*O⍟[-⌹-]⌹OO○77⌈FF⌈ll⌊LL⌊T_⌶II⌶|_⊥TT⊤-|⊣|-⊢=/≠L-≠<=≤<_≤>=≥>_≥==≡=_≡7=≢Z-≢vv∨^^∧^~⍲v~⍱^|↑v|↓((⊂cc⊂(_⊆c_⊆))⊃[|⌷|]⌷A|⍋V|⍒ii⍳i_⍸ee∊e_⍷' +
|
|
95
|
+
'uu∪UU∪nn∩/-⌿\\-⍀,-⍪rr⍴pp⍴O|⌽O-⊖O\\⍉::¨""¨~:⍨~"⍨*:⍣*"⍣oo∘o:⍤o"⍤O:⍥O"⍥[\'⍞\']⍞[]⎕[:⍠:]⍠[=⌸=]⌸[<⌺>]⌺o_⍎oT⍕o-⍕<>⋄^v⋄on⍝->→aa⍺ww⍵VV∇v-∇--¯0~⍬' +
|
|
96
|
+
'AA∆^-∆A_⍙^=⍙[?⍰?]⍰:V⍢∇"⍢||∥ox¤)_⊇_)⊇V~⍫\'\'`'
|
|
97
|
+
, lbs = ['←←\nASSIGN', ' ', '++\nconjugate\nplus', '--\nnegate\nminus', '××\ndirection\ntimes', '÷÷\nreciprocal\ndivide', '**\nexponential\npower', '⍟⍟\nnatural logarithm\nlogarithm',
|
|
98
|
+
'⌹⌹\nmatrix inverse\nmatrix divide', '○○\npi times\ncircular', '!!\nfactorial\nbinomial', '??\nroll\ndeal', ' ', '||\nmagnitude\nresidue',
|
|
99
|
+
'⌈⌈\nceiling\nmaximum', '⌊⌊\nfloor\nminimum', '⊥⊥\ndecode', '⊤⊤\nencode', '⊣⊣\nsame\nleft', '⊢⊢\nsame\nright', ' ', '==\nequal', '≠≠\nunique mask\nnot equal',
|
|
100
|
+
'≤≤\nless than or equal to', '<<\nless than', '>>\ngreater than', '≥≥\ngreater than or equal to', '≡≡\ndepth\nmatch', '≢≢\ntally\nnot match', ' ', '∨∨\ngreatest common divisor/or',
|
|
101
|
+
'∧∧\nlowest common multiple/and', '⍲⍲\nnand', '⍱⍱\nnor', ' ', '↑↑\nmix\ntake', '↓↓\nsplit\ndrop', '⊂⊂\nenclose\npartioned enclose', '⊃⊃\nfirst\npick', '⊆⊆\nnest\npartition', '⌷⌷\nmaterialise\nindex', '⍋⍋\ngrade up\ngrades up',
|
|
102
|
+
'⍒⍒\ngrade down\ngrades down', ' ', '⍳⍳\nindices\nindices of', '⍸⍸\nwhere\ninterval index', '∊∊\nenlist\nmember of', '⍷⍷\nfind', '∪∪\nunique\nunion', '∩∩\nintersection', '~~\nnot\nwithout', ' ',
|
|
103
|
+
'//\nreplicate\nReduce', '\\\\\n\expand\nScan', '⌿⌿\nreplicate first\nReduce First', '⍀⍀\nexpand first\nScan First', ' ', ',,\nravel\ncatenate/laminate',
|
|
104
|
+
'⍪⍪\ntable\ncatenate first/laminate', '⍴⍴\nshape\nreshape', '⌽⌽\nreverse\nrotate', '⊖⊖\nreverse first\nrotate first',
|
|
105
|
+
'⍉⍉\ntranspose\nreorder axes', ' ', '¨¨\nEach', '⍨⍨\nConstant\nSelf\nSwap', '⍣⍣\nRepeat\nUntil', '..\nOuter Product (∘.)\nInner Product',
|
|
106
|
+
'∘∘\nOUTER PRODUCT (∘.)\nBind\nBeside', '⍤⍤\nRank\nAtop', '⍥⍥\nOver', '@@\nAt', ' ', '⍞⍞\nSTDIN\nSTDERR', '⎕⎕\nEVALUATED STDIN\nSTDOUT\nSYSTEM NAME PREFIX', '⍠⍠\nVariant',
|
|
107
|
+
'⌸⌸\nIndex Key\nKey', '⌺⌺\nStencil', '⌶⌶\nI-Beam', '⍎⍎\nexecute', '⍕⍕\nformat', ' ', '⋄⋄\nSTATEMENT SEPARATOR', '⍝⍝\nCOMMENT', '→→\nABORT\nBRANCH', '⍵⍵\nRIGHT ARGUMENT\nRIGHT OPERAND (⍵⍵)', '⍺⍺\nLEFT ARGUMENT\nLEFT OPERAND (⍺⍺)',
|
|
108
|
+
'∇∇\nrecursion\nRecursion (∇∇)', '&&\nSpawn', ' ', '¯¯\nNEGATIVE', '⍬⍬\nEMPTY NUMERIC VECTOR', '∆∆\nIDENTIFIER CHARACTER', '⍙⍙\nIDENTIFIER CHARACTER']
|
|
109
|
+
, bqk = ' =1234567890-qwertyuiop\\asdfghjk∙l;\'zxcvbnm,./q[]+!@#$%^&*()_QWERTYUIOP|ASDFGHJKL:"ZXCVBNM<>?~{}'.replace(/∙/g, '')
|
|
110
|
+
, bqv = '`÷¨¯<≤=≥>≠∨∧×⋄⍵∊⍴~↑↓⍳○*⊢∙⍺⌈⌊_∇∆∘\'⎕⍎⍕∙⊂⊃∩∪⊥⊤|⍝⍀⌿⋄←→⌹⌶⍫⍒⍋⌽⍉⊖⍟⍱⍲!⍰W⍷R⍨YU⍸⍥⍣⊣ASDF⍢H⍤⌸⌷≡≢⊆⊇CVB¤∥⍪⍙⍠⌺⍞⍬'.replace(/∙/g, '')
|
|
111
|
+
, tc = {}, bqc = {} //tab completions and ` completions
|
|
112
|
+
for (let i = 0; i < bqk.length; i++)bqc[bqk[i]] = bqv[i]
|
|
113
|
+
for (let i = 0; i < tcs.length; i += 3)tc[tcs[i] + tcs[i + 1]] = tcs[i + 2]
|
|
114
|
+
for (let i = 0; i < tcs.length; i += 3) { let k = tcs[i + 1] + tcs[i]; tc[k] = tc[k] || tcs[i + 2] }
|
|
115
|
+
let lbh = ''; for (let i = 0; i < lbs.length; i++) {
|
|
116
|
+
let ks = []
|
|
117
|
+
for (let j = 0; j < tcs.length; j += 3)if (lbs[i][0] === tcs[j + 2]) ks.push('\n' + tcs[j] + ' ' + tcs[j + 1] + ' <tab>')
|
|
118
|
+
for (let j = 0; j < bqk.length; j++)if (lbs[i][0] === bqv[j]) ks.push('\n` ' + bqk[j])
|
|
119
|
+
lbh += '<b title="' + he(lbs[i].slice(1) + (ks.length ? '\n' + ks.join('') : '')) + '">' + lbs[i][0] + '</b>'
|
|
120
|
+
}
|
|
121
|
+
let ovl; try { ovl = localStorage.getItem('ngn_lb_overlay') === '1' } catch (e) { ovl = !1 } //overlay mode: bar covers the top instead of pushing the page down
|
|
122
|
+
let d = document, el = d.createElement('div'); el.innerHTML =
|
|
123
|
+
`<div class=ngn_lb><span class=ngn_x title=Close>❎</span><span class=ngn_o title="Toggle overlay/push-down">${ovl ? '▼' : '▲'}</span>${lbh}</div>
|
|
124
|
+
<style>
|
|
125
|
+
.ngn_lb{position:fixed;top:0;left:0;right:0;background-color:#eee;color:#000;cursor:default;z-index:2147483647;
|
|
126
|
+
font-family:"DejaVu Sans Mono",monospace;border-bottom:solid #999 1px;padding:2px 2px 0 2px;word-wrap:break-word;}
|
|
127
|
+
.ngn_lb b{cursor:pointer;padding:0 1px;font-weight:normal}
|
|
128
|
+
.ngn_lb b:hover,.ngn_bq .ngn_lb{background-color:#777;color:#fff}
|
|
129
|
+
.ngn_x,.ngn_o{float:right;color:#999;cursor:pointer;margin-top:-3px}
|
|
130
|
+
.ngn_o{margin-right:6px}
|
|
131
|
+
.ngn_o:hover{color:#00d}
|
|
132
|
+
.ngn_x:hover{color:#f00}
|
|
133
|
+
@media (prefers-color-scheme:dark){
|
|
134
|
+
.ngn_lb{background-color:#222;color:#ddd;border-bottom-color:#555}
|
|
135
|
+
.ngn_lb b:hover,.ngn_bq .ngn_lb{background-color:#bbb;color:#000}
|
|
136
|
+
.ngn_x,.ngn_o{color:#666}
|
|
137
|
+
}
|
|
138
|
+
</style>`
|
|
139
|
+
d.body.appendChild(el)
|
|
140
|
+
let t, lb = el.firstChild, bqm = 0 //t:textarea or input, lb:language bar, bqm:backquote mode
|
|
141
|
+
let pd = x => x.preventDefault()
|
|
142
|
+
let ev = (x, t, f, c) => x.addEventListener(t, f, c)
|
|
143
|
+
let med = _ => { try { return window.monaco?.editor?.getEditors?.().find(e => e.hasTextFocus()) } catch (e) { } } //focused Monaco editor, if any
|
|
144
|
+
let ins = (t, s, del = 0) => { //insert s at caret (replacing selection, or del chars before it), keeping undo & input events
|
|
145
|
+
let m = med()
|
|
146
|
+
if (m) {
|
|
147
|
+
if (del) {
|
|
148
|
+
let p = m.getPosition()
|
|
149
|
+
m.executeEdits('lb', [{ range: { startLineNumber: p.lineNumber, startColumn: p.column - del, endLineNumber: p.lineNumber, endColumn: p.column }, text: s }])
|
|
150
|
+
} else m.trigger('keyboard', 'type', { text: s })
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
if (!t || t.selectionStart == null) return
|
|
154
|
+
if (del) t.selectionStart = t.selectionStart - del
|
|
155
|
+
if (!(d.execCommand && d.execCommand('insertText', !1, s))) {
|
|
156
|
+
let i = t.selectionStart
|
|
157
|
+
t.value = t.value.slice(0, i) + s + t.value.slice(t.selectionEnd)
|
|
158
|
+
t.selectionStart = t.selectionEnd = i + s.length
|
|
159
|
+
t.dispatchEvent(new Event('input', { bubbles: !0 }))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
ev(lb, 'mousedown', x => {
|
|
163
|
+
if (x.target.classList.contains('ngn_x')) { lb.hidden = 1; upd() }
|
|
164
|
+
else if (x.target.classList.contains('ngn_o')) {
|
|
165
|
+
ovl = !ovl
|
|
166
|
+
x.target.textContent = ovl ? '▼' : '▲'
|
|
167
|
+
try { localStorage.setItem('ngn_lb_overlay', ovl ? '1' : '0') } catch (e) { }
|
|
168
|
+
upd()
|
|
169
|
+
} else if (x.target.nodeName === 'B') {
|
|
170
|
+
let s = x.target.textContent, m = med()
|
|
171
|
+
if (m) { m.focus(); ins(t, s) }
|
|
172
|
+
else if (t && t.selectionStart != null) { t.focus(); ins(t, s) }
|
|
173
|
+
}
|
|
174
|
+
pd(x) //always: clicking the bar must never steal focus
|
|
175
|
+
})
|
|
176
|
+
let fk = x => {
|
|
177
|
+
let t = x.target, m = med(), i, v
|
|
178
|
+
if (m) { let p = m.getPosition(); i = p.column - 1; v = m.getModel().getLineContent(p.lineNumber) }
|
|
179
|
+
else { i = t.selectionStart; v = t.value }
|
|
180
|
+
if (bqm) {
|
|
181
|
+
let c = bqc[x.key]
|
|
182
|
+
if (x.key === '`') {
|
|
183
|
+
ins(t, '```')
|
|
184
|
+
if (m) { let p = m.getPosition(); m.setPosition({ lineNumber: p.lineNumber, column: p.column - 2 }) }
|
|
185
|
+
else t.selectionStart = t.selectionEnd = i + 1
|
|
186
|
+
bqm = 0
|
|
187
|
+
d.body.classList.remove('ngn_bq')
|
|
188
|
+
pd(x)
|
|
189
|
+
return !1
|
|
190
|
+
}
|
|
191
|
+
if (x.which > 31) { bqm = 0; d.body.classList.remove('ngn_bq') }
|
|
192
|
+
if (c) { ins(t, c); pd(x); return !1 }
|
|
193
|
+
}
|
|
194
|
+
if (!x.ctrlKey && !x.shiftKey && !x.altKey && !x.metaKey) {
|
|
195
|
+
if ("`½²^º§ùµ°".indexOf(x.key) > -1) {
|
|
196
|
+
bqm = 1; d.body.classList.add('ngn_bq'); pd(x); // ` or other trigger symbol pressed, wait for next key
|
|
197
|
+
} else if (x.key == "Tab") {
|
|
198
|
+
let c = i >= 2 && tc[v.slice(i - 2, i)]
|
|
199
|
+
if (c) { ins(t, c, 2); pd(x) }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
let ff = x => {
|
|
204
|
+
let t0 = x.target, nn = t0.nodeName.toLowerCase()
|
|
205
|
+
if (nn !== 'textarea' && (nn !== 'input' || t0.type !== 'text' && t0.type !== 'search')) return
|
|
206
|
+
t = t0; if (!t.ngn) { t.ngn = 1; ev(t, 'keydown', fk) }
|
|
207
|
+
}
|
|
208
|
+
let upd = _ => { d.body.style.paddingTop = ovl ? '' : lb.clientHeight + 'px' }
|
|
209
|
+
upd(); (window.ResizeObserver ? new ResizeObserver(upd).observe(lb) : ev(window, 'resize', upd))
|
|
210
|
+
ev(d, 'focus', ff, !0); let ae = d.activeElement; ae && ff({ type: 'focus', target: ae })
|
|
211
|
+
ev(d, 'keydown', x => { if (!x.target.ngn && x.target.closest?.('.monaco-editor')) fk(x) }, !0) //EditContext-mode Monaco has no textarea for ff to register
|
|
212
|
+
})();
|
|
213
|
+
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
<style>
|
|
217
|
+
@font-face { font-family:'SAX2'; src: local('SAX2'), url('https://cdn.jsdelivr.net/gh/abrudz/SAX2@master/SAX2.ttf') format('truetype') }
|
|
218
|
+
.sax2 { font-family:'SAX2',monospace !important; line-height:1.05 !important }
|
|
219
|
+
</style>
|
|
220
|
+
|
|
221
|
+
<pre class="iversonnb_out sax2">10 20 30
|
|
222
|
+
40 50 60
|
|
223
|
+
70 80 90</pre>
|
|
224
|
+
|
|
225
|
+
Assignments are shy, just like in the Dyalog session, so the `m←` line
|
|
226
|
+
above printed nothing. The line magic (`%apl`) instead evaluates one
|
|
227
|
+
expression and returns it as a Python value:
|
|
228
|
+
|
|
229
|
+
``` python
|
|
230
|
+
%apl 3×⍳4
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
[3, 6, 9, 12]
|
|
234
|
+
|
|
235
|
+
``` python
|
|
236
|
+
%apl ⎕A
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
240
|
+
|
|
241
|
+
Because the line magic returns a value, you can store it in a Python
|
|
242
|
+
variable. Scalars come back as numbers or strings, vectors as lists, and
|
|
243
|
+
higher-rank arrays as nested lists:
|
|
244
|
+
|
|
245
|
+
``` python
|
|
246
|
+
z = %apl m
|
|
247
|
+
z
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
251
|
+
|
|
252
|
+
To suppress a cell’s output, end the last line with a `;`:
|
|
253
|
+
|
|
254
|
+
``` python
|
|
255
|
+
%%apl
|
|
256
|
+
m×10;
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`⎕←` displays a value explicitly, which is how you show something that
|
|
260
|
+
would otherwise be shy:
|
|
261
|
+
|
|
262
|
+
``` python
|
|
263
|
+
%%apl
|
|
264
|
+
v←2×⍳5
|
|
265
|
+
⎕←v
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
<pre class="iversonnb_out sax2">2 4 6 8 10</pre>
|
|
269
|
+
|
|
270
|
+
To use numpy, just pass the result of `%apl` into `np.array`:
|
|
271
|
+
|
|
272
|
+
``` python
|
|
273
|
+
import numpy as np
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
``` python
|
|
277
|
+
a = %apl m
|
|
278
|
+
np.array(a)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
array([[1, 2, 3],
|
|
282
|
+
[4, 5, 6],
|
|
283
|
+
[7, 8, 9]])
|
|
284
|
+
|
|
285
|
+
### Example algorithms
|
|
286
|
+
|
|
287
|
+
The fibonacci sequence:
|
|
288
|
+
|
|
289
|
+
``` python
|
|
290
|
+
%apl {⍵,+/¯2↑⍵}⍣15⊢1 1
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597]
|
|
294
|
+
|
|
295
|
+
Explanation:
|
|
296
|
+
|
|
297
|
+
1. `1 1`: Initial seed (first two Fibonacci numbers)
|
|
298
|
+
2. `{⍵,+/¯2↑⍵}`: Function that appends the sum of the last two elements
|
|
299
|
+
3. `⍣15`: Apply the function 15 times
|
|
300
|
+
4. `⊢`: Identity function, passes the initial argument (1 1) to the
|
|
301
|
+
iteration
|
|
302
|
+
|
|
303
|
+
Prime number sieve:
|
|
304
|
+
|
|
305
|
+
``` python
|
|
306
|
+
%%apl
|
|
307
|
+
primes ← {⍵×2=+⌿0=⍵∘.|⍵}⍳
|
|
308
|
+
(primes 50)~0
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
<pre class="iversonnb_out sax2">2 3 5 7 11 13 17 19 23 29 31 37 41 43 47</pre>
|
|
312
|
+
|
|
313
|
+
Explanation:
|
|
314
|
+
|
|
315
|
+
1. `⍳50` generates integers 1 to 50
|
|
316
|
+
2. `⍵∘.|⍵` creates a 50x50 matrix of divisibility (1 if divisible, 0 if
|
|
317
|
+
not)
|
|
318
|
+
3. `0=` inverts the matrix (1 for non-divisible)
|
|
319
|
+
4. `+⌿` sums columns, counting non-divisors for each number
|
|
320
|
+
5. `2=` checks if count equals 2 (prime property)
|
|
321
|
+
6. `⍵×` multiplies result with original numbers, keeping primes
|
|
322
|
+
7. `~0` removes zero from the result
|
|
323
|
+
|
|
324
|
+
## The `j` magics
|
|
325
|
+
|
|
326
|
+
J works the same way: `%%j` runs code in a persistent J session and
|
|
327
|
+
displays its output verbatim, and `%j expr` returns an expression’s
|
|
328
|
+
value as a Python object. The session keeps nouns and verbs across
|
|
329
|
+
cells:
|
|
330
|
+
|
|
331
|
+
``` python
|
|
332
|
+
%%j
|
|
333
|
+
m =: 3 3 $ i. 9
|
|
334
|
+
m +/ . * m
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
15 18 21
|
|
338
|
+
42 54 66
|
|
339
|
+
69 90 111
|
|
340
|
+
|
|
341
|
+
``` python
|
|
342
|
+
z = %j m
|
|
343
|
+
z
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
|
|
347
|
+
|
|
348
|
+
Under the hood there’s no separate process or protocol: iversonnb loads
|
|
349
|
+
**libj** via ctypes, so completion, errors, and interrupts come straight
|
|
350
|
+
from the engine’s C API. The
|
|
351
|
+
[`J`](https://answerdotai.github.io/iversonnb/j.html#j) session class
|
|
352
|
+
mirrors [`Apl`](https://answerdotai.github.io/iversonnb/core.html#apl) –
|
|
353
|
+
call it to run code, move values in both directions with `[]`, lift
|
|
354
|
+
verbs into Python callables with `fn` – see
|
|
355
|
+
[`j`](https://answerdotai.github.io/iversonnb/j.html) for the full
|
|
356
|
+
walkthrough.
|
|
357
|
+
|
|
358
|
+
## Using iversonnb from Python
|
|
359
|
+
|
|
360
|
+
The magics are a thin layer over the
|
|
361
|
+
[`Apl`](https://answerdotai.github.io/iversonnb/core.html#apl) class,
|
|
362
|
+
which you can use directly in scripts, tests, and other tooling. Calling
|
|
363
|
+
the session runs code and returns the output exactly as Dyalog formats
|
|
364
|
+
it (or None if there’s no output); APL errors raise
|
|
365
|
+
[`AplError`](https://answerdotai.github.io/iversonnb/core.html#aplerror):
|
|
366
|
+
|
|
367
|
+
``` python
|
|
368
|
+
from iversonnb import Apl
|
|
369
|
+
|
|
370
|
+
apl = Apl()
|
|
371
|
+
apl('3 3⍴⍳9')
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
<pre class="iversonnb_out sax2">1 2 3
|
|
375
|
+
4 5 6
|
|
376
|
+
7 8 9</pre>
|
|
377
|
+
|
|
378
|
+
Square brackets move values between Python and the workspace, in both
|
|
379
|
+
directions, and take any expression:
|
|
380
|
+
|
|
381
|
+
``` python
|
|
382
|
+
apl['v'] = [3,1,4,1,5]
|
|
383
|
+
apl['{⍵[⍋⍵]}v']
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
[1, 1, 3, 4, 5]
|
|
387
|
+
|
|
388
|
+
`fn` lifts an APL function into a Python callable (one argument applies
|
|
389
|
+
it monadically, two dyadically):
|
|
390
|
+
|
|
391
|
+
``` python
|
|
392
|
+
mean = apl.fn('{(+/⍵)÷≢⍵}')
|
|
393
|
+
mean([1,2,3,4])
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
2.5
|
|
397
|
+
|
|
398
|
+
Sessions shut themselves down at process exit; use `close`, or a
|
|
399
|
+
`with Apl() as apl:` block, to do it sooner:
|
|
400
|
+
|
|
401
|
+
``` python
|
|
402
|
+
apl.close()
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## The LLM kernels
|
|
406
|
+
|
|
407
|
+
iversonnb also ships persistent APL and J sessions for LLM agents, built
|
|
408
|
+
on [clikernel](https://github.com/AnswerDotAI/clikernel)’s stream
|
|
409
|
+
protocol. `aplkernel` and `jkernel` run the stdin/stdout workers
|
|
410
|
+
directly, and `aplkernel-mcp`/`jkernel-mcp` wrap them as MCP servers
|
|
411
|
+
with `execute`, `restart`, and `interrupt` tools. Workspace state
|
|
412
|
+
persists across calls, and `interrupt` stops a long computation while
|
|
413
|
+
keeping it; send `)OFF` (APL) or `exit 0` (J) to stop a worker. If a
|
|
414
|
+
session has to be replaced mid-conversation (a crash, or the APL
|
|
415
|
+
incomplete-input bug below), the response starts with a NOTE saying
|
|
416
|
+
workspace state was lost.
|
|
417
|
+
|
|
418
|
+
On startup each kernel runs `~/.config/iversonnb/startup.apl` or
|
|
419
|
+
`startup.ijs` (if it exists) in the session, and reports its source and
|
|
420
|
+
output in the banner, which the MCP form forwards as the server’s
|
|
421
|
+
instructions.
|
|
422
|
+
|
|
423
|
+
## Limitations
|
|
424
|
+
|
|
425
|
+
- Keyboard input through `⎕` or `⍞` can’t work in a notebook, so it
|
|
426
|
+
raises an error. The session survives.
|
|
427
|
+
- A cell that ends inside an unfinished block, such as an unclosed
|
|
428
|
+
`:If`, wedges the Dyalog interpreter with no way back
|
|
429
|
+
([Dyalog/ride#1401](https://github.com/Dyalog/ride/issues/1401)).
|
|
430
|
+
iversonnb detects this and starts a fresh session, raising an error
|
|
431
|
+
that says so (with `reset` set on it), but workspace state is lost
|
|
432
|
+
when it happens.
|
|
433
|
+
- `%apl` transfers values with `⎕JSON`, so it’s limited to arrays and
|
|
434
|
+
scalars that JSON can represent, serializing to at most 32767
|
|
435
|
+
characters. For bigger data, write a file from APL instead.
|
|
436
|
+
- `%j` transfers values through the engine’s typed-array API, so it’s
|
|
437
|
+
limited to booleans, integers, floats, and characters; boxed,
|
|
438
|
+
extended, and rational values raise (display them with `%%j` instead).
|
|
439
|
+
- Windows isn’t supported yet: iversonnb configures Dyalog through
|
|
440
|
+
environment variables, which is the Unix convention, and the J binding
|
|
441
|
+
is untested there. macOS and Linux are supported.
|
|
442
|
+
|
|
443
|
+
## Learning APL and J
|
|
444
|
+
|
|
445
|
+
To start learning APL, follow the [17 video
|
|
446
|
+
series](https://forums.fast.ai/t/apl-array-programming/97188) run by
|
|
447
|
+
Jeremy Howard, and have a look at the [study
|
|
448
|
+
notes](https://fastai.github.io/apl-study/apl.html). The `]` user
|
|
449
|
+
commands mentioned there, such as `]Help ≠`, work in iversonnb too.
|
|
450
|
+
|
|
451
|
+
For J, start with [Learning
|
|
452
|
+
J](https://www.jsoftware.com/help/learning/contents.htm) and the [J
|
|
453
|
+
wiki](https://code.jsoftware.com/wiki/), whose
|
|
454
|
+
[NuVoc](https://code.jsoftware.com/wiki/NuVoc) page is the reference for
|
|
455
|
+
every primitive.
|