Кто хорошо знаком с историей C — подскажите, из каких соображений фаза препроцессирования была принципиально отделена от фаз компиляции, с постулированием, по сути, полного отсутствия связей между ними?
Соображениями простоты это объяснить не получается — препроцессор C крайне примитивен, ресурсов для реализации требует мизерных (в сравнении с ресурсами для последующей компиляции).
В то же время, будь там вместо препроцессора нормальный макрогенератор, частично связанный с собственно компилятором (например, по типам выражений/идентификаторов) — можно было бы еще в 70-е малой кровью делать ряд удобных вещей.
С ностальгией вспоминаю макрогенератор ассемблера System/360, воспроизведенный затем в трансляторе автокода БЕМШ для БЭСМ-6.
Вообще, не надо искать смысл там, где его нет. Я вот у нас в конторе частенько наблюдаю, как молодые сотрудники почему то свято уверены, что какие то решения в том числе и у нас в проектах, принимались чуть ли не консилиумом, сборищем супер инженеров в белых халатах... хыхы... Ан нет, просто в тот момент было быстрее костыль подставить покавырявшись в носу в течении 20 сек.
ЕМ>Кто хорошо знаком с историей C — подскажите
Только Путин, и никого кроме Путина! О Великий и Могучий Путин — царь на веки веков, навсегда!
Смотрю только Соловьева и Михеева, для меня это самые авторитетные эксперты.
КРЫМ НАШ! СКОРО И ВСЯ УКРАИНА БУДЕТ НАШЕЙ!
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>нормальный макрогенератор, частично связанный с собственно компилятором (например, по типам выражений/идентификаторов)
Здравствуйте, Мёртвый Даун, Вы писали:
МД>Вообще, не надо искать смысл там, где его нет.
Ну вот Вы, доведись Вам реализовывать транслятор с макросредствами, стали бы выводить макрообработку в совершенно отдельную фазу, вплоть до создания макропроцессором отдельного файла, читаемого затем транслятором? Если да — из каких соображений?
Мне бы такое даже в голову не пришло — гораздо логичнее встроить макрообработку в лексический анализатор, при этом вывод промежуточных результатов одинаково доступен и оттуда.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Кто хорошо знаком с историей C — подскажите, из каких соображений фаза препроцессирования была принципиально отделена от фаз компиляции, с постулированием, по сути, полного отсутствия связей между ними?
ЕМ>Соображениями простоты это объяснить не получается — препроцессор C крайне примитивен, ресурсов для реализации требует мизерных (в сравнении с ресурсами для последующей компиляции).
Первая реализация языка С делалась на машине PDP-11.
Собственно это объясняет всё.
Например, почему в С нет функции факториала.
ЕМ>В то же время, будь там вместо препроцессора нормальный макрогенератор, частично связанный с собственно компилятором (например, по типам выражений/идентификаторов) — можно было бы еще в 70-е малой кровью делать ряд удобных вещей.
Любой алгоритм можно описать всего тремя элементами — циклом, условием и вызовом подпрограммы.
Если тебе нужен PL/1 ты знаешь, где его взять (с).
Здравствуйте, alpha21264, Вы писали:
A>Первая реализация языка С делалась на машине PDP-11. A>Собственно это объясняет всё.
Что именно это объясняет, кроме наличия операций инкремента/декремента?
A>Например, почему в С нет функции факториала.
Чем объясняется отсутствие такой функции в большинстве остальных языков?
A>Любой алгоритм можно описать всего тремя элементами — циклом, условием и вызовом подпрограммы.
Здравствуйте, alpha21264, Вы писали:
A>Первая реализация языка С делалась на машине PDP-11. A>Собственно это объясняет всё. A>Например, почему в С нет функции факториала.
А как это объясняет отсутствие в stdlib функции факториала?
A>Любой алгоритм можно описать всего тремя элементами — циклом, условием и вызовом подпрограммы. A>Если тебе нужен PL/1 ты знаешь, где его взять (с).
В языке Go, кстати, из 3-х видов сишных циклов остался только один
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Здравствуйте, alpha21264, Вы писали:
A>>Первая реализация языка С делалась на машине PDP-11. A>>Собственно это объясняет всё.
ЕМ>Что именно это объясняет, кроме наличия операций инкремента/декремента?
Ты наверное не знаешь, что такое PDP-11.
Это вычислительная машина с адресным пространством в 64 килобайта.
И с машинным словом длинной 16 бит.
И с системой команд, в которой строку удобно заканчивать нулём.
A>>Например, почему в С нет функции факториала.
ЕМ>Чем объясняется отсутствие такой функции в большинстве остальных языков?
А какие ты знаешь остальные языки?
Если они выросли из С как Java то объяснение очень простое.
A>>Любой алгоритм можно описать всего тремя элементами — циклом, условием и вызовом подпрограммы.
ЕМ>А это и вовсе не к месту и не в тему.
К месту и в тему.
Если бы ты писал программы а не языки (как Керниган и Ричи), то заковыристые возможности языка тебе были бы ни к чему.
Собственно и препроцессор ни к чему. Потому что смотря на программу ты видишь не то, что будет работать.
Здравствуйте, Евгений Музыченко, Вы писали:
A>>Первая реализация языка С делалась на машине PDP-11. A>>Собственно это объясняет всё. ЕМ>Что именно это объясняет, кроме наличия операций инкремента/декремента?
Замечание по ходу — инкремент и декремент к PDP-11 ни при чём, сказал сам Dennis Ritchie.
Так что и их не объясняет
Здравствуйте, alpha21264, Вы писали:
A>Первая реализация языка С делалась на машине PDP-11. A>Собственно это объясняет всё. A>Например, почему в С нет функции факториала.
Сюрприз — она есть в C. Хотя на PDP-11, да, её не было.
ЕМ>>В то же время, будь там вместо препроцессора нормальный макрогенератор, частично связанный с собственно компилятором (например, по типам выражений/идентификаторов) — можно было бы еще в 70-е малой кровью делать ряд удобных вещей. A>Любой алгоритм можно описать всего тремя элементами — циклом, условием и вызовом подпрограммы. A>Если тебе нужен PL/1 ты знаешь, где его взять (с).
Здравствуйте, alpha21264, Вы писали:
A>Ты наверное не знаешь, что такое PDP-11.
Немножко знаю. Так, слегонца, на уровне начальных загрузчиков, драйверов ядра RSX-11M, и тому подобной мелочевки.
A>Это вычислительная машина с адресным пространством в 64 килобайта. A>И с машинным словом длинной 16 бит. A>И с системой команд, в которой строку удобно заканчивать нулём.
Я до сих пор не понимаю, как все перечисленное может быть связано с логикой обработки макрорасширений в C. Вас не затруднит развернуть цепочку умозаключений?
A>А какие ты знаешь остальные языки?
Если учитывать только ЯВУ, и на уровне "листал описание, написал несколько строк" — с десяткок. А сколько Вы знаете языков времен 60-70-х, где есть встроенный факториал?
A>Если они выросли из С как Java то объяснение очень простое.
И каково же Ваше простое объяснение отсутствия встроенного факториала в раннем C?
A>Собственно и препроцессор ни к чему. Потому что смотря на программу ты видишь не то, что будет работать.
Для языка, главной целью создания которого является как можно более легкая человекочитаемость, идея хорошая. Для языка, в котором требуется разумный баланс сложности, удобства и эффективности (как конечного кода, так и компилятора) — негодная.
A># define true false // приятной отладки, суки!
Здравствуйте, netch80, Вы писали:
N>Замечание по ходу — инкремент и декремент к PDP-11 ни при чём
Конкретно PDP-11 ни при чем, а вообще DEC был одним из первых, кто реализовал аппаратный автоинкремент, так что сама идея где-то в тамошнем сообществе и родилась.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Кто хорошо знаком с историей C — подскажите, из каких соображений фаза препроцессирования была принципиально отделена от фаз компиляции, с постулированием, по сути, полного отсутствия связей между ними?
Сишный препроцессор это фаза лексера. В процессе трансляции программа сначала бьётся на препроцессорные токены, потом они трансформируются в соответствии с директивами и потом из них получаются обычные токены.
Если сделать возможность встроиться в парсер, то получатся синтаксические макросы как в лиспе.
Почему именно в лексер? Потому что там больше возможностей. Рассмотрим простой пример: вам нужно манглировать имя функции в зависимости от дефайна времени компиляции. То есть чтобы функция называлась по умолчанию foo0, но, если определена DFOONAME=1 то foo1 и так далее. На этапе когда все лексемы уже сформированы, вы этого сделать просто не сможете. Аналогично вы не смоете стрингифицировать и конкатенрировать (## и #). Так что именно сишный препроцессор рулит.
Здравствуйте, Tilir, Вы писали:
T>Почему именно в лексер? Потому что там больше возможностей. Рассмотрим простой пример: вам нужно манглировать имя функции в зависимости от дефайна времени компиляции. То есть чтобы функция называлась по умолчанию foo0, но, если определена DFOONAME=1 то foo1 и так далее. На этапе когда все лексемы уже сформированы, вы этого сделать просто не сможете.
Вот именно поэтому он и _не_ рулит, и возможностей меньше.
Ты меняешь foo на foo1, но в результате у тебя заменились все foo: и функции, и локальные переменные, и функция в другом пространстве имён (а без пространств рано или поздно язык превращается в аццкий хламовник — именно это случилось с C).
Замена на уровне синтаксиса позволит заменить именно нужный foo, а не все подряд.
T> Аналогично вы не смоете стрингифицировать и конкатенрировать (## и #). Так что именно сишный препроцессор рулит.
Это тоже делается на уровне уже синтаксиса, хоть и сложнее, но возможностей там больше (например, для Java стиля при конкатенации полезно менять регистр по типу zuka -> getZuka — сишное # такого не сумеет).
Здравствуйте, Tilir, Вы писали:
T>На этапе когда все лексемы уже сформированы, вы этого сделать просто не сможете.
Я в первую очередь имел в виду не сами фазы трансляции, а объем информации, которой владеет компилятор. Например, классический #ifdef работает только со списком макроопределений, подразумевая, что препроцессор может быть реализован независимой утилитой, как это сделано в *nix. Но я не вижу ровным счетом никакого смысла в таком искусственном разделении. А при реализации макрообработки в составе компилятора параметр #ifdef мог бы проверяться заодно и по спискам объявленных типов, переменных, функций. А в #if можно было бы реализовать операции вроде typeof (а sizeof таки реализовали в Turbo C).
Потом, даже простейшая доработка в виде многострочных макросов, в составе которых допустимы #if, резко расширяет возможности макрогенерации. Это есть в любом неигрушечном макроассемблере, и реализуется достаточно просто.
Ну и потом, кто заставляет выполнять этапы строго последовательно, по канонам и заветам? Даже на этапе синтаксического анализа можно в любой момент вызвать лексер, а затем запустить анализ конструкции заново.
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, alpha21264, Вы писали:
ЕМ>>>В то же время, будь там вместо препроцессора нормальный макрогенератор, частично связанный с собственно компилятором (например, по типам выражений/идентификаторов) — можно было бы еще в 70-е малой кровью делать ряд удобных вещей. A>>Любой алгоритм можно описать всего тремя элементами — циклом, условием и вызовом подпрограммы. A>>Если тебе нужен PL/1 ты знаешь, где его взять (с).
N>При чём тут PL/1?
Ну вот Керниган считал, что причём. Это его слова.
Хрестоматийный пример того, что не надо тянуть в язык всё, что напридумала фантазия.
К С/С++ тоже относится.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Какой еще "стандарт языка" в начале 70-х?
А вы про этап разработки. Я думал вы уже высказываете идеи как доработать существующий. Ну тогда просто не знали как правильно делать. Когда на всё наступили, сделали правильный и мощный m4.