Привет, Хабр! Сегодня речь пойдет про whitespace — язык программирования, который использует в качестве своего алфавита только 3 непечатных символа.
Этот эзотерический язык был создан Эдвином Брэди и Крисом Моррисом в 2003 и, разумеется, не предназначен для практического применения, существуя как определенный вызов для программистов - мол, попробуй отладить невидимый код.
Итак, как вы уже поняли, основная особенность whitespace – это то, что весь его алфавит состоит всего из трех непечатных символов: пробела, табуляции, и переноса строки. Кусок кода на этом языке выглядит как большой отступ, и, что интересно, любые символы, кроме этих трёх, интерпретатор просто пропускает, и их можно использовать как комментарии. Из этого следует, что этот язык можно встраивать в существующие программы или документацию, незаметно для рядового читателя – ну на случай, если очень хочется поиграть в шпионов.
Давайте, для читаемости, в рамках этой статьи, определим следующие условные обозначения: S — пробел, T — табуляция, L — перенос строки.
Как же работают команды в этом языке?
Разберем скелет каждой команды языка:
Во-первых, каждая команда содержит так называемый IMP или параметр модификации инструкции — en. instruction modification parameter (Когда ваш язык состоит всего из трех символов, глупо ждать, что он будет выполнять очень много команд. Но мы можем расширить их количество за счет комбинации IMP+Основная команда, так что можете относиться к IMP просто как к способу увеличить функциональность языка).
Во‑вторых, сама основная команда (например сложение, push в стек, завершение программы и тд.)
И, в‑третьих, ее аргумент (при наличии такового), почти всегда аргументы будут двоичным числом. Я думаю, что это единственный тип данных, который мы собираемся рассмотреть в этой статье.
Итак, обычно параметр команды — это двоичное число.
Но как мы можем представить двоичные числа на этом языке?
Напоминаю, у нас есть только 3 символа, так что это потребует от нас немного креативности. Создатели языка придумали следующее:
Двоичные числа они решили представлять табуляциями и пробелами, вместо единиц и нулей. Табуляция будет единицей, а пробел нулем. (прим. переводчика: первый знак каждого двоичного числа хранит в себе + или -. Пробел для положительных и табуляция для отрицательных)
Но возникает другая проблема. Как нам понять, когда заканчивается двоичное число?
Например, у нас есть следующая программа: Наше IMP= S, основная команда тоже будет S, и мы захотим передать в нее двоичное число SSTSTT. Тогда это будет выглядеть так: S S SSTSTT
И следующая команда будет начинаться прямо здесь, SSSSTSTTSS…
Но как компьютеру понять, где закончилось число и началась следующая команда? Для этого мы используем последний оставшийся в языке символ: переноса строки L. Двоичные числа достаточно просты в этом языке, поскольку они всегда заканчиваются переносом строки.
Поэтому, когда будете думать о двоичном числе в языке whitespace, просто помните, что первый символ — знак числа, табуляция — это единицы, пробелы — это нули, а закончить число всегда нужно переводом строки, и тогда у вас все получится.
IMP
Теперья расскажу о всех IMP — их существует 5 типов, каждый из которых делает что‑то свое. У каждой IMP есть специфичные для нее команды (я вынес это на рисунок ниже)
![Whitespace IMPs Whitespace IMPs](https://habrastorage.org/getpro/habr/upload_files/51d/055/41a/51d05541abc51842f2dec01afcc40623.png)
То есть, когда вы пишете IMP, после него вы можете написать одну команду из набора, специфичного для выбранного IMP.
Давайте быстро пробежимся по ним:
Манипуляции над стеком. Этот IMP обозначатся просто пробелом S, а команды, принадлежащие ему, характерны для любого стека: это push, pop и тд.
Арифметика. Обозначается сочетанием TS, и подразумевает продолжение в виде команд сложения, умножения, вычитания и тд. В этой статье мы не рассмотрим арифметические примеры, но вы можете это сделать самостоятельно.
Доступ к куче (heap) — предназначен только для хранения и извлечения данных. Этот IMP — TT. Я не буду приводить пример здесь, но опять, же, если вам очень хочется сохранять и извлекать какие‑то данные, то вы можете это сделать.
Контроль потока, он же flow control — мне будет сложно объяснить сакральный смысл этого IMP, но скажу, что мы обозначаем этот IMP символом перевода строки L, и, например, можем корректно завершить нашу программу с помощью него. Это как бы контроль того места, где мы находимся в программе. С помощью этого IMP мы также можем расставлять метки в коде, и потом возвращаться к ним, реализуя концепцию цикла.
Ввод‑вывод — заключительное IMP, и, вероятно, самая важное для нас. Обозначается TL и позволяет нам работать с вводом и выводом. Например, рассмотрим команду print — как указано на официальном веб‑сайте (вы можете посмотреть его здесь, поскольку он уже давно снят с хостинга) эта команда просто печатает в консоль ASCII‑ представление числа, находящегося на вершине стека. Эта программа не принимает никаких аргументов, просто печатает то, что находится наверху стека.
Поскольку теперь у нас есть хотя бы примерная основа того, как работают команды в whitespace, давайте попробуем написать что-нибудь очень простое, например, выведем в консоль букву A.
Да, у нас нет букв в Whitespace, у нас есть только 3 символа, из которых мы умеем составлять команды и двоичные числа. Как было сказано выше, команда print выводит на консоль именно ASCII‑представление верхнего числа в стеке (прим.переводчика — вообще то есть 2 вариации команды print в whitespace — одна(ST) выводит именно число, а вторая(SS) — его ASCII представление, то бишь символ). Если вы не знаете, что такое ASCII, то скажу кратко — это просто таблица, которая ставит в соответствие почти каждому существующему символу определенное число. Давайте оставлю здесь стандартную часть таблицы ASCII:
![ASCII-таблица ASCII-таблица](https://habrastorage.org/getpro/habr/upload_files/e6d/081/6bf/e6d0816bffd91473165db3ac96f0116c.png)
Мы видим, что в этой таблице за букву A отвечает число 65. Представить его в двоичном виде не составит труда, поскольку это 64+1, то есть 1000000+1 = 1000001
Не забываем про положительный знак перед числом, который представлен пробелом, и затем переводим наше 1 000 001 в табуляции и пробелы. Получаем: STSSSSSTL — в конце не забыли символ новой строки.
Разделим нашу программу на 2 части:
Часть 1 — кладем число в стек
Часть 2 — вызываем команду печати в консоль
Часть 1: Реализация. Как мы помним, для любых операций над стеком на понадобится соответствующий IMP — операции над стеком это S.
Теперь нам нужна сама команда помещения числа в стек. По счастливому стечению обстоятельств эта команда — также S. Затем мы передаем параметр – наше число, которое мы кладем в стек (STSSSSSTL).
Итого команда 1: SSSTSSSSSTL
S — стековый IMP, S — команда push, S — знак +, указывающий на то что дальнейшее двоичное число будет положительным, TSSSSST — двоичное представление числа 65, L — знак конца двоичного числа, и, поскольку эта команда принимает всего 1 аргумент, конца команды соответственно.
Часть 2: Реализация
Используем Input-output IMP: TL, затем команду print, представленной символами SS. Еще раз повторю, что она не принимает никаких аргументов, а просто смотрит в стек, и печатает число оттуда. Итого 2я команда: TLSS
По сути, на этом мы могли бы и закончить, код работал бы и так. Но хорошим тоном считается указать интерпретатору на завершение программы явно. Для этого добавим 3ю команду, которая будет завершать программу:
Она будет состоять из flow control IMP — L, и команды завершения программы — LL. Таким образом 3я команда целиком: LLL
Объединяем все 3 команды и получаем: SSSTSSSSSTLTLSSLLL — наша первая программа!
Я написал небольшой код на java, который переводит программу в таком виде в настоящие непечатные символы, оставлю ее здесь:
public class LettersToWhitespace {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String whitespaceCodeAsLetters = scanner.nextLine();
for(char c : whitespaceCodeAsLetters.toLowerCase().toCharArray()){
if(c=='s') System.out.print("\s");
if(c=='t') System.out.print("\t");
if(c=='l') System.out.print("\n");
}
}
}
Также оставлю ссылки на онлайн-интерпретаторы whitespace: раз и два
Наш код действительно выполнился, и вывел букву А на консоль, ура.
Итого:
Чтобы не думать об этом языке, как о полной мешанине из символов (черт, побери, мой код не работает, а я просто смотрю на белый экран!), думайте о нем как о комбинациях его обязательных элементов: IMP, конкретной команды, параметрах этой команды, и ее завершением.
Если бы мы хотели напечатать больше букв, можно было бы повторить этот код несколько раз с разными символами. Оставлю здесь пример, который выводит на консоль «Hello, world of spaces!» — пример позаимствовал отсюда
SSSSLSSSTSSTSSSLTTSSSSTLSSSTTSSTSTLTTSSSSTSLSSSTTSTTSSLTTSSSSTTLSSSTTSTTSSLTTSSSSTSSLSSSTTSTTTTLTTSSSSTSTLSSSTSTTSSLTTSSSSTTSLSSSTSSSSSLTTSSSSTTTLSSSTTTSTTTLTTSSSSTSSSLSSSTTSTTTTLTTSSSSTSSTLSSSTTTSSTSLTTSSSSTSTSLSSSTTSTTSSLTTSSSSTSTTLSSSTTSSTSSLTTSSSSTTSSLSSSTSSSSSLTTSSSSTTSTLSSSTTSTTTTLTTSSSSTTTSLSSSTTSSTTSLTTSSSSTTTTLSSSTSSSSSLTTSSSSTSSSSLSSSTTTSSTTLTTSSSSTSSSTLSSSTTTSSSSLTTSSSSTSSTSLSSSTTSSSSTLTTSSSSTSSTTLSSSTTSSSTTLTTSSSSTSTSSLSSSTTSSTSTLTTSSSSTSTSTLSSSTTTSSTTLTTSSSSTSTTSLSSSTSSSSTLTTSSSSTSTTTLSSSSLTTSSSSSLLSTSTTTSTTTSTTTSSTSSTTSTSSTSTTTSTSSSTTSSTSTLLSTSTTSTTTSSTTSSTSTSTTTSTTTSTTSTTSSSTTSTSSTSTTSTTTSSTTSSTSTLLLLLSSSTTSSSSTSTTSSTSSSTTSSTSSLTSSSLTLLSSSTTTSTTTSTTTSSTSSTTSTSSTSTTTSTSSSTTSSTSTLSLSTTTSLSLTSSTTTSTTTSTTTSSTSSTTSTSSTSTTTSTSSSTTSSTSTSTSTTTTTSTTSSTSTSTTSTTTSSTTSSTSSLTLSSSSSTLTSSSLSLSTTTSTTTSTTTSSTSSTTSTSSTSTTTSTSSSTTSSTSTLLSSSTTTSTTTSTTTSSTSSTTSTSSTSTTTSTSSSTTSSTSTSTSTTTTTSTTSSTSTSTTSTTTSSTTSSTSSLSLLSLLLTLLSSSTTTSSTSSTTSSTSTSTTSSSSTSTTSSTSSLSLSSLSTLTSTTTSLSSSSTSTSLTSSTLTSSTTTSSTSSTTSSTSTSTTSSSSTSTTSSTSSSTSTTTTTSTTSSTSTSTTSTTTSSTTSSTSSLSLLSSSTLTSSSLSLSTTTSSTSSTTSSTSTSTTSSSSTSTTSSTSSLLSSSTTTSSTSSTTSSTSTSTTSSSSTSTTSSTSSSTSTTTTTSTTSSTSTSTTSTTTSSTTSSTSSLSLLSSSTLTSSSSSSSLTTSLTLLSSSTTSTTTSSTTSSTSTSTTTSTTTSTTSTTSSSTTSTSSTSTTSTTTSSTTSSTSTLSSSTSTSLSSSTTSTLTLSSTLSSLT
На этом данная статья подошла к завершению, всем спасибо за внимание.
Если зайдет, сделаю продолжение, где использую на практике все IMP и приведу гораздо больше примеров кода на whitespace, и возможно даже напишу свою читабельную обертку под его команды
Источники и полезные ссылки:
Скачивание исходников whitespace: тут
Онлайн интерпретаторы: раз и два
Пожалуй, самое целостное видео по языку на сегодняшний день: тык
ky0
Непременно надо создать Bloody Eyes - аналогичный язык с символами "L строчная", "i заглавная" и "вертикальная черта |".
lIlIIIlIl|ll||lIII
Sabin
И русский вариант из букв Ш, Щ, Ц, а для рукописного л, и, ш
ШЩЦЩЦШЦЩЩЦШЦШЦЩ