Здесь не будет песен и сказок о том какой крутой и недооценённый язык Forth :) По совокупности обстоятельств он скорее уже история. Однако некоторые связанные с ним моменты поучительны и перспективны - и хочется их записать на память, да и поделиться.
Историческая справка

Насчет Спутника - это не шутка. Язык в некоторой степени обязан своим появлением советскому ПС-1 (а значит и многим людям во главе с Королёвым). Автор языка, Чарльз "Чак" Мор (или Мур?) именно с него начинает статью об истории языка:
В октябре 1957 случился Спутник-1 - потрясающее время! Я был второкурсником в MIT и устроился на подработку в SAO (Смитсоновская Астрофизическая Обсерватория) в Гарварде. SAO отвечала за отслеживание спутников. Спутник-1 застал их врасплох и они стали нанимать студентов для вычислений... [коллега] рассказал мне о IBM EDPM 704 в MIT и одолжил своё руководство по Фортрану‑II. Моя первая программа Ephemeris-4 выполняла за меня мою работу.
Так я стал программистом... моя «подработка» занимала не менее 40 часов в неделю, а оценки в ВУЗе посыпались к чертям...
Работа программиста в 50е годы была суровее чем теперь. Моя программа занимала 2 лотка перфокарт, которые мне приходилось таскать с собой [одна перфокарта обычно использовалась для записи одной строки кода] - и собственноручно прогружать их в машину... Компиляция занимала минут 30, но поскольку компьютерное время было ограниченным, у меня получался обычно 1 запуск в день.
Не буду пытаться пересказать всю статью. В общем, суть в том что при небольших изменениях в вычислениях приходилось менять часть программы и следовательно перекомпилировать её. Чтобы сэкономить время автор стал выносить описание вычислительных шагов в текстовый файл — так что можно было поменять только его, а программа просто выполняла указанные шаги в новом порядке или с другими параметрами.
Сменив за десяток лет несколько мест работы, автор применял этот же подход обычно и в других проектах — понемногу из его «синтаксиса» для интерпретируемого файла вырастал некий самостоятельный язык программирования.
С какого‑то момента он стал рассматривать своё поделие именно как достаточно универсальный язык и в результате довёл его до состояния когда при переносе на другую архитектуру можно было переделать только описания базовых «слов».
В этом смысле получился язык имеющий в общем‑то ту же цель что семейка от BCPL до C — но значительно отличающийся синтаксисом, а значит парсингом — и довольно необычный в смысле выполнения тоже.
В отличие от C он не компилировался в исполнимый файл — а в отличие от интерпретаторов типа BASIC он был значительно быстрее (в зависимости от реализации - в десятки и сотни раз) - потому что всё-таки, гм, компилировался :-) При этом умещался в машины с жалкими несколькими килобайтами памяти.
Сейчас мы посмотрим-вспомним как это было устроено — и чем это актуально «в современности».
Краткий обзор FORTH
Чтобы объяснить чем интересна внутренняя реализция FORTH вспомним, как он выглядел внешне. Наверняка многие это знают и помнят - извините заранее :-)

Характерным был синтаксис — программа выглядела как последовательность «слов», каждое из которых производит некую операцию с «операционным» стеком — хранилищем, куда данные можно класть по очереди, а потом снимать в обратном порядке — вроде стопки тарелок.
Как помним, в большинстве компьютерных архитектур есть один стек — он используется для сохранения адресов возврата при входе в подпрограммы. Некоторые ранние языки и системы не имели такой возможности — поэтому «молодые» версии Фортрана, например, сохраняют адрес возврата в специальной (скрытой) переменной внутри самой подпрограммы — и больше одного раза войти в подпрограмму нельзя — для возврата только одна ячейка. Многие архитектуры позволяют на этот же стек складывать и произвольные значения — например чтобы кратковременно сохранить какие‑то значения. Со времён C через стек же передают параметры в подпрограммы — правда нужно следить чтобы он оставался правильно выровненным, иначе забыв вытолкнуть сохранённые данные мы сломаем всю цепочку адресов возвратов, хранящуюся вместе с ними.

Ну а FORTH использует два стека. Один для адресов возврата (в основном), а другой именно для работы с данными. Поэтому выражения выглядели так:
3 5 * .
в этом выражении 4 «слова» разделённых пробелами: те что в виде чисел — просто заталкивают соответсвующее значение в стек; звёздочка вытаскивает пару чисел из стека, перемножает и кладёт результат обратно (похоже действуют и другие арифметические слова); «точка» выталкивает значение из стека и печатает его. Таким образом это аналог print(3*5)
. Слова бывали и более «вербальными» — вот другой пример:
KEY 1 + EMIT
Слово KEY ожидает нажатия клавиши пользователем и заталкивает в стек код введённого символа. Единица запихнёт в стек ещё и значение 1 — после чего слово «плюс» вытолкнет их, сложит и поместит результат обратно в стек — это будет код введённого символа увеличенный на 1, то есть код следующего (по алфавиту) символа. Наконец слово EMIT выталкивает число из стека и печатает символ с соответствующим кодом. То есть у нас тут «шифрование» по способу Цезаря — осталось только в цикл завернуть.

Можно было и определять собственные слова, конечно. Синтаксис выглядел так:
: STAR 42 EMIT ;
: TRI-STAR STAR STAR STAR ;
Здесь два определения — каждое начинается со слова «двоеточие» и заканчивается словом «точка‑с-запятой». Автор в упомянутой исторической статье поясняет почему так вышло — эти двоеточия перед определяемым словом — аналоги меток (например в языке Algol и более поздних) — только для упрощения парсинга они идут перед словом а не после.
Итак, за двоеточием следует определяемое (новое) слово — а дальше тупо последовательность действий — то есть последовательность других слов. Фактически мы видим в этом «определении слов» определение пользовательских подпрограмм. FORTH в этом смысле строго «структурный» язык.
Как видите, синтаксис до безобразия простой — просто «слова» и пробелы между ними. Это проще чем LISP (там кроме пробелов ещё и скобки).
Работает FORTH в режиме интерпретатора — если пользователь вводит простые выражения — они тут же выполняются. Если вводит описания слов — они «компилируются» — добавляются в словарь (и в код). Смысл этой «компиляции» мы сейчас и рассмотрим.
Как выполняется FORTH
Простота синтаксиса ведёт к важному последствию (кроме простоты парсинга).
Что происходит когда пользователь определяет новое слово? Как оно сохраняется?
Из‑за простоты синтаксиса нам нет нужды хранить его исходным текстом. Можно сразу скомпилировать либо в коротенький интерпретируемый код, либо даже прямо в машинный.
Почему это так просто? Потому что определение состоит из цепочки слов — а каждое слово это вызов другого слова (т. е. некоей подпрограммы). Параметров у этих подпрограмм нет (они оперируют стеком данных) — поэтому нужно просто записать список адресов этих подпрограмм!
Точнее говоря, можно рассмотреть всё‑таки два типа слов — слова в виде чисел, которые вызывают добавление значени на стек — и слова в виде вызовов других слов.
Само слово (его «имя») добавляется в некий глобальный «словарь», например просто список — в этом словаре для каждого слова хранится его адрес или сама последовательность команд, если код хранится вместе со словарём (возможны разные реализации):

Таким образом «скомпилированный» код будет состоят из операций только трёх типов:
добавить число в стек
вызвать слово (подпрограмму) с таким‑то адресом
выйти из подпрограммы
То есть «скомпилированный» код может выглядеть в условных инструкциях так:
CALL "KEY"
PUSH 1
CALL "ADD"
CALL "EMIT"
RETURN
Мы немножко упрощаем — есть ведь ещё слова которые организуют ветвление и циклы — но они не много добавляют к этой картине — нужны ещё инструкции для условного и безусловного перехода например, и всё.
Чем хорош такой подход?
Хорош он тем, что даже если этот код (по‑английски его называют threaded code — в русском я встречал перевод «шитый код») не является машинным, а интерпретируется, он всё равно работает довольно быстро — не нужно ничего парсить, знай себе выполняй вызовы один за другим и всё.
Но и в машинный вариант его превратить очень легко! Действительно, если у нас есть лишь 5–7 типов команд, то не составит труда сделать генератор машинного кода который будет вставлять соответствующие 5–7 инструкций модифицируя их параметры.
Например вызов подпрограммы в 8086 архитектуре это код в духе 0xBExxxx
где четыре икса означают 4-значное (двухбайтовое) шестнадцатеричное число — адрес подпрограммы. Понятно что нам не составит труда сгенерировать это BE и приписать к нему адрес.
Так же и с операциями занесения чисел в стек — например, если стек данных организован на регистре BP, то в простейшей реализации нам нужно две команды:
MOV [BP], 0x1234 ; код С746003412
ADD BP, 2 ; код 83C502
из которых поменять нужно только саму константу — два последних байта в первой инструкции. В этом варианте, впрочем, на помещение константы в стек уходит 8 байт, можно сделать и короче. Можно использовать инструкции 80386, но они длиннее сами по себе.
Ну и поскольку FORTH в принципе несложен (сравнительно с многими другими языками) в реализации, воплощений в том числе с нативной кодогенерацией было сделано немало. Нередко даже программисты создавали реализации под конкретную задачу (например, так поступили разработчики игры Starflight). Это немало способствовало возникновению популярного мнения что язык «быстрее Си» или даже «быстрее Ассемблера» — хотя понятно что к таким замечаниям следует относиться осторожно:)
О производительности
Бесспорно что FORTH позволял создавать программы очень компактные — впрочем максимальная компактность скомпилированного кода необязательно вела к максимальному быстродействию. Что стоит за мнением о «скорости» этого языка?
Во‑первых сравнение с современными ему интерпретаторами на машинах со схожими скромными возможностями. Мы теперь понимаем почему так — он не парсит строки исходного текста по мере исполнения а готовит довольно оптимизированный код (интерпретируемый или нативный) заранее.
Во‑вторых он предлагает особенную Calling Convention — способ передачи параметров в подпрограммы и обратно — за счет своего стека данных.
Действительно, привычная calling convention из Си — с ней значения хранятся в переменных в памяти (в основном) и перед вызовом запихиваются в стек вызовов, откуда подпрограмма их уже использует как локально адресуемые переменные. Вроде бы почти то же самое — но вот для возврата значений такой механики нет. Кроме того если есть вложенный вызов, и в нижележащую функцию надо передать значение «полученное свыше» — его придётся ещё раз пихнуть в стек. В общем, неоптимально.
При программировании на ассемблере мы можем вместо этого передавать параметры в регистрах. В идеальном случае это будет быстрее. К сожалению с одной стороны часто трудно угадать идеальный вариант и мы сохраняем сами регистры на стеке чтобы случайно их не испортить — с другой проблема вложенных вызовов и тут не решена.
FORTH предлагает непохожую на эти две крайности концепцию — мы используем стек, но живущий независимо от стека вызовов. Можно и возвращать значения на нём же. И при вложенных вызовах данные могут просто спокойно лежать там же где лежали.
Интересно что этот подход в принципе можно использовать и в Си и в Ассемблере — кто мешает отдельный стек создать.
Поэтому хотя строго говоря ясно что идеальную программу на этих языках можно написать уж точно не хуже чем на FORTH — но в реальной ситуации часто код на них не будет идеален, из‑за чего и возникают «чрезмерно оптимистичные» впечатления от FORTH.
Стековые Виртуальные Машины
Но это мы отвлеклись. Хвалить FORTH — не наша цель. Вместе с достоинствами в нём сложилось и достаточно особенностей которые не дали ему удержать даже ту популярность которая у него была. Сейчас он скорее архаизм.
Но те ключевые моменты на которые мы обратили внимание — они получили очень широкое распространение. Действительно — если кому‑то захочется изобрести новый язык программирования — выглядит очень привлекательно сделать так чтобы он транслировался в памяти в «цепочки слов» наподобие FORTH — и исполнялся в некоей виртуальной стековой машине.
И действительно — если мы посмотрим на более популярные языки — например Java, Python (и PyPy), Lua — а с ними же и несколько более узкоспециализированные WebAssembly, Ethereum EVM, TON TVM из тех что болше на слуху — оказывается что внутри они используют именно стековую машину. Кроме определенного удобства при трансляции это облегчает и внедрение JIT‑компилятора. Фактически, FORTH компилирующий слова в цепочки нативного threaded code в памяти — это тоже JIT‑компиляция!
Мы не говорим о том что стек для данных или threaded code был придуман вместе с FORTH (скорее эти концепции появились паралельно ему и были удачно подхвачены) — но безусловно он сыграл значительную роль в популяризации этого подхода.
И это мы себе намотаем на ус на случай если тоже соберемся изобретат языки и компиляторы:)
Заключение
В обзоре FORTH дело представлено так будто это совсем простой язык — тут следует оговориться, он прост, да не прост :-) под капотом у него создана остроумная идея разных типов слов — одни работают только в определениях других слов — другие наоборот только при интерпретации — и т. п. — и это позволяет использовать его как «мета‑язык» (авторское название) для самоописания и саморасширения. Эту очень интересную тему мы совершенно игнорируем в данной статье т.к. все таки рассказ был о другом — не столько о FORTH, сколько о «пути» в разработке языков, который он нам «указывает». Тем не менее если Вы забавы ради захотите подробнее познакомиться с этим по‑своему культовым языком — вы, пожалуй, не пожалеете :-)
Желающим осмелюсь порекомендовать книжки Лео Броуди. К сожалению, кажется, идеальной литературы по FORTH не сложилось (отчасти из‑за его необычности и обманчивой простоты) — но это вероятно лучшее что есть. Популярная Starting Forth опубликована официально в интернете: https://www.forth.com/starting‑forth/ — из этой книжки позаимствованы и некоторые забавные иллюстрации использованные в данной статье.
Комментарии (26)
kt97679
21.08.2025 05:25Вот здесь много интересных людей общается: https://github.com/ForthHub/discussion/discussions
RodionGork Автор
21.08.2025 05:25спасибо :) кажется я впервые узнал что в качестве форума можно гитхаб использовать!
Denis_Chernyshev
21.08.2025 05:25Ох, книги Броуди, "какая боль". Комикс для чайников, и материал поданный задом наперед. А главное в форт-системе это же не синтаксис, а организация динамической памяти структурой словаря. И синтаксис - это системное средство для работы со словарем.
Если рискнете читать Броуди, то держите постоянно в голове: написано по старому стандарту Forth-83, и есть серьезные отличия от ANSI Forth-95 (современный неофициальный Forth уже не ломает ANSI-95, а только дополняет).
RodionGork Автор
21.08.2025 05:25Ох, книги Броуди, "какая боль". Комикс для чайников, и материал поданный задом наперед.
яростно поддержу :) мне он всегда казался излишне вербозным - и да, то что в общем-то самое интересное там лишь очень кратко затронуто где-то в главе "under the hood"
к сожалению на русском видел ещё только одну (Келли, Спайс) и прямо скажем тоже не готов её порекомендовать - там и Forth-79 с Forth-83 спорит постоянно и много материала который сейчас уж точно даже историческую ценность едва ли представляет.
было бы интересно полистать хорошую книжку как раз о реализации языка, точнее о вариантах реализации даже. но такой я не видел и на английском.
atues
21.08.2025 05:25А что именно Вы смотрели? Есть книга Баранова и Ноздрунова "Язык Форт и его реализации", есть Семенов "Программирование на языке Форт" (даже исходники на ассемблере приложены). Книги старые, конечно, но информация там вполне корректная и вполне может служить в качестве руководства, как мне кацца )))
RodionGork Автор
21.08.2025 05:25спасибо за рекомендации, этих я точно не видел, полистаю с любопытством!
atues
21.08.2025 05:25:))) Выше я давал ссылку на русскоязычный форум. Там информации вагон и маленькая тележка. Главное - найти. И народ там доброжелательный: спрашивайте - наведут на цель
atues
21.08.2025 05:25Книги Броуди, imho, и не предназначались для детального технического раскрытия того, как именно функционируют Форт-системы. Да, рассказано "по верхам", но для начинающих и это, по-большому счету, не самая нужная информация. Научить делать самые фундаментальные вещи - и этого вполне достаточно. А уж потом, когда появится навык программирования, можно сделать попытку "слепить" свою реализацию. Тут, конечно, уже нужно рыть глубже. Но точно не начинающим.
RodionGork Автор
21.08.2025 05:25ну да, судя по картинкам с разъяснением что такое стек книжка именно для широкой публики, м.б. для популяризации домашних компьютеров в которых был прошит форт вместо бейсика... правда их вроде было не очень много
asksoftware
21.08.2025 05:25>>По совокупности обстоятельств FORTH скорее уже история.
Наверное, так. Но ещё жив форум nnCron (движка, который выполняет FORTH-скрипты в Windows) и я тоже иногда туда заглядываю: https://nncron.ru/forums/viewforum.php?f=5&sid=c05059a63997d440bb7350c27f9348ac
Ну и сделал на его базе вполне работающий, как теперь принято говорить, пет-проект, скачанный десятки тысяч раз: https://optimum.asksoft.netkt97679
21.08.2025 05:25Думаю пока есть люди, которые пишут свои форт системы, списывать форт в утиль преждевременно.
RodionGork Автор
21.08.2025 05:25Я не имею в виду списывать его в утиль (и в общем-то думаю точно так же - за счет того что форт сравнительно легко воплотить любому энтузиасту - всегда будут оставаться энтузиасты воплощающие его для собственных проектов). Речь только о том что пик популярности его явно был во времена когда компьютеры были стеснены в ресурсах, а людям хотелось писать быстрые программы но всё-таки не на ассемблере. Сейчас диспозиция поменялась - компьютеры быстрые а программисты совсем обленились :)
kt97679
21.08.2025 05:25Сложно сказать когда был пик популярности форта с учетом того, что сам форт никогда не был очень популярным языком. Все же работа со стеком и обратная польская нотация воспринимается далеко не всеми мозгами. Тем интереснее сообщество людей, которые используют форт до сих пор.
IgnatF
21.08.2025 05:25Форт на БК 0001 вроде бы вторым языком работал. Дальше его сомнительно, что применяли.
RodionGork Автор
21.08.2025 05:25Его применяли и применяют время от времени - но обычно это ситуация когда какой-нибудь энтузиаст создаёт собственный ФОРТ для собственных целей (благодаря тому что это язык который сравнительно легко воплотить). Из довольно поздних таких "отпрысков" я помню например язык Factor созданный в начале 2000х для разработки игры.
Причём я о нём узнал т.к. мне показал его кто-то из посетителей решающих задачки на моём сайте - т.е. спустя 10-15 лет... Из чего делаю вывод что даже такие "боковые ветви" оставляют след в истории.
Другое дело что он точно ушёл из "мэйнстрима" и даже в эмбеддед-разработке не закрепился (тут думаю есть проблема в архитектуре с разделённой ROM/RAM памятью которая мешает прочувствовать пользу форта).
axe_chita
21.08.2025 05:25На БК-0010 исторически первым языком программирования был ФОКАЛ, который к ФОРТУ не имел никакого отношения. А единственным домашним компьютером с зашитым в ПЗУ Forth ЕМНИП был британский Jupiter ACE от создателей ZX Spectrum.
RodionGork Автор
21.08.2025 05:25сам рядом не стоял но похоже пзу с фортом бля бк0010 были - но точно неясно, любительская ли это разработка или в недрах какого то из заводов рожденная, хотя, возможно, тоже по инициативе энтузиастов
а про Jupiter ACE точно! хотя пишут что он не завоевал популярности, в том числе из-за форта.
axe_chita
21.08.2025 05:25сам рядом не стоял но похоже пзу с фортом бля бк0010 были - но точно неясно, любительская ли это разработка или в недрах какого то из заводов рожденная, хотя, возможно, тоже по инициативе энтузиастов
Так на PDP-11 была не одна реализация Forth, создать ещё одну, с учетом особенностей БК-001х, было вопросом усидчивости. Была-ли она реализована в виде ПЗУ/картриджа? Этот вопрос лучше адресовать энтузиастам БК, я к примеру ничего подобного не слышал, но я могу и ошибаться.
а про Jupiter ACE точно! хотя пишут что он не завоевал популярности, в том числе из-за форта.
Домашний компьютер без поддержки графики, пусть даже монохромной, в 1982? Вот это и было основной причиной его провала, покупать ZX80/81 на стероидах уже никто не хотел.
ebt
21.08.2025 05:25Не случайно Николай Дуров назвал язык виртуальной машины телеграм-блокчейна Fift.
RodionGork Автор
21.08.2025 05:25Здорово, спасибо, не знал! Прекрасный образец профессионального юмора "для своих" со стороны автора :)
axe_chita
21.08.2025 05:25Во‑первых сравнение с современными ему интерпретаторами на машинах со схожими скромными возможностями. Мы теперь понимаем почему так — он не парсит строки исходного текста по мере исполнения а готовит довольно оптимизированный код (интерпретируемый или нативный) заранее.
Не совсем так. Практически все интерпретаторы Basic, тех времен, ради экономии памяти токенизировали текст программы превращая команды бейсика в последовательность байтов, при исполнении которых интерпретатору не приходилось парсить код при исполнении. текста программы.
А когда мы делали просмотр текста программы командой LIST, то интерпретатор вынужденно делал обратную операцию, превращая токенезированый поток в текст для человека. Вот тут пример реализации команды LIST для MSX BASIC на Си
Побочным эффектом токенизации, кроме экономии памяти, было ускорение исполнения программы.
Более того, Вильнюсский Basic (ДВК, БК, УКНЦ) не интерпретировал текст программы, а компилировал его прямо в шитый код.
Из любопытного, вот тут показана реализация Qbasic на JavaScript, и объясняется принцип работы P-Code VM в который транслируется программа на Basic
RodionGork Автор
21.08.2025 05:25Это верно, даже если вспомнить реализацию в ZX-спектрум, там ведь на самом деле даже прямо токены и вводились и эти токены (типа
PRINT
) были в кодовой таблице. Так что пожалуй этот пассаж я недостаточно вдумчиво написал, не раскрывает различия. В идеале нужно брать пару реализаций с одной платформы и их сравнивать подробно. Ну и да, знатоки напомнят что изначально бейсик вообще компилируемым был создан :)
muhachev
21.08.2025 05:25В биткойне и тоне фортовые языки, да и вообще, в блокчейнах стековые скрипт-машины в основном.
atues
Отечественный и достаточно активный форум по Forth-у: https://fforum.winglion.ru/index.php
RodionGork Автор
спасибо! последнее сообщение 5 августа :) но безусловно хорошо когда есть где задать вопрос или поделиться мыслями :)
я сам по большей части реддитом пользуюсь соответствующим, тоже достаточно активен