99 бутылок: язык B («Би»)
В последнее время я довольно много пишу на Си, как-то так получилось. А поскольку мне хочется углубить свои знания этого языка, иногда что-нибудь ищу и читаю по теме. Так как авторы статей, по всей видимости, любят исторические экскурсы, примерно в каждой третьей написано, что язык Си произошёл от Би.
При этом ни в одной проштудированной мной статье нет ни одной даже самой простенькой программы на Би, а я человек любопытный. В общем, я решил сам посмотреть и вам показать.
81. B (язык «Би»), после появления Си, был полностью вытеснен последним и сейчас совершенное не используется. В отличие от, например, Кобола, который старше на десять лет, но при этом вполне ещё встречается.
Думаю, причин тут несколько.
Во-первых, это нацеленность на одну платформу — указатели в Би работают с машинными словами, а не байтами. Во-вторых, неудачные решения в синтаксисе, например, a =* 10 и a = *10 — это разные операции. Первая — умножение переменной a на десять, вторая — чтение в переменную a значения по адресу 10. В-третьих, у Си явно больше возможностей, а учитывая, что программы переписываются довольно легко (кроме части с адресацией, где легко наделать ошибок), переход выглядит очень оправданным.
Язык Би и правда очень похож на Си, в этом можно убедиться, взглянув на написанную мной программу. Что тут режет глаз сишному программисту? Прежде всего — отсутствие типов у заголовка функции, импорта функции printf и странное ключевое слово extrn внутри функций. extrn означает, что этот символ (в терминах компилятора) пришёл из внешней зоны видимости. Современное extern явно произошло от него, только значение немного поменялось.
Немного непривычно, что все переменные объявлены как auto, но в современном Си так тоже можно. auto, в данном случае, — класс хранения. В современном Си это класс хранения по-умолчанию, поэтому его не указывают. При этом, если отсутствует указание типа, Си по-умолчанию подставит int. Как раз для совместимости с Би. То есть auto beer Си даже правильно поймёт, но выдаст предупреждение.
Впрочем, в стандарте Си23 auto теперь означает автоматический вывод типа, что для совместимости с Би подходит ещё больше, так как, если приглядеться, строковые массивы («строки») в Би не выделены в отдельный тип.
Из того, что заметить чуть сложнее — экранирующий символ в строке не обратный слэш, а «звёздочка» (*), поэтому символ перевода строки не \n, а *n.
Ещё немного странно, что тут цикл while, а не for, но сильно это в глаза не бросается. В «Би» цикла for просто нет, поэтому вот так.
Вообще, ощущение такое, что Си взял от Би буквально всё, но, как ни странно, это не так. В Би есть конструкции ===, =!=, =<= и =>=. Наверное они не очень-то нужны, но выглядят интересно. Например, a === 5 означает «если переменная a равна 5, записать в a число 1, иначе — 0».
Кроме того, другая почти вся стандартная библиотека. Совпадают всего несколько функций, по больше части это ввод-вывод. Например, в Би можно объединить несколько строк функцией concat, а char не тип, а функция получения символа строки по индексу.
/* Beer song. Evgeny Stepanischev, Dec 2024 */
print_bottles(b, tail) {
extrn printf;
auto s;
s = "s";
switch (b) {
case 0: printf("No"); break;
case 1: printf("%d", b); s = ""; break;
default: printf("%d", b);
}
printf(" bottle%s of beer%s", s, tail);
}
main() {
extrn printf, print_bottles;
auto beer;
beer = 99;
while (beer) {
print_bottles(beer, " on the wall, ");
print_bottles(beer, "!*n");
beer--;
printf("Take one down, pass it around.*n");
print_bottles(beer, " on the wall!*n*n");
}
printf("No more bottles of beer on the wall, ");
printf("no more bottles of beer.*n");
printf("Go to the store and buy some more,*n");
printf("99 bottles of beer on the wall...*n");
}
Я долго пытался найти хоть какой-нибудь работающий под современными платформами компилятор, перебрал около десятка, но везде были свои сложности — некоторые не собирались, другие падали на совершенно валидном коде (например, printf("%c", 't')).
В итоге, остановился на онлайн-версии ybc (Yasha’s B Compiler).