I first read about TECO in the Real Programmers Don't Use Pascal parody article written shortly before my birth. It was written there that real programmers do not use the modern editors EMACS and VI:
No, the Real Programmer wants the editor of the “Requested? So get it! ”- complex, mysterious, powerful, error-free, dangerous. TECO, to be exact.OriginalNo, the Real Programmer wants a complex, cryptic, powerful, unforgiving, dangerous. TECO, to be precise.
It intrigued me. What kind of beast is this, can you touch it? Wikipedia said that TECO is a T ext E ditor & CO rrector, it was created in 1962 at DEC and was used on PDP computers, and later on OpenVMS systems. It turned out that there is a port on C , which is maintained by enthusiasts up to date and assembled under modern operating systems. So I decided to feel like a real programmer at least a little.
Compiling for Linux did not cause any difficulties (you just need to put libncurses-dev). And here we start tecoc
and see the powerful editor interface:
*
Yes, one star. This is an invitation to enter commands. It seems that without instructions can not do. Good news - the instruction is easily searched on the Internet. This is a book of almost 300 pages called "Standard TECO Text Editor and Corrector for the VAX, PDP-11, PDP-10, and PDP-8". Here you can read the latest version of 1990 in PDF, at this time TECO was almost forgotten by everyone.
To get started is to find out how to get out of this miracle. It turns out that you need to enter EX
, and then press Escape twice. In general, double-clicking Escape executes the entered command. The Enter key is simply used as a line feed. Escape is displayed on the screen as a dollar, but by entering a dollar, you will not get the effect of Escape. However, further if I write $
, it is implied that I need to press Escape. So we exit with EX$$
(the files are saved). Well, you learned how to get in and out - half the battle is done.
In general, the format of TECO commands is something like this:
[[<1>,] <2>] [:] <> [<1> [ $ <2> ] $ ]
Quite unusual that the arguments can go both left and right. However, it is better to take it differently: the numbers on the left are not part of the team, but another command that returns a number (or a pair of numbers) as a result. In this case, the subsequent command can use the result of the previous one.
With escapes inconvenient, because if you copy and paste it, you’ll get just a dollar, which is not perceived as a separator. Fortunately, there is an alternative syntax for commands with a text argument: @ <> <> <> <>
, where delimiter is any character. For example, you can insert the text 'Habr' into the current position of the cursor using IHabr$
(I, text, Escape - doesn’t resemble anything?), But you can use @I/Habr/
. To simplify my life, I switched to the second syntax.
Perfectly. It would be nice to learn how to enter some text into the file. To specify the output file, use the EW
command, and the input file is the ER
command. In an amicable way, they should not coincide. It turns out that in those days it was a popular multi-page presentation of text files divided into pages using the <FF>
symbol or form-feed. This is a character with the code 12 (0xC), which was entered via Ctrl + L (L is the twelfth letter of the English alphabet). This not only instructed the printer to spit out the page and start a new one, but also allowed editing a long file with modest amounts of RAM. TECO loads only one page of the input file into memory (up to the <FF>
character) and allows you to edit it, write it to the output file and go to the next one. There are also operations of gluing and splitting pages. If the file is the same, it is clear that nothing good will come of it. Well, we will not play with the pages, nowadays it is absolutely wild. Our files will be one-page. So, create a file from scratch:
*@EW/habr.txt/$$ *@I/Hello, Habrahabr! This is TECO, the most powerful editor in the world. Stop using your fancy IDEs, TECO for the win! Bye. /$$ *EX$$
It worked, the habr.txt
file really appeared with our text. I completed each command with two Escape, but in general this is not necessary. Well, what about editing an existing file? Reluctance to invent a new name every time. To do this, there is a special EB
command, which, when first recorded, renames the input file to *.bak
, thus observing that the input and output files are different. After opening the file, you need to remember to turn over to the first page (the Y
command), and then you can display the entire contents of the file with the HT
command:
*@EB/habr.txt/YHT$$ Hello, Habrahabr! This is TECO, the most powerful editor in the world. Stop using your fancy IDEs, TECO for the win! Bye. *
Strictly speaking, HT
is two teams. The H
command returns a pair of numbers - 0 and the length of the text buffer, that is, the page that is now in memory. And the T
command prints a fragment of a file in a given range of offsets:
*0,5T$$ Hello*7,17T$$ Habrahabr!*
Yes, no one will add the new line to you, asked for five characters - get it. But everything is clear. If the command T
passed to the input only one number, it is interpreted as the number of lines to be printed, counting from the cursor position forward or backward. Moreover, if the cursor is in the middle of a line, then 0T
prints a fragment from the beginning of the current line to the cursor, and simply T
without parameters - from the cursor to the end of the line:
*5J$$ *0T$$ Hello*T$$ , Habrahabr! *
The J
command, which we used above, moves the cursor to the specified absolute offset (the cursor is always between the characters). Well, somehow ugly to look, I would like to see this very cursor. Can between 0T
and T
just print wand? Yes you can. The print command is ^A
(you can type Ctrl + A, or you can directly tick and the letter A in turn):
*0T@^A/|/T$$ Hello|, Habrahabr! *
Hooray, we now know how to see the position of the cursor. It would be good to write this command and, if necessary, execute it. If immediately after the execution of a command, dial *
and then a letter or number, then the text of the previous command will be written into the corresponding text Q-register (I have not figured out why not just the register, but the Q-register). Let's type, for example, *Z
Now the contents of register Z
can be executed as a command using the MZ
command:
*MZ$$ Hello|, Habrahabr! *
Great, we recorded the first TECO macro. Well, to run around the positions is boring, it would be good to be able to look for something. For example, look for the word IDE. Help says that for this there is a command S
:
*@S/IDE/$$ *
So what? Gave nothing. Found or not found? And if found, then where? Yes, interactive editors are corrupting. If I did not give anything out, it means I found it. TECO just moved the cursor after the found text. Let's repeat and immediately draw a line with the cursor:
*@S/IDE/MZ$$ ?SRH Search failure "IDE"
Oh, and now what? Yeah, he is looking for something from the current position of the cursor, so there is no second IDE. We must first go to the beginning of the file (you can just J
):
*J@S/IDE/MZ$$ Stop using your fancy IDE|s, TECO for the win!
Wow, beauty. How about highlighting the found text on both sides? There had to pretty dig documentation. Such things were useful:
^S
- returns the length of the result of the last search or the last insertion (with a minus sign, so that it is more fun).
- the point returns the current position of the cursor in the buffer.C
- moves the cursor to the right by the number of characters returned by the previous command (there is also the opposite command that goes to the left - R
).Accordingly, using ^SC
you can go to the beginning of the found string, then the familiar 0T
prints the prefix, then displays [
. Next you need to print a fragment from the position .
before .-^S
(remember that ^S
is a negative number). Then type ]
, return the cursor to the location with -^SC
and print the rest of the line with T
Here is the whole program:
*^SC0T@^A/[/.,.-^ST@^A/]/-^SCT$$ Stop using your fancy [IDE]s, TECO for the win! *
Great, we already started making Pearl. It's time for the following quote from an article about real programmers:
It is noticed that the sequence of TECO commands is more like the transmission of noise than readable text. Fun entertainment - type your name in TECO as a team and try to guess what will happen. Virtually any typo in a conversation with TECO can destroy your program or, even worse, introduce an elusive and mysterious bug into the once working procedure.OriginalIt has been observed that a TECO command sequence is more closely resembles transmission line noise than readable text. It is a line of command line. If you’re, you’ll never know what to do.
By the way, some similarity of regular expressions in TECO is also available. For example, the analogue of [AZ]\d+
would be ^EW^EM^ED
. If you don’t like regular regular expressions, work a bit at TECO . After that you will love.
Now I would like some control structures. Let's say such a task: assuming that the cursor is at the beginning of a line, take the text of the line into a beautiful frame. By the way, move down the lines - the command L
, and up - L
You can also press Ctrl + H and Ctrl + J to quickly execute the -LT
and LT
commands and run through the text here and there.
For this task, we need to insert as many minusics as there are letters in the current line. How to measure it? You can call .
twice, before and after L
, and calculate the difference. We can use the recording and reading of numbers in Q-registers (the number and text are stored in the Q-register with the same name independently). UA
writes the number to the register A
, and the QA
reads it. A simple loop with n iterations is n<...>
. For example, if we want to insert minusik A
times, we will write QA<@I/-/>
. The whole macro will look like this:
.UAL.-QA-2UA-L@I/+/QA<@I/-/>@I/+ |/L2R@I/| +/QA<@I/-/>@I/+ /-LC
Perhaps for someone the most incomprehensible in this macro is -2. And then 2R
. Everything is very simple: a line break in those times always occupied two characters, '\r\n'
. There was no disagreement, it was beautiful. We need to subtract it from the difference in coordinates of the beginnings of the lines, and to draw the right frame, we need to go two characters to the left.
Save this macro to its register Y
This, by the way, can be done not only through *Y
after the command is executed, but using the ^U
command to write a string to the register: @^UY/ /
. Let's execute it, being in the beginning of the buffer, and we will receive:
*MY$$ *HT$$ +-----------------+ |Hello, Habrahabr!| +-----------------+ This is TECO, the most powerful editor in the world. Stop using your fancy IDEs, TECO for the win! Bye. *
Super! Variables, cycles - this is similar to real programming. You can go for an interview for Senior TECO Developer. Speaking of interviews. Let's write a macro FizzBuzz on TECO. I do not know if someone did this before me.
Here, division with a remainder by 15 would be useful, but unfortunately there is no division operation with a remainder. But there is a division entirely, so you can express it in xx/15*15
. True, there is no priority of operations either, so you have to write -x/15*15+x
. Further, depending on the balance, you need to do different things. If the remainder is 0, type FizzBuzz
, if 3, 6, 9, or 12, then Fizz
, if 5 or 10, then Buzz
, otherwise the input number. For this useful command O
Without a numeric argument, this is an unconditional jump (that is, GOTO), and with a numeric argument, it is a kind of switch: nO
jumps to the n-th label, separated by commas, if there is one. Tags look like !!
(comments are also written - this is just a label that no one jumps to). Create tags !f!
(for Fizz) !b!
(for Buzz) and !fb!
(for FizzBuzz) as well !e!
- the end for jumping out, a la break
. Here is the entire macro, including the command to write it to the Q-register F
:
*^UF UA-QA/15*15+QA@O/fb,,,f,,b,f,,,f,b,,f/QA=@O/e/!fb!@^A/Fizz/!b!@^A/Buzz /@O/e/!f!@^A/Fizz /!e!$$
Notice that in the ^A
command I am passing the line feed to display it. Imagine, there are still no multi-line literals in Java, and in TECO they were already more than half a century ago! I also saved a little by making "fallthrough" after the thread !fb!
and typing "FizzBuzz" of the two halves. Just like in the usual switch-case statement.
Interestingly, the macro starts with UA
: write a number in register A
And what number? Very simple - the argument of this macro. It must be specified just before the call. Checking:
*4MF$$ 4 *5MF$$ Buzz *105MF$$ FizzBuzz *87MF$$ Fizz *44MF$$ 44 *
Super, we had an interview! My last experiment was required to make the KDPV to this article. How to write a program that will display the desired letters with the grids? It seems that there are no arrays and data structures in the language. But there is a text buffer! I put the character generator in the form <>< ><>< >...
(The HK
command clears the current buffer contents):
*HK@I/H130H130H124H130H130A62A66A254A130A130B254B128B252B130B252R252R130R252R128R128R/$$
You can position it on the desired number using J< >@S/<>/
, because the numeric parameter S allows you to find the nth occurrence of the specified string. I did not find how to execute the command parameterized by an arbitrary text parameter, so I formed a command in the Q-register D ( :^UD
appends to the end, but simply ^UD
replaces the text in the Q-register) and executed it as a macro. You can parse the number using the backslash \
command. We also need conditional operators: "N<>'
- execute if the numeric argument is not zero, but "E<>'
- execute if the numeric argument is zero. The “otherwise” branch can also be created by separating it with a pipe. Thus, the output of a space or lattice is done through "E32^T|35^T'
, where ^T
prints the character with the corresponding ASCII code. Another useful command is %<>
, increasing the numerical value in the corresponding Q-register by one.
I used registers like this (not to get confused, each was used only for a number or for a string):
@^UC/HABRAHABR/1UF!bl!0UE!bm!J@^UD/@S|/QEQC:^UD@:^UD/|/QFMD$ \UAC128UB!br!QB&QA"E32^T|35^T'QB/2UBQB"N@O/br/'%E^[QE-:QC"N@O/bm/'@^A/ /%F^[QF-6"N@O/bl/'$$
A number of macros were written on TECO, and quite complex ones. Of course, in order to understand at least something, it would be nice to structure the macro, insert line breaks, indents and comments. However, it turns out that this greatly slows down the processing of the macro. Therefore, came up with a minimizer. Here is the code of such a minimizer before minification, here it is after. Something like we are seeing today in the world of JavaScript.
Interestingly, there are commands to read the character from the keyboard, and therefore the macro can be made interactive. Using the terminal output capabilities through escape sequences, it is easy to position the cursor on the screen, update text with fragments and switch colors. Thus, using a macro, you can process each character entered or a special key and make an interactive editing mode. Approximately in this way, Emacs was born, which was originally a macro to TECO, and only then was rewritten as a separate application.
Source: https://habr.com/ru/post/351416/
All Articles