<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://pascalabc.net/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ulysses</id>
	<title>Вики проекта PascalABC.NET - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://pascalabc.net/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ulysses"/>
	<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Ulysses"/>
	<updated>2026-04-18T08:33:57Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.39.1</generator>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2730</id>
		<title>Начало. Возможные ошибки</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2730"/>
		<updated>2015-08-26T09:39:35Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Ошибка ввода */ + ReadInteger&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
=== Неописанная переменная ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  S := 1; // Неизвестное имя S&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Все используемые переменные должны быть предварительно описаны с помощью ключевого слова '''var''' (внутри блока begin/end или, что обычно хуже, в разделе описаний вначале программы).&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующая ; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer &lt;br /&gt;
  S := 1;         // Компилятор здесь скажет: Ожидалась ; — имеется ввиду предыдущая строка!&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1 &lt;br /&gt;
  S := S + 1  // Аналогично: проблема на предыдущей строке, а на текущей нет, потому что после неё идёт end.&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Очень частая ошибка у начинающих. Курсор, как правило, позиционируется '''в начале следующей строки'''.&lt;br /&gt;
&lt;br /&gt;
=== Несовместимость типов при присваивании ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer := 1.3; // Нельзя преобразовать тип real к integer&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Безболезненно можно присваивать только данные одного типа. Если данные - разных типов, то в редких случаях можно преобразовать данные одного типа в данные другого. Например, целое можно преобразовать в вещественное, а символ - в строку. Обратные преобразования не допускаются. &lt;br /&gt;
&lt;br /&gt;
Чтобы не ошибаться в подобных простых случаях в Паскале есть следующая '''краткая''' форма объявления и инициализации переменной:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1.3; // Тип real будет выведен компилятором автоматически&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Отсутствие закрывающего апострофа литеральной строки ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln('Результат сложения равен =, x + y); // Не хватает закрывающего апострофа&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Закрыть апостроф надо на той же строке, где расположен открывающий апостроф&lt;br /&gt;
&lt;br /&gt;
=== Ошибки расстановки запятых и апострофов при выводе строк и выражений ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln(x, '+,' y, '=', x+y); // Неверная расстановка запятых и апострофов&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Слишком много запятых и апострофов рядом, потому начинающие часто путаются :) &lt;br /&gt;
Надо уяснить правила: &lt;br /&gt;
*запятые разделяют разные элементы вывода&lt;br /&gt;
*все, что находится в апострофах, будет выведено на экран без изменений&lt;br /&gt;
&lt;br /&gt;
=== Ошибка ввода ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  read(x); // введите блаблабла и посмотрите, что получится&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Это - ошибка во время выполнения. Программа пытается преобразовать введенную строку в число, не может это сделать и завершается с ошибкой.&lt;br /&gt;
&lt;br /&gt;
Аналогичный фрагмент в более современном и предпочтительном синтаксисе:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := ReadInteger;&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ошибка неинициализированной переменной ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  // Забыли инициализировать или ввести x&lt;br /&gt;
  var r := x * x;  // r получает тип integer&lt;br /&gt;
  writeln('Квадрат числа ', x, ' = ', r);&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Перед использованием любую переменную надо ввести или присвоить ей начальное значение. Это действие называется инициализацией переменной.&lt;br /&gt;
&lt;br /&gt;
=== Деление на 0 ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 0;&lt;br /&gt;
  var c := 666 div x; // Здесь происходит деление на 0&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Если во время выполнения программа выполнит деление на 0, то она завершится с ошибкой.&lt;br /&gt;
&lt;br /&gt;
=== Корень из отрицательного числа ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  writeln(sqrt(-1)); // Корень из отрицательного числа &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
В обычном Паскале возникает ошибка времени выполнения. &lt;br /&gt;
В PascalABC.NET выводится NaN - Not a Number&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
*[[Программы для начинающих]]&lt;br /&gt;
*[http://pascalabc.net/ru/programmyi-i-algoritmyi-dlya-nachinayuschih.html Сайт PascalABC.NET: Программы и алгоритмы для начинающих]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2729</id>
		<title>Начало. Возможные ошибки</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2729"/>
		<updated>2015-08-26T09:38:37Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Деление на 0 */ убираем типы, работает автовывод&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
=== Неописанная переменная ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  S := 1; // Неизвестное имя S&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Все используемые переменные должны быть предварительно описаны с помощью ключевого слова '''var''' (внутри блока begin/end или, что обычно хуже, в разделе описаний вначале программы).&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующая ; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer &lt;br /&gt;
  S := 1;         // Компилятор здесь скажет: Ожидалась ; — имеется ввиду предыдущая строка!&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1 &lt;br /&gt;
  S := S + 1  // Аналогично: проблема на предыдущей строке, а на текущей нет, потому что после неё идёт end.&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Очень частая ошибка у начинающих. Курсор, как правило, позиционируется '''в начале следующей строки'''.&lt;br /&gt;
&lt;br /&gt;
=== Несовместимость типов при присваивании ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer := 1.3; // Нельзя преобразовать тип real к integer&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Безболезненно можно присваивать только данные одного типа. Если данные - разных типов, то в редких случаях можно преобразовать данные одного типа в данные другого. Например, целое можно преобразовать в вещественное, а символ - в строку. Обратные преобразования не допускаются. &lt;br /&gt;
&lt;br /&gt;
Чтобы не ошибаться в подобных простых случаях в Паскале есть следующая '''краткая''' форма объявления и инициализации переменной:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1.3; // Тип real будет выведен компилятором автоматически&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Отсутствие закрывающего апострофа литеральной строки ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln('Результат сложения равен =, x + y); // Не хватает закрывающего апострофа&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Закрыть апостроф надо на той же строке, где расположен открывающий апостроф&lt;br /&gt;
&lt;br /&gt;
=== Ошибки расстановки запятых и апострофов при выводе строк и выражений ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln(x, '+,' y, '=', x+y); // Неверная расстановка запятых и апострофов&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Слишком много запятых и апострофов рядом, потому начинающие часто путаются :) &lt;br /&gt;
Надо уяснить правила: &lt;br /&gt;
*запятые разделяют разные элементы вывода&lt;br /&gt;
*все, что находится в апострофах, будет выведено на экран без изменений&lt;br /&gt;
&lt;br /&gt;
=== Ошибка ввода ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  read(x); // введите блаблабла и посмотрите, что получится&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Это - ошибка во время выполнения. Программа пытается преобразовать введенную строку в число, не может это сделать и завершается с ошибкой&lt;br /&gt;
&lt;br /&gt;
=== Ошибка неинициализированной переменной ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  // Забыли инициализировать или ввести x&lt;br /&gt;
  var r := x * x;  // r получает тип integer&lt;br /&gt;
  writeln('Квадрат числа ', x, ' = ', r);&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Перед использованием любую переменную надо ввести или присвоить ей начальное значение. Это действие называется инициализацией переменной.&lt;br /&gt;
&lt;br /&gt;
=== Деление на 0 ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 0;&lt;br /&gt;
  var c := 666 div x; // Здесь происходит деление на 0&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Если во время выполнения программа выполнит деление на 0, то она завершится с ошибкой.&lt;br /&gt;
&lt;br /&gt;
=== Корень из отрицательного числа ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  writeln(sqrt(-1)); // Корень из отрицательного числа &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
В обычном Паскале возникает ошибка времени выполнения. &lt;br /&gt;
В PascalABC.NET выводится NaN - Not a Number&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
*[[Программы для начинающих]]&lt;br /&gt;
*[http://pascalabc.net/ru/programmyi-i-algoritmyi-dlya-nachinayuschih.html Сайт PascalABC.NET: Программы и алгоритмы для начинающих]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2728</id>
		<title>Начало. Возможные ошибки</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2728"/>
		<updated>2015-08-26T09:37:41Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Несовместимость типов при присваивании */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
=== Неописанная переменная ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  S := 1; // Неизвестное имя S&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Все используемые переменные должны быть предварительно описаны с помощью ключевого слова '''var''' (внутри блока begin/end или, что обычно хуже, в разделе описаний вначале программы).&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующая ; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer &lt;br /&gt;
  S := 1;         // Компилятор здесь скажет: Ожидалась ; — имеется ввиду предыдущая строка!&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1 &lt;br /&gt;
  S := S + 1  // Аналогично: проблема на предыдущей строке, а на текущей нет, потому что после неё идёт end.&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Очень частая ошибка у начинающих. Курсор, как правило, позиционируется '''в начале следующей строки'''.&lt;br /&gt;
&lt;br /&gt;
=== Несовместимость типов при присваивании ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer := 1.3; // Нельзя преобразовать тип real к integer&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Безболезненно можно присваивать только данные одного типа. Если данные - разных типов, то в редких случаях можно преобразовать данные одного типа в данные другого. Например, целое можно преобразовать в вещественное, а символ - в строку. Обратные преобразования не допускаются. &lt;br /&gt;
&lt;br /&gt;
Чтобы не ошибаться в подобных простых случаях в Паскале есть следующая '''краткая''' форма объявления и инициализации переменной:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1.3; // Тип real будет выведен компилятором автоматически&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Отсутствие закрывающего апострофа литеральной строки ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln('Результат сложения равен =, x + y); // Не хватает закрывающего апострофа&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Закрыть апостроф надо на той же строке, где расположен открывающий апостроф&lt;br /&gt;
&lt;br /&gt;
=== Ошибки расстановки запятых и апострофов при выводе строк и выражений ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln(x, '+,' y, '=', x+y); // Неверная расстановка запятых и апострофов&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Слишком много запятых и апострофов рядом, потому начинающие часто путаются :) &lt;br /&gt;
Надо уяснить правила: &lt;br /&gt;
*запятые разделяют разные элементы вывода&lt;br /&gt;
*все, что находится в апострофах, будет выведено на экран без изменений&lt;br /&gt;
&lt;br /&gt;
=== Ошибка ввода ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  read(x); // введите блаблабла и посмотрите, что получится&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Это - ошибка во время выполнения. Программа пытается преобразовать введенную строку в число, не может это сделать и завершается с ошибкой&lt;br /&gt;
&lt;br /&gt;
=== Ошибка неинициализированной переменной ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  // Забыли инициализировать или ввести x&lt;br /&gt;
  var r := x * x;  // r получает тип integer&lt;br /&gt;
  writeln('Квадрат числа ', x, ' = ', r);&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Перед использованием любую переменную надо ввести или присвоить ей начальное значение. Это действие называется инициализацией переменной.&lt;br /&gt;
&lt;br /&gt;
=== Деление на 0 ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer := 0;&lt;br /&gt;
  var c: integer := 666 div x; // Здесь происходит деление на 0&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Если во время выполнения программа выполнит деление на 0, то она завершится с ошибкой.&lt;br /&gt;
&lt;br /&gt;
=== Корень из отрицательного числа ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  writeln(sqrt(-1)); // Корень из отрицательного числа &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
В обычном Паскале возникает ошибка времени выполнения. &lt;br /&gt;
В PascalABC.NET выводится NaN - Not a Number&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
*[[Программы для начинающих]]&lt;br /&gt;
*[http://pascalabc.net/ru/programmyi-i-algoritmyi-dlya-nachinayuschih.html Сайт PascalABC.NET: Программы и алгоритмы для начинающих]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2727</id>
		<title>Начало. Возможные ошибки</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2727"/>
		<updated>2015-08-26T09:36:18Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Отсутствующая ; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
=== Неописанная переменная ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  S := 1; // Неизвестное имя S&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Все используемые переменные должны быть предварительно описаны с помощью ключевого слова '''var''' (внутри блока begin/end или, что обычно хуже, в разделе описаний вначале программы).&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующая ; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer &lt;br /&gt;
  S := 1;         // Компилятор здесь скажет: Ожидалась ; — имеется ввиду предыдущая строка!&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S := 1 &lt;br /&gt;
  S := S + 1  // Аналогично: проблема на предыдущей строке, а на текущей нет, потому что после неё идёт end.&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Очень частая ошибка у начинающих. Курсор, как правило, позиционируется '''в начале следующей строки'''.&lt;br /&gt;
&lt;br /&gt;
=== Несовместимость типов при присваивании ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer := 1.3; // Нельзя преобразовать тип real к integer&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Безболезненно можно присваивать только данные одного типа. Если данные - разных типов, то в редких случаях можно преобразовать данные одного типа в данные другого. Например, целое можно преобразовать в вещественное, а символ - в строку. Обратные преобразования не допускаются.&lt;br /&gt;
&lt;br /&gt;
=== Отсутствие закрывающего апострофа литеральной строки ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln('Результат сложения равен =, x + y); // Не хватает закрывающего апострофа&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Закрыть апостроф надо на той же строке, где расположен открывающий апостроф&lt;br /&gt;
&lt;br /&gt;
=== Ошибки расстановки запятых и апострофов при выводе строк и выражений ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln(x, '+,' y, '=', x+y); // Неверная расстановка запятых и апострофов&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Слишком много запятых и апострофов рядом, потому начинающие часто путаются :) &lt;br /&gt;
Надо уяснить правила: &lt;br /&gt;
*запятые разделяют разные элементы вывода&lt;br /&gt;
*все, что находится в апострофах, будет выведено на экран без изменений&lt;br /&gt;
&lt;br /&gt;
=== Ошибка ввода ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  read(x); // введите блаблабла и посмотрите, что получится&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Это - ошибка во время выполнения. Программа пытается преобразовать введенную строку в число, не может это сделать и завершается с ошибкой&lt;br /&gt;
&lt;br /&gt;
=== Ошибка неинициализированной переменной ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  // Забыли инициализировать или ввести x&lt;br /&gt;
  var r := x * x;  // r получает тип integer&lt;br /&gt;
  writeln('Квадрат числа ', x, ' = ', r);&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Перед использованием любую переменную надо ввести или присвоить ей начальное значение. Это действие называется инициализацией переменной.&lt;br /&gt;
&lt;br /&gt;
=== Деление на 0 ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer := 0;&lt;br /&gt;
  var c: integer := 666 div x; // Здесь происходит деление на 0&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Если во время выполнения программа выполнит деление на 0, то она завершится с ошибкой.&lt;br /&gt;
&lt;br /&gt;
=== Корень из отрицательного числа ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  writeln(sqrt(-1)); // Корень из отрицательного числа &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
В обычном Паскале возникает ошибка времени выполнения. &lt;br /&gt;
В PascalABC.NET выводится NaN - Not a Number&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
*[[Программы для начинающих]]&lt;br /&gt;
*[http://pascalabc.net/ru/programmyi-i-algoritmyi-dlya-nachinayuschih.html Сайт PascalABC.NET: Программы и алгоритмы для начинающих]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2726</id>
		<title>Начало. Возможные ошибки</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE._%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8&amp;diff=2726"/>
		<updated>2015-08-26T09:33:52Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Неописанная переменная */ var наше всио, а раздел описаний менее предпочтителен в общем случае&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
=== Неописанная переменная ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  S := 1; // Неизвестное имя S&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Все используемые переменные должны быть предварительно описаны с помощью ключевого слова '''var''' (внутри блока begin/end или, что обычно хуже, в разделе описаний вначале программы).&lt;br /&gt;
&lt;br /&gt;
=== Отсутствующая ; ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer // Ожидалась ;&lt;br /&gt;
  S := 1; &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer := 1 // Ожидалась ;&lt;br /&gt;
  S := S + 1&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Очень частая ошибка у начинающих. Курсор, как правило, позиционируется в начале следующей строки&lt;br /&gt;
&lt;br /&gt;
=== Несовместимость типов при присваивании ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var S: integer := 1.3; // Нельзя преобразовать тип real к integer&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Безболезненно можно присваивать только данные одного типа. Если данные - разных типов, то в редких случаях можно преобразовать данные одного типа в данные другого. Например, целое можно преобразовать в вещественное, а символ - в строку. Обратные преобразования не допускаются.&lt;br /&gt;
&lt;br /&gt;
=== Отсутствие закрывающего апострофа литеральной строки ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln('Результат сложения равен =, x + y); // Не хватает закрывающего апострофа&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Закрыть апостроф надо на той же строке, где расположен открывающий апостроф&lt;br /&gt;
&lt;br /&gt;
=== Ошибки расстановки запятых и апострофов при выводе строк и выражений ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x := 2;   // x получает тип integer&lt;br /&gt;
  var y := 3;   // y получает тип integer&lt;br /&gt;
  writeln(x, '+,' y, '=', x+y); // Неверная расстановка запятых и апострофов&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Слишком много запятых и апострофов рядом, потому начинающие часто путаются :) &lt;br /&gt;
Надо уяснить правила: &lt;br /&gt;
*запятые разделяют разные элементы вывода&lt;br /&gt;
*все, что находится в апострофах, будет выведено на экран без изменений&lt;br /&gt;
&lt;br /&gt;
=== Ошибка ввода ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  read(x); // введите блаблабла и посмотрите, что получится&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Это - ошибка во время выполнения. Программа пытается преобразовать введенную строку в число, не может это сделать и завершается с ошибкой&lt;br /&gt;
&lt;br /&gt;
=== Ошибка неинициализированной переменной ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer;&lt;br /&gt;
  // Забыли инициализировать или ввести x&lt;br /&gt;
  var r := x * x;  // r получает тип integer&lt;br /&gt;
  writeln('Квадрат числа ', x, ' = ', r);&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Перед использованием любую переменную надо ввести или присвоить ей начальное значение. Это действие называется инициализацией переменной.&lt;br /&gt;
&lt;br /&gt;
=== Деление на 0 ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
begin&lt;br /&gt;
  var x: integer := 0;&lt;br /&gt;
  var c: integer := 666 div x; // Здесь происходит деление на 0&lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Если во время выполнения программа выполнит деление на 0, то она завершится с ошибкой.&lt;br /&gt;
&lt;br /&gt;
=== Корень из отрицательного числа ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;begin&lt;br /&gt;
  writeln(sqrt(-1)); // Корень из отрицательного числа &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
В обычном Паскале возникает ошибка времени выполнения. &lt;br /&gt;
В PascalABC.NET выводится NaN - Not a Number&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
*[[Программы для начинающих]]&lt;br /&gt;
*[http://pascalabc.net/ru/programmyi-i-algoritmyi-dlya-nachinayuschih.html Сайт PascalABC.NET: Программы и алгоритмы для начинающих]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%A1%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%BA%D0%B0_%D1%81%D0%B5%D0%BC%D0%B8%D0%BD%D0%B0%D1%80%D0%B0_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D1%82%D0%B8%D0%BF%D0%BE%D0%B2&amp;diff=2596</id>
		<title>Страничка семинара теории типов</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%A1%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%BA%D0%B0_%D1%81%D0%B5%D0%BC%D0%B8%D0%BD%D0%B0%D1%80%D0%B0_%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D0%B8_%D1%82%D0%B8%D0%BF%D0%BE%D0%B2&amp;diff=2596"/>
		<updated>2013-02-23T17:25:47Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Generics of a Higher Kind */ «передаём модели»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;По концептам, типам следует читать современные статьи&lt;br /&gt;
&lt;br /&gt;
Можно начинать со школы Одерского:&lt;br /&gt;
&lt;br /&gt;
[http://www.scala-lang.org/node/143 Статьи по теоретическим основам языка Скала]&lt;br /&gt;
&lt;br /&gt;
Выделю тут ряд статей, с которых, по моему мнению, следует начинать:&lt;br /&gt;
&lt;br /&gt;
* [http://drops.dagstuhl.de/opus/volltexte/2009/2338/pdf/09005.OderskyM.2338.pdf Fighting Bit Rot with Types (Experience Report: Scala Collections)]&lt;br /&gt;
* [http://lamp.epfl.ch/~dragos/files/scala-spec.pdf Compiling Generics Through User-Directed Type Specialization]&lt;br /&gt;
* [http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf Implementing First-Class Polymorphic Delimited Continuations by a Type-Directed Selective CPS-Transform]&lt;br /&gt;
* [http://www.comlab.ox.ac.uk//files/2187/ModularVisitor.pdf Modular Visitor Components: A Practical Solution to the Expression Families Problem]&lt;br /&gt;
* [http://adriaanm.github.com/files/higher.pdf Generics of a Higher Kind]&lt;br /&gt;
* [http://adriaanm.github.com/files/scalina-final.pdf Safe Type-level Abstraction in Scala]&lt;br /&gt;
* [http://infoscience.epfl.ch/getfile.py?docid=13847&amp;amp;name=TranslationCorrectness-TR&amp;amp;format=pdf&amp;amp;version=1 Translation Correctness for First-Order Object-Oriented Pattern Matching]&lt;br /&gt;
* [http://www.scala-lang.org/sites/default/files/odersky/mfcs06.pdf A Core Calculus for Scala Type Checking]&lt;br /&gt;
* [http://www.scala-lang.org/sites/default/files/odersky/ecoop03.pdf A Nominal Theory of Objects with Dependent Types]&lt;br /&gt;
&lt;br /&gt;
== Краткий отчет о статьях ==&lt;br /&gt;
==== Compiling Generics Through User-Directed Type Specialization ====&lt;br /&gt;
''Juliet:''&lt;br /&gt;
&lt;br /&gt;
Небольшая статья, 6 страниц. О технических особенностях генерации кода для шаблонов. &lt;br /&gt;
&lt;br /&gt;
В двух словах речь о том, что использовать код, который генерируется для шаблона после стирания типов (где вместо типа &amp;lt;tt&amp;gt;T&amp;lt;/tt&amp;gt; возникает &amp;lt;tt&amp;gt;Object&amp;lt;/tt&amp;gt;) неэффективно для примитивных типов. Так как их приходится заворачивать в классы-обертки, и, соответственно, в специализированной версии возникают лишние операции boxing/unboxing.&lt;br /&gt;
&lt;br /&gt;
В качестве альтернативы при описании шаблона для типа-параметра шаблона можно указать ключевое слово '''specialized''':&lt;br /&gt;
&amp;lt;source lang=&amp;quot;scala&amp;quot;&amp;gt;&lt;br /&gt;
def someFun[@specialized T](...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
В этом случае помимо кода с &amp;lt;tt&amp;gt;Object&amp;lt;/tt&amp;gt; будут сгенерированы специализации для примитивных типов.&lt;br /&gt;
&lt;br /&gt;
==== Generics of a Higher Kind ==== &lt;br /&gt;
'''''Juliet''':''&lt;br /&gt;
&lt;br /&gt;
16 страниц. &lt;br /&gt;
&lt;br /&gt;
Насколько я понимаю, концептов в том смысле, как мы уже привыкли их воспринимать, здесь нет. Сплошные классы.&lt;br /&gt;
&lt;br /&gt;
Точнее, есть '''trait''' и '''class'''. Из статьи: класс может наследоваться от другого класса и нескольких траитов. Траит это класс, который может комбинироваться с другими траитами с помощью некой ограниченной версии множественного наследования.&lt;br /&gt;
&lt;br /&gt;
Далее говорится, что в статье разница между траитами и классами не так важна, так что все называется классами.&lt;br /&gt;
&lt;br /&gt;
Класс может быть параметризован. А может содержать абстрактный тип-член. И они вроде бы ничем по сути не отличаются кроме области видимости.&lt;br /&gt;
&lt;br /&gt;
Соль типов-параметров шаблона в том, что в качестве параметра может быть не конкретный тип, а '''конструктор типа'''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Scala&amp;quot;&amp;gt;&lt;br /&gt;
trait Iterable[T, Container[X]] {&lt;br /&gt;
  def filter(p: T =&amp;gt; Boolean): Container[T]&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это означает, что первый параметр — конкретный тип, а второй параметр — конструктор типа от одного параметра. И далее можем написать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Scala&amp;quot;&amp;gt;&lt;br /&gt;
trait List[T] extends Iterable[T, List]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На параметры шаблона можно наложить только два вида ограничений: надтипа и подтипа. Соответственно, если хочется получить несколько разносортных ограничений (то есть по-нашему — если мы хотим, чтобы тип удовлетворял нескольким концептам), надо сделать trait-наследник всех интересующих нас концептов и указать его в качестве надтипа.&lt;br /&gt;
&lt;br /&gt;
Еще один интересный момент, рассматривающийся в статье — сорта типов. Из Haskell мы помним (про расширения я ничего не знаю, к сожалению), что конкретный тип имеет сорт «*», конструктор с одним параметром — «* → *», и так далее. Здесь предлагается рассматривать не просто «*», а «*(lowerBound, upperBound)». То есть сорт содержит информацию об ограничениях типовых составляющих.&lt;br /&gt;
&lt;br /&gt;
Далее говорится о том, что на сортах можно ввести отношение «подсортирования». И еще есть раздел с красивым названием «kind soundness».&lt;br /&gt;
&lt;br /&gt;
В общем, по-моему это крутая статья. Надо над ней подумать...&lt;br /&gt;
&lt;br /&gt;
'''''Miks''':''&lt;br /&gt;
&lt;br /&gt;
Раз концепты есть, то как-то можно обеспечить концепт, связывающий два и более параметров шаблона. Как? &lt;br /&gt;
Может, в статье всё-таки ничего не говорится о концептах?&lt;br /&gt;
&lt;br /&gt;
'''''Juliet''':''&lt;br /&gt;
&lt;br /&gt;
Слова «концепты» в статье вообще нет, это правда. Есть пример моделирования классов типов Haskell.&lt;br /&gt;
&lt;br /&gt;
А связать несколько параметров можно, просто trait с несколькими параметрами.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;scala&amp;quot;&amp;gt;&lt;br /&gt;
trait myTrait[S, T] {&lt;br /&gt;
  def convert(x: S): T&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''Miks''':''&lt;br /&gt;
&lt;br /&gt;
Ну, это не очень - это и на C++ старом можно написать. А вот пример использования triats как концептов в новом C++ можно? В стиле&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;template&amp;lt;InputIterator Iter, typename V&amp;gt;&lt;br /&gt;
requires EqualityComparable&amp;lt;Iter::value_type, V&amp;gt;&lt;br /&gt;
Iter find(Iter first, Iter last, V v) { &lt;br /&gt;
  while (first &amp;lt; last &amp;amp;&amp;amp; *first != v)&lt;br /&gt;
    ++first;&lt;br /&gt;
  return first;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''Juliet''':''&lt;br /&gt;
&lt;br /&gt;
По идее можно, но я опять ничего не понимаю, пытаюсь написать...&lt;br /&gt;
&lt;br /&gt;
'''''Juliet''':''&lt;br /&gt;
&lt;br /&gt;
В общем, у меня все печально. С value_type, спрятанным внутрь траита, совсем ничего не получается. Получился какой-то такой ужас:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Scala&amp;quot;&amp;gt;&lt;br /&gt;
  trait EqualityComparable[S, T] {&lt;br /&gt;
  	def ==(s: S, t: T): Boolean&lt;br /&gt;
  	def !=(s: S, t: T): Boolean = !(s == t)&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  implicit object EqIntInt extends EqualityComparable[Int, Int] {&lt;br /&gt;
  	def ==(a: Int, b: Int): Boolean = { a == b }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  trait PInputIterator[value_type] {&lt;br /&gt;
  	def &amp;lt;(b: PInputIterator[value_type]): Boolean&lt;br /&gt;
  	def ++(): Unit&lt;br /&gt;
  	def *(): value_type&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  class IntNumber(x: Int) extends PInputIterator[Int]{&lt;br /&gt;
  	var n = x&lt;br /&gt;
  	&lt;br /&gt;
  	def &amp;lt;(b: PInputIterator[Int]): Boolean = {&lt;br /&gt;
  		val y = b.asInstanceOf[IntNumber];&lt;br /&gt;
  		n &amp;lt; y.n&lt;br /&gt;
  	}&lt;br /&gt;
  	def ++(): Unit = { n += 1; }&lt;br /&gt;
  	def *(): Int = n&lt;br /&gt;
  	&lt;br /&gt;
  	override def toString(): String = n.toString()&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  def pfind[X, Iter &amp;lt;: PInputIterator[X], V](first: Iter, last: Iter, v: V)&lt;br /&gt;
  		(implicit cmp: EqualityComparable[X, V]): Iter =&lt;br /&gt;
  {&lt;br /&gt;
    	while (first &amp;lt; last &amp;amp;&amp;amp; cmp.!=(first.*(), v))&lt;br /&gt;
    		first.++()&lt;br /&gt;
  	first&lt;br /&gt;
  }                                               //&amp;gt; pfind: [X, Iter &amp;lt;: concepts.iterator.PInputIterator[X], V](first: Iter, las&lt;br /&gt;
                                                  //| t: Iter, v: V)(implicit cmp: concepts.iterator.EqualityComparable[X,V])Iter&lt;br /&gt;
                                                  //| &lt;br /&gt;
  &lt;br /&gt;
  var c5 = new IntNumber(5)                //&amp;gt; c5  : concepts.iterator.IntNumber = 5&lt;br /&gt;
  var c15 = new IntNumber(15)              //&amp;gt; c15  : concepts.iterator.IntNumber = 15&lt;br /&gt;
  val d11 = pfind[Int, IntNumber, Int](c5, c15, 11)&lt;br /&gt;
                                                  //&amp;gt; d11  : concepts.iterator.IntNumber = 11&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Причем ''не указывать'' параметры при вызове pfind не получается, ошибка: &amp;lt;tt&amp;gt;Type inferred type arguments [Nothing,IntNumber,Int] do not conform to method pfind's type parameter bounds [X,Iter &amp;lt;: PInputIterator[X],V]&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''''Juliet''':''&lt;br /&gt;
&lt;br /&gt;
Я снова открыла статью, про которую Артем на семинаре докладывал. Она несколько нивелировала влияние статьи «Generics of a Higher Kind». Теперь у меня вот так получилось (добраться до внутреннего типа все равно никак не получается):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Scala&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
trait EqualityComparable[S, T] {&lt;br /&gt;
	def ==(s: S, t: T): Boolean&lt;br /&gt;
  	def !=(s: S, t: T): Boolean = !(s == t)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trait InputIterator[Iter] {&lt;br /&gt;
	type value_type&lt;br /&gt;
  	&lt;br /&gt;
  	def &amp;lt;(a: Iter, b: Iter): Boolean&lt;br /&gt;
  	def ++(it: Iter): Unit&lt;br /&gt;
  	def *(it: Iter): value_type&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trait VTInputIterator[Iter, VT] extends InputIterator[Iter] {&lt;br /&gt;
	type value_type = VT&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class ArrayListIterator[T](a: ArrayList[T], i: Int) {&lt;br /&gt;
	val arr: ArrayList[T] = a&lt;br /&gt;
	var ind: Int = i&lt;br /&gt;
	&lt;br /&gt;
	def curr(): T = arr.get(ind)&lt;br /&gt;
	def ++(): Unit = { ind += 1 }&lt;br /&gt;
	&lt;br /&gt;
	override def toString() = &amp;quot;[&amp;quot; + ind.toString() + &amp;quot;]&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class InputIterArrList[T] extends VTInputIterator[ArrayListIterator[T], T]{	&lt;br /&gt;
	def &amp;lt;(a: ArrayListIterator[T], b: ArrayListIterator[T]) = {&lt;br /&gt;
	  if (a.arr == b.arr) a.ind &amp;lt; b.ind&lt;br /&gt;
	  else throw new IllegalArgumentException()&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	def ++(it: ArrayListIterator[T]): Unit = it.++()&lt;br /&gt;
	def *(it: ArrayListIterator[T]) = it.curr()&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
object TestInputIterator extends Application{	&lt;br /&gt;
  &lt;br /&gt;
	def find[Iter, VT, V](first: Iter, last: Iter, x: V)(implicit iterator: VTInputIterator[Iter, VT], &lt;br /&gt;
	    cmp: EqualityComparable[VT, V]): Iter = &lt;br /&gt;
	{&lt;br /&gt;
	  while (iterator.&amp;lt;(first, last) &amp;amp;&amp;amp; cmp.!=(iterator.*(first), x))&lt;br /&gt;
	    iterator.++(first)&lt;br /&gt;
	  first&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	implicit object EqIntInt extends EqualityComparable[Int, Int] {&lt;br /&gt;
	  def ==(a: Int, b: Int): Boolean = { a == b }&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	implicit object inputIterArrListInt extends InputIterArrList[Int]{}&lt;br /&gt;
	&lt;br /&gt;
	val len = 10;&lt;br /&gt;
	var arr: ArrayList[Int] = new ArrayList(len);&lt;br /&gt;
	for (i: Int &amp;lt;- 1 to len)&lt;br /&gt;
	  arr.add(i)&lt;br /&gt;
	var arrFirst = new ArrayListIterator(arr, 0)&lt;br /&gt;
	var arrLast = new ArrayListIterator(arr, len)&lt;br /&gt;
	var r = find(arrFirst, arrLast, 7)&lt;br /&gt;
	println(r)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''''Juliet:'''''&lt;br /&gt;
&lt;br /&gt;
В общем, итоги на SO (http://stackoverflow.com/questions/15001138/modelling-of-c-concepts-with-scala-traits).&lt;br /&gt;
&lt;br /&gt;
То есть я так понимаю, на Scala мы пишем примерно то, во что генерируются концепты у Сика. То есть явно передаем модели.&lt;br /&gt;
&lt;br /&gt;
И с другой стороны есть возможность указать надтип и подтип для параметров шаблона.&lt;br /&gt;
&lt;br /&gt;
'''''Ulysses:'''''&lt;br /&gt;
&lt;br /&gt;
Не «передаём», а указываем при описании функций — это полный аналог requires. Если есть implicit, то явно передавать при вызове не требуется.&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2019</id>
		<title>Заглавная страница</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2019"/>
		<updated>2011-02-09T21:11:10Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: Отмена правки 2016 участника Hyldenlearwik (обсуждение)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cтатьи==&lt;br /&gt;
* [[FAQ по PascalABC.NET]]&lt;br /&gt;
&lt;br /&gt;
== Обучение==&lt;br /&gt;
* [[GPLex + GPPG|Задания по курсу &amp;quot;Методы построения компиляторов&amp;quot;]]&lt;br /&gt;
* [[Комплект юного разработчика парсеров]]&lt;br /&gt;
* [[Как создать компилятор]]&lt;br /&gt;
&lt;br /&gt;
== Примеры==&lt;br /&gt;
* [[Программы для начинающих|Программы и алгоритмы для начинающих]]&lt;br /&gt;
* [[Особенности языка: базовый уровень]]&lt;br /&gt;
* [[Стандартные модули: базовый уровень]]&lt;br /&gt;
* [[Особенности языка: продвинутый уровень]]&lt;br /&gt;
* [[Стандартные модули: продвинутый уровень]]&lt;br /&gt;
* [[Алгоритмы для студентов|Программы и алгоритмы для студентов ИТ]]&lt;br /&gt;
* [[Алгоритмы средней сложности]] (пока не реализовано)&lt;br /&gt;
&lt;br /&gt;
== Web-среда разработки PascalABC.NET ==&lt;br /&gt;
&lt;br /&gt;
*[[Основная страница проекта WDE PascalABC.NET|Основная страница проекта]]&lt;br /&gt;
* [[2011 год - планы]]&lt;br /&gt;
[[Ош1]]&lt;br /&gt;
&lt;br /&gt;
==Другие проекты==&lt;br /&gt;
&lt;br /&gt;
*[[Обзор реализации основных возможностей функционального языка программирования в среде PascalABC.NET|Проект &amp;quot;Реализация основных возможностей функционального языка программирования в среде PascalABC.NET&amp;quot; (Бушманова Н.)]]&lt;br /&gt;
*[[Рефакторинг|Проект &amp;quot;Рефакторинг по семантическому дереву PascalABC.NET&amp;quot; (Малеванный М., Коноплев Е.)]]&lt;br /&gt;
*[[Аспектно-ориентированное программирование|Проект &amp;quot;Аспектно-ориентированное программирование&amp;quot; (Зарубин М.)]]&lt;br /&gt;
* [[Реализация языка программирования Оберон на базе PascalABC.NET]]&lt;br /&gt;
&lt;br /&gt;
* [[Проект Optimus - учебная система оптимизации программ]]&lt;br /&gt;
&lt;br /&gt;
== Статьи о внутреннем представлении PascalABC.NET ==&lt;br /&gt;
&lt;br /&gt;
* [[Архитектура PascalABC.NET]]&lt;br /&gt;
* [[Создание нового языка программирования - с чего начать]]&lt;br /&gt;
* [[Интерфейс подключения нового языка программирования|Интерфейс подключения нового языка программирования (проект)]]&lt;br /&gt;
* [[Директивы компилятора]]&lt;br /&gt;
* [[Синтаксическое дерево]]&lt;br /&gt;
* [[Семантическое дерево]]&lt;br /&gt;
* [[Формат PCU]]&lt;br /&gt;
* [[Отладчик]]&lt;br /&gt;
* [[Intellisense]]&lt;br /&gt;
* [[Шаблоны кода]]&lt;br /&gt;
* [[Создание нового стандартного модуля (для разработчика)]]&lt;br /&gt;
&lt;br /&gt;
== Статьи по программированию в PascalABC.NET ==&lt;br /&gt;
* [[Локализация приложений]]&lt;br /&gt;
* [[Плагины для PascalABC.NET]]&lt;br /&gt;
* [[Отслеживание изменений в файловой системе]]&lt;br /&gt;
* [[OpenGL]]&lt;br /&gt;
&lt;br /&gt;
== Разное ==&lt;br /&gt;
* [[GPPG+GPLex - что исправить для русской кодировки]]&lt;br /&gt;
[[Ош]]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2018</id>
		<title>Заглавная страница</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0&amp;diff=2018"/>
		<updated>2011-02-09T21:10:55Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: Отмена правки 2017 участника Hyldenlearwik (обсуждение)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Cтатьи==&lt;br /&gt;
* [[FAQ по PascalABC.NET]]&lt;br /&gt;
&lt;br /&gt;
== Обучение==&lt;br /&gt;
* [[GPLex + GPPG|Задания по курсу &amp;quot;Методы построения компиляторов&amp;quot;]]&lt;br /&gt;
* [[Комплект юного разработчика парсеров]]&lt;br /&gt;
* [[Как создать компилятор]]&lt;br /&gt;
&lt;br /&gt;
== Примеры==&lt;br /&gt;
* [[Программы для начинающих|Программы и алгоритмы для начинающих]]&lt;br /&gt;
* [[Особенности языка: базовый уровень]]&lt;br /&gt;
* [[Стандартные модули: базовый уровень]]&lt;br /&gt;
* [[Особенности языка: продвинутый уровень]]&lt;br /&gt;
* [[Стандартные модули: продвинутый уровень]]&lt;br /&gt;
* [[Алгоритмы для студентов|Программы и алгоритмы для студентов ИТ]]&lt;br /&gt;
* [[Алгоритмы средней сложности]] (пока не реализовано)&lt;br /&gt;
&lt;br /&gt;
== Web-среда разработки PascalABC.NET ==&lt;br /&gt;
&lt;br /&gt;
*[[Основная страница проекта WDE PascalABC.NET|Основная страница проекта]]&lt;br /&gt;
* [[2011 год - планы]]&lt;br /&gt;
[[Ош1]]&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
*[[http://home.ru|квартиры в Москве]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*[[Обзор реализации основных возможностей функционального языка программирования в среде PascalABC.NET|Проект &amp;quot;Реализация основных возможностей функционального языка программирования в среде PascalABC.NET&amp;quot; (Бушманова Н.)]]&lt;br /&gt;
*[[Рефакторинг|Проект &amp;quot;Рефакторинг по семантическому дереву PascalABC.NET&amp;quot; (Малеванный М., Коноплев Е.)]]&lt;br /&gt;
*[[Аспектно-ориентированное программирование|Проект &amp;quot;Аспектно-ориентированное программирование&amp;quot; (Зарубин М.)]]&lt;br /&gt;
* [[Реализация языка программирования Оберон на базе PascalABC.NET]]&lt;br /&gt;
&lt;br /&gt;
* [[Проект Optimus - учебная система оптимизации программ]]&lt;br /&gt;
&lt;br /&gt;
== Статьи о внутреннем представлении PascalABC.NET ==&lt;br /&gt;
&lt;br /&gt;
* [[Архитектура PascalABC.NET]]&lt;br /&gt;
* [[Создание нового языка программирования - с чего начать]]&lt;br /&gt;
* [[Интерфейс подключения нового языка программирования|Интерфейс подключения нового языка программирования (проект)]]&lt;br /&gt;
* [[Директивы компилятора]]&lt;br /&gt;
* [[Синтаксическое дерево]]&lt;br /&gt;
* [[Семантическое дерево]]&lt;br /&gt;
* [[Формат PCU]]&lt;br /&gt;
* [[Отладчик]]&lt;br /&gt;
* [[Intellisense]]&lt;br /&gt;
* [[Шаблоны кода]]&lt;br /&gt;
* [[Создание нового стандартного модуля (для разработчика)]]&lt;br /&gt;
&lt;br /&gt;
== Статьи по программированию в PascalABC.NET ==&lt;br /&gt;
* [[Локализация приложений]]&lt;br /&gt;
* [[Плагины для PascalABC.NET]]&lt;br /&gt;
* [[Отслеживание изменений в файловой системе]]&lt;br /&gt;
* [[OpenGL]]&lt;br /&gt;
&lt;br /&gt;
== Разное ==&lt;br /&gt;
* [[GPPG+GPLex - что исправить для русской кодировки]]&lt;br /&gt;
[[Ош]]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C_%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%82%D0%BE%D1%80&amp;diff=1749</id>
		<title>Как создать компилятор</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C_%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%82%D0%BE%D1%80&amp;diff=1749"/>
		<updated>2010-07-28T15:04:41Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Лексический анализатор GPLex и синтаксический анализатор GPPG */ пункт.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Общие замечания ===&lt;br /&gt;
Сразу скачиваем [http://pascalabc.net/downloads/CompilerConstruction/Oberon00Independent.zip полный комплект разработчика парсеров].&lt;br /&gt;
&lt;br /&gt;
В инфраструктуре PascalABC.NET несложно создавать компиляторы простых языков программирования. Программы, получаемые на таких языках, компилируются в .NET-код и работают, как и любой .NET-код, практически так же производительно, как и машинный.&lt;br /&gt;
&lt;br /&gt;
Для создания компилятора важно понимать следующее. Компилятор имеет несколько внутренних представлений. &lt;br /&gt;
&lt;br /&gt;
* Вначале текст программы разбирается им в так называемое '''синтаксическое дерево'''. Синтаксическое дерево содержит представленный в виде дерева текст программы - семантика на этом уровне не учитывается, проверяется только соответствие синтаксическим конструкциям. Например, на уровне синтаксического дерева не распознаётся ошибка описания двух переменных с одним именем - с точки зрения синтаксиса ошибки нет.&lt;br /&gt;
* Затем синтаксическое дерево переводится в '''семантическое дерево'''. Семантическое дерево учитывает все правила, которые сформулированы в описании языка: в частности, то, что в одном пространстве имен нельзя описать две переменные с одним именем. Кроме этого, в семантическом дереве хранится существенно больше информации, чем в синтаксическом: каждая переменная имеет тип, относится к определенному пространству имен, для подпрограмм проверяется соответствие количества и типов формальных и фактических параметров и т.д. &lt;br /&gt;
:Заметим, что семантическое дерево, если получено, то содержит '''правильную''' программу. Семантическое дерево откомпилированного модуля можно сохранить на диск для ускорения компиляции. В PascalABC.NET так и происходит: если модуль успешно откомпилирован, то он сохраняется в .pcu-файл - это не что иное как записанное на диск семантическое дерево модуля. Если при компиляции основной программы к ней подключен данный модуль, то он повторно не компилируется - его дерево разворачивается из .pcu-файла в память, что гораздо быстрее.&lt;br /&gt;
* Наконец, по семантическому дереву генерируется .NET-код. &lt;br /&gt;
&lt;br /&gt;
Заметим, что такое разделение на синтаксическое и семантическое деревья позволяет упростить процесс создания новых языков. В частности, если язык достаточно простой и похож на Паскаль, то он может быть переведен в синтаксическое дерево, после чего достаточно использовать стандартный преобразователь в семантическое дерево и генератор кода. Для более сложных языков с семантикой, отличной от Паскаля, для некоторых узлов синтаксического дерева необходимо писать свой преобразователь в узлы семантического дерева. в ещё более сложных ситуациях имеющихся узлов семантического дерева может оказаться недостаточно, поэтому придется создавать новые и писать для них генерацию .NET-кода.&lt;br /&gt;
&lt;br /&gt;
Далее мы будем рассматривать только такие языки, в которых текст программы достаточно перевести в синтаксическое дерево, что означает, что семантика конструкций - такая же, как и в Паскале. Например, это означает, что целый тип неявно преобразуется в вещественный, но не наоборот.&lt;br /&gt;
&lt;br /&gt;
Следует отметить, что в большинстве ситуаций за счёт проведения дополнительных проверок перевода в узлы синтаксического дерева вполне достаточно. Далее это станет понятнее на конкретных примерах.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим создание простого языка, который мы назовём Oberon00. Это - подмножество языка Oberon с двумя типами - INTEGER и BOOLEAN - и возможностью вызывать процедуры из внешних модулей.&lt;br /&gt;
&lt;br /&gt;
=== Что нужно для создания компилятора ===&lt;br /&gt;
&lt;br /&gt;
Для создания компилятора, интегрированного в среду PascalABC.NET, нам потребуется создать несколько файлов. В случае языка Oberon00 потребуется создать:&lt;br /&gt;
* Oberon00Parser.dll - библиотеку, содержащую парсер языка (ее следует скопировать в папку установки PascalABC.NET)&lt;br /&gt;
* Oberon00.xhsd - файл подсветки синтаксиса (скопировать в папку PascalABC.NET\Highlighting)&lt;br /&gt;
Кроме этого, обычно требуется системный модуль, содержащий стандартные подпрограммы. Его можно создать на языке Паскаль. Пусть для Оберона00 этот модуль называется Oberon00System.pas. Требуется откомпилировать его в .pcu и затем &lt;br /&gt;
* Скопировать Oberon00System.pas в папку PascalABC.NET\LibSource&lt;br /&gt;
* Скопировать Oberon00System.pcu в папку PascalABC.NET\Lib&lt;br /&gt;
&lt;br /&gt;
Перезапустив среду PascalABC.NET, мы получим новый компилятор.&lt;br /&gt;
&lt;br /&gt;
Для того чтобы сразу увидеть конечный результат, можно скопировать [http://pascalabc.net/downloads/CompilerConstruction/Oberon00Independent.zip полный комплект разработчика парсеров].&lt;br /&gt;
&lt;br /&gt;
=== Что входит в комплект разработчика парсеров ===&lt;br /&gt;
# Папка DLL, содержащая dll из комплекта PascalABC.NET, необходимые для создания парсера.&lt;br /&gt;
# Папка GPLex_GPPG, содержащая генератор компиляторов GPLex+GPPG и документацию к нему.&lt;br /&gt;
# Папка Install, содержащая файлы, которые необходимо скопировать в папку PascalABC.NET для того чтобы в оболочке PascalABc.NET появился компилятор нового языка.&lt;br /&gt;
# Файл generate_all.bat для компиляции файлов .lex и .y в файлы .cs, компиляции проекта и получения библиотеки Oberon00Parser.dll и копирования этого файла в папку Install.&lt;br /&gt;
&lt;br /&gt;
Далее разберем последовательно, как создать парсер.&lt;br /&gt;
&lt;br /&gt;
=== Лексический анализатор GPLex и синтаксический анализатор GPPG ===&lt;br /&gt;
&lt;br /&gt;
Итак, наша задача - разобрать текст программы и по нему получить синтаксическое дерево программы. Такой построитель синтаксического дерева программы будем называть '''парсером''' или '''синтаксическим анализатором'''. Для работы синтаксического анализатора необходим также '''сканер''' или '''лексический анализатор''', который разбивает программу на лексемы - неделимые слова. Например, в языке Паскаль лексемами являются ключевые слова, идентификаторы, числовые и строковые константы, знаки операций (:= &amp;lt;&amp;gt; и т.д.).&lt;br /&gt;
&lt;br /&gt;
Синтаксические анализаторы обычно создаются с помощью специальных программ, называемых '''генераторами компиляторов'''. Одним из наиболее известных генераторов компиляторов является '''Yacc''' (Yet Another Compiler of Compilers) - и в паре с ним работает генератор лексических анализаторов '''Lex'''.&lt;br /&gt;
&lt;br /&gt;
Наиболее полной реализацией Yacc-Lex для .NET, генерирующей код парсера на C#, является GPLex+GPPG, разработанный Queensland University of Technology, Австралия. Вот странички продуктов: [http://plas2003.fit.qut.edu.au/gplex/ GPLex] и [http://plas2003.fit.qut.edu.au/gppg/ GPPG]. Однако рекомендуется [http://pascalabc.net/downloads/CompilerConstruction/GPLex_GPPG_2010.zip скачать исполнимые файлы отсюда] - они содержат небольшие модификации, связанные с корректной русификацией.&lt;br /&gt;
&lt;br /&gt;
=== Создание лексического анализатора ===&lt;br /&gt;
==== Класс Scanner ====&lt;br /&gt;
Создаваемый в результате компиляции .lex-файла класс '''Scanner''' содержит несколько важных методов и свойств. Рассмотрим их подробнее.&lt;br /&gt;
&lt;br /&gt;
* Функция int '''yylex'''() возвращает уникальный номер лексемы. Все лексемы хранятся в перечислимом типе Tokens. По-существу, возвращается номер константы в перечислимом типе Tokens - например, для лексемы ID возвращается (int)Tokens.ID&lt;br /&gt;
* Помимо уникального номера некоторые лексемы должны возвращать дополнительные значения. Таким значением для идентификатора является его строковое представление, для INTNUM - целое число и т.д. Дополнительное значение для некоторых лексем возвращается в свойстве '''yylval''', которое имеет тип ValueType и содержит ряд полей различных типов - для каждой лексемы может быть предусмотрено своё поле. Например, для лексемы ID предусмотрено поле string sVal, а для лексемы INTNUM - поле iVal.&lt;br /&gt;
Объяснение того, как это поле сопоставляется с типом лексемы, отложим до знакомства с содержимым файла .y&lt;br /&gt;
* Свойство '''yytext''' возвращает строковое представление текущей лексемы&lt;br /&gt;
* Свойство '''yylloc''' содержит положение лексемы в тексте, задаваемое типом LexLocation из пространства имен QUT.Gppg. Он хранит координаты начала и конца лексемы&lt;br /&gt;
&lt;br /&gt;
==== Файл .lex ====&lt;br /&gt;
Лексический анализатор обычно создается в файле с расширением .lex. Он имеет вид:&lt;br /&gt;
&lt;br /&gt;
 Определения&lt;br /&gt;
 %%&lt;br /&gt;
 Правила&lt;br /&gt;
 %%&lt;br /&gt;
 Пользовательский код&lt;br /&gt;
&lt;br /&gt;
Рассмотрим файл лексического анализатора Oberon00.lex:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%namespace GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser;&lt;br /&gt;
&lt;br /&gt;
Alpha [a-zA-Z_]&lt;br /&gt;
Digit [0-9]&lt;br /&gt;
AlphaDigit {Alpha}|{Digit}&lt;br /&gt;
INTNUM {Digit}+&lt;br /&gt;
ID {Alpha}{AlphaDigit}* &lt;br /&gt;
&lt;br /&gt;
%%&lt;br /&gt;
&lt;br /&gt;
&amp;quot;:=&amp;quot; { return (int)Tokens.ASSIGN; }&lt;br /&gt;
&amp;quot;;&amp;quot; { return (int)Tokens.SEMICOLUMN; }&lt;br /&gt;
&amp;quot;-&amp;quot; { return (int)Tokens.MINUS; }&lt;br /&gt;
&amp;quot;+&amp;quot; { return (int)Tokens.PLUS; }&lt;br /&gt;
&amp;quot;*&amp;quot; { return (int)Tokens.MULT; }&lt;br /&gt;
&amp;quot;/&amp;quot; { return (int)Tokens.DIVIDE; }&lt;br /&gt;
&amp;quot;&amp;lt;&amp;quot; { return (int)Tokens.LT; }&lt;br /&gt;
&amp;quot;&amp;gt;&amp;quot; { return (int)Tokens.GT; }&lt;br /&gt;
&amp;quot;&amp;lt;=&amp;quot; { return (int)Tokens.LE; }&lt;br /&gt;
&amp;quot;&amp;gt;=&amp;quot; { return (int)Tokens.GE; }&lt;br /&gt;
&amp;quot;=&amp;quot; { return (int)Tokens.EQ; }&lt;br /&gt;
&amp;quot;#&amp;quot; { return (int)Tokens.NE; }&lt;br /&gt;
&amp;quot;(&amp;quot; { return (int)Tokens.LPAREN; }&lt;br /&gt;
&amp;quot;)&amp;quot; { return (int)Tokens.RPAREN; }&lt;br /&gt;
&amp;quot;,&amp;quot; { return (int)Tokens.COLUMN; }&lt;br /&gt;
&amp;quot;~&amp;quot; { return (int)Tokens.NOT; }&lt;br /&gt;
&amp;quot;&amp;amp;&amp;quot; { return (int)Tokens.AND; }&lt;br /&gt;
&amp;quot;.&amp;quot; { return (int)Tokens.COMMA; }&lt;br /&gt;
&amp;quot;:&amp;quot; { return (int)Tokens.COLON; }&lt;br /&gt;
&amp;quot;!&amp;quot; { return (int)Tokens.EXCLAMATION; }&lt;br /&gt;
\x01 { return (int)Tokens.INVISIBLE; }&lt;br /&gt;
&lt;br /&gt;
{ID}  { &lt;br /&gt;
  int res = Keywords.KeywordOrIDToken(yytext);&lt;br /&gt;
  if (res == (int)Tokens.ID)&lt;br /&gt;
    yylval.sVal = yytext;&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
{INTNUM} { &lt;br /&gt;
  yylval.iVal = int.Parse(yytext); &lt;br /&gt;
  return (int)Tokens.INTNUM; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
%{&lt;br /&gt;
  yylloc = new QUT.Gppg.LexLocation(tokLin, tokCol, tokELin, tokECol);&lt;br /&gt;
%}&lt;br /&gt;
&lt;br /&gt;
%%&lt;br /&gt;
&lt;br /&gt;
public override void yyerror(string format, params object[] args) &lt;br /&gt;
{&lt;br /&gt;
  string errorMsg = PT.CreateErrorString(args);&lt;br /&gt;
  PT.AddError(errorMsg,yylloc);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После обработки генератором лексических анализаторов GPLex мы получим одноименный cs-файл Oberon00.cs, содержащий класс Scanner лексического анализатора.&lt;br /&gt;
&lt;br /&gt;
Разберем содержимое .lex-файла подробнее.&lt;br /&gt;
&lt;br /&gt;
==== Секция определений ====&lt;br /&gt;
&lt;br /&gt;
Вначале рассмотрим первую часть, содержащую определения лексем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%namespace GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser;&lt;br /&gt;
&lt;br /&gt;
Alpha [a-zA-Z_]&lt;br /&gt;
Digit [0-9]&lt;br /&gt;
AlphaDigit {Alpha}|{Digit}&lt;br /&gt;
INTNUM {Digit}+&lt;br /&gt;
ID {Alpha}{AlphaDigit}* &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Строка %namespace GPPGParserScanner означает, что класс сканера будет помещен в пространство имен GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
Строка %using PascalABCCompiler.Oberon00Parser; приводит к генерации соответствующей строки в cs-файле. Пространство имен PascalABCCompiler.Oberon00Parser полностью находится во вспомогательном файле Oberon00ParserTools.cs, который содержит различные глобальные описания и подпрограммы (подробнее содержимое этого файла будет объяснено позже).&lt;br /&gt;
&lt;br /&gt;
Далее идёт описание некоторых лексем в виде регулярных выражений. Например, целое число INTNUM представляет собой последовательность одной или более цифр {Digit}+, а идентификатор ID - последовательность букв или цифр, начинающуюся с буквы: {Alpha}{AlphaDigit}*&lt;br /&gt;
&lt;br /&gt;
==== Секция правил ====&lt;br /&gt;
&lt;br /&gt;
Вторая секция (между первым и вторым %%) является основной и содержит действия, которые необходимо выполнить, когда распознана та или иная лексема.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;&amp;quot;:=&amp;quot; { return (int)Tokens.ASSIGN; }&lt;br /&gt;
&amp;quot;;&amp;quot; { return (int)Tokens.SEMICOLUMN; }&lt;br /&gt;
&amp;quot;-&amp;quot; { return (int)Tokens.MINUS; }&lt;br /&gt;
&amp;quot;+&amp;quot; { return (int)Tokens.PLUS; }&lt;br /&gt;
&amp;quot;*&amp;quot; { return (int)Tokens.MULT; }&lt;br /&gt;
&amp;quot;/&amp;quot; { return (int)Tokens.DIVIDE; }&lt;br /&gt;
&amp;quot;&amp;lt;&amp;quot; { return (int)Tokens.LT; }&lt;br /&gt;
&amp;quot;&amp;gt;&amp;quot; { return (int)Tokens.GT; }&lt;br /&gt;
&amp;quot;&amp;lt;=&amp;quot; { return (int)Tokens.LE; }&lt;br /&gt;
&amp;quot;&amp;gt;=&amp;quot; { return (int)Tokens.GE; }&lt;br /&gt;
&amp;quot;=&amp;quot; { return (int)Tokens.EQ; }&lt;br /&gt;
&amp;quot;#&amp;quot; { return (int)Tokens.NE; }&lt;br /&gt;
&amp;quot;(&amp;quot; { return (int)Tokens.LPAREN; }&lt;br /&gt;
&amp;quot;)&amp;quot; { return (int)Tokens.RPAREN; }&lt;br /&gt;
&amp;quot;,&amp;quot; { return (int)Tokens.COLUMN; }&lt;br /&gt;
&amp;quot;~&amp;quot; { return (int)Tokens.NOT; }&lt;br /&gt;
&amp;quot;&amp;amp;&amp;quot; { return (int)Tokens.AND; }&lt;br /&gt;
&amp;quot;.&amp;quot; { return (int)Tokens.COMMA; }&lt;br /&gt;
&amp;quot;:&amp;quot; { return (int)Tokens.COLON; }&lt;br /&gt;
&amp;quot;!&amp;quot; { return (int)Tokens.EXCLAMATION; }&lt;br /&gt;
\x01 { return (int)Tokens.INVISIBLE; }&lt;br /&gt;
&lt;br /&gt;
{ID}  { &lt;br /&gt;
  int res = Keywords.KeywordOrIDToken(yytext);&lt;br /&gt;
  if (res == (int)Tokens.ID)&lt;br /&gt;
    yylval.sVal = yytext;&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
{INTNUM} { &lt;br /&gt;
  yylval.iVal = int.Parse(yytext); &lt;br /&gt;
  return (int)Tokens.INTNUM; &lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь приведены действия в случае если встречена та или иная лексема. Большинство действий просто возвращает порядковый номер лексемы в перечислимом типе Tokens. &lt;br /&gt;
&lt;br /&gt;
Последняя лексема - INVISIBLE - символ с кодом 1 - необязательна и является вспомогательной для компилятора выражений языка (необходим для поддержки IntelliSense).&lt;br /&gt;
&lt;br /&gt;
В случае лексемы INTNUM в поле yylval.iVal дополнительно возвращается целое число, соответствующее лексеме: int.Parse(yytext). Обратим внимание, что здесь нет никакой защиты от неправильного преобразования (например, в случае очень длинного целого). Причина - чтобы пока не усложнять изложение обработкой ошибок.&lt;br /&gt;
&lt;br /&gt;
В случае лексемы ID вначале проверяется, не является ли она ключевым словом, и если является, то возвращается целое, соответствующее лексеме ключевого слова (например, для BEGIN возвращается (int)Tokens.BEGIN), а если не является, то возвращается (int)Tokens.ID и параллельно в поле yylval.sVal возвращается строковое представление идентификатора. Именно на этом уровне мы можем задать, чувствителен ли наш язык к регистру (например, преобразованием всех идентификаторов к UpperCase).&lt;br /&gt;
&lt;br /&gt;
Секция правил завершается следующим кодом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%{&lt;br /&gt;
  yylloc = new QUT.Gppg.LexLocation(tokLin, tokCol, tokELin, tokECol);&lt;br /&gt;
%}&amp;lt;/source&amp;gt;&lt;br /&gt;
Этот код позволяет для каждой лексемы получать её местоположение в тексте. Вникать в этот код не надо - он просто необходим.&lt;br /&gt;
&lt;br /&gt;
==== Секция пользовательского кода ====&lt;br /&gt;
Наконец, в секции пользовательского кода содержится единственный переопределенный в классе Scanner метод &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;public override void yyerror(string format, params object[] args) &lt;br /&gt;
{&lt;br /&gt;
  string errorMsg = PT.CreateErrorString(args);&lt;br /&gt;
  PT.AddError(errorMsg,yylloc);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Он вызывается всякий раз когда происходит синтаксическая ошибка. Здесь мы пользуемся услугами статического класса PT, который находится в файле Oberon00ParserTools.cs. PT.CreateErrorString(args) формирует строку ошибки, а PT.AddError(errorMsg,yylloc); добавляет ошибку в список ошибок компиляции PascalABC.NET. Здесь интересно то, что в случае автоматического вызова yyerror в args попадают токены, которые ожидались после данного.&lt;br /&gt;
&lt;br /&gt;
==== Генерация кода лексического анализатора ====&lt;br /&gt;
Для генерации кода лексического анализатора следует выполнить команду:&lt;br /&gt;
 gplex.exe /unicode oberon00.lex&lt;br /&gt;
Здесь gplex.exe - генератор лексических анализаторов, хранящийся в папке GPLex_GPPG, параметр /unicode указывает на то, что созданный лексический анализатор будет распознавать файлы в кодировке unicode (заметим, что он будет распознавать и однобайтные кодировки).&lt;br /&gt;
&lt;br /&gt;
=== Создание синтаксического анализатора ===&lt;br /&gt;
==== Общая информация ====&lt;br /&gt;
Синтаксический анализатор - ядро нашего компилятора. Он строится по .y - файлу, в котором записываются правила '''грамматики языка''' и действия, которые выполняются по этим правилам. Каждое из действий создает соответствующий узел в синтаксическом дереве. Все узлы синтаксического дерева представляют собой иерархию классов с общим предком syntax_tree_node и описаны во внешней библиотеке SyntaxTree.dll. &lt;br /&gt;
&lt;br /&gt;
Для понимания дальнейшего следует иметь некоторое начальное представление о грамматиках языков программирования, о том, как они порождают языки и об автоматах, которые проверяют, принадлежит ли данная цепочка символов языку, порождаемому грамматикой. Более конкретно, следует представлять, что такое '''контекстно-свободная грамматика''', что Yacc-Lex  системы работают с так называемыми LL(k) грамматиками, являющимися достаточно широким подмножеством контекстно-свободных грамматик. &lt;br /&gt;
&lt;br /&gt;
В процессе построения грамматик придется сталкиваться с недостижимыми и циклическими продукциями грамматик, неоднозначными грамматиками. Необходимо уметь исправлять подобные ситуации хотя бы методом проб и ошибок. Особенно необходимо понимать, что Shift-Reduce - конфликты грамматик допустимы и разрешаются в пользу более длинной правой части продукции, а Reduce-Reduce - конфликты недопустимы и свидетельствуют об ошибках проектирования грамматики.&lt;br /&gt;
&lt;br /&gt;
Соответствующую информацию можно прочитать в книге Ахо &amp;quot;Компиляторы: принципы, технологии, инструменты&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==== Некоторые классы синтаксического дерева ====&lt;br /&gt;
Как уже было сказано, синтаксическое дерево состоит из объектов классов - наследников syntax_tree_node. Перечислим некоторые важные классы - наследники syntax_tree_node, которые будут нами использованы для реализации парсера Оберона00:&lt;br /&gt;
&lt;br /&gt;
 ident                   - идентификатор&lt;br /&gt;
 ident_list              - список идентификаторов&lt;br /&gt;
 uses_list               - список модулей и пространств имен в секции uses&lt;br /&gt;
 unit_or_namespace       - модуль или пространство имен&lt;br /&gt;
 block                   - блок &lt;br /&gt;
 program_module          - программа&lt;br /&gt;
 int32_const             - целая константа&lt;br /&gt;
 bool_const              - логическая константа&lt;br /&gt;
 expression              - выражение (базовый класс для всех выражений)&lt;br /&gt;
 expression_list         - список выражений&lt;br /&gt;
 un_expr                 - унарное ввыражение&lt;br /&gt;
 bin_expr                - бинарное выражение&lt;br /&gt;
 assign                  - оператор присваивания&lt;br /&gt;
 if_node                 - условный оператор&lt;br /&gt;
 while_node              - оператор цикла while&lt;br /&gt;
 empty_statement         - пустой оператор&lt;br /&gt;
 method_call             - вызов метода или внешней подпрограммы&lt;br /&gt;
 statement_list          - последовательность операторов&lt;br /&gt;
 named_type_reference    - имя типа&lt;br /&gt;
 declarations            - описания&lt;br /&gt;
 var_def_statement       - описания переменных с одним типом&lt;br /&gt;
 variable_definitions    - секция объявлений переменных&lt;br /&gt;
 simple_const_definition - определение именованной константы&lt;br /&gt;
 consts_definitions_list - секция определений именованных констант&lt;br /&gt;
&lt;br /&gt;
Каждый класс имеет свои поля, все необходимые поля заполняются обычно в конструкторе класса. В базовом классе syntax_tree_node имеется поле source_context типа SourceContext, определяющее местоположение конструкции, соответствующей синтаксическому узлу, в тексте программы. Именно поэтому каждый конструктор синтаксического дерева содержит последним параметром объект класса SourceContext.&lt;br /&gt;
&lt;br /&gt;
Просмотреть все узлы синтаксического дерева, их свойства и методы можно с помощью утилиты&lt;br /&gt;
[http://pascalabc.net/downloads/CompilerConstruction/NodesGenReader.zip Просмотрщик классов синтаксического дерева]&lt;br /&gt;
&lt;br /&gt;
==== Правила грамматики ====&lt;br /&gt;
Правила грамматики содержат терминальные символы (терминалы), которые распознаются лексическим анализатором, и нетерминальные символы (нетерминалы). Каждое правило грамматики выражает нетерминал через другие терминалы и нетерминалы. Например, для оператора присваивания имеется следующее правило:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;AssignOperator : ID ASSIGN expr ;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь нетерминал AssignOperator выражается через терминалы ID и ASSIGN и нетерминал expr, который будет определен в другом правиле.&lt;br /&gt;
&lt;br /&gt;
Нетерминал, с которого начинается разбор, называется стартовым символом грамматики. Этот нетерминал описывает всю программу. В нашей грамматике это module, а соответствующее стартовое правило имеет вид:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;module : MODULE ident SEMICOLUMN Declarations BEGIN StatementSequence END ident COMMA&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В конечном итоге все нетерминалы выражаются через терминалы, образующие основную программу. Соответствующий процесс получения программы из стартового символа называется выводом, а правила грамматики - правилами вывода.&lt;br /&gt;
&lt;br /&gt;
В каждом правиле может быть несколько альтернатив, разделяемых символом |:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;IfStatement &lt;br /&gt;
	: IF expr THEN StatementSequence END &lt;br /&gt;
	| IF expr THEN StatementSequence ELSE StatementSequence END &lt;br /&gt;
	;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правила могут быть рекурсивными. Например, правило для фактических параметров имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;factparams&lt;br /&gt;
	: expr &lt;br /&gt;
	| factparams COLUMN expr &lt;br /&gt;
	;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратим внимание, что данное правило является леворекурсивным, поскольку рекурсивное вхождение нетерминала factparams в правую часть правила находится с левой стороны. Для рассматриваемых нами генераторов компиляторов леворекурсивные правила являются предпочтительнее праворекурсивных.&lt;br /&gt;
&lt;br /&gt;
==== Формат .y-файла ====&lt;br /&gt;
.y-файл имеет такой же формат, как и .lex:&lt;br /&gt;
 Определения&lt;br /&gt;
 %%&lt;br /&gt;
 Правила&lt;br /&gt;
 %%&lt;br /&gt;
 Пользовательский код&lt;br /&gt;
&lt;br /&gt;
==== Раздел определений .y-файла ====&lt;br /&gt;
===== Общий вид =====&lt;br /&gt;
Рассмотрим раздел определений файла Oberon00.y&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%{&lt;br /&gt;
    public syntax_tree_node root; // Корневой узел синтаксического дерева &lt;br /&gt;
    public GPPGParser(AbstractScanner&amp;lt;ValueType, LexLocation&amp;gt; scanner) : base(scanner) { }&lt;br /&gt;
%}&lt;br /&gt;
&lt;br /&gt;
%output=oberon00yacc.cs &lt;br /&gt;
&lt;br /&gt;
%parsertype GPPGParser&lt;br /&gt;
&lt;br /&gt;
%union  &lt;br /&gt;
{ &lt;br /&gt;
  public bool bVal; &lt;br /&gt;
  public string sVal; &lt;br /&gt;
  public int iVal; &lt;br /&gt;
  public named_type_reference ntr;&lt;br /&gt;
  public ident_list il;&lt;br /&gt;
  public var_def_statement vds;&lt;br /&gt;
  public variable_definitions vdss;&lt;br /&gt;
  public expression ex;&lt;br /&gt;
  public expression_list el;&lt;br /&gt;
  public ident id;&lt;br /&gt;
  public statement st;&lt;br /&gt;
  public statement_list sl;&lt;br /&gt;
  public declarations decl;&lt;br /&gt;
  public Operators op;&lt;br /&gt;
  public simple_const_definition scd;&lt;br /&gt;
  public consts_definitions_list cdl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
%using PascalABCCompiler.SyntaxTree&lt;br /&gt;
%using PascalABCCompiler.Errors&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser&lt;br /&gt;
&lt;br /&gt;
%namespace GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
%start module&lt;br /&gt;
&lt;br /&gt;
%token &amp;lt;sVal&amp;gt; ID&lt;br /&gt;
%token &amp;lt;iVal&amp;gt; INTNUM &lt;br /&gt;
%token &amp;lt;bVal&amp;gt; TRUE FALSE&lt;br /&gt;
%token &amp;lt;op&amp;gt; PLUS MINUS MULT DIVIDE AND OR LT GT LE GE EQ NE &lt;br /&gt;
%token NOT &lt;br /&gt;
%token ASSIGN SEMICOLUMN LPAREN RPAREN COLUMN COMMA COLON EXCLAMATION&lt;br /&gt;
%token TRUE FALSE ODD BOOLEAN INTEGER&lt;br /&gt;
%token IF THEN ELSE BEGIN END WHILE DO MODULE CONST VAR&lt;br /&gt;
%token INVISIBLE&lt;br /&gt;
&lt;br /&gt;
%type &amp;lt;id&amp;gt; ident&lt;br /&gt;
%type &amp;lt;ntr&amp;gt; type&lt;br /&gt;
%type &amp;lt;ex&amp;gt; expr ConstExpr&lt;br /&gt;
%type &amp;lt;st&amp;gt; Assignment IfStatement WhileStatement WriteStatement Statement  &lt;br /&gt;
%type &amp;lt;st&amp;gt; EmptyStatement ProcCallStatement&lt;br /&gt;
%type &amp;lt;sl&amp;gt; StatementSequence&lt;br /&gt;
%type &amp;lt;decl&amp;gt; Declarations&lt;br /&gt;
%type &amp;lt;el&amp;gt; factparams&lt;br /&gt;
%type &amp;lt;il&amp;gt; IDList&lt;br /&gt;
%type &amp;lt;vds&amp;gt; VarDecl&lt;br /&gt;
%type &amp;lt;vdss&amp;gt; VarDeclarations VarDeclarationsSect&lt;br /&gt;
%type &amp;lt;scd&amp;gt; ConstDecl&lt;br /&gt;
%type &amp;lt;cdl&amp;gt; ConstDeclarations ConstDeclarationsSect&lt;br /&gt;
&lt;br /&gt;
%left LT GT LE GE EQ NE&lt;br /&gt;
%left PLUS MINUS OR&lt;br /&gt;
%left MULT DIVIDE AND &lt;br /&gt;
%left NOT&lt;br /&gt;
%left UMINUS&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Начальные строки =====&lt;br /&gt;
Он состоит из нескольких неравнозначных частей. Первая часть заключена в символы %{  %} и содержит код на C#, который будет вставлен в создаваемый класс Parser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;    public syntax_tree_node root; // Корневой узел синтаксического дерева &lt;br /&gt;
    public GPPGParser(AbstractScanner&amp;lt;ValueType, LexLocation&amp;gt; scanner) : base(scanner) { }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь root - корневой узел синтаксического дерева программы, он явно описан как поле класса GPPGParser.&lt;br /&gt;
Описываемый во второй строке конструктор носит технический характер - его просто необходимо в этом месте написать. Думаю, что эта строчка связана с неудачным проектированием генератора парсеров GPPG - разработчик забыл в генерируемый класс включить этот нужный конструктор. &lt;br /&gt;
&lt;br /&gt;
Строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;%output=oberon00yacc.cs&amp;lt;/source&amp;gt; &lt;br /&gt;
означает, что компиляция .y-файла с помощью gppg.exe будет осуществляться в файл oberon00yacc.cs&lt;br /&gt;
&lt;br /&gt;
Строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%parsertype GPPGParser&amp;lt;/source&amp;gt;&lt;br /&gt;
говорит о том, что класс парсера будет иметь имя GPPGParser&lt;br /&gt;
&lt;br /&gt;
Строки&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%using PascalABCCompiler.SyntaxTree&lt;br /&gt;
%using PascalABCCompiler.Errors&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser&amp;lt;/source&amp;gt;&lt;br /&gt;
означают подключение в коде парсера соответствующих пространств имен (точки с запятой в конце строк не нужны в отличие от лексического анализатора - видимо, это ошибка проектирования gppg),&lt;br /&gt;
а строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%namespace GPPGParserScanner&amp;lt;/source&amp;gt;&lt;br /&gt;
означает, что класс парсера будет помещен в пространство имен GPPGParserScanner.&lt;br /&gt;
&lt;br /&gt;
Строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;%start module&amp;lt;/source&amp;gt;&lt;br /&gt;
означает, что стартовым в нашей грамматике является нетерминал module (обычно эту строку можно не писать - считается, что стартовым является нетерминал в левой части первого правила).&lt;br /&gt;
&lt;br /&gt;
===== Описание используемых терминалов =====&lt;br /&gt;
Все используемые терминалы должны быть описаны в секции определений следующим образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%token ASSIGN SEMICOLUMN LPAREN RPAREN COLUMN COMMA COLON EXCLAMATION&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Именно по этим определениям формируется перечислимый тип Tokens, который встречался в .lex-файле.&lt;br /&gt;
&lt;br /&gt;
Кроме этого, некоторым терминалам и нетерминалам можно задавать тип.&lt;br /&gt;
&lt;br /&gt;
===== Типы терминалов и нетерминалов =====&lt;br /&gt;
&lt;br /&gt;
Большинство нетерминалов и некоторые терминалы должны иметь тип. Например, терминал ID имеет тип string, а терминал INTNUM - тип int.&lt;br /&gt;
Для задания этих типов используют структуру-объединение вида&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%union  &lt;br /&gt;
{ &lt;br /&gt;
  public bool bVal; &lt;br /&gt;
  public string sVal; &lt;br /&gt;
  public int iVal; &lt;br /&gt;
  public ident id;&lt;br /&gt;
  public ident_list il;&lt;br /&gt;
  public statement st;&lt;br /&gt;
  public statement_list sl;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если с терминалом или нетерминалом необходимо связать значение некоторого типа, то поле этого типа описывается в структуре union. Например, чтобы связать с терминалом ID значение строкового типа, необходимо описать в структуре union  поле &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;public string sVal;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
После этого необходимо описать типизированный терминал следующим образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%token &amp;lt;sVal&amp;gt; ID&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Аналогично чтобы связать с нетерминалом Assignment тип statement, необходимо описать в структуре union поле &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;public statement st;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
и после этого связать поле st с нетерминалом Assignment следующим образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%type &amp;lt;st&amp;gt; Assignment&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Полный код описаний для типов терминалов и нетерминалов, а также полей структуры union имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%union  &lt;br /&gt;
{ &lt;br /&gt;
  public bool bVal; &lt;br /&gt;
  public string sVal; &lt;br /&gt;
  public int iVal; &lt;br /&gt;
  public named_type_reference ntr;&lt;br /&gt;
  public ident_list il;&lt;br /&gt;
  public var_def_statement vds;&lt;br /&gt;
  public variable_definitions vdss;&lt;br /&gt;
  public expression ex;&lt;br /&gt;
  public expression_list el;&lt;br /&gt;
  public ident id;&lt;br /&gt;
  public statement st;&lt;br /&gt;
  public statement_list sl;&lt;br /&gt;
  public declarations decl;&lt;br /&gt;
  public Operators op;&lt;br /&gt;
  public simple_const_definition scd;&lt;br /&gt;
  public consts_definitions_list cdl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
%token &amp;lt;sVal&amp;gt; ID&lt;br /&gt;
%token &amp;lt;iVal&amp;gt; INTNUM &lt;br /&gt;
%token &amp;lt;bVal&amp;gt; TRUE FALSE&lt;br /&gt;
%token &amp;lt;op&amp;gt; PLUS MINUS MULT DIVIDE AND OR LT GT LE GE EQ NE &lt;br /&gt;
%token NOT &lt;br /&gt;
%token ASSIGN SEMICOLUMN LPAREN RPAREN COLUMN COMMA COLON EXCLAMATION&lt;br /&gt;
%token TRUE FALSE ODD BOOLEAN INTEGER&lt;br /&gt;
%token IF THEN ELSE BEGIN END WHILE DO MODULE CONST VAR&lt;br /&gt;
%token INVISIBLE&lt;br /&gt;
&lt;br /&gt;
%type &amp;lt;id&amp;gt; ident&lt;br /&gt;
%type &amp;lt;ntr&amp;gt; type&lt;br /&gt;
%type &amp;lt;ex&amp;gt; expr ConstExpr&lt;br /&gt;
%type &amp;lt;st&amp;gt; Assignment IfStatement WhileStatement WriteStatement Statement  &lt;br /&gt;
%type &amp;lt;st&amp;gt; EmptyStatement ProcCallStatement&lt;br /&gt;
%type &amp;lt;sl&amp;gt; StatementSequence&lt;br /&gt;
%type &amp;lt;decl&amp;gt; Declarations&lt;br /&gt;
%type &amp;lt;el&amp;gt; factparams&lt;br /&gt;
%type &amp;lt;il&amp;gt; IDList&lt;br /&gt;
%type &amp;lt;vds&amp;gt; VarDecl&lt;br /&gt;
%type &amp;lt;vdss&amp;gt; VarDeclarations VarDeclarationsSect&lt;br /&gt;
%type &amp;lt;scd&amp;gt; ConstDecl&lt;br /&gt;
%type &amp;lt;cdl&amp;gt; ConstDeclarations ConstDeclarationsSect&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Приоритеты операций языка =====&lt;br /&gt;
Чтобы не задавать приоритеты операций в грамматике, в Yacc-системах используется секция задания приоритетов операций. Она имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;&lt;br /&gt;
%left LT GT LE GE EQ NE&lt;br /&gt;
%left PLUS MINUS OR&lt;br /&gt;
%left MULT DIVIDE AND &lt;br /&gt;
%left NOT&lt;br /&gt;
%left UMINUS&amp;lt;/source&amp;gt;&lt;br /&gt;
Операции задаются в порядке от самого низкого приоритета до самого высокого.&lt;br /&gt;
&lt;br /&gt;
После такого задания приоритетов операций в грамматике выражений можно писать:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;expr &lt;br /&gt;
  : expr PLUS expr &lt;br /&gt;
  | expr MULT expr &lt;br /&gt;
  | expr AND expr &lt;br /&gt;
  | expr OR expr &lt;br /&gt;
  | expr EQ expr&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
и это не вызовет неоднозначности грамматики.&lt;br /&gt;
&lt;br /&gt;
==== Секция правил грамматики ====&lt;br /&gt;
Попробуем проследить, как распознаются правила грамматики.&lt;br /&gt;
&lt;br /&gt;
===== Правило Assignment =====&lt;br /&gt;
Рассмотрим вначале правило для оператора присваивания:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;Assignment : ident ASSIGN expr {&lt;br /&gt;
	$$ = new assign($1, $3, Operators.Assignment,@$);&lt;br /&gt;
}&lt;br /&gt;
;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь формируется узел синтаксического дерева для оператора присваивания. assign - это класс - наследник syntax_tree_node, который хранит всю синтаксическую информацию об операторе присваивания: идентификатор в левой части, выражение в правой части, тип оператора присваивания (в данном случае обычное присваивание, есть ещё += *= и т.д.), а также позиция конструкции присваивания в тексте программы. В данной записи $$ обозначает нетерминал в левой части, $1 - первый символ (нетерминал или терминал) в правой части правила, $2 - второй символ в правой части правила и т.д. Таким образом, $$ соответствует символу &lt;br /&gt;
Assignment, $1 - символу ident, а $3 - символу expr. Очень важно, что если типы соответствующих символов были прописаны в секции описаний, то выражения $$, $1, $2 и т.д. имеют ровно эти типы. Узнаем в разделе описаний типы Assignment, ident и expr:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%type &amp;lt;st&amp;gt; Assignment &lt;br /&gt;
%type &amp;lt;id&amp;gt; ident&lt;br /&gt;
%type &amp;lt;ex&amp;gt; expr&amp;lt;/source&amp;gt;&lt;br /&gt;
Теперь заглянём в структуру union:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%union  &lt;br /&gt;
{ &lt;br /&gt;
  public expression ex;&lt;br /&gt;
  public ident id;&lt;br /&gt;
  public statement st;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Таким образом, можно сделать вывод, что $$ имеет тип statement, $1 - тип ident, а $3 - тип expression. &lt;br /&gt;
Если окажется, что для какого-то нетерминала или терминала не определен тип, то считается, что соответствующий символ $... имеет тип Object.&lt;br /&gt;
&lt;br /&gt;
Наконец, обратим внимание на символ @$. Он соответствует положению в тексте символа Assignment из левой части правила. Тип символа @$ - LexLocation (это стандартный тип библиотеки GPPG), неявно преобразующийся в тип SourceContext (это тип библиотеки PascalABC.NET; можно для простоты считать, что @$ имеет тип SourceContext). Аналогично @1 - это SourceContext для первого символа в правой части, @2 - для второго и т.д. &lt;br /&gt;
&lt;br /&gt;
Как правило, в большинстве узлов @$ будет передаваться в качестве последнего параметра конструктора.&lt;br /&gt;
&lt;br /&gt;
===== Правило StatementSequence =====&lt;br /&gt;
Правило для StatementSequence имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;StatementSequence &lt;br /&gt;
	: Statement {&lt;br /&gt;
		$$ = new statement_list($1,@$);&lt;br /&gt;
	}&lt;br /&gt;
	| StatementSequence SEMICOLUMN Statement {&lt;br /&gt;
		$1.Add($3,@$);&lt;br /&gt;
		$$ = $1;&lt;br /&gt;
	}&lt;br /&gt;
	;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь формируется список операторов, которые в программе разделены точкой с запятой. Обратим внимание, что за счёт леворекурсивности этого правила первым заканчивается разбор первого оператора Statement - в этот момент мы и создаём statement_list вызовом конструктора. В рекурсивной части считается, что statement_list уже создан, и мы добавляем в него следующий Statement с помощью метода Add класса statement_list.&lt;br /&gt;
&lt;br /&gt;
Во втором правиле есть и ещё одна тонкость, рассмотрим её подробнее - она часто встречается. Рассмотрим ещё раз второе правило внимательнее:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;StatementSequence : StatementSequence SEMICOLUMN Statement &lt;br /&gt;
{&lt;br /&gt;
  $1.Add($3,@$);&lt;br /&gt;
  $$ = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
Здесь надо понимать, что на момент разбора этого правила StatementSequence в левой части ещё не определен, а StatementSequence в правой части, напротив, определен на предыдущих шагах. Поэтому мы вначале к переменной $1, связанной со StatementSequence в правой части и имеющей тип statement_list, добавляем Statement, хранящийся в $3, после чего инициализируем StatementSequence в левой части, присваивая ему StatementSequence из правой части: $$ = $1. За счёт ссылочной модели объектов в C# здесь можно было бы поступить и иначе:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;{&lt;br /&gt;
  $$ = $1;&lt;br /&gt;
  $$.Add($3,@$);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
или даже так:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;{&lt;br /&gt;
  $$ = $1;&lt;br /&gt;
  $1.Add($3,@$);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
- всё равно после присваивания $$ = $1 переменные $$ и $1 указывают на один объект.&lt;br /&gt;
&lt;br /&gt;
===== Правила Statement =====&lt;br /&gt;
Проследим далее за правилами Statement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;Statement: Assignment&lt;br /&gt;
	| IfStatement&lt;br /&gt;
	| WhileStatement&lt;br /&gt;
	| WriteStatement&lt;br /&gt;
	| ProcCallStatement&lt;br /&gt;
	| EmptyStatement&lt;br /&gt;
	;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь нет действий в {}, поэтому по умолчанию всегда подразумевается действие '''$$ := $1''', что нам и надо.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим ещё несколько правил, в которых есть ранее не встречавшиеся моменты.&lt;br /&gt;
&lt;br /&gt;
===== Правило ident =====&lt;br /&gt;
Чтобы не преобразовывать всякий раз строковый ID в узел синтаксического дерева ident, введено правило:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;ident : ID {&lt;br /&gt;
  $$ = new ident($1,@$); &lt;br /&gt;
}&lt;br /&gt;
;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Правило для унарного минуса =====&lt;br /&gt;
Правило для унарного минуса имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;expr : MINUS expr %prec UMINUS {&lt;br /&gt;
  $$ = new un_expr($2,Operators.Minus,@$);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Ключевое слово %prec меняет в рамках одного правила приоритет операции MINUS и делает его таким же, как и у UMINUS. Заметим, что терминал UMINUS - фиктивный - он не может возникнуть при лексическом разборе, и задаётся только в секции приоритетов операций - последним и, значит, самым приоритетным.&lt;br /&gt;
&lt;br /&gt;
===== Правило для всей программы =====&lt;br /&gt;
Вся программа на Oberon00 представляет собой модуль:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;module : MODULE ident SEMICOLUMN mainblock ident COMMA &lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
По этому правилу производятся следующие действия:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;{&lt;br /&gt;
if ($2.name != $5.name)&lt;br /&gt;
  PT.AddError(&amp;quot;Имя &amp;quot;+$5.name+&amp;quot; должно совпадать с именем модуля &amp;quot;+$2.name,@5);&lt;br /&gt;
		&lt;br /&gt;
  // Подключение стандартного модуля Oberon00System, написанного на PascalABC.NET&lt;br /&gt;
  var ul = new uses_list(&amp;quot;Oberon00System&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // Формирование модуля основной программы (используется фабричный метод вместо конструктора)&lt;br /&gt;
  root = program_module.create($2, ul, $4, @$);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Поясним их подробнее. Во-первых, проверяется, совпадает ли имя модуля в начале и в конце программы и если нет. то генерируется семантическая ошибка с помощью PT.AddError (эта ошибка будет добавлена в окно ошибок компиляции). Заметим, что на этапе построения синтаксического дерева можно распознать и диагностировать некоторые синтаксические ошибки.&lt;br /&gt;
&lt;br /&gt;
Затем в начало списка подключаемых модулей добавляется так называемый системный модуль Oberon00System - он написан на Паскале и должен быть откомпилирован в .pcu.&lt;br /&gt;
&lt;br /&gt;
После этого формируется синтаксический узел для всей программы (класс program_module) и присваивается переменной root.&lt;br /&gt;
&lt;br /&gt;
=== Создание системного модуля ===&lt;br /&gt;
Напишем для компилятора Oberon00 простой системный модуль на паскале Obеron00System.pas:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;&lt;br /&gt;
unit Oberon00System;&lt;br /&gt;
&lt;br /&gt;
interface&lt;br /&gt;
&lt;br /&gt;
procedure Print(o: object);&lt;br /&gt;
procedure Println(o: object);&lt;br /&gt;
procedure Println;&lt;br /&gt;
&lt;br /&gt;
implementation&lt;br /&gt;
&lt;br /&gt;
uses System; &lt;br /&gt;
&lt;br /&gt;
procedure Print(o: object);&lt;br /&gt;
begin&lt;br /&gt;
  Console.Write(o);&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
/// Вывести значение&lt;br /&gt;
procedure Println(o: object);&lt;br /&gt;
begin&lt;br /&gt;
  Console.WriteLine(o);&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
procedure Println;&lt;br /&gt;
begin&lt;br /&gt;
  Console.WriteLine;&lt;br /&gt;
end;&lt;br /&gt;
 &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Именно он подключается автоматически к любой программе на Обероне в действиях для правила module:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;...&lt;br /&gt;
var ul = new uses_list(&amp;quot;Oberon00System&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
В принципе, этот модуль можно было написать на самом Обероне, но в Обероне нет возможности прямого доступа к .NET-библиотекам.&lt;br /&gt;
&lt;br /&gt;
Замечательная особенность среды PascalABC.NET - возможность совмещать разные языки программирования в одном проекте на уровне исходных кодов.&lt;br /&gt;
&lt;br /&gt;
=== Обработка ошибок ===&lt;br /&gt;
&lt;br /&gt;
=== Создание парсера-плагина для среды PascalABC.NET ===&lt;br /&gt;
&lt;br /&gt;
=== Инсталляция плагина ===&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C_%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%82%D0%BE%D1%80&amp;diff=1748</id>
		<title>Как создать компилятор</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9A%D0%B0%D0%BA_%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C_%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%82%D0%BE%D1%80&amp;diff=1748"/>
		<updated>2010-07-28T15:01:12Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Что входит в комплект разработчика парсеров */ оформлен wiki-список&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Общие замечания ===&lt;br /&gt;
Сразу скачиваем [http://pascalabc.net/downloads/CompilerConstruction/Oberon00Independent.zip полный комплект разработчика парсеров].&lt;br /&gt;
&lt;br /&gt;
В инфраструктуре PascalABC.NET несложно создавать компиляторы простых языков программирования. Программы, получаемые на таких языках, компилируются в .NET-код и работают, как и любой .NET-код, практически так же производительно, как и машинный.&lt;br /&gt;
&lt;br /&gt;
Для создания компилятора важно понимать следующее. Компилятор имеет несколько внутренних представлений. &lt;br /&gt;
&lt;br /&gt;
* Вначале текст программы разбирается им в так называемое '''синтаксическое дерево'''. Синтаксическое дерево содержит представленный в виде дерева текст программы - семантика на этом уровне не учитывается, проверяется только соответствие синтаксическим конструкциям. Например, на уровне синтаксического дерева не распознаётся ошибка описания двух переменных с одним именем - с точки зрения синтаксиса ошибки нет.&lt;br /&gt;
* Затем синтаксическое дерево переводится в '''семантическое дерево'''. Семантическое дерево учитывает все правила, которые сформулированы в описании языка: в частности, то, что в одном пространстве имен нельзя описать две переменные с одним именем. Кроме этого, в семантическом дереве хранится существенно больше информации, чем в синтаксическом: каждая переменная имеет тип, относится к определенному пространству имен, для подпрограмм проверяется соответствие количества и типов формальных и фактических параметров и т.д. &lt;br /&gt;
:Заметим, что семантическое дерево, если получено, то содержит '''правильную''' программу. Семантическое дерево откомпилированного модуля можно сохранить на диск для ускорения компиляции. В PascalABC.NET так и происходит: если модуль успешно откомпилирован, то он сохраняется в .pcu-файл - это не что иное как записанное на диск семантическое дерево модуля. Если при компиляции основной программы к ней подключен данный модуль, то он повторно не компилируется - его дерево разворачивается из .pcu-файла в память, что гораздо быстрее.&lt;br /&gt;
* Наконец, по семантическому дереву генерируется .NET-код. &lt;br /&gt;
&lt;br /&gt;
Заметим, что такое разделение на синтаксическое и семантическое деревья позволяет упростить процесс создания новых языков. В частности, если язык достаточно простой и похож на Паскаль, то он может быть переведен в синтаксическое дерево, после чего достаточно использовать стандартный преобразователь в семантическое дерево и генератор кода. Для более сложных языков с семантикой, отличной от Паскаля, для некоторых узлов синтаксического дерева необходимо писать свой преобразователь в узлы семантического дерева. в ещё более сложных ситуациях имеющихся узлов семантического дерева может оказаться недостаточно, поэтому придется создавать новые и писать для них генерацию .NET-кода.&lt;br /&gt;
&lt;br /&gt;
Далее мы будем рассматривать только такие языки, в которых текст программы достаточно перевести в синтаксическое дерево, что означает, что семантика конструкций - такая же, как и в Паскале. Например, это означает, что целый тип неявно преобразуется в вещественный, но не наоборот.&lt;br /&gt;
&lt;br /&gt;
Следует отметить, что в большинстве ситуаций за счёт проведения дополнительных проверок перевода в узлы синтаксического дерева вполне достаточно. Далее это станет понятнее на конкретных примерах.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим создание простого языка, который мы назовём Oberon00. Это - подмножество языка Oberon с двумя типами - INTEGER и BOOLEAN - и возможностью вызывать процедуры из внешних модулей.&lt;br /&gt;
&lt;br /&gt;
=== Что нужно для создания компилятора ===&lt;br /&gt;
&lt;br /&gt;
Для создания компилятора, интегрированного в среду PascalABC.NET, нам потребуется создать несколько файлов. В случае языка Oberon00 потребуется создать:&lt;br /&gt;
* Oberon00Parser.dll - библиотеку, содержащую парсер языка (ее следует скопировать в папку установки PascalABC.NET)&lt;br /&gt;
* Oberon00.xhsd - файл подсветки синтаксиса (скопировать в папку PascalABC.NET\Highlighting)&lt;br /&gt;
Кроме этого, обычно требуется системный модуль, содержащий стандартные подпрограммы. Его можно создать на языке Паскаль. Пусть для Оберона00 этот модуль называется Oberon00System.pas. Требуется откомпилировать его в .pcu и затем &lt;br /&gt;
* Скопировать Oberon00System.pas в папку PascalABC.NET\LibSource&lt;br /&gt;
* Скопировать Oberon00System.pcu в папку PascalABC.NET\Lib&lt;br /&gt;
&lt;br /&gt;
Перезапустив среду PascalABC.NET, мы получим новый компилятор.&lt;br /&gt;
&lt;br /&gt;
Для того чтобы сразу увидеть конечный результат, можно скопировать [http://pascalabc.net/downloads/CompilerConstruction/Oberon00Independent.zip полный комплект разработчика парсеров].&lt;br /&gt;
&lt;br /&gt;
=== Что входит в комплект разработчика парсеров ===&lt;br /&gt;
# Папка DLL, содержащая dll из комплекта PascalABC.NET, необходимые для создания парсера.&lt;br /&gt;
# Папка GPLex_GPPG, содержащая генератор компиляторов GPLex+GPPG и документацию к нему.&lt;br /&gt;
# Папка Install, содержащая файлы, которые необходимо скопировать в папку PascalABC.NET для того чтобы в оболочке PascalABc.NET появился компилятор нового языка.&lt;br /&gt;
# Файл generate_all.bat для компиляции файлов .lex и .y в файлы .cs, компиляции проекта и получения библиотеки Oberon00Parser.dll и копирования этого файла в папку Install.&lt;br /&gt;
&lt;br /&gt;
Далее разберем последовательно, как создать парсер.&lt;br /&gt;
&lt;br /&gt;
=== Лексический анализатор GPLex и синтаксический анализатор GPPG ===&lt;br /&gt;
&lt;br /&gt;
Итак, наша задача - разобрать текст программы и по нему получить синтаксическое дерево программы. Такой построитель синтаксического дерева программы будем называть '''парсером''' или '''синтаксическим анализатором'''. Для работы синтаксического анализатора необходим также '''сканер''' или '''лексический анализатор''', который разбивает программу на лексемы - неделимые слова. Например, в языке Паскаль лексемами являются ключевые слова, идентификаторы, числовые и строковые константы, знаки операций (:= &amp;lt;&amp;gt; и т.д.).&lt;br /&gt;
&lt;br /&gt;
Синтаксические анализаторы обычно создаются с помощью специальных программ, называемых '''генераторами компиляторов'''. Одним из наиболее известных генераторов компиляторов является '''Yacc''' (Yet Another Compiler of Compilers) - и в паре с ним работает генератор лексических анализаторов '''Lex'''.&lt;br /&gt;
&lt;br /&gt;
Наиболее полной реализацией Yacc-Lex для .NET, генерирующей код парсера на C#, является GPLex+GPPG, разработанный Queensland University of Technology, Австралия. Вот странички продуктов: [http://plas2003.fit.qut.edu.au/gplex/ GPLex] и [http://plas2003.fit.qut.edu.au/gppg/ GPPG]. Однако, рекомендуется [http://pascalabc.net/downloads/CompilerConstruction/GPLex_GPPG_2010.zip скачать исполнимые файлы отсюда] - они содержат небольшие модификации, связанные с корректной русификацией.&lt;br /&gt;
&lt;br /&gt;
=== Создание лексического анализатора ===&lt;br /&gt;
==== Класс Scanner ====&lt;br /&gt;
Создаваемый в результате компиляции .lex-файла класс '''Scanner''' содержит несколько важных методов и свойств. Рассмотрим их подробнее.&lt;br /&gt;
&lt;br /&gt;
* Функция int '''yylex'''() возвращает уникальный номер лексемы. Все лексемы хранятся в перечислимом типе Tokens. По-существу, возвращается номер константы в перечислимом типе Tokens - например, для лексемы ID возвращается (int)Tokens.ID&lt;br /&gt;
* Помимо уникального номера некоторые лексемы должны возвращать дополнительные значения. Таким значением для идентификатора является его строковое представление, для INTNUM - целое число и т.д. Дополнительное значение для некоторых лексем возвращается в свойстве '''yylval''', которое имеет тип ValueType и содержит ряд полей различных типов - для каждой лексемы может быть предусмотрено своё поле. Например, для лексемы ID предусмотрено поле string sVal, а для лексемы INTNUM - поле iVal.&lt;br /&gt;
Объяснение того, как это поле сопоставляется с типом лексемы, отложим до знакомства с содержимым файла .y&lt;br /&gt;
* Свойство '''yytext''' возвращает строковое представление текущей лексемы&lt;br /&gt;
* Свойство '''yylloc''' содержит положение лексемы в тексте, задаваемое типом LexLocation из пространства имен QUT.Gppg. Он хранит координаты начала и конца лексемы&lt;br /&gt;
&lt;br /&gt;
==== Файл .lex ====&lt;br /&gt;
Лексический анализатор обычно создается в файле с расширением .lex. Он имеет вид:&lt;br /&gt;
&lt;br /&gt;
 Определения&lt;br /&gt;
 %%&lt;br /&gt;
 Правила&lt;br /&gt;
 %%&lt;br /&gt;
 Пользовательский код&lt;br /&gt;
&lt;br /&gt;
Рассмотрим файл лексического анализатора Oberon00.lex:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%namespace GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser;&lt;br /&gt;
&lt;br /&gt;
Alpha [a-zA-Z_]&lt;br /&gt;
Digit [0-9]&lt;br /&gt;
AlphaDigit {Alpha}|{Digit}&lt;br /&gt;
INTNUM {Digit}+&lt;br /&gt;
ID {Alpha}{AlphaDigit}* &lt;br /&gt;
&lt;br /&gt;
%%&lt;br /&gt;
&lt;br /&gt;
&amp;quot;:=&amp;quot; { return (int)Tokens.ASSIGN; }&lt;br /&gt;
&amp;quot;;&amp;quot; { return (int)Tokens.SEMICOLUMN; }&lt;br /&gt;
&amp;quot;-&amp;quot; { return (int)Tokens.MINUS; }&lt;br /&gt;
&amp;quot;+&amp;quot; { return (int)Tokens.PLUS; }&lt;br /&gt;
&amp;quot;*&amp;quot; { return (int)Tokens.MULT; }&lt;br /&gt;
&amp;quot;/&amp;quot; { return (int)Tokens.DIVIDE; }&lt;br /&gt;
&amp;quot;&amp;lt;&amp;quot; { return (int)Tokens.LT; }&lt;br /&gt;
&amp;quot;&amp;gt;&amp;quot; { return (int)Tokens.GT; }&lt;br /&gt;
&amp;quot;&amp;lt;=&amp;quot; { return (int)Tokens.LE; }&lt;br /&gt;
&amp;quot;&amp;gt;=&amp;quot; { return (int)Tokens.GE; }&lt;br /&gt;
&amp;quot;=&amp;quot; { return (int)Tokens.EQ; }&lt;br /&gt;
&amp;quot;#&amp;quot; { return (int)Tokens.NE; }&lt;br /&gt;
&amp;quot;(&amp;quot; { return (int)Tokens.LPAREN; }&lt;br /&gt;
&amp;quot;)&amp;quot; { return (int)Tokens.RPAREN; }&lt;br /&gt;
&amp;quot;,&amp;quot; { return (int)Tokens.COLUMN; }&lt;br /&gt;
&amp;quot;~&amp;quot; { return (int)Tokens.NOT; }&lt;br /&gt;
&amp;quot;&amp;amp;&amp;quot; { return (int)Tokens.AND; }&lt;br /&gt;
&amp;quot;.&amp;quot; { return (int)Tokens.COMMA; }&lt;br /&gt;
&amp;quot;:&amp;quot; { return (int)Tokens.COLON; }&lt;br /&gt;
&amp;quot;!&amp;quot; { return (int)Tokens.EXCLAMATION; }&lt;br /&gt;
\x01 { return (int)Tokens.INVISIBLE; }&lt;br /&gt;
&lt;br /&gt;
{ID}  { &lt;br /&gt;
  int res = Keywords.KeywordOrIDToken(yytext);&lt;br /&gt;
  if (res == (int)Tokens.ID)&lt;br /&gt;
    yylval.sVal = yytext;&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
{INTNUM} { &lt;br /&gt;
  yylval.iVal = int.Parse(yytext); &lt;br /&gt;
  return (int)Tokens.INTNUM; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
%{&lt;br /&gt;
  yylloc = new QUT.Gppg.LexLocation(tokLin, tokCol, tokELin, tokECol);&lt;br /&gt;
%}&lt;br /&gt;
&lt;br /&gt;
%%&lt;br /&gt;
&lt;br /&gt;
public override void yyerror(string format, params object[] args) &lt;br /&gt;
{&lt;br /&gt;
  string errorMsg = PT.CreateErrorString(args);&lt;br /&gt;
  PT.AddError(errorMsg,yylloc);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После обработки генератором лексических анализаторов GPLex мы получим одноименный cs-файл Oberon00.cs, содержащий класс Scanner лексического анализатора.&lt;br /&gt;
&lt;br /&gt;
Разберем содержимое .lex-файла подробнее.&lt;br /&gt;
&lt;br /&gt;
==== Секция определений ====&lt;br /&gt;
&lt;br /&gt;
Вначале рассмотрим первую часть, содержащую определения лексем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%namespace GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser;&lt;br /&gt;
&lt;br /&gt;
Alpha [a-zA-Z_]&lt;br /&gt;
Digit [0-9]&lt;br /&gt;
AlphaDigit {Alpha}|{Digit}&lt;br /&gt;
INTNUM {Digit}+&lt;br /&gt;
ID {Alpha}{AlphaDigit}* &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Строка %namespace GPPGParserScanner означает, что класс сканера будет помещен в пространство имен GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
Строка %using PascalABCCompiler.Oberon00Parser; приводит к генерации соответствующей строки в cs-файле. Пространство имен PascalABCCompiler.Oberon00Parser полностью находится во вспомогательном файле Oberon00ParserTools.cs, который содержит различные глобальные описания и подпрограммы (подробнее содержимое этого файла будет объяснено позже).&lt;br /&gt;
&lt;br /&gt;
Далее идёт описание некоторых лексем в виде регулярных выражений. Например, целое число INTNUM представляет собой последовательность одной или более цифр {Digit}+, а идентификатор ID - последовательность букв или цифр, начинающуюся с буквы: {Alpha}{AlphaDigit}*&lt;br /&gt;
&lt;br /&gt;
==== Секция правил ====&lt;br /&gt;
&lt;br /&gt;
Вторая секция (между первым и вторым %%) является основной и содержит действия, которые необходимо выполнить, когда распознана та или иная лексема.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;&amp;quot;:=&amp;quot; { return (int)Tokens.ASSIGN; }&lt;br /&gt;
&amp;quot;;&amp;quot; { return (int)Tokens.SEMICOLUMN; }&lt;br /&gt;
&amp;quot;-&amp;quot; { return (int)Tokens.MINUS; }&lt;br /&gt;
&amp;quot;+&amp;quot; { return (int)Tokens.PLUS; }&lt;br /&gt;
&amp;quot;*&amp;quot; { return (int)Tokens.MULT; }&lt;br /&gt;
&amp;quot;/&amp;quot; { return (int)Tokens.DIVIDE; }&lt;br /&gt;
&amp;quot;&amp;lt;&amp;quot; { return (int)Tokens.LT; }&lt;br /&gt;
&amp;quot;&amp;gt;&amp;quot; { return (int)Tokens.GT; }&lt;br /&gt;
&amp;quot;&amp;lt;=&amp;quot; { return (int)Tokens.LE; }&lt;br /&gt;
&amp;quot;&amp;gt;=&amp;quot; { return (int)Tokens.GE; }&lt;br /&gt;
&amp;quot;=&amp;quot; { return (int)Tokens.EQ; }&lt;br /&gt;
&amp;quot;#&amp;quot; { return (int)Tokens.NE; }&lt;br /&gt;
&amp;quot;(&amp;quot; { return (int)Tokens.LPAREN; }&lt;br /&gt;
&amp;quot;)&amp;quot; { return (int)Tokens.RPAREN; }&lt;br /&gt;
&amp;quot;,&amp;quot; { return (int)Tokens.COLUMN; }&lt;br /&gt;
&amp;quot;~&amp;quot; { return (int)Tokens.NOT; }&lt;br /&gt;
&amp;quot;&amp;amp;&amp;quot; { return (int)Tokens.AND; }&lt;br /&gt;
&amp;quot;.&amp;quot; { return (int)Tokens.COMMA; }&lt;br /&gt;
&amp;quot;:&amp;quot; { return (int)Tokens.COLON; }&lt;br /&gt;
&amp;quot;!&amp;quot; { return (int)Tokens.EXCLAMATION; }&lt;br /&gt;
\x01 { return (int)Tokens.INVISIBLE; }&lt;br /&gt;
&lt;br /&gt;
{ID}  { &lt;br /&gt;
  int res = Keywords.KeywordOrIDToken(yytext);&lt;br /&gt;
  if (res == (int)Tokens.ID)&lt;br /&gt;
    yylval.sVal = yytext;&lt;br /&gt;
  return res;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
{INTNUM} { &lt;br /&gt;
  yylval.iVal = int.Parse(yytext); &lt;br /&gt;
  return (int)Tokens.INTNUM; &lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь приведены действия в случае если встречена та или иная лексема. Большинство действий просто возвращает порядковый номер лексемы в перечислимом типе Tokens. &lt;br /&gt;
&lt;br /&gt;
Последняя лексема - INVISIBLE - символ с кодом 1 - необязательна и является вспомогательной для компилятора выражений языка (необходим для поддержки IntelliSense).&lt;br /&gt;
&lt;br /&gt;
В случае лексемы INTNUM в поле yylval.iVal дополнительно возвращается целое число, соответствующее лексеме: int.Parse(yytext). Обратим внимание, что здесь нет никакой защиты от неправильного преобразования (например, в случае очень длинного целого). Причина - чтобы пока не усложнять изложение обработкой ошибок.&lt;br /&gt;
&lt;br /&gt;
В случае лексемы ID вначале проверяется, не является ли она ключевым словом, и если является, то возвращается целое, соответствующее лексеме ключевого слова (например, для BEGIN возвращается (int)Tokens.BEGIN), а если не является, то возвращается (int)Tokens.ID и параллельно в поле yylval.sVal возвращается строковое представление идентификатора. Именно на этом уровне мы можем задать, чувствителен ли наш язык к регистру (например, преобразованием всех идентификаторов к UpperCase).&lt;br /&gt;
&lt;br /&gt;
Секция правил завершается следующим кодом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%{&lt;br /&gt;
  yylloc = new QUT.Gppg.LexLocation(tokLin, tokCol, tokELin, tokECol);&lt;br /&gt;
%}&amp;lt;/source&amp;gt;&lt;br /&gt;
Этот код позволяет для каждой лексемы получать её местоположение в тексте. Вникать в этот код не надо - он просто необходим.&lt;br /&gt;
&lt;br /&gt;
==== Секция пользовательского кода ====&lt;br /&gt;
Наконец, в секции пользовательского кода содержится единственный переопределенный в классе Scanner метод &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;public override void yyerror(string format, params object[] args) &lt;br /&gt;
{&lt;br /&gt;
  string errorMsg = PT.CreateErrorString(args);&lt;br /&gt;
  PT.AddError(errorMsg,yylloc);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Он вызывается всякий раз когда происходит синтаксическая ошибка. Здесь мы пользуемся услугами статического класса PT, который находится в файле Oberon00ParserTools.cs. PT.CreateErrorString(args) формирует строку ошибки, а PT.AddError(errorMsg,yylloc); добавляет ошибку в список ошибок компиляции PascalABC.NET. Здесь интересно то, что в случае автоматического вызова yyerror в args попадают токены, которые ожидались после данного.&lt;br /&gt;
&lt;br /&gt;
==== Генерация кода лексического анализатора ====&lt;br /&gt;
Для генерации кода лексического анализатора следует выполнить команду:&lt;br /&gt;
 gplex.exe /unicode oberon00.lex&lt;br /&gt;
Здесь gplex.exe - генератор лексических анализаторов, хранящийся в папке GPLex_GPPG, параметр /unicode указывает на то, что созданный лексический анализатор будет распознавать файлы в кодировке unicode (заметим, что он будет распознавать и однобайтные кодировки).&lt;br /&gt;
&lt;br /&gt;
=== Создание синтаксического анализатора ===&lt;br /&gt;
==== Общая информация ====&lt;br /&gt;
Синтаксический анализатор - ядро нашего компилятора. Он строится по .y - файлу, в котором записываются правила '''грамматики языка''' и действия, которые выполняются по этим правилам. Каждое из действий создает соответствующий узел в синтаксическом дереве. Все узлы синтаксического дерева представляют собой иерархию классов с общим предком syntax_tree_node и описаны во внешней библиотеке SyntaxTree.dll. &lt;br /&gt;
&lt;br /&gt;
Для понимания дальнейшего следует иметь некоторое начальное представление о грамматиках языков программирования, о том, как они порождают языки и об автоматах, которые проверяют, принадлежит ли данная цепочка символов языку, порождаемому грамматикой. Более конкретно, следует представлять, что такое '''контекстно-свободная грамматика''', что Yacc-Lex  системы работают с так называемыми LL(k) грамматиками, являющимися достаточно широким подмножеством контекстно-свободных грамматик. &lt;br /&gt;
&lt;br /&gt;
В процессе построения грамматик придется сталкиваться с недостижимыми и циклическими продукциями грамматик, неоднозначными грамматиками. Необходимо уметь исправлять подобные ситуации хотя бы методом проб и ошибок. Особенно необходимо понимать, что Shift-Reduce - конфликты грамматик допустимы и разрешаются в пользу более длинной правой части продукции, а Reduce-Reduce - конфликты недопустимы и свидетельствуют об ошибках проектирования грамматики.&lt;br /&gt;
&lt;br /&gt;
Соответствующую информацию можно прочитать в книге Ахо &amp;quot;Компиляторы: принципы, технологии, инструменты&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==== Некоторые классы синтаксического дерева ====&lt;br /&gt;
Как уже было сказано, синтаксическое дерево состоит из объектов классов - наследников syntax_tree_node. Перечислим некоторые важные классы - наследники syntax_tree_node, которые будут нами использованы для реализации парсера Оберона00:&lt;br /&gt;
&lt;br /&gt;
 ident                   - идентификатор&lt;br /&gt;
 ident_list              - список идентификаторов&lt;br /&gt;
 uses_list               - список модулей и пространств имен в секции uses&lt;br /&gt;
 unit_or_namespace       - модуль или пространство имен&lt;br /&gt;
 block                   - блок &lt;br /&gt;
 program_module          - программа&lt;br /&gt;
 int32_const             - целая константа&lt;br /&gt;
 bool_const              - логическая константа&lt;br /&gt;
 expression              - выражение (базовый класс для всех выражений)&lt;br /&gt;
 expression_list         - список выражений&lt;br /&gt;
 un_expr                 - унарное ввыражение&lt;br /&gt;
 bin_expr                - бинарное выражение&lt;br /&gt;
 assign                  - оператор присваивания&lt;br /&gt;
 if_node                 - условный оператор&lt;br /&gt;
 while_node              - оператор цикла while&lt;br /&gt;
 empty_statement         - пустой оператор&lt;br /&gt;
 method_call             - вызов метода или внешней подпрограммы&lt;br /&gt;
 statement_list          - последовательность операторов&lt;br /&gt;
 named_type_reference    - имя типа&lt;br /&gt;
 declarations            - описания&lt;br /&gt;
 var_def_statement       - описания переменных с одним типом&lt;br /&gt;
 variable_definitions    - секция объявлений переменных&lt;br /&gt;
 simple_const_definition - определение именованной константы&lt;br /&gt;
 consts_definitions_list - секция определений именованных констант&lt;br /&gt;
&lt;br /&gt;
Каждый класс имеет свои поля, все необходимые поля заполняются обычно в конструкторе класса. В базовом классе syntax_tree_node имеется поле source_context типа SourceContext, определяющее местоположение конструкции, соответствующей синтаксическому узлу, в тексте программы. Именно поэтому каждый конструктор синтаксического дерева содержит последним параметром объект класса SourceContext.&lt;br /&gt;
&lt;br /&gt;
Просмотреть все узлы синтаксического дерева, их свойства и методы можно с помощью утилиты&lt;br /&gt;
[http://pascalabc.net/downloads/CompilerConstruction/NodesGenReader.zip Просмотрщик классов синтаксического дерева]&lt;br /&gt;
&lt;br /&gt;
==== Правила грамматики ====&lt;br /&gt;
Правила грамматики содержат терминальные символы (терминалы), которые распознаются лексическим анализатором, и нетерминальные символы (нетерминалы). Каждое правило грамматики выражает нетерминал через другие терминалы и нетерминалы. Например, для оператора присваивания имеется следующее правило:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;AssignOperator : ID ASSIGN expr ;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь нетерминал AssignOperator выражается через терминалы ID и ASSIGN и нетерминал expr, который будет определен в другом правиле.&lt;br /&gt;
&lt;br /&gt;
Нетерминал, с которого начинается разбор, называется стартовым символом грамматики. Этот нетерминал описывает всю программу. В нашей грамматике это module, а соответствующее стартовое правило имеет вид:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;module : MODULE ident SEMICOLUMN Declarations BEGIN StatementSequence END ident COMMA&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В конечном итоге все нетерминалы выражаются через терминалы, образующие основную программу. Соответствующий процесс получения программы из стартового символа называется выводом, а правила грамматики - правилами вывода.&lt;br /&gt;
&lt;br /&gt;
В каждом правиле может быть несколько альтернатив, разделяемых символом |:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;IfStatement &lt;br /&gt;
	: IF expr THEN StatementSequence END &lt;br /&gt;
	| IF expr THEN StatementSequence ELSE StatementSequence END &lt;br /&gt;
	;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Правила могут быть рекурсивными. Например, правило для фактических параметров имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;factparams&lt;br /&gt;
	: expr &lt;br /&gt;
	| factparams COLUMN expr &lt;br /&gt;
	;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обратим внимание, что данное правило является леворекурсивным, поскольку рекурсивное вхождение нетерминала factparams в правую часть правила находится с левой стороны. Для рассматриваемых нами генераторов компиляторов леворекурсивные правила являются предпочтительнее праворекурсивных.&lt;br /&gt;
&lt;br /&gt;
==== Формат .y-файла ====&lt;br /&gt;
.y-файл имеет такой же формат, как и .lex:&lt;br /&gt;
 Определения&lt;br /&gt;
 %%&lt;br /&gt;
 Правила&lt;br /&gt;
 %%&lt;br /&gt;
 Пользовательский код&lt;br /&gt;
&lt;br /&gt;
==== Раздел определений .y-файла ====&lt;br /&gt;
===== Общий вид =====&lt;br /&gt;
Рассмотрим раздел определений файла Oberon00.y&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%{&lt;br /&gt;
    public syntax_tree_node root; // Корневой узел синтаксического дерева &lt;br /&gt;
    public GPPGParser(AbstractScanner&amp;lt;ValueType, LexLocation&amp;gt; scanner) : base(scanner) { }&lt;br /&gt;
%}&lt;br /&gt;
&lt;br /&gt;
%output=oberon00yacc.cs &lt;br /&gt;
&lt;br /&gt;
%parsertype GPPGParser&lt;br /&gt;
&lt;br /&gt;
%union  &lt;br /&gt;
{ &lt;br /&gt;
  public bool bVal; &lt;br /&gt;
  public string sVal; &lt;br /&gt;
  public int iVal; &lt;br /&gt;
  public named_type_reference ntr;&lt;br /&gt;
  public ident_list il;&lt;br /&gt;
  public var_def_statement vds;&lt;br /&gt;
  public variable_definitions vdss;&lt;br /&gt;
  public expression ex;&lt;br /&gt;
  public expression_list el;&lt;br /&gt;
  public ident id;&lt;br /&gt;
  public statement st;&lt;br /&gt;
  public statement_list sl;&lt;br /&gt;
  public declarations decl;&lt;br /&gt;
  public Operators op;&lt;br /&gt;
  public simple_const_definition scd;&lt;br /&gt;
  public consts_definitions_list cdl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
%using PascalABCCompiler.SyntaxTree&lt;br /&gt;
%using PascalABCCompiler.Errors&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser&lt;br /&gt;
&lt;br /&gt;
%namespace GPPGParserScanner&lt;br /&gt;
&lt;br /&gt;
%start module&lt;br /&gt;
&lt;br /&gt;
%token &amp;lt;sVal&amp;gt; ID&lt;br /&gt;
%token &amp;lt;iVal&amp;gt; INTNUM &lt;br /&gt;
%token &amp;lt;bVal&amp;gt; TRUE FALSE&lt;br /&gt;
%token &amp;lt;op&amp;gt; PLUS MINUS MULT DIVIDE AND OR LT GT LE GE EQ NE &lt;br /&gt;
%token NOT &lt;br /&gt;
%token ASSIGN SEMICOLUMN LPAREN RPAREN COLUMN COMMA COLON EXCLAMATION&lt;br /&gt;
%token TRUE FALSE ODD BOOLEAN INTEGER&lt;br /&gt;
%token IF THEN ELSE BEGIN END WHILE DO MODULE CONST VAR&lt;br /&gt;
%token INVISIBLE&lt;br /&gt;
&lt;br /&gt;
%type &amp;lt;id&amp;gt; ident&lt;br /&gt;
%type &amp;lt;ntr&amp;gt; type&lt;br /&gt;
%type &amp;lt;ex&amp;gt; expr ConstExpr&lt;br /&gt;
%type &amp;lt;st&amp;gt; Assignment IfStatement WhileStatement WriteStatement Statement  &lt;br /&gt;
%type &amp;lt;st&amp;gt; EmptyStatement ProcCallStatement&lt;br /&gt;
%type &amp;lt;sl&amp;gt; StatementSequence&lt;br /&gt;
%type &amp;lt;decl&amp;gt; Declarations&lt;br /&gt;
%type &amp;lt;el&amp;gt; factparams&lt;br /&gt;
%type &amp;lt;il&amp;gt; IDList&lt;br /&gt;
%type &amp;lt;vds&amp;gt; VarDecl&lt;br /&gt;
%type &amp;lt;vdss&amp;gt; VarDeclarations VarDeclarationsSect&lt;br /&gt;
%type &amp;lt;scd&amp;gt; ConstDecl&lt;br /&gt;
%type &amp;lt;cdl&amp;gt; ConstDeclarations ConstDeclarationsSect&lt;br /&gt;
&lt;br /&gt;
%left LT GT LE GE EQ NE&lt;br /&gt;
%left PLUS MINUS OR&lt;br /&gt;
%left MULT DIVIDE AND &lt;br /&gt;
%left NOT&lt;br /&gt;
%left UMINUS&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Начальные строки =====&lt;br /&gt;
Он состоит из нескольких неравнозначных частей. Первая часть заключена в символы %{  %} и содержит код на C#, который будет вставлен в создаваемый класс Parser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;    public syntax_tree_node root; // Корневой узел синтаксического дерева &lt;br /&gt;
    public GPPGParser(AbstractScanner&amp;lt;ValueType, LexLocation&amp;gt; scanner) : base(scanner) { }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь root - корневой узел синтаксического дерева программы, он явно описан как поле класса GPPGParser.&lt;br /&gt;
Описываемый во второй строке конструктор носит технический характер - его просто необходимо в этом месте написать. Думаю, что эта строчка связана с неудачным проектированием генератора парсеров GPPG - разработчик забыл в генерируемый класс включить этот нужный конструктор. &lt;br /&gt;
&lt;br /&gt;
Строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;%output=oberon00yacc.cs&amp;lt;/source&amp;gt; &lt;br /&gt;
означает, что компиляция .y-файла с помощью gppg.exe будет осуществляться в файл oberon00yacc.cs&lt;br /&gt;
&lt;br /&gt;
Строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%parsertype GPPGParser&amp;lt;/source&amp;gt;&lt;br /&gt;
говорит о том, что класс парсера будет иметь имя GPPGParser&lt;br /&gt;
&lt;br /&gt;
Строки&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%using PascalABCCompiler.SyntaxTree&lt;br /&gt;
%using PascalABCCompiler.Errors&lt;br /&gt;
%using PascalABCCompiler.Oberon00Parser&amp;lt;/source&amp;gt;&lt;br /&gt;
означают подключение в коде парсера соответствующих пространств имен (точки с запятой в конце строк не нужны в отличие от лексического анализатора - видимо, это ошибка проектирования gppg),&lt;br /&gt;
а строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%namespace GPPGParserScanner&amp;lt;/source&amp;gt;&lt;br /&gt;
означает, что класс парсера будет помещен в пространство имен GPPGParserScanner.&lt;br /&gt;
&lt;br /&gt;
Строка &lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;%start module&amp;lt;/source&amp;gt;&lt;br /&gt;
означает, что стартовым в нашей грамматике является нетерминал module (обычно эту строку можно не писать - считается, что стартовым является нетерминал в левой части первого правила).&lt;br /&gt;
&lt;br /&gt;
===== Описание используемых терминалов =====&lt;br /&gt;
Все используемые терминалы должны быть описаны в секции определений следующим образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%token ASSIGN SEMICOLUMN LPAREN RPAREN COLUMN COMMA COLON EXCLAMATION&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Именно по этим определениям формируется перечислимый тип Tokens, который встречался в .lex-файле.&lt;br /&gt;
&lt;br /&gt;
Кроме этого, некоторым терминалам и нетерминалам можно задавать тип.&lt;br /&gt;
&lt;br /&gt;
===== Типы терминалов и нетерминалов =====&lt;br /&gt;
&lt;br /&gt;
Большинство нетерминалов и некоторые терминалы должны иметь тип. Например, терминал ID имеет тип string, а терминал INTNUM - тип int.&lt;br /&gt;
Для задания этих типов используют структуру-объединение вида&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%union  &lt;br /&gt;
{ &lt;br /&gt;
  public bool bVal; &lt;br /&gt;
  public string sVal; &lt;br /&gt;
  public int iVal; &lt;br /&gt;
  public ident id;&lt;br /&gt;
  public ident_list il;&lt;br /&gt;
  public statement st;&lt;br /&gt;
  public statement_list sl;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если с терминалом или нетерминалом необходимо связать значение некоторого типа, то поле этого типа описывается в структуре union. Например, чтобы связать с терминалом ID значение строкового типа, необходимо описать в структуре union  поле &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;public string sVal;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
После этого необходимо описать типизированный терминал следующим образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%token &amp;lt;sVal&amp;gt; ID&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Аналогично чтобы связать с нетерминалом Assignment тип statement, необходимо описать в структуре union поле &lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;public statement st;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
и после этого связать поле st с нетерминалом Assignment следующим образом:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%type &amp;lt;st&amp;gt; Assignment&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Полный код описаний для типов терминалов и нетерминалов, а также полей структуры union имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%union  &lt;br /&gt;
{ &lt;br /&gt;
  public bool bVal; &lt;br /&gt;
  public string sVal; &lt;br /&gt;
  public int iVal; &lt;br /&gt;
  public named_type_reference ntr;&lt;br /&gt;
  public ident_list il;&lt;br /&gt;
  public var_def_statement vds;&lt;br /&gt;
  public variable_definitions vdss;&lt;br /&gt;
  public expression ex;&lt;br /&gt;
  public expression_list el;&lt;br /&gt;
  public ident id;&lt;br /&gt;
  public statement st;&lt;br /&gt;
  public statement_list sl;&lt;br /&gt;
  public declarations decl;&lt;br /&gt;
  public Operators op;&lt;br /&gt;
  public simple_const_definition scd;&lt;br /&gt;
  public consts_definitions_list cdl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
%token &amp;lt;sVal&amp;gt; ID&lt;br /&gt;
%token &amp;lt;iVal&amp;gt; INTNUM &lt;br /&gt;
%token &amp;lt;bVal&amp;gt; TRUE FALSE&lt;br /&gt;
%token &amp;lt;op&amp;gt; PLUS MINUS MULT DIVIDE AND OR LT GT LE GE EQ NE &lt;br /&gt;
%token NOT &lt;br /&gt;
%token ASSIGN SEMICOLUMN LPAREN RPAREN COLUMN COMMA COLON EXCLAMATION&lt;br /&gt;
%token TRUE FALSE ODD BOOLEAN INTEGER&lt;br /&gt;
%token IF THEN ELSE BEGIN END WHILE DO MODULE CONST VAR&lt;br /&gt;
%token INVISIBLE&lt;br /&gt;
&lt;br /&gt;
%type &amp;lt;id&amp;gt; ident&lt;br /&gt;
%type &amp;lt;ntr&amp;gt; type&lt;br /&gt;
%type &amp;lt;ex&amp;gt; expr ConstExpr&lt;br /&gt;
%type &amp;lt;st&amp;gt; Assignment IfStatement WhileStatement WriteStatement Statement  &lt;br /&gt;
%type &amp;lt;st&amp;gt; EmptyStatement ProcCallStatement&lt;br /&gt;
%type &amp;lt;sl&amp;gt; StatementSequence&lt;br /&gt;
%type &amp;lt;decl&amp;gt; Declarations&lt;br /&gt;
%type &amp;lt;el&amp;gt; factparams&lt;br /&gt;
%type &amp;lt;il&amp;gt; IDList&lt;br /&gt;
%type &amp;lt;vds&amp;gt; VarDecl&lt;br /&gt;
%type &amp;lt;vdss&amp;gt; VarDeclarations VarDeclarationsSect&lt;br /&gt;
%type &amp;lt;scd&amp;gt; ConstDecl&lt;br /&gt;
%type &amp;lt;cdl&amp;gt; ConstDeclarations ConstDeclarationsSect&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Приоритеты операций языка =====&lt;br /&gt;
Чтобы не задавать приоритеты операций в грамматике, в Yacc-системах используется секция задания приоритетов операций. Она имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;&lt;br /&gt;
%left LT GT LE GE EQ NE&lt;br /&gt;
%left PLUS MINUS OR&lt;br /&gt;
%left MULT DIVIDE AND &lt;br /&gt;
%left NOT&lt;br /&gt;
%left UMINUS&amp;lt;/source&amp;gt;&lt;br /&gt;
Операции задаются в порядке от самого низкого приоритета до самого высокого.&lt;br /&gt;
&lt;br /&gt;
После такого задания приоритетов операций в грамматике выражений можно писать:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;expr &lt;br /&gt;
  : expr PLUS expr &lt;br /&gt;
  | expr MULT expr &lt;br /&gt;
  | expr AND expr &lt;br /&gt;
  | expr OR expr &lt;br /&gt;
  | expr EQ expr&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
и это не вызовет неоднозначности грамматики.&lt;br /&gt;
&lt;br /&gt;
==== Секция правил грамматики ====&lt;br /&gt;
Попробуем проследить, как распознаются правила грамматики.&lt;br /&gt;
&lt;br /&gt;
===== Правило Assignment =====&lt;br /&gt;
Рассмотрим вначале правило для оператора присваивания:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;Assignment : ident ASSIGN expr {&lt;br /&gt;
	$$ = new assign($1, $3, Operators.Assignment,@$);&lt;br /&gt;
}&lt;br /&gt;
;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь формируется узел синтаксического дерева для оператора присваивания. assign - это класс - наследник syntax_tree_node, который хранит всю синтаксическую информацию об операторе присваивания: идентификатор в левой части, выражение в правой части, тип оператора присваивания (в данном случае обычное присваивание, есть ещё += *= и т.д.), а также позиция конструкции присваивания в тексте программы. В данной записи $$ обозначает нетерминал в левой части, $1 - первый символ (нетерминал или терминал) в правой части правила, $2 - второй символ в правой части правила и т.д. Таким образом, $$ соответствует символу &lt;br /&gt;
Assignment, $1 - символу ident, а $3 - символу expr. Очень важно, что если типы соответствующих символов были прописаны в секции описаний, то выражения $$, $1, $2 и т.д. имеют ровно эти типы. Узнаем в разделе описаний типы Assignment, ident и expr:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%type &amp;lt;st&amp;gt; Assignment &lt;br /&gt;
%type &amp;lt;id&amp;gt; ident&lt;br /&gt;
%type &amp;lt;ex&amp;gt; expr&amp;lt;/source&amp;gt;&lt;br /&gt;
Теперь заглянём в структуру union:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;%union  &lt;br /&gt;
{ &lt;br /&gt;
  public expression ex;&lt;br /&gt;
  public ident id;&lt;br /&gt;
  public statement st;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Таким образом, можно сделать вывод, что $$ имеет тип statement, $1 - тип ident, а $3 - тип expression. &lt;br /&gt;
Если окажется, что для какого-то нетерминала или терминала не определен тип, то считается, что соответствующий символ $... имеет тип Object.&lt;br /&gt;
&lt;br /&gt;
Наконец, обратим внимание на символ @$. Он соответствует положению в тексте символа Assignment из левой части правила. Тип символа @$ - LexLocation (это стандартный тип библиотеки GPPG), неявно преобразующийся в тип SourceContext (это тип библиотеки PascalABC.NET; можно для простоты считать, что @$ имеет тип SourceContext). Аналогично @1 - это SourceContext для первого символа в правой части, @2 - для второго и т.д. &lt;br /&gt;
&lt;br /&gt;
Как правило, в большинстве узлов @$ будет передаваться в качестве последнего параметра конструктора.&lt;br /&gt;
&lt;br /&gt;
===== Правило StatementSequence =====&lt;br /&gt;
Правило для StatementSequence имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;StatementSequence &lt;br /&gt;
	: Statement {&lt;br /&gt;
		$$ = new statement_list($1,@$);&lt;br /&gt;
	}&lt;br /&gt;
	| StatementSequence SEMICOLUMN Statement {&lt;br /&gt;
		$1.Add($3,@$);&lt;br /&gt;
		$$ = $1;&lt;br /&gt;
	}&lt;br /&gt;
	;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь формируется список операторов, которые в программе разделены точкой с запятой. Обратим внимание, что за счёт леворекурсивности этого правила первым заканчивается разбор первого оператора Statement - в этот момент мы и создаём statement_list вызовом конструктора. В рекурсивной части считается, что statement_list уже создан, и мы добавляем в него следующий Statement с помощью метода Add класса statement_list.&lt;br /&gt;
&lt;br /&gt;
Во втором правиле есть и ещё одна тонкость, рассмотрим её подробнее - она часто встречается. Рассмотрим ещё раз второе правило внимательнее:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;StatementSequence : StatementSequence SEMICOLUMN Statement &lt;br /&gt;
{&lt;br /&gt;
  $1.Add($3,@$);&lt;br /&gt;
  $$ = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
Здесь надо понимать, что на момент разбора этого правила StatementSequence в левой части ещё не определен, а StatementSequence в правой части, напротив, определен на предыдущих шагах. Поэтому мы вначале к переменной $1, связанной со StatementSequence в правой части и имеющей тип statement_list, добавляем Statement, хранящийся в $3, после чего инициализируем StatementSequence в левой части, присваивая ему StatementSequence из правой части: $$ = $1. За счёт ссылочной модели объектов в C# здесь можно было бы поступить и иначе:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;{&lt;br /&gt;
  $$ = $1;&lt;br /&gt;
  $$.Add($3,@$);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
или даже так:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;{&lt;br /&gt;
  $$ = $1;&lt;br /&gt;
  $1.Add($3,@$);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
- всё равно после присваивания $$ = $1 переменные $$ и $1 указывают на один объект.&lt;br /&gt;
&lt;br /&gt;
===== Правила Statement =====&lt;br /&gt;
Проследим далее за правилами Statement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;Statement: Assignment&lt;br /&gt;
	| IfStatement&lt;br /&gt;
	| WhileStatement&lt;br /&gt;
	| WriteStatement&lt;br /&gt;
	| ProcCallStatement&lt;br /&gt;
	| EmptyStatement&lt;br /&gt;
	;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Здесь нет действий в {}, поэтому по умолчанию всегда подразумевается действие '''$$ := $1''', что нам и надо.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим ещё несколько правил, в которых есть ранее не встречавшиеся моменты.&lt;br /&gt;
&lt;br /&gt;
===== Правило ident =====&lt;br /&gt;
Чтобы не преобразовывать всякий раз строковый ID в узел синтаксического дерева ident, введено правило:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;ident : ID {&lt;br /&gt;
  $$ = new ident($1,@$); &lt;br /&gt;
}&lt;br /&gt;
;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Правило для унарного минуса =====&lt;br /&gt;
Правило для унарного минуса имеет вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;expr : MINUS expr %prec UMINUS {&lt;br /&gt;
  $$ = new un_expr($2,Operators.Minus,@$);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Ключевое слово %prec меняет в рамках одного правила приоритет операции MINUS и делает его таким же, как и у UMINUS. Заметим, что терминал UMINUS - фиктивный - он не может возникнуть при лексическом разборе, и задаётся только в секции приоритетов операций - последним и, значит, самым приоритетным.&lt;br /&gt;
&lt;br /&gt;
===== Правило для всей программы =====&lt;br /&gt;
Вся программа на Oberon00 представляет собой модуль:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;module : MODULE ident SEMICOLUMN mainblock ident COMMA &lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
По этому правилу производятся следующие действия:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;CSharp&amp;quot;&amp;gt;{&lt;br /&gt;
if ($2.name != $5.name)&lt;br /&gt;
  PT.AddError(&amp;quot;Имя &amp;quot;+$5.name+&amp;quot; должно совпадать с именем модуля &amp;quot;+$2.name,@5);&lt;br /&gt;
		&lt;br /&gt;
  // Подключение стандартного модуля Oberon00System, написанного на PascalABC.NET&lt;br /&gt;
  var ul = new uses_list(&amp;quot;Oberon00System&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // Формирование модуля основной программы (используется фабричный метод вместо конструктора)&lt;br /&gt;
  root = program_module.create($2, ul, $4, @$);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Поясним их подробнее. Во-первых, проверяется, совпадает ли имя модуля в начале и в конце программы и если нет. то генерируется семантическая ошибка с помощью PT.AddError (эта ошибка будет добавлена в окно ошибок компиляции). Заметим, что на этапе построения синтаксического дерева можно распознать и диагностировать некоторые синтаксические ошибки.&lt;br /&gt;
&lt;br /&gt;
Затем в начало списка подключаемых модулей добавляется так называемый системный модуль Oberon00System - он написан на Паскале и должен быть откомпилирован в .pcu.&lt;br /&gt;
&lt;br /&gt;
После этого формируется синтаксический узел для всей программы (класс program_module) и присваивается переменной root.&lt;br /&gt;
&lt;br /&gt;
=== Создание системного модуля ===&lt;br /&gt;
Напишем для компилятора Oberon00 простой системный модуль на паскале Obеron00System.pas:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;&lt;br /&gt;
unit Oberon00System;&lt;br /&gt;
&lt;br /&gt;
interface&lt;br /&gt;
&lt;br /&gt;
procedure Print(o: object);&lt;br /&gt;
procedure Println(o: object);&lt;br /&gt;
procedure Println;&lt;br /&gt;
&lt;br /&gt;
implementation&lt;br /&gt;
&lt;br /&gt;
uses System; &lt;br /&gt;
&lt;br /&gt;
procedure Print(o: object);&lt;br /&gt;
begin&lt;br /&gt;
  Console.Write(o);&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
/// Вывести значение&lt;br /&gt;
procedure Println(o: object);&lt;br /&gt;
begin&lt;br /&gt;
  Console.WriteLine(o);&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
procedure Println;&lt;br /&gt;
begin&lt;br /&gt;
  Console.WriteLine;&lt;br /&gt;
end;&lt;br /&gt;
 &lt;br /&gt;
end.&amp;lt;/source&amp;gt;&lt;br /&gt;
Именно он подключается автоматически к любой программе на Обероне в действиях для правила module:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Delphi&amp;quot;&amp;gt;...&lt;br /&gt;
var ul = new uses_list(&amp;quot;Oberon00System&amp;quot;);&amp;lt;/source&amp;gt;&lt;br /&gt;
В принципе, этот модуль можно было написать на самом Обероне, но в Обероне нет возможности прямого доступа к .NET-библиотекам.&lt;br /&gt;
&lt;br /&gt;
Замечательная особенность среды PascalABC.NET - возможность совмещать разные языки программирования в одном проекте на уровне исходных кодов.&lt;br /&gt;
&lt;br /&gt;
=== Обработка ошибок ===&lt;br /&gt;
&lt;br /&gt;
=== Создание парсера-плагина для среды PascalABC.NET ===&lt;br /&gt;
&lt;br /&gt;
=== Инсталляция плагина ===&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82_%22MonoDevelop_Add-in_%D0%B4%D0%BB%D1%8F_PascalABC.NET%22&amp;diff=1104</id>
		<title>Обсуждение:Проект &quot;MonoDevelop Add-in для PascalABC.NET&quot;</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82_%22MonoDevelop_Add-in_%D0%B4%D0%BB%D1%8F_PascalABC.NET%22&amp;diff=1104"/>
		<updated>2009-12-19T23:15:14Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: орфография&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;blockquote&amp;gt;Добавление в программу MonoDevelop &amp;quot;плагинов&amp;quot; (Mono.Addins) реализованно намного удобней, чем по сравнению с инструментом SharpDevelop, который реализован на основе идей от Eclipse add-in engine (добавления &amp;quot;плагинов&amp;quot; в системе Eclipse).&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
А можно какие-то пруфлинки: 1) насчёт «удобней», 2) насчёт того, что SharpDevelop использует идеи Eclipse (беглый просмотр [http://wiki.sharpdevelop.net/AddinWritingHelp.ashx офсайта] ничего подобного не показал), 3) что за зверь Eclipse add-in engine — модульность Eclipse основана на том, что Eclipse, а верней, одна из компонент его, Equinox, реализует стандарт [http://en.wikipedia.org/wiki/OSGi OSGi], на который сейчас перешли уже почти все Java app-сервера (GlassFish, JBoss, Weblogic и т.д.), в связи с чем эти заявления про «неудобность» выглядят как-то неправдоподобно... — [[Участник:Ulysses|Ulysses]] 16:43, 15 декабря 2009 (UTC).&lt;br /&gt;
&lt;br /&gt;
Да уж, удобнее. То-то мы не можем уже 4 месяца разобраться, как это делается. И форум Mono Developа молчит. Еще немного - будем делать плагин к менее удобному Eclipse.&lt;br /&gt;
[[Участник:Mikst|Mikst]] 20:09, 19 декабря 2009 (UTC)&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%A0%D0%B5%D1%84%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3&amp;diff=1093</id>
		<title>Рефакторинг</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%A0%D0%B5%D1%84%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3&amp;diff=1093"/>
		<updated>2009-12-15T16:54:12Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: /* Ссылки */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Рефакторинг''' — процесс полного или частичного преобразования внутренней структуры программы при сохранении её внешнего поведения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [http://djvu.504.com1.ru:8019/WWW/0dd8afebee3171e2ce06a1a0ef42e4f4.pdf Фаулер М. Рефакторинг. Улучшение существующего кода 2003]&lt;br /&gt;
* [http://www.brain2life.com/book/358.html Макконнелл С. Совершенный код. 2-e издание]&lt;br /&gt;
&lt;br /&gt;
== Ссылки ==&lt;br /&gt;
* [http://www.refactoring.com/ Домашняя страница Мартина Фаулера о рефакторинге]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Рефакторинг http://ru.wikipedia.org/wiki/Рефакторинг]&lt;br /&gt;
* [http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.jdt.doc.user/reference/ref-menu-refactor.htm Возможности по рефакторингу Eclipse IDE]&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82_%22MonoDevelop_Add-in_%D0%B4%D0%BB%D1%8F_PascalABC.NET%22&amp;diff=1092</id>
		<title>Обсуждение:Проект &quot;MonoDevelop Add-in для PascalABC.NET&quot;</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82_%22MonoDevelop_Add-in_%D0%B4%D0%BB%D1%8F_PascalABC.NET%22&amp;diff=1092"/>
		<updated>2009-12-15T16:43:31Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: Новая: &amp;lt;blockquote&amp;gt;Добавление в программу MonoDevelop &amp;quot;плагинов&amp;quot; (Mono.Addins) реализованно намного удобней, чем по сравнению...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;blockquote&amp;gt;Добавление в программу MonoDevelop &amp;quot;плагинов&amp;quot; (Mono.Addins) реализованно намного удобней, чем по сравнению с инструментом SharpDevelop, который реализован на основе идей от Eclipse add-in engine (добавления &amp;quot;плагинов&amp;quot; в системе Eclipse).&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
А можно какие-то пруфы: 1) насчёт «удобней», 2) насчёт того, что SharpDevelop использует идеи Eclipse (беглый просмотр [http://wiki.sharpdevelop.net/AddinWritingHelp.ashx офсайта] ничего подобного не показал), 3) что за зверь Eclipse add-in engine — модульность Eclipse основана на том, что Eclipse, а верней, одна из компонент его, Equinox, реализует стандарт [http://en.wikipedia.org/wiki/OSGi OSGi], на который сейчас перешли уже почти все Java app-сервера (GlassFish, JBoss, Weblogic и т.д.), в связи с чем эти заявления про неудобность выгледят как-то неправдоподобно... — [[Участник:Ulysses|Ulysses]] 16:43, 15 декабря 2009 (UTC).&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9E%D0%B1%D0%B7%D0%BE%D1%80_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D1%85_%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B5%D0%B9_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%B2_%D1%81%D1%80%D0%B5%D0%B4%D0%B5_PascalABC.NET&amp;diff=1091</id>
		<title>Обсуждение:Обзор реализации основных возможностей функционального языка программирования в среде PascalABC.NET</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9E%D0%B1%D0%B7%D0%BE%D1%80_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D1%85_%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B5%D0%B9_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%B2_%D1%81%D1%80%D0%B5%D0%B4%D0%B5_PascalABC.NET&amp;diff=1091"/>
		<updated>2009-12-15T16:28:25Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;blockquote&amp;gt;Такие языки как Haskell и Clean имеют 100% соответствие своей семантики с семантикой подразумеваемых конструкций лямбда-исчисления.&amp;lt;/blockquote&amp;gt;Такая фраза выглядит некорректно: под словосочетанием «семантика лямбда-исчисления» понимается [http://en.wikipedia.org/wiki/Lambda_calculus#Semantics вполне определённая вещь], которая здесь явно ввиду не имелась. Так что я бы рекомендовал изложить эту мысль какими-то другими словами. Можно взять какую-нибудь книжку по Хаскелю и оттуда списать. — [[Участник:Ulysses|Ulysses]] 16:28, 15 декабря 2009 (UTC).&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ulysses&amp;diff=1090</id>
		<title>Участник:Ulysses</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Ulysses&amp;diff=1090"/>
		<updated>2009-12-15T16:25:27Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: Новая: Это он. Вы не ошиблись.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Это он. Вы не ошиблись.&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
	<entry>
		<id>https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9E%D0%B1%D0%B7%D0%BE%D1%80_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D1%85_%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B5%D0%B9_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%B2_%D1%81%D1%80%D0%B5%D0%B4%D0%B5_PascalABC.NET&amp;diff=1089</id>
		<title>Обсуждение:Обзор реализации основных возможностей функционального языка программирования в среде PascalABC.NET</title>
		<link rel="alternate" type="text/html" href="https://pascalabc.net/wiki/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:%D0%9E%D0%B1%D0%B7%D0%BE%D1%80_%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D1%85_%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B5%D0%B9_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%B2_%D1%81%D1%80%D0%B5%D0%B4%D0%B5_PascalABC.NET&amp;diff=1089"/>
		<updated>2009-12-15T16:25:05Z</updated>

		<summary type="html">&lt;p&gt;Ulysses: Новая: &amp;lt;blockquote&amp;gt;Такие языки как Haskell и Clean имеют 100% соответствие своей семантики с семантикой подразумеваемых к...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;blockquote&amp;gt;Такие языки как Haskell и Clean имеют 100% соответствие своей семантики с семантикой подразумеваемых конструкций лямбда-исчисления.&amp;lt;/blockquote&amp;gt;Такая фраза взрывает мозг любому хоть немного знакомому с лямбда-исчислением. Под словосочетанием «семантика лямбда-исчисления» понимается [http://en.wikipedia.org/wiki/Lambda_calculus#Semantics вполне конкретная вещь], которая здесь явно ввиду не имелась. Так что я бы рекомендовал изложить эту мысль какими-то другими словами. Можно взять какую-нибудь книжку по Хаскелю и оттуда списать. — [[Участник:Ulysses|Ulysses]] 16:25, 15 декабря 2009 (UTC).&lt;/div&gt;</summary>
		<author><name>Ulysses</name></author>
	</entry>
</feed>