TSEPro Editor Macro
SAL, the SemWare Applications Language: the Pascal-flavoured macro language that Sammy Mitchell built into The SemWare Editor Professional in 1991, in which not only user macros but most of the editor's own menus, help screens, key bindings and file manager are written - and which is still being shipped and patched in 2026
Created by Sammy Mitchell, founder of The SemWare Corporation of Marietta, Georgia, who wrote QEdit in 1985 and designed and implemented SAL for its successor. The editor's licence is held jointly by Sammy and Bobbi Mitchell. Changes credited by name in the current read.me and in tse.ui include contributions and bug reports from Carlo Hogeveen, Knud van Eeden, H. Pikaar, Zhong Zhao, Eliyahu Trigoub and Michael Graham, but the language and its compiler are Mitchell's work
SAL - the SemWare Applications Language - is what you get when a text editor’s macro facility is taken seriously enough to become the editor. It is a small, statically typed, Pascal-shaped procedural language, compiled to an object form the editor loads and executes, with exactly two data types and several hundred built-in commands, every one of them about moving a cursor, marking a block or opening a window. It first appeared in March 1991 in the initial beta of The SemWare Editor Professional, reached customers in TSE Pro 1.0 in March 1993, and is still being shipped, compiled and patched in 2026 - which makes the status “dormant”, under which this language is usually catalogued, one of the more inaccurate labels in the encyclopaedia.
The name it is catalogued under is itself an artefact. “TSEPro Editor Macro” is not what SemWare calls the language; it is the label on entry 466 of 99-bottles-of-beer.net, submitted by Sammy Mitchell - the editor’s own author - and stamped in his source header “Sammy Mitchell, Dec 9, 1998”. SemWare calls it SAL.
From QEdit to a language
SemWare’s first product was QEdit, released as MS-DOS shareware in November 1985 and written in Turbo Pascal. Its reputation was built on being small and quick: reviewing it for The Globe and Mail in January 1993, Bob Rife observed that it was “compact, taking only 50 KB of space, compared with many other editors that can be 10 times the size”. QEdit had keyboard macros - record a sequence of keystrokes, play it back - but nothing you could call programming.
Two changes made SAL possible. In February 1990, QEdit 2.1 was rewritten from Turbo Pascal into C, giving the product a native core that could host an interpreter. Then, in March 1991, SemWare shipped the first beta of a new, larger editor, The SemWare Editor Professional, which added virtual memory, multifile editing, block support - and, per the version timeline maintained on Wikipedia, the “first version of SAL”. In 1992 the line split formally: QEdit was renamed TSE Jr, and TSE Pro went its own way. TSE Pro 1.0 shipped in March 1993.
The migration path was taken seriously. SemWare still ships doc/jr2pro.txt, a
command-by-command translation table from the Junior editor’s flat command names
into SAL calls, and it reads like a small manifesto for what had changed:
TSE Jr Command TSE Pro Command
-------------------- ---------------
addline AddLine()
align ExecMacro("Align")
altwordset Set(WordSet, ChrSet("0-9A-Za-z_!#$%&`'()-./\@{}~:^"))
dirtree ExecMacro("Tree")
A command had become a function call; a mode had become a call with an argument; a feature had become a macro you loaded.
What the language looks like
SAL uses proc ... end, if ... endif, for ... endfor, case ... endcase -
Pascal’s shape, with C’s comment syntax (// and /* */) and C’s preprocessor
(#include, #ifdef, #define) grafted on. Mitchell’s own beer-song macro from
December 1998 is a fair sample of the whole language:
// return "n bottles", "1 bottle", or "no more..." based on num_bottles
string proc bottles(integer num_bottles)
case num_bottles
when 0 return ("no more bottles of beer")
when 1 return ("1 bottle of beer")
endcase
return (Str(num_bottles) + " bottles of beer")
end
// display the beer song in a pop-up window
proc main()
if PopWinOpen(1, 1, 40, Query(ScreenRows),
1, "Sing along...", Color(Bright Yellow on Blue))
Set(Attr, Color(Bright Yellow on Blue))
ClrScr()
Sing()
GetKey()
PopWinClose()
endif
end
Note what the language spends its vocabulary on. PopWinOpen, Color(Bright Yellow on Blue), Query(ScreenRows), Set(Attr, ...), GetKey - a colour
expression is part of the grammar, and the standard library is a text editor.
(Mitchell’s original also separates some WriteLine arguments with ; rather
than ,; the current manual documents only the comma form, so the semicolon
appears to be a legacy spelling that survived in his own code.)
Two types, and no more
SAL’s type system is as small as a type system gets while still being one:
| Type | Range or limit |
|---|---|
integer | 32-bit signed, MININT (-2,147,483,648) to MAXINT (+2,147,483,647); decimal, hex (0ffh or 0xff) or binary (11111111b) literals |
string | fixed capacity chosen at declaration, up to MAXSTRINGLEN, which the manual gives as 255 |
Strings are declared with their capacity in brackets, and the compiler will compute it for you if you initialise the variable:
string Astring[10], // reserve 10 characters
Hello[] = "Hello, world", // creates 12-character string
fn[_MAXPATH_] // as long as this build allows for a path
+ concatenates; s[n] indexes a single character; the editor’s own line length
limit lives in the constant MAXLINELEN, which the manual gives as 30,000 - so a
line of text can be longer than any string that could hold it, and macros that
process long lines have to work in pieces. Variables are global (file-scoped, and
alive as long as their macro file stays loaded) or local to a procedure;
procedures take parameters by value or by reference (VAR STRING s), return
values, and may recurse.
The constructs that are not procedures
Where SAL departs most sharply from a general-purpose language is in the four declarative forms the compiler understands directly, because they exist to build an editor’s furniture:
keydef- a named block of key bindings that can be enabled and disabled at runtime, so a macro can install a temporary keymap while a pick-list is up.menuandmenubar- pull-down menus as a language construct, with forward declaration (forward menu MouseMenu()) for mutual references.helpdef- help screens compiled into the macro that needs them.datadef- arbitrary data embedded in the compiled macro.
The Hook() mechanism completes the picture: macros attach themselves to editor
events - _ON_EDITOR_STARTUP_, _AFTER_FILE_SAVE_, _PRE_UPDATE_ALL_WINDOWS_
and others added in 3.0 - and Unhook()/BreakHookChain() let them detach or
stop the chain.
Compiling and debugging
Macros are written in .s files (or .ui files for user interfaces) and
compiled by sc32, the SAL compiler, into .mac object files, either from a
shell or from inside the editor with Ctrl-F9. External macros are loaded with
LoadMacro, run with ExecMacro, and can be purged again; each is classified as
Main, Public, WhenLoaded or WhenPurged, which determines when its
procedures fire. A user-interface macro, by contrast, is installed into the
editor and cannot be purged - it is the editor’s interface.
There is a real interactive debugger, invoked with the Debug command: it
compiles the source if needed, then hands you control of the running macro so you
can single-step it, inspect buffers and variables, and modify variables mid-run.
Reaching outside
For anything the editor cannot do, SAL has a foreign function interface. The
DLL directive declares procedures living in a Windows DLL:
DLL "<user32.dll>"
integer proc MessageBox(integer hwnd, string txt, string caption,
integer utype) : "MessageBoxA"
end
Angle brackets around the file name send the loader to the system search path;
the : "name" form supplies the real export name when it differs from the SAL
identifier, which it usually does thanks to Windows’ A/W suffixes. The manual
is explicit that DLL procedures returning strings are not supported - integers
only. This is also the part of the system that has aged least well: the roadmap
in the shipped read.me lists “Need new FFI” against every build target Mitchell
is working towards.
Evolution
SAL’s growth follows the editor’s, and it is a story of accretion rather than
redesign. TSE Pro 3.0 (April 2001) is the high-water mark for the language: its
notes advertise “over 500 native commands that can be called by user-written
macros”, introduce the EDITOR_VERSION and MAXSTRINGLEN constants, and add
four hookable events. 4.0 (May 2002) turned TSE into a real GUI application and
gave macros font control plus isGUI() to tell the builds apart. 4.2 (February
2004) added file-system and clipboard commands. 4.4 (May 2005) is where the
documentation effectively stops - the generated manual carries the warning that
TSE’s help “has not been significantly updated since TSE 4.4 (2005)”, pointing
readers at read.me and an “Undocumented Features” page for anything later.
The code, however, did not stop. In 2021 the licence changed: TSE became
freeware under a two-clause BSD licence, over the copyright line “(C) Copyright
1991-2026 by Sammy and Bobbi Mitchell” that the generated manual still carries
(the shipped read.me now reads “(C) Copyright 1991-2025 SemWare”). Releases have continued at a
steady, unhurried clip since - 4.42 in January 2022, 4.49 in January 2023,
4.50.23 on 30 April 2026, 4.50.26 on 1 July 2026 - each for Windows and Linux,
each crediting the same handful of long-time users by name, and each still adding
the occasional SAL command - GetSynQuote() and GetSynMultiLnDlmt() arrived in
4.49.00 in January 2023, FFisExec() in 4.50.22 in March 2026. The file ui/tse.ui in the current tarball carries a
change entry dated 28 March 2026. Whatever else it is, this is not a dormant
language.
Running SAL today
There is no Docker image, no package in the mainstream Linux distributions'
repositories, and no public source repository for the editor itself; SemWare distributes a Windows
installer and a Linux tarball from semware.com, with Carlo Hogeveen mirroring
both. The Linux build is a self-contained directory: unpack it, add it to your
PATH, and run e. Both the editor and sc32 are statically linked 32-bit x86
ELF binaries, which on modern 64-bit Linux means you need 32-bit support
installed; 64-bit builds are on Mitchell’s roadmap but, as of the current
read.me, unticked, along with UTF-8 support and code folding. A DOS build
(2.50e, 1997) is also still on the download page.
Why it matters
SAL belongs to a family of editor languages - Emacs Lisp, Vim script, BRIEF’s
macro language, EDT and TPU on VMS - and among them it makes an unusually clean
argument. Its designer took a fast C editor and pushed the boundary between
“the program” and “the configuration” as far towards the configuration as he
could: the menus, the help, the key bindings, the file manager, the calculator,
the grep, the emulations of four rival editors, all of it lives in .s and .ui
files that ship as source next to their compiled form, and all of it is written
in the same language the user gets. That is roughly 88,000 lines of SAL in the
current release, in the same files a user may edit and recompile.
The cost of that design is visible too. Two types and 255-character strings are a 1991 budget, not a 2026 one, and thirty-five years of accretion has left a language whose grammar is Pascal, whose comments are C, and whose foreign function interface is a Win32 artefact awaiting replacement. But the trade Mitchell made - a small language, deeply wired into one domain, shipped as the implementation of the product rather than as a bolted-on courtesy - is the same trade Emacs made, and it has kept a shareware editor from 1985 alive, in active maintenance, under a BSD licence, into its fifth decade.
Timeline
Notable Uses & Legacy
The SemWare Editor itself
The strongest claim for SAL is that the editor is largely written in it. The manual states flatly that "much of the editor itself has been implemented internally using the macro language" and that "many of the built-in native commands in the editor are actually macros written in SAL". In the 4.50.23 Linux tarball, ui/tse.ui - the standard user interface, comprising the menus, help screen, key assignments and a good many commands - is 2,567 lines of SAL
The bundled macro library and the Potpourri
The same tarball carries 117 SAL source files under mac/ (109 .s and 8 .si), 75,297 lines in all, most with a compiled .mac beside them: the file manager (f.s, 3,250 lines), grep, a calendar, an expression evaluator, file comparison, sorting, ASCII charts, an autosave, and a shelf of games from Tetris to Star Trek. Many are reachable from the Potpourri PickList, TSE's menu of optional macros
Editor emulations
TSE's ability to impersonate other editors is implemented as interchangeable SAL user-interface files rather than as C code. The shipped ui/ directory contains brief.ui, ws.ui (WordStar), tsejr.ui (the QEdit-descended Junior interface) and win.ui (CUA/Windows) alongside the native tse.ui - 13,322 lines of SAL in that directory between them
Carlo Hogeveen's TSE tools (ecarlo.nl/tse)
A long-running third-party collection of SAL extensions, including Context (context-sensitive helper), a documentation generator and the Hlp2txt macro that converts TSE's interactive help into the HTML manual most people now read - the version of that manual cited on this page was produced by Hlp2txt 3.0.3 on 18 July 2026. Hogeveen also mirrors SemWare's releases and is credited by name in the vendor's own change lists
Knud van Eeden's TSE knowledge base
A large public archive of SAL macros and TSE notes, linked from semware.com's own front page, which lists it among its "cool places"; van Eeden is likewise credited in the shipped read.me for bug reports against recent releases
Rosetta Code
SAL has a small but real presence outside the editor's own ecosystem: approximately seventeen tasks were solved under Category:TSE SAL when it was checked in September 2026, from Ackermann function and Levenshtein distance to HTTP and 100 doors. SemWare links the category from its home page, reportedly with the note "Please add more!"