Привет, Хабр! Сегодня речь пойдет про 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 есть специфичные для нее команды (я вынес это на рисунок ниже)
То есть, когда вы пишете 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:
Мы видим, что в этой таблице за букву 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
И русский вариант из букв Ш, Щ, Ц, а для рукописного л, и, ш
ШЩЦЩЦШЦЩЩЦШЦШЦЩ