Как и прежде, никакими вспомогательными инструментами для автоматической генерации компиляторов я не пользовался. Такое упрямство может выглядеть странным, однако проект имел единственную цель — моё собственное удовольствие, и дополнительные инструменты послужили бы здесь лишь помехой. В этом смысле компилятор разрабатывался с чистого листа.
Пять шагов к самокомпиляции в Windows
Стоит сказать несколько слов об основных задачах, которые пришлось решить на пути от DOS к Windows:
Формирование заголовков и секций исполняемого файла. Помимо официального описания формата Portable Executable, отличным подспорьем на этом этапе стала статья Creating the smallest possible PE executable. Поскольку в заголовках и секциях требуются точные адреса процедур и переменных, а их можно узнать лишь после вычисления размера кода и глобальных данных, то компиляцию пришлось сделать трёхпроходной. При первом проходе строится граф вызовов процедур и отмечаются «мёртвые» процедуры; при втором — вычисляются адреса, размер кода и данных, заполняются заголовки; при третьем — генерируется код. Такой кунштюк весьма неизящен, особенно с учётом того, что при каждом проходе заново повторяются все этапы компиляции, начиная с лексического разбора. Однако он приводит к очень лаконичному исходному коду компилятора и не требует никакого промежуточного представления программы.
Новый генератор кода. Компиляция для Windows потребовала заменить пары регистров «сегмент — смещение» на 32-битные регистры смещений, а также удалить (а местами добавить) префиксы изменения длины операнда (66h) и длины адреса (67h).
Директива для объявления внешних функций Windows API. Все имена функций, объявленных с директивой
external
, заносятся в таблицы секции импорта исполняемого файла. Поскольку эти функции требуют передачи аргументов справа налево, пришлось вручную инвертировать порядок аргументов в объявлении и вызовах всех таких функций. Тем самым отпала необходимость в инверсии средствами компилятора. Ради простоты все аргументы процедур и функций в XD Pascal передаются в виде 32-битных величин; к счастью, это правило справедливо и для функций Windows API, так что взаимодействие с системными библиотеками не привело к усложнению механизма передачи аргументов.Удаление множеств и инфиксных строковых операций из исходного кода. Это требование связано с задачей самокомпиляции. Вычисление любых выражений в XD Pascal строится так, что все промежуточные результаты имеют длину 32 бита и сохраняются в стеке. Для строк и множеств Паскаля этот подход неприемлем. Точнее, он позволил бы иметь множества размером до 32 элементов, но такие множества оказались бы практически бесполезны.
Обёртки для некоторых процедур. Идея самокомпиляции привела к обёртыванию вызовов некоторых процедур стандартной библиотеки. Сигнатура обёртки едина для случаев компиляции внешним компилятором (Delphi/Free Pascal) и самокомпиляции; обёртываемые процедуры при этом различаются. Таким образом, вся специфика способа компиляции локализуется внутри нескольких обёрток. Паскаль пестрит процедурами, которые при ближайшем рассмотрении оказывается невозможно реализовать по правилам самого Паскаля:
Read
, Write
, Move
и т.д. Для самых употребительных процедур, в том числе Read
и Write
, я сделал исключение и реализовал их нетипичными для грамматики языка, но привычными любому знатоку Паскаля. Для большинства остальных нетипичных процедур понадобились обёртки. Таким образом, XD Pascal не во всём совместим с Delphi или Free Pascal, однако большой беды в этом нет, поскольку даже сам Free Pascal в режиме совместимости с Delphi фактически остаётся несовместимым.Компиляция программ с GUI
Задача самокомпиляции, несмотря на своё символическое значение, остаётся ограниченной: компилятор является консольной программой и поэтому выглядит не совсем полноправным обитателем мира Windows. Понадобилось ещё несколько нововведений на пути к компиляции программ с оконным интерфейсом:
Директива компилятору для установки типа интерфейса. Тип интерфейса (консольный или графический) должен быть указан в отдельном поле заголовка исполняемого файла. Как известно, в Delphi и Free Pascal для этого существует директива
$APPTYPE
. Аналогичная директива $A
появилась и в XD Pascal.Операция взятия адреса процедур и функций. В классическом Паскале нет полноценных указателей на процедуры и функции — их в некоторой мере заменяет процедурный тип. Этот тип в XD Pascal не реализован. Как бы то ни было, до сих пор применение операции
@
к процедурам в моём скромном проекте казалось мне бесполезным. Однако обработка событий Windows API построена на обратных вызовах (callbacks), и здесь передача адреса вызываемой процедуры-обработчика вдруг стала насущной необходимостью.Явное указание имён подключаемых библиотек. Для консольных программ было достаточно импорта функций Windows API из библиотеки
KERNEL32.DLL
. Программы с GUI потянули за собой USER32.DLL
, GDI32.DLL
и т.д. Понадобилось расширить синтаксис директивы external
, добавив туда имя библиотеки.Демонстрационная программа с GUI
Что в итоге
В результате получился очень простой самокомпилируемый компилятор для Windows. Вряд ли корректно сравнивать его с могучими коллективными проектами типа Free Pascal. Скорее он попадает в весовую категорию известного любительского BeRo Tiny Pascal. По сравнению с ним XD Pascal имеет заметные преимущества: более строго соблюдается грамматика Паскаля и контролируются ошибки, есть полноценный файловый ввод/вывод, поддерживается арифметика чисел с плавающей точкой, нет зависимости от внешнего ассемблера, допускается компиляция программ с оконным интерфейсом.
Далее мне предстоит разобраться с ложноположительным срабатыванием некоторых антивирусов — новой проблемой, о которой я и не задумывался в маленьком уютном мирке MS-DOS. Если повезёт, XD Pascal будет внедрён, наряду с BeRo Tiny Pascal, в лабораторный практикум по курсу конструирования компиляторов в МГТУ им. Н.Э. Баумана.
Комментарии (104)
FForth
10.08.2019 14:52Как вариант замены DOS может быть перенос компилятора под операционную систему KolibriOS.
P.S. Есть прямые варианты запуска программ KolibriOS почти как родные в рамках Linux и Windows.
Эмуляция KolibriOS API (Windows)
Эмулятор под Linux
Ну и разные варианты Паскаль компиляторов тоже есть под KolibriOS, включая и Oberon.Tereshkov Автор
10.08.2019 15:00Слышал о ней. Милая вещица, как и её родной ассемблер FASM. Но уход от DOS мне был нужен не ради самого ухода, а ради того, чтобы хоть кому-то можно было показать работу откомпилированных программ. А KolibriOS пришлось бы прикладывать как довесок к каждой программе :)
sbnur
10.08.2019 14:59Видно вы увлечены языком Паскаль, а в чем по вашему мнению достоинства этого языка по сравнению с другими — скажем из первой десятки популярности (на выбор)
Tereshkov Автор
10.08.2019 15:38+12Совершенно верно, увлечён. Считаю, что это очень гармонично спроектированный, лёгкий для чтения и удобный для компиляции язык. Да, у раннего Паскаля было слишком много ограничений по сравнению с C, о чём красноречиво писал Керниган. Из-за этого, видимо, C сразу получил фору.
Однако Паскаль, как и всякий язык, требовалось развивать. Компания Borland внесла здесь огромный вклад, и уже в ранних Turbo Pascal все недостатки, описанные Керниганом, были устранены. Но, фактически став детищем Borland, Паскаль вместе с этой корпорацией и умер (увы, судьба языков слишком зависит от судьбы корпораций).
В результате чуть не половина языков из «первой десятки» тащат за собой наследие C, пытаясь с ним совладать. Вот, нам мой взгляд, недостатки C, которые так или иначе просвечивают и в его потомках и которые совершенно чужды Паскалю:
- Отсутствие ключевых слов, обозначающих вид объявляемого идентификатора:
function
,var
,type
и т.д. Из-за этого начинается нездоровая игра со скобками, а читаемость программы резко ухудшается. - Сильная зависимость от препроцессора. Без него невозможно подключить внешний модуль или задать подлинную константу, не занимающую памяти.
- Провалившаяся попытка отождествить массивы и указатели. Насколько я понимаю, сначала разработчики языка надеялись на полное отождествление. Однако как только пришлось допустить массивы в качестве полей структуры, тождество стало фикцией. В результате для двумерного массива
A
все величиныA
,&A
,*A
,A[0]
,&A[0]
равны друг другу. Не абсурд ли это? - Неудобный оператор
switch
. Это вынужден был признать ещё Керниган. - Параллельное существование у структур «имени типа» и «тега типа». Как такое могло появиться, я вообще не представляю.
Я понимаю, что от многих этих недостатков пытаются уйти — если не в C++, то в каком-нибудь Go. Однако здесь мне остаётся присоединиться к одному меткому комментарию на Хабре:
Не понимаю, зачем индустрия хоронит Паскаль, а потом много раз изобретает его заново.
gecube
10.08.2019 16:13+8в С++ от этих недостатков не ушли, а добавили новые, усложнив при этом и так не простой процесс компиляции кода. Неудивительно, что современный тренд — делать языки с максимально простым синтаксисом и простейшим компилятор. Тем более в тренде автогенерации кода
math_coder
10.08.2019 16:24+2У C-подобных языков есть большое преимущество: возможность объявлять переменную там, где она нужна и тогда, когда она нужна. В паскале-подобных языках приходится или заранее, до написания собственно кода, придумывать, что написать в
var
блоке, либо бегать туда-сюда при написании кода, либо заполнятьvar
блок когда функция уже написана. Ни один из вариантов не похож на заботу о программисте.kisskin
10.08.2019 16:28+7У Паскаля есть большое преимущество — программиста учат думать перед тем, как что-то написать. А необьявление переменных было в бейсике)
DoctorMoriarty
11.08.2019 10:17+3>преимущество — программиста учат думать перед тем, как что-то написать
Не более, чем для любого другого языка программирования.
math_coder
11.08.2019 12:23программиста учат думать перед тем, как что-то написать
Да, возможно, что для обучения это хорошо. Но это для обучения. В промышленном же программировании удобнее, когда можно писать не думая.
gecube
11.08.2019 14:30Што-а? А потом мы имеем глючные, медленные программы.
Потому что думать нужно всегда
Но соглашусь в том, что когда язык именно, что вставляет палки в колеса разработки, заставляя отвлекаться от самой функциональной нагрузки кода — это очень плохо
andrey_ssh
13.08.2019 12:31В промышленном же программировании удобнее, когда можно писать не думая.
Истинно.
(утверждение прямо в стиле Черномырдина)
DrPass
11.08.2019 16:37+1Как по мне, основное преимущество Паскаля в том, что простую программу на Паскале можно объяснить падавану «в лоб» строчка за строчкой, не оставляя «на потом» непонятных сущностей. С другими языками такое обычно не проходит, «тут у нас импортируются библиотеки, это мы рассмотрим позже, пока просто запомните это», «void указывает, что функция не возвращает никакого значения, что такое функция и возвращаемые значения, мы рассмотрим позже, пока просто запомните это» и так далее.
TonyLorencio
10.08.2019 16:34+6У C-подобных языков есть большое преимущество: возможность объявлять переменную там, где она нужна и тогда, когда она нужна
Да, но до C99 в C (например, C89) ситуация с объявлением переменных тоже была почти как в Паскале. За тем исключением, что:
1) можно было объявлять переменные не только в начале функции, но и в начале блока (scope), глобальные переменные в расчёт не берём
2) не было необходимости в отдельном var-блоке
Из-за этого в C89 даже привычную сейчас декларацию переменную цикла for приходилось объявлять заранее, а не внутри for-конструкции.
Tereshkov Автор
10.08.2019 16:40+5Да, это так. Однако в первых вариантах C такой возможности не было, она появилась позже. Ничто не мешает развивать в том же направлении Паскаль. Есть хороший пример Паскаля, в котором переменные объявляются именно там, где они нужны.
lgorSL
10.08.2019 17:14Это уже будет не совсем паскаль. Если начать добавлять в него подобные нюансы, то они потянут за собой другие изменения и в итоге получится что-то сильно другое.
- допустим, мы разрешили объявлять переменные в произвольных местах.
- Было бы логично добавить автовывод типов, чтобы не писать лишнего конструкции вида
var a := 2
- можно прикрутить константы (напимер,
val a := 2
), чтобы код стал более строгим. - тогда захочется, чтобы конструкции типа if возвращали выражения, чтобы было удобно инициализировать константы:
val sign := if (a>=0) then 1 else 0 end
В итоге получается сильно другой язык с более сложным компилятором. Мне кажется, уж если менять, то всё сразу.
Если остановиться на пол-пути и сделать только первое изменение, то возможность легко разложить переменные функции по регистрам/стеку пропадает (придётся анализировать все возможные пути исполнения, включая использование goto), а сам язык не станет сильно лучше.
Tereshkov Автор
10.08.2019 17:38+4Возможно, все эти нововведения действительно нужны. Когда заходит речь о конкуренции в сфере профессионального программирования, приходится, видимо, поступаться простотой компилятора. Иначе, не имея всего перечисленного вами, Паскаль навсегда останется слабее С++ или чего-то подобного. Будет ли ваш язык «не совсем» Паскалем? Не знаю. Однако Бейсик до сих пор жив только потому, что он «не совсем» прежний Бейсик. Да даже и Delphi — это «не совсем» Паскаль от Никлауса Вирта. Язык должен эволюционировать — иначе он умирает. Паскаль, увы, оказался не в самых заботливых руках после смерти Borland.
gecube
10.08.2019 18:43-2Язык и эволюция? Это такой же миф как и апгрейд.
Может просто стоит выкидывать каждый старый язык и писать ещё один лучший новый, на основе новых достижений и ошибок прошлого?
Иначе мы имеем кейс с++, когда нужно тянуть ворох всей обратной совместимости с классическим Си и его модификациями, так и предыдущими ревизиями С++. Это усложняет и удорожает разработку компилятора, но кого это волнует? А вот то, что это удорожает разработку на этом языке, т.к. постоянно нужно допиливать код и обходить "ловушки", которые появляются из-за разных версий стандарта. Ну, что ж.
И, действительно, если б все было так хорошо с эволюцией, то не появились бы golang, d, rust. Хотя и они менее популярны, как кресты, по крайне мере сейчас
ignorance
10.08.2019 21:26Эволюцией Паскаля были Модула и Оберон. К сожалению, по некоторым причинам, они не получили широкого распространения в 90-е и 2000-е. А сейчас уже поздно.
FLABER
10.08.2019 17:58+4Последние версии Delphi уже умеют делать inline variable declaration
AWSVladimir
11.08.2019 15:14Да да, сишная хрень теперь и в паскале.
begin begin var a:=123; Writeln(a); end; begin var a:=123.456; Writeln(a); end; begin var a:=now; Writeln(a); end; begin var a:='Marasmus C in Pascal'; Writeln(a); end; readln; end;
Ждем реализации сишных макросов, обязательной препроцессорной обработки и регистро-зависимого объявления переменных.
Брр-р-р-р, шикарный язык уродуют чудаки на букву м (с)Maccimo
12.08.2019 03:16Ждем реализации сишных макросов, обязательной препроцессорной обработки и регистро-зависимого объявления переменных.
Зачем ждать это в паскале, если прямой сейчас можно перейти на C++ и получить все эти преимущества немедленно?
AWSVladimir
12.08.2019 16:00О том и речь. что идет переход от паскаля к недо-си, все это при наличии собственного Билдера.
Maccimo
12.08.2019 23:27Предлагаете вообще ничего в язык не добавлять?
Это популярности не прибавит, а с ней и так не очень хорошо.
vvmtutby
10.08.2019 20:01+2возможность объявлять переменную там, где она нужна и тогда, когда она нужна в PASCAL-подобных языках
Пожалуйста:
В Modula-3 ( ...).Однако, локальная область определения переменной цикла в данном случае вполне соответствует «духу» языка (поскольку возможны прочие локальные области в виде вложенных блоков «VAR… BEGIN… END» для поддержки «горячего определения переменных в коде», в т.ч. с возможностью вывода типа, но оставаясь в рамках «структуризации по-Паскалевски»).
Практика использования вышеупомянутого в Modula-3:
caltech-parser\cit_util\src\SIsuffix.m3
FOR i := FIRST(List) TO LAST(List) DO VAR x : BOOLEAN; BEGIN x := tbl.put(List[i].char, List[i]); <* ASSERT NOT x *> END END
m3-libs\bitvector\test\src\TestBitVector.m3
FOR i := 0 TO Max - 1 BY 7 DO FOR j := 1 TO 6 DO VAR set := bv2.set(i+j); BEGIN <* ASSERT NOT set *> END END END;
Аналогично — в ADA
Siemargl
10.08.2019 23:32+3Прямой наследник Паскаля и Оберона — Go (для меня это был сюрприз)
Когда доделаю очередную статью по надежности, будет чуть подробнее.nemavasi
11.08.2019 16:06да — один из разработчиков языка Go как раз шел от Oberon и Pascal Роберт Гризмер — Эволюция языка программирования Go
DoctorMoriarty
11.08.2019 10:16+1>нездоровая игра со скобками
Уж точно не более нездоровая, чем вечное «begin...end».Tereshkov Автор
11.08.2019 13:38Я имел в виду не фигурные скобки (они действительно ничем не хуже
begin
...end
), а круглые в предложениях видаvoid (*fun_ptr)(int) = &fun
. Их обилие напрямую связано с отсутствием ключевых слов для вида объявляемого идентификатора.DoctorMoriarty
11.08.2019 14:07-1Хм. Но что плохого в приведении типа? Да, у C-style cast есть проблема, при использовании следует быть внимательным и знать, что делаешь :-) Но есть и С++ со static_cast и прочими иными _cast.
Tereshkov Автор
11.08.2019 14:15А разве это приведение типа? Это «просто» объявление указателя на функцию.
DoctorMoriarty
11.08.2019 15:18В вашем примере — да, объявление указателя. Простое, без кавычек, а также логичное: скобки имеют тот же смысл что и в знакомой любому программисту школьной алгебре — устанавливают приоритет.
Tereshkov Автор
11.08.2019 15:32+1Мне кажется, одно только то, что вы сами приняли эту запись за приведение типа, говорит против такого синтаксиса. И разве это логично — оставлять за скобками список аргументов и тип возвращаемого результата? Ведь это часть определения функции, на которую объявляется указатель. Так что скобки здесь только нарушают естественный порядок действий: 1) задать сигнатуру функции; 2) задать указатель на неё.
А стоит только появитьсяtypedef
в начале, такfun_ptr
, стоящий в середине, вдруг окажется именем типа. И это тоже логично?DoctorMoriarty
11.08.2019 16:47>вы сами приняли эту запись за приведение типа
Нет, независимо от вашего конкретного примера я всего лишь подумал о часто встречающемся использовании круглых скобок помимо математических операций и перечисления аргументов.
>И разве это логично
Вы всего лишь привыкли к паскалевскому синтаксису и субъективно, т.е. для вас, непривычный синтаксис в C/C++ объявляете объективно нелогичным :-)netch80
12.08.2019 20:20+1Я за много лет привык к сишному синтаксису и тем не менее считаю его категорически неудобным. Логичным или нет — сказать сложно — у каждого варианта своя логика, но та, что в C, сильно сложнее читается, чем стиль паскаля/Go/etc., и с усложнением конструкции сложность её чтения растёт экспоненциально.
safari2012
12.08.2019 16:19Я помню, в универе, в те времена, когда существовало понятие «машинного времени», одна и та же (по сути) программа компилировалась на Турбо Паскале с считанные секунды, а на Турбо Си десятки секунд. Сложный проект на Паскале компилировался десятки секунд, а такой же на на Си можно было сходить покурить…
- Отсутствие ключевых слов, обозначающих вид объявляемого идентификатора:
kisskin
10.08.2019 16:31+5Скорость компиляции в Дельфи) у меня может быть проект на 500 тыщ строк и я могу компилировать после любого изменения и через 1сек видеть результат
ru_vlad
10.08.2019 15:58+4Считаю, что это очень гармонично спроектированный, лёгкий для чтения и удобный для компиляции язык.
Золотые слова!
А еще наиболее удобный (с мой точки зрения) для начального обучения.Tereshkov Автор
10.08.2019 17:19+1Согласен. В обучении программированию первенство Паскаля может оспорить разве что Питон. Но здесь есть коварный вопрос: стоит ли сразу ставить ученика перед необходимостью объявлять переменные? Мой опыт программирования начинался с Бейсика, и переход к языкам с явной статической типизацией был мучителен. Есть риск, что с тем же столкнутся и взращенные на Питоне. К тому же, Питон местами совсем не очевиден. Как объяснить начинающему хитрости передачи аргументов в функцию? Как задать большой двумерный массив?
gecube
10.08.2019 18:46+2Проблема питона в нескольких вещах
- Конкуренция между питон 2. и питон 3. Если б своевременно закопали второй, то мы бы быстрее получили нормальный третий
- Отсутствие четкости в тех же типах переменных. Это приводит к тому, что быстро наговнякать скрипт или какой-то маленький проект на питоне действительно очень легко. Но создать большое и надёжное продакшн решени тяжело. Я не говорю, что невозможно, это решаемая проблема, но на какой-нибудь джаве это будет проще, т.к. она больше страхует разраба от него самого. Но ценой своеобразного тулинга и большей многословности кода
Zenitchik
12.08.2019 15:58Мой опыт программирования начинался с Бейсика, и переход к языкам с явной статической типизацией был мучителен.
А у меня — не был. В Бейсике очень быстро привыкаешь объявлять все переменные в начале процедуры, это просто хороший тон.
Меня настораживает такое явление: Те люди, кто не стал программистами, Паскаль — забыли как страшный сон, а Бейсик — помнят и иногда используют для прикладных расчётов. Те же, кто стал программистами — просто знают несколько языков, и совсем неважно, с которого они начинали.vvmtutby
12.08.2019 17:46Мы говорим о Qiuck Basic и наследниках, где уже «всё как у людей» — IF / While, строки без нумерации?
P.S. В январе Вы критиковали один из Basic-ов:
где из переходов только GOSUB/GOTO и из циклов только FOR — то это может быть слишком оторвано от современности.
И чё? На нём же не проект писать, а чисто с алгоритмами экспериментировать.
Для чего-то более серьёзного — переходить на более актуальные языки.
Zenitchik
12.08.2019 18:14Мы говорим о Qiuck Basic и наследниках
Естественно.
P.S. В январе Вы критиковали один из Basic-ов:
Критиковал тот, кого я процитировал. И речь шла о каком-то очень старом бейсике.
Кроме того, в своей защите этого языка, я несколько осторожничал. Сами знаете, чем чревато.
JackKatch
10.08.2019 17:23+1В 50-60 годы, происходил бурный рост программирования как научной дисциплины. К сожалению авторы языка Си, осознано или нет, выбросили все наработки мирового сообщества в мусорное ведро. Их сложно за это винить (тем более что сам Вирт не осмелился убрать GOTO из Паскаля), они хотели иметь Unix и написали себе компилятор так как могли, хотели, считали нужным. К сожалению Си распространился на столько, что теперь мы имеем бесконечные обновления безопасности программ, глюки, зависания. Невозможно найти качественного программного обеспечения (это не всё из-за Си, но львиная доля). Создание всевозможных «костылей» принципиально ни чего не решает. И многих, к сожалению, такое положение вещей устраивает. PS. Просто о Си заговорили. Автору спасибо за статью.
Tereshkov Автор
10.08.2019 17:46+2Думаю, не стоит винить C во всех бедах отрасли. В нём, безусловно, были прогрессивные черты (типа операции
+=
или возможности передать в функцию массив произвольной длины, чего не было в Паскале). Однако C вырос из бестипового B, и отпечатки этого, к сожалению, видны до сих пор.gecube
10.08.2019 18:47-1История явно движется по кругу, ибо мы вернулись к "бестиповым" языкам — а точнее — языкам с автовыводом типов переменных (щтоа?) и утиной типизацией
Tereshkov Автор
10.08.2019 22:00+1Вряд ли по кругу, в лучшем случае — по спирали. Язык B был бестиповым в абсолютном смысле: любая переменная была целочисленным «машинным словом», и более ничем. Язык был как-то пригоден только для системного программирования. Никакие численные расчёты были на нём невозможны.
gecube
11.08.2019 14:32Не лукавьте. Расчеты были возможны, но количество служебного кода… Зашкалило бы. Поэтому разработка вряд ли была бы эффективной или надёжной.
Насчёт спирали — да, можно и так назвать. Это не столь принципиально.Tereshkov Автор
11.08.2019 14:39Ну да, на чистой машине Тьюринга принципиально тоже можно вести расчёты с плавающей точкой, если вы об этом.
DrPass
11.08.2019 16:45Ну так, минуточку, абсолютно все языки программирования в своих прямых «математических» возможностях ограничены возможностями процессоров. Просто сейчас там есть FPU с 80-битными вещественными числами, матричные вычисления и целочисленная арифметика от 8 до 64 бит. А процессор PDP-11 ничего кроме этих 16-битных целых и не умел переваривать. В этом плане возможности тогдашнего С ничем не превосходили возможности В.
Все остальные вещи, будь-то математика, строковые операции и т.д., выполнялись с помощью внешних библиотек. Возможность использовать библиотеки в В имелась, возможность форматировать вывод в нужном виде также была. Поэтому он вполне себе подходил для повседневных задач начала 1970-х.
DoctorMoriarty
11.08.2019 10:31+3>Си распространился на столько, что теперь мы имеем бесконечные обновления безопасности программ, глюки, зависания.
(#sarcasm_mode_on) Но конечно же, если бы основная масса софта писалась на б-жественном Паскале, не было бы ни обновлений безопасности, ни глюков, ни зависаний.(#sarcasm_mode_off)
На самом деле — все указанные проблемы связаны с ростом спроса на софт и снижением качества софта в угоду скорости выпуска софта на рынок. Издержки массового производства. И использование «серебряной пилюли» в виде «единственно правильного ЯП, который точно принес бы счастье, но про него незаслуженно забыли» не решает эту проблему.
gatoazul
11.08.2019 11:20С очень много позаимствовал из Алгол-68, который был очень даже мощной наработкой всего мирового сообщества
DrMefistO
10.08.2019 18:27+3Как человек, который все старшие классы, всё студенчество, и пару лет после кодил исключительно на Паскале/Делфи, переписывая нужный мне сишный код на свой любимый язык, но потом всё же перешедший к изучению (нормальных) языков из топ-десятки, могу сказать, что пытаться оживить труп смысла совершенно нет, кроме как для себя. Работодателей — пара штук всего.
Язык операционных систем — С/С++, и пишем мы всё под операционные системы, ну или под железо, у которого вендорский код либо сишный, опять же, либо азм.
Язык для обучения в школе? Имея на плечах опыт кодинга на разных языках, вижу идеальными языки Го и Питон, т.к. на них проще всего уделять больше времени логике, нежели работе с синтаксисом языка.
Очевидный плюс Дельфы: непревзойдённый пока редактор графического интерфейса, да. И именно он меня привлекал когда-то. Но это не так страшно:)Tereshkov Автор
10.08.2019 22:33+2Увы, со многим вынужден согласиться. Однако вы говорите скорее о состоянии индустрии, чем о достоинствах языков как таковых. Я не думаю, что индустрия несёт высшую справедливость и всегда воздаёт языкам по их достоинствам. Тот же Паскаль сам по себе не имеет никаких неисправимых пороков, которые помешали бы ему при должном развитии сравняться по мощи с современным С++. Но для этого была бы нужна воля корпораций — не только сейчас, но и в прошлом. Стоило Паскалю в 80-х годах чуть опоздать с возможностями системного программирования, и вот уже все ключевые операционные системы написаны на C, а Паскаль вынужден вечно догонять.
DoctorMoriarty
11.08.2019 10:33>а Паскаль вынужден вечно догонять
Тогда зачем заниматься некромантией, ну, кроме разве что just for fun? Все равно масштабного использования результатов, «Паскаль-экосистемы», которая бы имела шансы на конкуренцию с существующими, уже не будет.
Londoner
10.08.2019 19:28Вопрос в тему, почему Паскаль так быстро вышел из моды? Неплохой же был язык по меркам своего времени.
gecube
10.08.2019 19:39+2Потому что по сути его поддерживал только один вендор — Борланд.
Или хотите дальше копнуть?Londoner
10.08.2019 19:43+2Ну, а сколько вендоров изначально поддерживали, например, Джаву или C#. И да, хочу копнуть дальше.
blackstrip
10.08.2019 22:30+7Майкрософт переманили из Борланда вот этого товарища ru.wikipedia.org/wiki/%D0%A5%D0%B5%D0%B9%D0%BB%D1%81%D0%B1%D0%B5%D1%80%D0%B3,_%D0%90%D0%BD%D0%B4%D0%B5%D1%80%D1%81, и он перестал делать Delphi и вместо этого сделал С#
netch80
11.08.2019 08:20+3Я считаю, это потому, что от Паскаля отвернулась основная масса хакеров (в реймондовском смысле — то есть, копателей на самые глубины).
Паскаль очень активно продвигался в 80-е: был Паскаль от Apple (и у них была первая объектная реализация — Borland уже копировал её идеи, хотя они все следовали за ранним C++), был от Microsoft (и очень неплохой, и совместимый с Borland по многим фичам — это редко упоминают, что они синхронизировались).
Но всё это сорвалось: он перестал быть привлекательным для тех, кто оказался тут ключевым. Я писал несколько раз его проблемы, как их вижу по результату обучения других — и мне кажется, что именно подходы типа «вам это не позволено» стали главными в его падении.
Ряд свойств паскалевского стиля был бы крайне полезен и сейчас — например, порядок слов в объявлениях. Но это надо ждать следующей волны языков…
tormozedison
10.08.2019 21:07Скачал компилятор для DOS, прочитал документацию по диагонали. Возможностей работы со звуком не обнаружил. Они есть? Спасибо.
Tereshkov Автор
10.08.2019 21:50+2А какие возможности вы хотели бы видеть? Если мне не изменяет память, в Turbo Pascal была только процедура
Sound
, управляющая PC Speaker'ом. Если вам нужен именно он, то можете управлять им напрямую путём записи в системные порты. Для этого в XD Pascal, как и в Turbo Pascal, есть процедураOutP
.
LAutour
10.08.2019 22:03+1А для микроконтроллеров компилятор паскаля писать не пробовали? Там с Виртовскими языками страшный дефицит.
Tereshkov Автор
10.08.2019 22:13Нет, не пробовал. Знаю только, что мой компилятор для DOS какие-то поляки портировали на Atari с процессором 6502. Сам немножко работал с Паскалем для 8051, но быстро забросил. Если уж такой компилятор и делать, то уже не игрушечный, а тогда придётся использовать LEX/YACC/ANTLR/LLVM и т.д.
math_coder
10.08.2019 23:25+2LLVM — да, а вот насчёт LEXX/YACC… Насколько я знаю (но это больше с чужих слов), как раз в неигрушечных компиляторах они, как правило, и не используются.
Они удобны — и мой небольшой опыт это подтверждает — 1) когда невысоки требования к обработке ошибок и вообще работе с невалидным исходным кодом, 2) когда язык только разрабатывается и нужно иметь возможность многократно и кардинально переделывать грамматику.
А при написании полноценного компилятора, с одной стороны, они не особо и помогают (если использовать LL-парсер — он легко пишется руками), а с другой создают лишние проблемы (отдельный язык, влекущий проблему сопряжения с ним; пляски когда надо сделать что-то хоть чуть-чуть нестандартное).
slonopotamus
11.08.2019 01:21Плюс использования lexx/yacc в том что грамматика вашего языка будет LALR(1), а не ад типа Perl 5, который вообще undecidable.
math_coder
11.08.2019 12:45Это важно, когда вы придумываете язык. Когда грамматика уже есть как данность, это работает в обратную сторону: если в грамматике что-то не ложиться хорошо в LALR(1), вам будет сложно это сделать. Отход от LALR(1) — это проблема не техническая, и должна решаться не техническими средствами.
phantom-code
11.08.2019 13:29Walter Bright, создатель первого полноценного компилятора C++ и автор языка D писал, что
There are a few things I (perhaps surprisingly) suggest should not be considerations… Easy parsing. It isn’t hard to write parsers with arbitrary lookahead. The looks of the language shouldn’t be compromised to save a few lines of code in the parser. Remember, you’ll spend a lot of time staring at the code. That comes first.
…Somewhat more controversial, I wouldn’t bother wasting time with lexer or parser generators… They’re a waste of time. Writing a lexer and parser is a tiny percentage of the job of writing a compiler.
Мой очень скромный опыт тоже подтверждает эту мысль. Написание LL-парсера вручную не является особой проблемой. Гораздо больше сил уходит на все остальное.
impwx
10.08.2019 22:55-3Я уважаю ваш труд и считаю проект интересным, но:
Если повезёт, XD Pascal будет внедрён, наряду с BeRo Tiny Pascal, в лабораторный практикум по курсу конструирования компиляторов в МГТУ им. Н.Э. Баумана.
Это очень плохая идея. Хорошее методическое пособие должно быть простым и прививать правильные подходы. Вы же развивали компилятор, исходя из личной любви к Паскалю, и сделали несколько очень спорных дизайн-решений, если не сказать костылей, из-за которых проект нельзя использовать как пример для подражания:при каждом проходе заново повторяются все этапы компиляции, начиная с лексического разбора.
Вычисление любых выражений в XD Pascal строится так, что все промежуточные результаты имеют длину 32 бита и сохраняются в стеке. Для строк и множеств Паскаля этот подход неприемлем.
Для самых употребительных процедур, в том числе Read и Write, я сделал исключение и реализовал их нетипичными для грамматики языка
Возьмите лучше специально разработанный для этого минимальный язык\компилятор, типа ChibiCC.Tereshkov Автор
10.08.2019 23:23+5Коллега, я призываю не торопиться с выводами. На той кафедре, о которой идёт речь (а это вовсе не моя родная кафедра и курс веду не я), сейчас для некоторых лабораторных работ используются компиляторы Паскаля P5 и BeRo Tiny Pascal. Речь там вовсе не идёт о каких-то образцах для подражания. Студентам предлагается всего лишь вносить точечные изменения в готовый компилятор и наблюдать их эффект после «раскрутки» (bootstrap'а). Кто-то из студентов на курсовой может заняться и более масштабной задачей (например, была работа по портированию BeRo на Linux). Подробнее об этом можно прочесть тут. Там есть и примеры заданий. Если вы видите какие-либо недостатки XD Pascal в сравнении с BeRo, я с интересом вас выслушаю.
Вы отчего-то не задались вопросом, как именно построен учебный процесс и как именно предполагается применять компилятор, и почему-то спешите предложить компилятор C вместо Паскаля и, кажется, даже без самокомпиляции. Далее, вы неизвестно почему видите недостаток в том, что «для самых употребительных процедур, в том числеRead
иWrite
, я сделал исключение и реализовал их нетипичными для грамматики языка». Однако эти процедуры в Паскале действительно нетипичны для грамматики (принимают любое количество любых аргументов) и действительно требуют реализации как особого случая, что я и сделал. Упрекать же за отсутствие поддержки множеств в любительском компиляторе, ей-богу, чрезмерно.
В общем, извините, я не принимаю ваших доводов.math_coder
10.08.2019 23:38Однако эти процедуры в Паскале действительно нетипичны для грамматики (принимают любое количество любых аргументов)
Иэ это, кстати, очень уродующая язык штука. Я в своё время из-за этого на С и перешёл: пусть
printf
и сложен и неудобен, но зато всё честно, а это важно для душевного здоровья программиста.Tereshkov Автор
10.08.2019 23:46+1Соглашусь, в этом есть какое-то коварство. И если бы дело ограничивалось только
Read
иWrite
, было бы не так тяжко. Но таких процедур оказываются десятки. Конечно, хотелось бы видеть грамматику Паскаля сразу достаточно гибкой для того, чтобы списки аргументовRead
иWrite
укладывались в эту грамматику и не торчали бы неудобными исключениями.Siemargl
11.08.2019 00:10Но зачем?
К сожалению, в языках вечно чего то не хватает, и создатели добавляют то атрибуты то трейты…
А когда эта магия добавлена, то язык начинает расплываться от спецификаций.
Kemet
11.08.2019 09:08Не, здесь всё нормально — паскаль ( и его наследники ), это язык со статической типизацией. Для процедуры типизация определяется сигнатурой. Если разрешить на уровне языка определение процедур с произвольным количеством формальных параметров ( да ещё и с произвольными типами аргументов ). В этом случае вся статика летит к чертям. Поэтому они и реализованы как не языковые средства. Впрочем, частично это можно решить с помощью дженериков, открытых массивов ( вариантов или указаателей )).
math_coder
11.08.2019 13:01В этом случае вся статика летит к чертям.
Не обязательно. Просто решение получается сложным, даже монструозным.
Но это не значит, что так как сделано в Паскале — это единственный выход. Есть и другой: не иметь функций с переменным числом параметров ни в каком виде, а проблему удобного ввода-вывода решать иначе. Так сделано в Rust (ипользуются макросы), так сделано в современном C++ (используются темплейты). И решение из C# также может быть доведено до статического-типизированного (
params IFormattable[] args
вместоparams object[] args
), хотя оверхед из-за динамической диспатчеризации останется.
vvmtutby
12.08.2019 18:03Если разрешить на уровне языка определение процедур с произвольным количеством формальных параметров ( да ещё и с произвольными типами аргументов ). В этом случае вся статика летит к чертям.
Зачем же с абсолютно произвольными?
Objective Modula-2
Calling a Variadic Procedure
Index arguments and value terminating arguments are never supplied in the actual parameter
list of a variadic procedure call. The compiler automatically determines and
inserts these arguments into the procedure’s activation record.
Listing 16: variadic procedure calls omit index and value terminating arguments(* calling procedure Variadic from Listing 14 *) Variadic( (* counter omitted *) foo1, bar2, foo2, bar2 ); (* calling procedure Variadic from Listing 15 *) Variadic( foo1, bar1, foo2, bar2 (* -1 omitted *) );
Variadic Procedures with Counter Terminated Variadic Parameter Lists
The number of variadic parameters of a counter terminated variadic parameter list is
automatically passed as a hidden parameter immediately before the variadic list.
Listing 14: procedure with a counter terminated variadic parameter listPROCEDURE Variadic(v : VARIADIC OF (p1 : Bar; p2 : Baz)); BEGIN (* iterate over all variadic tuples by index *) FOR n OF CARDINAL := 0 to HIGH(v) DO DoSomethingWithBar(v[n].p1); DoSomethingWithBaz(v[n].p2); END; (* FOR *) END Variadic;
Variadic Procedures with Value Terminated Variadic Parameter Lists
A value terminated variadic parameter list is terminated by a given constant value.
Listing 15: procedure with a value terminated variadic parameter listPROCEDURE Variadic(v : VARIADIC [-1] OF (p1 : INT; p2 : Baz)); BEGIN (* v points to first variadic tuple *) WHILE v # NIL DO (* while there are tuples *) DoSomethingWithINT(v^.p1); DoSomethingWithBaz(v^.p2); v := NEXTV(v); (* next variadic tuple *) END; (* WHILE *) END Variadic;
В Modula-2 R10 ( в варианте M2Sharp) чуть менее наглядно:
( newVector и PrintList — разные функции; не стал исправлять, привёл 1 к 1, как в первоисточнике )
Variadic Parameters
The extended dialect supports the ARGLIST attribute in formal types and formal parameters. Parameters marked with the ARGLIST attribute may be passed a variable number of arguments.
PROCEDURE newVector ( values : ARGLIST OF REAL ) : Vector; VAR v1, v2, v3 : Vector; v1 := newVector(1.2, 3.4); (* two arguments *) v2 := newVector(1.2, 3.4, 5.6); (* three arguments *) v3 := newVector(1.2, 3.4, 5.6, 7.8); (* four arguments *)
Within the procedure or function, the argument count may be obtained using built-in function COUNT and the arguments are addressable using array subscript notation.
PROCEDURE PrintList ( values : ARGLIST OF REAL ); VAR index : CARDINAL; BEGIN FOR index := 0 TO COUNT(values)-1 DO WriteReal(values[index]); WriteLn END END PrintList;
Kemet
14.08.2019 11:26Зачем же с абсолютно произвольными?
В начале обсуждения об этом написано.
Вот, например, в Активном Обероне есть встроенная процедура TRACE, которая принимает любое количество параметров любых типов, и распечатывает их в системный лог ).
TRACE( «Hello», 123, 1.5, NIL );
Она реализована в компиляторе, потому что на уровне языка такое не определишь.
Если все параметры одного типа, то и изобретать особо ничего не нужно — достаточно открытых массивов и массивов-литералов.
Например:
PROCEDURE P( CONST args: ARRAY [ * ] OF HUGEINT );
и вызов
P( [5, 78, 3758, 383489, 585959, 5785759 ] );
можно даже определить так:
PROCEDURE P( CONST args: ARRAY [ * ] );
И тогда процедура будет принимать массивы с любым типом элементов, но это не решит проблему разнородности типов аргументов, которая, к примеру, в Delphi решается с помощью массива вариантов.
timofeevka
11.08.2019 01:46Я когда-то писал для нашего программного комплекса (ПК МВТУ-3 и МВТУ-4 (SimInTech)) свой встроенный язык — что-то среднее между Pascal и Matlab, думал тогда сделать его с JIT-компиляцией, но обошёлся тогда псевдокодом, сейчас вот думаю может допилить генерацию машинного кода для скорости ибо программа активно используется во многих местах и скриптов там написано очень много. Конкретно мне было бы интересно обеспечить оптимизированную генерацию кода для исполнения в оперативной памяти для скалярных математических выражений без лишних вызовов подпрограмм как минимум на x86 архитектуре. Сам программный комплекс у нас на Delphi написан исторически. Вы не пробовали сделать на базе вашего компилятора встраиваемую скриптовую машину с компиляцией в оперативную память? Это могло бы быть интересным.
Tereshkov Автор
11.08.2019 01:56Задача, о которой вы говорите, всё-таки очень специфична. Я всегда ориентировался только на классические исполняемые файлы.
DmitrySpb79
11.08.2019 12:16Не знаю, чего народ в этом Паскале нашел. Писал на нем еще в школе потом немного в институте, громоздкий в плане синтаксиса и устаревший язык, одни begin-end чего стоят. На С++ в свое время перешел с удовольствием. Сейчас никакой ностальгии и желания писать на нем снова не испытываю.
Если повезёт, XD Pascal будет внедрён, наряду с BeRo Tiny Pascal, в лабораторный практикум по курсу конструирования компиляторов в МГТУ им. Н.Э. Баумана.
Риторический вопрос, почему российское образование всегда оторвано от реальной жизни…
Нет бы на «конструировании компиляторов» какую-нибудь кодогенерацию для реально работающих микроконтроллеров или плис рассмотреть, нет, нужно что-то обязательно архаичное и нежизнеспособное :) Специально чтобы каждый студент чувствовал, что нигде кроме пар ему это в жизни не пригодится.Tereshkov Автор
11.08.2019 14:34+1Свои аргументы в пользу Паскаля и против C я высказал выше. Это, конечно, сугубо академический разговор о самих языках, а не об их востребованности в индустрии. «Громоздкость» языка оборачивается предельной лёгкостью чтения на нём, что немаловажно, если вспомнить завет Гвидо ван Россума: «Code is read much more often than it is written». «Устарелость» языка — это следствие, а не причина его ухода из индустрии.
Что касается образования, то курс вовсе не ограничивается упражнениями с простенькими компиляторами Паскаля. Вы же не станете утверждать, что нужно перестать преподавать черчение на бумаге, взятие интегралов на бумаге, расчёт прочности балок на бумаге и т.п. лишь потому, что это нечто «архаичное и нежизнеспособное»?DmitrySpb79
11.08.2019 14:40Я к тому, что студенты прекрасно понимают и чувствуют, когда им дают что-то, что никогда не пригодится в реальной жизни. И на мотивацию к изучению это отлично влияет. Можно же найти что-то для учебных примеров, что реально используется в продакшене прямо сейчас, а не нечто из прошлого века…
Tereshkov Автор
11.08.2019 14:47Найти в production'е самокомпилируемый компилятор, который студенты на первой же лабе смогут изучить, модифицировать, «раскрутить» и посмотреть эффект? Не очень понимаю, что это могло бы быть.
DmitrySpb79
11.08.2019 14:58А требование самокомпиляции и генерации exe является критичным? Так-то, можно хоть Lua для Ардуино компилировать, вполне пригодится для задачи домашней автоматизации.
Tereshkov Автор
11.08.2019 15:03Насколько я понимаю, принципиальной является «раскрутка» (bootstrap). Как бы то ни было, курс веду не я. Я просто предложил свой проект как альтернативу BeRo Tiny Pascal.
Kemet
14.08.2019 11:35+1Современная реинкарнация Project Oberon от Вирта. Там вообще весь стэк хелловордный — язык описания аппаратуры с компилятором, простейший процессор в ПЛИС, хелловордный яп Оберон-07, с таким же простейшим компилятором, хелловордная ОС Oberon-V. Ну и есть эмуляторы Виртовского процессора на Си, Дельфи, Обероне, Питоне. Впрочем, есть модификации компилятора и под реальное железо. Ну и в придачу книжки «Project Oberon The Design of an Operating System, a Compiler, and a Computer» и «Compiler Construction».
MSC6502
11.08.2019 20:04Всегда поражала дуальность мышления программирующих на С. С одной стороны, С декларирован как высокоуровневый язык, он абстрагирует программиста от особенностей конкретного процессора и велит работать со структурами, предоставляемыми языком.
С другой стороны, это адское желание непосредственно работать с памятью и переключать битики в регистрах ввода/вывода однозначно не даёт С права называться языком высокого уровня. Уж пора как-то определиться и разграничить.
И конечно синтаксис С получился таким исключительно из-за механических телетайпов, на которых работали Керниган и Co. Чтобы сократить шум, производимый при печати исходных текстов unix-a, а заодно поменьше отбивать себе пальцы, набивая тексты ядра, было решено отказаться от человеко-читаемого текста программ и использовать как можно больше сокращений, скобочек и условных непонятных значочков.gecube
11.08.2019 23:20Мне нравится, когда говорят о Си, как о виртуальной машине. Ибо действительно, если бы он однозначно компилировался в ассемблер, то не было возможности для UB. Получается, что на самом деле, он достаточно высокоуровневен, чтобы на нем нельзя было гарантировать последовательность исполнения и результат исполнения ЛЮБОЙ (не обязательно корректной!) программы на Си, и достаточно низкоуровневен, чтобы была возможность отстрелить себе руки и ноги из-за магии битиков.
Но вообще это мне не мешает любить этот язык, т.к. он реально прекрасен своей лаконичностью и своим распространением.
А, да, еще дичь в Си — это триграфы. Это к вопросу о скобочках и т.п., что иногда может тоже отсрелить обе ноги и обе руки.
perfect_genius
12.08.2019 21:19Возникала мысль переводить код в LLVM?
Tereshkov Автор
12.08.2019 21:25Возникала, и не раз. Вопрос в целях. Мне доставляло удовольствие знать каждый байт своего компилятора. Использование LLVM — это уже профессиональный размах. Но тогда проекту подобает иметь и профессиональный frontend. Да и нужен ли тогда Паскаль? Может быть, взять Clang? Так, шаг за шагом, от проекта ничего и не осталось бы :)
Zenitchik
12.08.2019 21:40Да и нужен ли тогда Паскаль?
Почему же? Пусть будет больше языков хороших и разных. С компиляцией в разные байткоды.
JackKatch
Приятно почитать, тем же страдаю. Только для языка Оберон. Кстати, писал одно программу на языке Модула-2, так вот исполняемый файл антивирусы тоже убивали.
Tereshkov Автор
Рад встретить союзника. Наработки где-нибудь выкладывали? Кстати, я присматривался и к Оберону, но смутило несколько обстоятельств: 1) он слишком мало распространён, для него не найти ни примеров, ни тестов, ни желающих в нём разбираться; 2) он слишком лаконичен (спасибо и за то, что вернули цикл FOR!); 3) ключевые слова непременно в верхнем регистре — ужасны.
JackKatch
Есть пара фрагментов в LinkedIn (для понтов), а так, зачем? У Вирта опубликованы исходники, кроме кодогенератора. Ну и, не хочется уподобится многим-многим проектам, так и не законченным. Будет результат, опубликую. У меня к Оберону, это уже наверное седьмой подход. Вирт настолько всё проработал, одним махом делает «семь» действий. У меня если больше двух — ступор. Тяжело было разбираться.
nemavasi
Через неделю программирования на Обероне не будете обращать внимание на заглавные буквы — поначалу те же ощущения были. Язык очень красив и быстро изучаем. Алгоритмы, по крайней мере, на нем пишутся удобно. Ну а для начального обучения вообще отличный выбор — позволяет быстро сосредоточиться на алгоритмах, а не на борьбе с языком. Жаль, что мало людей вообще пробовали на нем писать.