Some usability updates to GNU APL Emacs mode
Tags:
Small usability updates to GNU APL Emacs mode
- Introduction
- Finnapl - support for FinnAPL idioms list
- Access to build-in help from Emacs
- Customize keyboard layout for Dvorak users
- Tooltips on a keyboard in GNU APL Emacs mode
- Appendix A: Compiling GNU APL on OSX 10.6
- Appendix B: Previously submitted work
- Conclusion
Introduction
APL programming language is the language I’m really fascinated by. I’ve mainly used 2 different implementations: an excellent implementation (including IDE) from Dyalog and the Free Software GNU APL, a quality APL2 interpreter implemented by Dr. Jürgen Sauermann.
In this blog post I’ll explain some recent changes I’ve submitted to GNU APL and GNU APL Emacs mode.
They are not strongly related to each other, but rather evolve around access to documentation and ease of use.
Some of them are based on the previous documentation update idea proposed by Elias Mårtenson, the author of the excellent GNU APL Emacs mode, to move the documentation from Emacs GNU APL Mode to the interpreter itself.
Finnapl - support for FinnAPL idioms list
While trying the IBM APL2 interpreter there was one feature which I particularly liked: the quick help with a list of APL idioms.
Knowing that the list of FinnAPL idioms is available online, I’ve decided to implement the narrowing search in this list with ability to insert the idiom into the GNU APL buffer without leaving Emacs. Naturally the Emacs Helm narrowing framework is the best choice for this task. I’ve only needed to dowload the list of idioms, parse it and setup the parsed list as a source to Helm. Hitting Enter will paste the idiom into the GNU APL (either interpreter buffer or editor buffer wherever it was called using the hotkey Ctrl-c Ctrl-i):
For those not using the Helm package (seriously why? its awesome!) I’ve implemented the simple tabular view:
The buffer on this screenshot has the custom idioms choice mode activated, based on Emacs standard’s tabulated-list-mode, which is used for example to present the list of packages (via list-packages
interactive command).
I’ve already used the same idea in the C/C++ Find File package to present a list with a multiple selection. The idea is the same: if Helm available and some variable (dont-use-helm or something like this) is not set use Helm to present choices, otherwise use the custom mode derived from the tabulated-list-mode
.
Access to the build-in help from Emacs
The idea to move the documentation on symbols from Emacs to GNU APL was implemented only partly. The lacking part was a way to get this documentation back to the Emacs mode from GNU APL. This job requires 2 parts: the native part (on GNU APL side) and elisp side (on Emacs), since GNU APL and GNU Emacs communicate via sockets. The native part of the job consisted of implementing a “help” remote command which will return all the documentation (or only information on requested symbol), as it can be seen here.
Since the data flow is from C to Lisp, the natural choice of a data format was S-expressions, where the list looks like (a b c)
, so the data protocol is basically a list of lists containing all the data.
The only “complication” was to prepare a string for sending - surround by quotes, escape quotes and slashes and convert newlines, as in prepare_string function:
// prepare string for sending:
// quotify, escape " and '\', convert newlines
inline std::string prepare_string(const string& string)
{
std::string result;
result.reserve(string.length()*1.25);
result += "\"";
for (string::size_type i = 0; i < string.length(); ++i) {
switch (string[i]) {
case '\n':
result += "\\n";
break;
case '"': // escape double quotes
case '\\': // escape backslashes
result += '\\';
// Fall through.
default:
result += string[i];
}
}
result += "\"";
return result;
}
This code is an altered version of some stack overflow answer to the related topic.
On elisp side parsing is rather trivial. As it can be seen in the function gnu-apl–load-help the main work is done by elisp reader via read-from-string
which naturally parses the S-expressions from the string:
(read-from-string "(\"hello\" \"world\" (1 2 3))")
=> (("hello" "world" (1 2 3)) . 25)
The received list from GNU APL consist of entries like this:
(0 "⍬" "Zilde" "Zilde is the empty numeric vector (aka. ⍳0)" "Not a function but rather an alias for the empty
vector:
⍬≡⍳0
1")
which could be interpreted as arity, symbol, symbol name, short description, long description. The mapping for arity is described in the Help.def file in GNU APL sources as
- 0: niladic function
- 1: monadic function
- 2: dyadic function
- -1: monadic operator taking one argument
- -2: monadic operator taking two arguments
- -3: dyadic operator taking one argument
- -4: dyadic operator taking two arguments
- -5: quasi-dyadic operator (outer product)
Customize keyboard layout for Dvorak users
Next point in the journey to get convenient GNU APL environment was the possibility to provide custom keyboard layouts in the GNU APL Emacs mode. Now I was able to define exactly the same keys locations as on my old Macbook with Dvorak layout.
One can just “draw” the layout and set the variable gnu-apl-keymap-template
as in Readme (by the way to show/hide keyboard in Emacs GNU APL mode just use the C-c C-k hotkey).
The variable gnu-apl--symbols
has to be modified to provide mapping between letters in the layout and APL symbols which will be available on this key if prefixed with the “Super” key (i.e. Win key or Cmd on OSX).
For Dvorak layout with Macbook’s keyboard I’ve designed the following corresponding APL layout:
And with the Shift key:
After designing the layout I’ve just typed all the characters in (with some modifications of the layout above):
;; our layout - see above. basically
;; just typeing all the keys consequently,
;; without and with modifier, row by row
(defvar txm-apl-layout
'(("§1234567890[]" .
"◊¨¯⍒⍋⌽⍉⊖⍟∨∧←→")
("±!@#$%^&*(){}" .
" ⌶⍫ ⍱⍲⍞⍬")
("',.pyfgcrl/=" .
"⍕≤≥⊢↑⊥∇⊣⍴⎕⌿≠")
("\"<>PYFGCRL?+" .
"⍎⍪ ⍣ ⌷⍠⌹")
("aoeuidhtns-\\" .
"⍺○∊↓⍳⌊∆÷⊤⌈×⍀")
("AOEUIDHTNS_|" .
" ⍥⍷ ⍸ ⍨ ")
("`;qjkxbmwvz" .
"⋄⍝⌸∘≡≢⊃∩⍵∪⊂")
("~:QJKXBMWVZ" .
" ⍤ "))
"Simple layout description. It is a list of conses, where each cons represents the row on a keyboard. car of the cons are the characters in layout, while cdr of the cons is the APL characters of the same key.")
Each row we can convert to the structure corresponding to the entries in gnu-apl--symbols
, which we store as gnu-apl--symbols-old
. We need to get the short name out of the old table.
(defun txm-apl-row-to-gnu-apl-desc (row)
"Convert row from txm-apl-layout into the
list matching gnu-apl--symbols entry"
(let (result)
(dotimes (i (length (car row)))
(let ((found
(find-if (lambda (x)
(string= (second x) (string (elt (cdr row) i))))
gnu-apl--symbols-old)))
(when found
(push (list
(car found) (cadr found) (string (elt (car row) i)))
result))))
(nreverse result)))
And finally we need to iterate over all rows and replace existing apl symbols table in the GNU APL mode:
(defun txm-create-apl--symbols ()
"Create our layout based on variables txm-apl-layout and
gnu-apl--symbols-old"
(apply #'append
(mapcar #'txm-apl-row-to-gnu-apl-desc txm-apl-layout)))
;; finally replace the old GNU APL layout
(setq gnu-apl--symbols (txm-create-apl--symbols))
Tooltips on a keyboard in GNU APL Emacs mode
One feature I particularly like in Dyalog IDEs (both classic and RIDE) is the toolbar with all APL symbols. One could just move a mouse cursor over any symbol to get a tooltip with the information about this symbol. It makes it extremely convenient while studying and to just quickly recap the symbol meaning.
It could be helpful to have the same feature in GNU APL especially now since we have a documentation available directly from the interpreter. So this pull request does exactly that.
It provides tooltips with the documentation while moving the mouse over the APL symbol: (Too bad I couldn’t take a screenshot since on any keypress the tooltip disappears. Oh well)
Additionally the mouse click behavior was changed. Then the user clicks on a symbol on a popup keyboard the symbol is getting inserted into the last active buffer with GNU APL mode enabled.
For this in the mouse action callback we get the list of current visible windows for the buffer
(get-buffer-window-list session nil 'visible)
and after inserting the character into the current APL buffer we select the corresponding window and advance cursor position:
(when interactive-session-windows
(select-window (car interactive-session-windows))
;; advance point after the inserted string
(goto-char (+ (point) (length string))))
so the keyboard behaves like any other on-screen keyboard.
Appendix A: Compiling GNU APL on OSX 10.6
Recently I’ve revived my old white MacBook (late 2006 model, pre-unibody). Since the last version of OSX available for it is 10.6.8, not a lot of modern software could be installed on it. Hopefully with a free and open source software there is at least a chance to compile it on your own.
One of the tools I’m missing on this laptop is Dyalog APL interpreter. It installs but crashes on a startup.
Still it is possible to install the GNU APL interpreter on this machine. It is not available in Macports but one can easily checkout the bleeding edge version from the SVN repository:
svn co http://svn.savannah.gnu.org/svn/apl/trunk gnu-apl
In order to compile I had to modify 2 files since the function strndup is not available on this version of OSX 10.6:
Index: src/UserPreferences.cc
===================================================================
--- src/UserPreferences.cc (revision 1043)
+++ src/UserPreferences.cc (working copy)
@@ -742,7 +742,8 @@
if (arg_end) // more arguments
{
const int arg_len = arg_end - apl_args;
- const char * arg = strndup(apl_args, arg_len);
+ char* arg = static_cast<char*>(malloc(arg_len+1));
+ strncpy(arg, apl_args, arg_len);
expanded_argv.insert_before(index++, arg);
++script_argc;
apl_args += arg_len;
and
Index: src/libapl.cc
===================================================================
--- src/libapl.cc (revision 1043)
+++ src/libapl.cc (working copy)
@@ -388,8 +388,9 @@
ostringstream out;
Command::do_APL_command(out, command_ucs);
-
- return strndup(out.str().data(), out.str().size());
+ char* str = static_cast<char*>(malloc(out.str().size()+1));
+ strncpy(str, out.str().data(), out.str().size());
+ return str;
}
//-----------------------------------------------------------------------------
const unsigned int *
@@ -579,7 +580,9 @@
value->print(out);
const string st = out.str();
- return strndup(st.data(), st.size());
+ char* str = static_cast<char*>(malloc(st.size()+1));
+ strncpy(str, st.data(), st.size());
+ return str;
}
//-----------------------------------------------------------------------------
ostream &
Appendix B: Previously submitted work
In this section I just list the couple of ofter changes to GNU APL I’ve submitted some time ago out historical interest, so I can dig them up later:
- patch to be able to compile on Sharp Zaurus,
- additional API to be able to change current directory,
- update to be able to use lambdas in user-defined commands
Conclusion
These small features hopefully not the last of the usability contributions to the GNU APL and GNU APL Emacs mode. It is well-known fact that the tooling and environment are one of the major contributors to the success/popularity of the programming language. Hopefully the stable and convenient tooling could increase attractiveness of the APL within the small community of the Emacs users.
The next steps as I see could be the following:
- Investigate ways to setup a project with GNU APL, maybe with some sort of source generator. I dont see advantages of the GNU APL for scripting, however the data processing/data mining are the fields where APL is traditionally strong, but these tasks typically require more code and more interactive approach than simple scripting could provide.
- Work on a documentation for APL functions and operators. There is an excellent documentation from IBM, Dyalog, MicroAPL etc, but better community-owned documentation will ensure liveability of the language
- Make it possible to edit 2 dimentional APL arrays in Emacs.
As one of the side notes - working with Emacs lisp is a pleasure, dont hesitate to dive in and make your work environment even more comfortable with ELisp!