Система программирования PascalABC.NET

Структура компилятора PascalABC.NET. Структура дерева и примеры узлов. Упрощенный синтаксис записи модулей. Объявление имен, совпадающих с ключевыми словами. Генерация узла семантического дерева. Сериализация и десериализация узлов семантического дерева.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык русский
Дата добавления 18.12.2011
Размер файла 1,8 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

СОДЕРЖАНИЕ

  • 1. Введение
  • 2. Постановка задачи
  • 3. Структура компилятора PascalABC.NET
  • 4. Синтаксическое дерево
    • 4.1 Концепция визиторов
    • 4.2 Структура дерева и примеры узлов
    • 4.3 Редактор синтаксического дерева
  • 5. Синтаксический анализатор
    • 5.1 Интерфейс подключения
    • 5.2 Язык PascalABC.NET
      • 5.2.1 Процедуры с переменным числом параметров
      • 5.2.2 Операция new
      • 5.2.3 Упрощенный синтаксис записи модулей
      • 5.2.4 Определение методов внутри класса
      • 5.2.5 Перегрузка операций
      • 5.2.6 Шаблоны
      • 5.2.7 Директивы компилятора
      • 5.2.8 Операции += -=
      • 5.2.9 Методы в записях
      • 5.2.10 Объявление имен, совпадающих с ключевыми словами
    • 5.3 Генератор синтаксических анализаторов
    • 5.4 Генерация синтаксического анализатора языка PascalABC.NET
    • 5.5 Диагностика сообщений об ошибках
  • 6. Семантическое дерево
    • 6.1 Узел для представления операции is
    • 6.2 Узел для представления операции sizeof
  • 7. Семантический анализатор
    • 7.1 Таблица символов
      • 7.1.1 Дерево областей видимости
      • 7.1.2 Общий алгоритм поиска
      • 7.1.3 Иерархия областей видимости
      • 7.1.4 Алгоритмы поиска в областях видимости
      • 7.1.5 Алгоритмы поиска в таблице символов
    • 7.2 Генерация узлов семантического дерева
      • 7.2.1 Операции is, as
      • 7.2.2 Операции typeof, sizeof
      • 7.2.3 Операция new
      • 7.2.4 Типизированные и бинарные файлы
        • 7.2.4.1 Часть кода, реализованная на PascalABC.NET
        • 7.2.4.2 Генерация узла семантического дерева
        • 7.2.4.3 Проверки этапа компиляции
        • 7.2.4.4 Реализация процедуры read
      • 7.2.5 Изменяемые строки string
    • 7.3 Генерация сообщений об ошибках
  • 8. Генератор кода
    • 8.1 Перевод семантического дерева в IL код
    • 8.2 Перевод конструкций в IL код
      • 8.2.1 Операции is, as
      • 8.2.2 Операции typeof, sizeof
  • 9. Промежуточная форма хранения программы (PCU)
    • 9.1 Выбор промежуточной формы хранения программы
    • 9.2 Варианты использования PCU
    • 9.3 Схема компиляции с использованием PCU
    • 9.4 Сериализация и десериализация некоторых узлов семантического дерева
  • 10. Управляющий блок
    • 10.1 Алгоритм компиляции модулей
  • 11. Система локализации
  • 12. Консольная оболочка компилятора
  • 13. Модули визуальной оболочки
    • 13.1 Интерфейс подключения
    • 13.2 Модуль «Визуализатор синтаксического дерева»
    • 13.3 Модуль «Управление компилятором»
    • 13.4 Модуль «Контроль внутренних ошибок»
  • 14. Подключение задачника Programming Taskbook
    • 14.1 Модуль на языке PascalABC.NET
    • 14.2 Модуль визуальной оболочки
  • Заключение
  • Литература
  • ПРИЛОЖЕНИЕ 1. Грамматика языка PascalABC.NET
  • ПРИЛОЖЕНИЕ 2. Классы синтаксического дерева

1. Введение

Система программирования PascalABC.NET создается как система, ориентированная на начальное обучение. Ее прототип - компилятор Pascal ABC - основан на языке Object Pascal и успешно используется на факультете Математики Механики и Компьютерных Наук Южного федерального университета при обучении основам программирования. Однако Pascal ABC является интерпретатором, имеет низкую скорость выполнения программ и не генерирует исполнимые файлы. Таким образом, создание полноценного компилятора и системы программирования PascalABC.NET представляется актуальным.

Создание учебной системы для обучения программированию имеет несколько целей.

· Во-первых, создать бесплатную среду, базирующуюся на наиболее используемом в России для обучения начальному программированию языке Паскаль.

· Во-вторых, дополнить ее рядом библиотек и модулей, ориентированных на обучение. Среди них - несколько модулей, иллюстрирующих разные концепции создания графических и событийных приложений, модуль сетевого репозитария программ, модуль электронного задачника и др.

· В-третьих, упростить и осовременить используемый язык Delphi Pascal. Язык PascalABC.NET проектируется с целью обеспечить преподавателям и учащимся возможность использования накопленных методик обучения на языке Паскаль, сочетая их с самыми современными возможностями языков программирования (шаблоны классов, перегрузка операций, пространства имен, делегаты, исключения, сборка мусора).

В качестве целевой платформы для реализации выбрана Microsoft .NET. Платформа Microsoft .NET создавалась с целью обеспечения возможности совместной, равноправной и единообразной работы всех .NET-языков программирования. Она содержит богатые библиотеки классов, поддерживается на уровне операционной системы, активно развивается. Кроме того, генерация .NET-кода значительно проще генерации машинного кода. Наконец, еще одним существенным плюсом является бесплатность компилятора языка C#, на котором написан компилятор PascalABC.NET.

При создании компилятора автор существенно опирался на классический труд по компиляторам - книгу Ахо А. "Компиляторы: принципы, технологии и инструменты" [1].

Работа состоит из 14 глав. В главах 1-3 описывается общая проблематика задачи и структура компилятора. Главы 4,5 посвящены синтаксическому анализатору (front-end). Главы 6,7,9 описывают внутреннее представление компилятора и центральную часть компилятора - семантический анализатор (middle-end). Глава 8 посвящена генерации кода (back-end). Глава 10 описывает блок, соединяющий вместе все части компилятора. В главах 11-14 описываются различные модули и вспомогательные системы компилятора.

По тематике проекта автором опубликованы 4 работы [2-5].

2. Постановка задачи

Разработать следующие части системы программирования PascalABC.NET:

· синтаксическое дерево программы;

· интерфейс подключения синтаксических анализаторов;

· синтаксический анализатор языка PascalABC.NET;

· таблица символов компилятора;

· алгоритм компиляции модулей;

· система локализации;

· консольная оболочка компилятора;

· интерфейс подключения модулей к визуальной оболочке;

· некоторые модули к визуальной оболочке.

Также автором ведется поддержка и развитие следующих частей проекта:

· семантическое дерево;

· семантический анализатор;

· генерация кода;

· промежуточная форма хранения программы (PCU).

3. Структура компилятора PascalABC.NET

На схеме приведена структура компилятора PascalABC.NET

Размещено на http://www.allbest.ru/

Управляющий блок управляет процессом компиляции программы и является интерфейсом для подключения оболочек к компилятору. Он содержит также базовую иерархию ошибок, алгоритм компиляции модулей и алгоритмы чтения/записи внутреннего представления (PCU файлов).

Парсер (синтаксический анализатор) - программа, осуществляющая синтаксический анализ текста. При запросе на компиляцию файла контроллер парсеров решает, какой парсер необходимо использовать для компиляции этого файла. После компиляции парсер возвращает синтаксическое дерево программы.

Контроллер парсеров - это класс (класс - абстрактный тип данных, который включает в себя не только данные, но и функции и процедуры), который контролирует подключение различных парсеров к компилятору.

Синтаксическое дерево представляет собой разобранную программу без учета семантики. Синтаксическое дерево - это набор классов (около 170). Само дерево не содержит процедур для его обработки. Обработка дерева происходит при его обходе с помощью визитора.

Семантическое дерево является представлением программы в виде дерева, содержит исчерпывающую информацию о правильной программе и не зависит от исходного языка. В этом дереве также использована концепция визиторов.

Конвертор семантического дерева в синтаксическое переводит семантическое дерево программы в синтаксическое. На этом блоке лежит задача семантического анализа программы.

Таблица символов используется лишь на этапе перевода синтаксического дерева в семантическое для быстрого поиска имен и хранения структуры областей видимости.

Доступ к .NET используется для поиска имен в сборках .NET.

Интерфейс таблицы символов используется для сокрытия платформы .NET от конвертора. Это сделано для того, чтобы конвертор дерева не зависел от целевой платформы.

Генератор кода для платформы .NET - это набор классов, который обеспечивает перевод семантического дерева в MSIL код.

Процесс компиляции происходит в несколько этапов:

1. Оболочка вызывает процедуру компиляции в управляющем блоке. В эту процедуру передаются параметры компиляции и имя файла, который необходимо откомпилировать.

2. Алгоритм компиляции модулей в нужном порядке либо переводит текст программы с помощью парсера в синтаксическое и далее в семантическое дерево, либо считывает семантическое дерево из PCU файла.

3. Семантические деревья модулей сохраняются в PCU файлы

4. Полученное семантическое дерево передается генератору кода, который переводит семантическое дерево в MSIL код.

4. Синтаксическое дерево

Синтаксическое дерево представляет собой разобранную программу без учета семантики. Внутреннее представление - это иерархия классов на языке C#.

Разделение на синтаксическое и семантическое деревья несколько нетрадиционно [2]. Обычно принято аннотировать семантическое дерево за несколько проходов. Основная мотивировка разделения на синтаксическое и семантическое деревья - принципиально разная структура этих деревьев, а также независимость конвертора синтаксического дерева в семантическое от используемого парсера. Следует отметить, что структура синтаксического дерева позволяет перевести программы на родственных языках (Pascal, Modula, Oberon) в синтаксические деревья близкой структуры, так что уже на этапе синтаксического дерева обеспечивается относительная независимость от языка.

На схеме изображено синтаксическое и семантическое дерево для простой программы. Из схемы видно, насколько различны синтаксическое и семантическое деревья.

На данный момент синтаксическое дерево состоит из 170 классов, описанных в приложении 2.

4.1 Концепция визиторов

Концепция визиторов (паттерн «посетитель» [6]) позволяет отделить алгоритмы обработки дерева от самой структуры данных, т.е. визитор является набором алгоритмов для обработки узлов дерева. Рассмотрим реализацию паттерна «посетитель» на примере узла if_node:

public class if_node : statement

{

punlic expression condition;

public statement then_body;

public statement else_body;

public override void visit(IVisitor visitor)

{

visitor.visit(this);

}

}

У узла if_node есть функция visit, которой на вход подается объект visitor, реализующий интерфейс IVisitor. В теле этой функции происходит вызов метода visit объекта visitor. Рассмотрим интерфейс IVisitor:

public interface IVisitor

{

...

void visit(if_node _if_node);

}

Таким образом, определяя класс, удовлетворяющий этому интерфейсу, мы сможем запрограммировать алгоритм обработки для каждого узла:

class visitor : IVisitor

{

...

public void visit(if_node _if_node)

{

//алгоритм обработки узла if

prepare_node(_if_node.condition, "condition");

prepare_node(_if_node.then_body, "then_body");

prepare_node(_if_node.else_body, "else_body");

}

...

}

Запустить визитор по дереву можно следующим образом:

visitor vs = new visitor();

if_node if_n = new if_node();

if_n.visit(vs);

4.2 Структура дерева и примеры узлов

syntax_tree_node - базовый класс синтаксического дерева программы

public class syntax_tree_node

{

public syntax_tree_node(SourceContext _source_context)

{

source_context = _source_context;

}

public SourceContext source_context;

public virtual void visit(IVisitor visitor)

{

visitor.visit(this);

}

}

Базовый класс содержит функцию visit, необходимую для обхода дерева посетителем, и объект класса SourceContext.

public class file_position

{

public int line_num

public int column_num

}

public class SourceContext

{

public file_position begin_position

public file_position end_position

public override string ToString()

}

Класс SourceContext служит для хранения позиции данной конструкции в тесте программы.

compilation_unit - базовый класс для узлов program_module и unit_module.

public class compilation_unit : syntax_tree_node

{

public string file_name

public List<compiler_directive> compiler_directives

public override void visit(IVisitor visitor)

}

Хранит имя файла программы и список директив компилятора.

program_module - класс для представления основной программы.

public class program_module : compilation_unit

{

public program_name program_name

public uses_list used_units

public using_list using_namespaces

public block program_block

public override void visit(IVisitor visitor)

}

Хранит имя программы, список подключенных модулей, список подключенных пространств имен и блок программы.

unit_module - класс для представления модуля

public class unit_module : compilation_unit

{

public unit_name unit_name

public interface_node interface_part

public implementation_node implementation_part

public statement_list initialization_part

public statement_list finalization_part

public override void visit(IVisitor visitor)

}

Хранит имя модуля, интерфейсную часть, часть реализаций, список операторов в секциях initialization и finalization.

for_node - класс для представления цикла for.

public class for_node : statement

{

public ident loop_variable

public expression initial_value

public expression finish_value

public statement statements

public for_cycle_type cycle_type

public expression increment_value

public override void visit(IVisitor visitor)

}

Хранит переменную цикла, начальное значение, конечное значение, список операторов, тип цикла (to или downto) и выражение, на которое надо увеличивать переменную цикла.

4.3 Редактор синтаксического дерева

Для добавления новых узлов и редактирования уже имеющихся используется программа nodes_generator, которая позволяет в визуальном режиме редактировать, добавлять, удалять узлы дерева. Эта программа генерирует код всех классов дерева, а также интерфейс визитора. Программа была разработана автором совместно с Водолазовым Н. и Ивановым С.

5. Синтаксический анализатор

Синтаксический анализатор (парсер) - программа, осуществляющая разбор текста программы в синтаксическое дерево.

Синтаксические анализаторы подключаются к компилятору динамически. Для реализации нового языка программирования достаточно реализовать парсер, который будет строить синтаксическое дерево.

5.1 Интерфейс подключения

Синтаксический анализатор, подключаемый к компилятору PascalABC .NET, должен быть унаследован от класса BaseParser, определенного в пространсве имен PascalABCCompiler.ParserTools и находящегося в сборке CompilerTools.dll:

public class BaseParser

{

public List<Error> Errors;

public bool CaseSensitive;

public string[] FilesExtensions;

public string Name;

public BaseParser(string name, bool case_sensitive);

public virtual compilation_unit BuildTree(

string FileName, string Text);

public virtual void Reset();

public override string ToString();

}

Здесь:

Errors - список для хранения ошибок, возникших при синтаксическом анализе;

CaseSensitive - свойство, определяющее чувствительность парсера к регистру символов;

FilesExtensions - массив расширений имен файлов, которые может обрабатывать парсер;

Name - имя синтаксического анализатора;

BuildTree - функция, производящая анализ текста и возвращающая корнь синтаксического дерева; параметр Text - текст файла с именем FileName;

Reset - функция, которая вызывается компилятором в начале компиляции программы.

Компилятор взаимодействует с парсерами через класс Controller, определенный в пространстве имен PascalABCCompiler.Parsers и находящийся в сборке Compiler.dll:

public class Controller

{

public List<BaseParser> Parsers;

public BaseParser LastParser;

public Controller();

public SyntaxTree.compilation_unit Compile(

string FileName, string Text, List<Error> Errors);

public void Reset();

}

Здесь:

Parsers - список парсеров, подключенных к компилятору;

LastParser - последний использовавшийся парсер;

Compile - функция, которая производит выбор нужного парсера для данного файла и вызов функции BaseParser.BuildTree. Решение о выборе парсера пинимается взависимости от расширения файла;

Reset - функция, которая вызывается компилятором в начале компиляции программы.

В конструкторе класса Controller происходит поиск и подключение парсеров. Сборки Dll, содержащие парсеры, должны удовлетворять следующим требованиям:

· находиться в одной папке с компилятором;

· иметь имя Parser*.dll;

· содержать один или несколько парсеров: классов-потомков BaseParser;

· парсеры должны иметь имя *LanguageParser.

5.2 Язык PascalABC.NET

Язык PascalABC.NET базируется на языке Object Pascal (Delphi Pascal).

Рассмотрим особенности языка PascalABC.NET.

При объявлении класса можно пользоваться четырьмя модификаторами видимости:

public - видимо для всех;

internal - видимо для всех только в этой сборке, этот модификатор видимости используется по умолчанию;

protected - видимо только в классе, его потомках и в модуле в котором объявлен класс;

private - видимо только в классе и в модуле, в котором объявлен класс;

При объявлении методов можно пользоваться следующими модификаторами:

static - статический метод;

virtual - виртуальный метод;

override - переопределенный метод;

overload - перегруженная подпрограмма; для определения перегруженных подпрограмм можно не пользоваться этим модификатором, он оставлен для совместимости.

Рассмотрим некоторые особенности языка.

1. В языке все конструкторы имеют имя Create. Можно также объявить конструктор без имени, при этом ему будет назначено имя Create. Подробнее о способы вызова конструктора будут описаны в пункте 5.2.2.

2. В платформе .NET примитивные типы (integer,real) являются объектами, поэтому язык поддерживает вызовы методов у примитивных типов, например:

s := 10.ToString;

s := 3.14.ToString;

3. В секции uses можно подключать как модули, так и пространства имен .NET. Модуль вводит неявное пространство имен. Рассмотрим пример, как можно обращаться к именам в модулях и пространках имен .NET:

uses System, // пространство имен.NET

System.IO, // пространство имен.NET

Unit1; // модуль на языке PascalABC.NET

var s1: StreamReader; // класс из System.IO

s2: System.IO.StreamReader; // явное указание имени

begin

p; // процедура из модуля Unit1

Unit1.p; // явное обращение

end.

4. Определение внешних подпрограмм производится следующим образом:

function MessageBox(h: integer;

m,c: string; t: integer): integer;

external 'User32.dll' name 'MessageBox';

5. Из языка Delphi Pascal убраны устаревшая концепция объектов object и ключевое слово object.

6. Также убраны ключевые слова message и низкоуровневые конструкции, такие как absolute.

7. В языке принята структурная эквивалентность типов для процедурных переменных, поэтому типы можно описывать в заголовках методов и объявлениях переменных:

Type

Button = class(Control)

OnClick: procedure(Sender: Component);

end;

5.2.1 Процедуры с переменным числом параметров

Процедуры с переменным числом параметров реализуются с помощью введения в язык нового ключевого слова params.

Пример функции с переменным числом параметров:

function Concat(params strs: array of string): string;

var

i: integer;

sb: System.Text.StringBuilder;

begin

sb := new System.Text.StringBuilder;

for i:=0 to strs.length-1 do

sb.Append(strs[i]);

Result := sb.ToString;

end;

5.2.2 Операция new

Операция new является синонимом вызова конструктора Create и позволяет создать новый объект класса.

new_expr ::= `new' named_type_reference [`<' template_args `>']

[`(' expr_list `)']

Пример:

a := new Point(10, 20);

a := Point.Create(10, 20); //Эквивалентная запись

Предпочтительным синтаксисом вызова конструктора является операция new т.к. позволяет явно видеть места создания объектов в тексте программы. Вызов конструктора как процедуры в языке PascalABC.NET запрещен.

Также операция new позволяет инстанцировать шаблон:

a := new List<integer>;

Слово new не является ключевым словом, им можно пользоваться при объявлении идентификаторов.

5.2.3 Упрощенный синтаксис записи модулей

При объявлении модуля можно пользоваться как обычным, так и упрощенным синтаксисом записи модуля. Данная концепция была взята из языка программирования Pascal ABC.

Обычный синтаксис записи модуля:

unit a;

interface

//интерфейсная часть

implementation

//реализация

end.

Упрощенный синтаксис записи модуля:

unit a;

//реализация

end.

При упрощенном синтаксисе считается, что все сущности находятся в интерфейсной части модуля.

5.2.4 Определение методов внутри класса

Методы можно определять как внутри, так и вне класса. В методах, определенных внутри класса, не может быть вложенных подпрограмм. Данная концепция была взята из языка программирования Pascal ABC.

type

Point = class

//метод, определенный внутри класса

procedure Show;

begin

//код

end;

procedure Hide;

end;

//метод, определенный вне класса

procedure Point.Hide;

begin

//код

end;

5.2.5 Перегрузка операций

Для реализации синтаксиса перегрузки операций потребовалось ввести в язык ключевое слово operator. Ключевое слово operator является единственным «сильным» ключевым словом в языке, т.е. его нельзя использовать в составных идентификаторах (составной идентификатор - это идентификатор вида ident1.ident2....identN). Операция реализуется в виде статического метода класса с соответствующим своей арности числом параметров и может быть вызвана как обычным способом, так и явно.

Следующий пример иллюстрирует перегрузку операций:

type

Student = class

Name: string;

Height: integer;

public

constructor(Name: string; Height: integer);

begin

Self.Name := Name;

Self.Height := Height;

end;

function operator<(left,right: Student): boolean; static;

begin

Result := left.Height < right.Height;

end;

function ToString: string; override;

begin

Result := string.Format('{0} ({1})', Name, Height);

end;

end;

var

s1,s2: Student;

begin

s1:=new Student('Stepa Morkovkin',188);

s2:=new Student('Petya Pomidorov',180);

Writeln(s1,s2);

Writeln('s1<s2');

//Обычный вызов операции

Writeln(s1<s2);

//Явный вызов операции

Writeln(Student.operator<(s1,s2));

Readln;

end.

5.2.6 Шаблоны

В языке возможны два типа шаблонов:

· Управляемые(Generic) шаблоны - шаблоны, которые переводятся в шаблоны .NET. На данный момент семантика для таких шаблонов не реализована.

· Неуправляемые шаблоны - шаблоны в стиле C++; инстанцирование такого шаблона происходит непосредственно в семантическом дереве.

Определение шаблона производится следующим образом:

type

TemplateClass = class<ARG1,ARG2,...>(BaseClass,Interface1,...)

where //секция, необходимая для управляемых шаблонов

ARG1: IArg1Interface1, ..., constructor(Param1,...);

ARG2: IArg2Interface;

//объявление полей и методов

end;

ConcreteClass = TemplateClass<Class1,Class2,...>;

begin

//инстанцирование шаблона

Elem := new TemplateClass<Class1,Class2,...>(...);

Elem := new ConcreteClass({параметры конструктора});

end.

Здесь ARG1, ARG2 - шаблонные аргументы. Секция where необходима для объявления управляемых шаблонов; в этой секции определяется, какие интерфейсы реализуют шаблонные аргументы, а также какие параметры имеют их конструкторы.

Инстанцировать шаблон класса с конкретными аргументами возможно только с помощью операции new. Такое решение было принято из-за того, что инстанцирование шаблона в выражении приводит к неоднозначной грамматике.

Пример программы содержащей шаблоны:

type

Pair = class<T, Q>

public

First: T;

Second: Q;

constructor(First: T; Second: Q);

begin

Self.First := First;

Self.Second := Second;

end;

function operator+(Left,Right: Pair<T,Q>): Pair<T,Q>; static;

begin

Result := new Pair<T,Q>(Left.First + Right.First,

Left.Second + Right.Second);

end;

function ToString: string; override;

begin

Result := string.Format('[{0}; {1}]', First, Second);

end;

end;

var

a,b: Pair<integer, string>;

begin

a := new Pair<integer,string>(1, 'один ');

b := new Pair<integer,string>(2, 'два');

Writeln(a + b);

Readln;

end.

5.2.7 Директивы компилятора

Директивы компилятора имеют следующий формат:

#directive_name param1 param2 ...

Компилятор может обрабатывать следующие директивы:

1. #apptype application_type

Здесь application_type может иметь значения:

· console - для приложения генерируется консольное окно (по умолчанию);

· windows - для приложения не генерируется консольное окно;

· dll - динамическая библиотека.

2. #reference 'dllname'

Подключает сборку с именем dllname в проект.

Наличие директив компилятора позволило обойтись без файла проекта.

Приведем пример использования директив компилятора (простое оконное приложение):

#apptype windows

#reference 'System.Windows.Forms.Dll'

uses System.Windows.Forms;

begin

Application.Run(new Form);

end.

5.2.8 Операции += -=

В язык введены операции += -=. Пример использования таких операций - процедурные переменные:

procedure p1;

begin

writeln('p1');

end;

procedure p2;

begin

writeln('p2');

end;

var p: procedure;

begin

p += p1;

p += p2;

p;

readln;

end.

В данном примере операция += используется для добавления процедуры к процедурной переменной (делегат в .NET).

5.2.9 Методы в записях

В записях имеется возможность определять процедуры и функции. Синтаксис объявления не отличается от методов в классах.

type

Point = record

x,y: integer;

function ToString: String; override;

begin

result := string.Format('({0},{1})', x, y);

end;

end;

В данном примере у записи переопределяется метод ToString. Все методы в записи получают модификатор видимости Public.

Следует отметить, что в записях было принято решение отказаться от вариантной части.

5.2.10 Объявление имен, совпадающих с ключевыми словами

Ключевые слова могут выступать в качестве идентификаторов, если они находятся в составном идентификаторе, например var t: System.Type. Это необходимо, т.к. в платформе .NET классы могут иметь имена, совпадающие с ключевыми словами языка PascalABC.NET. Также в язык введен специальный символ `&' который “гасит” ключевое слово и делает из него обычный идентификатор.

Пример программы:

uses System;

var TReal: &Type;

TInteger: System.Type;

&if: integer;

begin

TInteger := typeof(integer);

Writeln(TInteger);

TReal := typeof(real);

Writeln(TReal);

&if := &if + 1;

Writeln(&if);

Readln;

end.

5.3 Генератор синтаксических анализаторов

В качестве генератора синтаксических анализаторов была выбрана система GOLDParserBuilder (GPB) [7]. Это свободно распространяемая система, которая является LALR (LookAhead Left Recursive, рекурсия влево с просмотром вперед) генератором синтаксических анализаторов. GOLD переводится как - Grammar Oriented Language Developer.

GPB поддерживает множество языков программирования для написания парсера: ANSI C, C#, C++, Delphi, Java, Python, Visual Basic.

Рассмотрим схему работы GOLDParserBuilder:

Файл грамматики, имеющий расширение .grm, содержит:

· определение множеств символов;

· описание терминалов в виде регулярных выражений;

· описание правил языка в форме Бекуса-Наура.

CGT файл - результат компиляции файла грамматики (Compiled Grammar Tables).

Движок - совокупность классов, которые позволяют интерпретировать CGT файл.

Шаблон для построения правил - файл, имеющий расширение PGT и содержащий набор правил, по которым следует построить скелет для правил на конкретном языке программирования.

5.4 Генерация синтаксического анализатора языка PascalABC.NET

В системе GPB грамматика и действия при свертке правил находятся в разных файлах. Отметим, что в YACC парсерах правила и действия при свертке правил находятся в одном файле, при этом действия при свертке правил записываются справа от правила. Также в GPB отсутствуют удобные символы $$, $1, $2,... которые служат для обращения к аргументам правила. Автору было неудобно редактировать грамматику и действия, поэтому была предложена следующая схема: действия записывать справа от правил, как и в YACC, но заключать их в комментарии.

Комментарии в GOLDParserBuilder заключаются в !* *!, например !*комментарий*!. Однострочный комментарий начинается символом !

Для реализации указанной идеи был написан анализатор grmCommentCompiler (используя GOLDParserBuilder), который анализирует grm файл и записывает содержимое комментариев вместо заглушек правил в скелет парсера. Таким образом, схема компиляции парсера теперь выглядит так:

GOLDParserBuilder компилирует grm файл в CGT файл, не обращая внимания на комментарии. Далее с помощью GOLDParserBuilder для движка GPBEngine генерируется скелет парсера. После этого запускается grmCommentCompiler, который анализирует grm файл и записывает содержимое комментариев вместо заглушек правил в скелет парсера. При этом grmCommentCompiler подвергает содержимое комментариев обработке с помощью набора шаблонов, определенных в grm файле, если в комментарии встречены треугольные скобки. Также обрабатываются символы $$ - результат правила, $1, $2, ... - значения терминалов и нетерминалов слева направо. Такие символы приняты в YACC для обработки аргументов правила. При этом символы $$, $n заменяются на:

$n -> LRParser.GetReductionSyntaxNode(n-1)

$$= -> return

Терминальные шаблоны используются grmCommentCompiler для формирования кода правила обработки терминального символа. Рассмотрим пример терминального шаблона:

!*

[TERMINALTEMPLATE]

{

%NAME% _%NAME%=new %NAME%(%PARAMS%);

_%NAME%.source_context=parsertools.create_source_context(token);

%CODE%

return _%NAME%;

}

*!

Терминальные правила, использующие такой шаблон, будут выглядеть так:

tkHex ='$'{Hex Digit}+

!*$$=parsertools.create_hex_const(this,$$);*!

tkAsciiChar='#'{Number}+

!*$$=parsertools.create_sharp_char_const(this,$$);*!

tkStringLiteral=''({String Char}|'''')*''

!*$$=parsertools.create_string_const(this,$$);*!

tkMinus ='-'

!*<op_type_node(op_type.sub_op)>*!

tkPlus ='+'

!*<op_type_node(op_type.plus_op)>*!

tkSlash = '/'

!*<op_type_node(op_type.div_op)>*!

Нетерминальные шаблоны используются grmCommentCompiler для формирования кода правила обработки нетерминального символа. Рассмотрим пример нетерминального шаблона:

!*

[NONTERMINALTEMPLATE6]

//TemplateList for %NAME% (create)

{

%NAME% _%NAME%=new %NAME%();

_%NAME%.source_context=((%CODE%)$1).source_context;

_%NAME%.%PARAMS%.Add((%CODE%)$1);

return _%NAME%;

}

*!

!*

[NONTERMINALTEMPLATE7]

//TemplateList for %NAME% (add)

{

%NAME% _%NAME%=(%NAME%)$1;

parsertools.create_source_context(_%NAME%,$$,$3);

_%NAME%.%PARAMS%.Add((%CODE%)$3);

return _%NAME%;

}

*!

Приведем пример использования нетерминальных шаблонов для создания списка:

<ident_list>

::= <identifier> !*6ident_list<idents>ident*!

| <ident_list> tkComma <identifier>

!*7ident_list<idents>ident*!

После генерации CGT файла и файла с правилами, запускается программа ResxMaker, которая упаковывает CGT файл (используется стандартный алгоритм GZIP), а затем генерирует файл ресурса. Текст программы ResxMaker ввиду ее простоты приведен ниже:

#define USEGZIP

using System;

using System.Resources;

using System.Reflection;

using System.Globalization;

using System.Threading;

using System.IO;

using System.IO.Compression;

namespace ResXMaker

{

public class Maker

{

public static void Make(string filename, string resname,string outputfilename)

{

FileStream fs = null;

BinaryReader br = null;

ResourceWriter w = null;

try

{

Stream infile = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);

byte[] data = new byte[infile.Length];

int count = infile.Read(data, 0, data.Length);

infile.Close();

MemoryStream ms = new MemoryStream();

#if USEGZIP

GZipStream compressedStream = new GZipStream(ms, CompressionMode.Compress, true);

compressedStream.Write(data, 0, data.Length);

compressedStream.Close();

#elif !USEGZIP

ms.Write(data, 0, data.Length);

#endif

ms.Position = 0;

br = new BinaryReader(ms);

byte[] arr = br.ReadBytes((int)br.BaseStream.Length);

w = new ResourceWriter(outputfilename);

w.AddResource(resname, arr);

w.Generate();

Console.WriteLine("Input file size={0}byte", data.Length);

Console.WriteLine("Output file size={0}byte", arr.Length);

float c = data.Length; c = c / 100; c = 100 - arr.Length / c;

Console.WriteLine("Compression={0}%", Convert.ToInt32(c));

}

catch (Exception e)

{

Console.WriteLine(e);

}

finally

{

if (fs != null)

{

fs.Close();

w.Close();

}

}

}

}

class MainClass

{

public static void Main(string[] args)

{

if (args.Length < 3)

{

Console.WriteLine("CGTResXMaker filename resname outputfilename");

return;

}

Console.WriteLine("ResMaker: FILE->RESX");

Maker.Make(args[0],args[1],args[2]);

}

}

}

В итоге, командный файл для компиляции парсера выглядит так:

echo Compile: GRM to CGT...

gpbcmd\goldbuilder_main.exe PascalABC.grm PascalABC.cgt

echo Compile: CGT,PGT to TEMPLATE...

gpbcmd\createskelprog_main.exe PascalABC.cgt

PascalABC.pgt PascalABC.tmpl

echo Compile: GRM,TEMPLATE to CS...

grmCommentCompiler.exe PascalABC.grm PascalABC.tmpl PascalABC.cs

echo Compile: CGT to RES...

ResMaker\ResXMaker.exe PascalABC.CGT PascalABCLanguage PascalABCLang.resources

Здесь:

· PascalABC.grm - файл грамматики и правыми частями правил;

· PascalABC.pgt - шаблон для построения скелета;

· в каталоге gpbcmd должна находиться консольная версия GOLDParserBuilder.

Результат: парсер, который находится в файле PascalABC.cs, и файл ресурса со сжатой грамматикой, который находится в PascalABCLang.resources.

Отметим, что грамматика языка PascalABC.NET содержит 161 терминальных символа, 796 правил и описана в приложении 1.

5.5 Диагностика сообщений об ошибках

При синтаксическом анализе текста программы движок (набор классов, умеющий интерпретировать CGT-файлы) позволяет обработать ошибки двух типов:

1) Неожиданный символ [символ]

2) Ожидались [набор терминалов], а встречено [терминал]

В движке GPE(Gold Parser Engine) обработка таких ошибок производится следующим образом.

· После очередного вызова LRParser.Parse() мы смотрим что сейчас произошло в парсере:

ParseMessage.LexicalError: Произошла ошибка типа 1

ParseMessage.SyntaxError: Произошла ошибка типа 2

· После того, как произошла ошибка, работу пасрера можно продолжить. Для этого необходимо удалить ошибочный токен с вершины стека: LRParser.PopInputToken().

Для обработки ошибок создана иерархия исключений.

error ->Exception

syntax_error ->error

bad_operand_type ->syntax_error

unexpected_token ->syntax_error

token_read_error ->syntax_error

bad_int ->syntax_error

bad_float ->syntax_error

bad_hex ->syntax_error

Потомки класса syntax_error содержат SourceContext (контекст места, где произошла ошибка), что позволяет отладчику выделить место ошибки в тексте программы.

При возникновении ошибки создается объект нужного типа и помещается в список ошибок. Парсер работает, пока количество ошибок не превысит заданное число; если это число превышено, работа парсера прерывается. Для нормальной обработки ошибок этого явно недостаточно. Также неудобно то, что обычно при возникновении ошибок второго типа [набор терминалов] является очень большим - в парсере PascalABC.NET в среднем 25 терминалов.

Эта проблема решена следующим образом:

1) введены приоритеты для терминальных символов;

2) модифицирован движок парсера так, что в момент возникновения ошибки можно было выяснить ожидаемые нетерминальные символы;

3) введены приоритеты для нетерминальных символов.

Ниже приведен код, который отвечает за назначения приоритетов для терминалов и нетерминалов.

public override int symbol_priority(Symbol symbol)

{

switch (symbol.Index)

case (int)SymbolConstants.SYMBOL_TKEND:

return 8;

case (int)SymbolConstants.SYMBOL_TKBEGIN:

case (int)SymbolConstants.SYMBOL_TKINTEGER:

case (int)SymbolConstants.SYMBOL_TKROUNDCLOSE:

return 9;

case (int)SymbolConstants.SYMBOL_TKIDENTIFIER:

return 10;

case (int)SymbolConstants.SYMBOL_TKASSIGN:

return 25;

case (int)SymbolConstants.SYMBOL_TKCOLON:

return 20;

case (int)SymbolConstants.SYMBOL_TKEQUAL:

return 25;

case (int)SymbolConstants.SYMBOL_TKSEMICOLON:

return 30;

case (int)SymbolConstants.SYMBOL_EXPR:

return 100;

case (int)SymbolConstants.SYMBOL_STMT:

return 110;

}

if (symbol.SymbolType == SymbolType.Terminal)

return 1;

return 0;

}

При возникновении ошибки второго типа из набора символов выбираются символы с наибольшим приоритетом.

Концепция ошибок, принятая в компиляторе PascalABC.NET, следующая: парсер старается разобрать текст программы до конца, несмотря на ошибки. В каждой ошибке запоминается, на каком узле синтаксического дерева она произошла. Далее конвертор синтаксического дерева в семантическое начинает компиляцию до первого встреченного им узла, в котором произошла синтаксическая ошибка. Если конвертор встретил ранее семантическую ошибку (например, повторное описание идентификатора), то его работа прерывается и выдается семантическая ошибка. Если же семантических ошибок раньше синтаксических не встречено, то выдается первая синтаксическая ошибка.

6. Семантическое дерево

Семантическое дерево содержит полую информацию о правильной программе, необходимую для генерации кода. Семантическое дерево представляет собой иерархию интерфейсов, которые реализуются в конверторе синтаксического дерева в семантическое. Семантическое дерево, так же как и синтаксическое, использует концепцию визиторов для отделения дерева от алгоритмов его обработки.

Рассмотрим ряд узлов семантического дерева, созданных автором вместе с алгоритмами преобразования из соответствующих узлов синтаксического дерева.

6.1 Узел для представления операции is

Синтаксис: obj is type_name

Операция is позволяет определить, является ли объект obj потомком класса type_name, либо объектом класса type_name.

Интерфейс:

public interface IIsNode : IExpressionNode

{

IExpressionNode left

{

get;

}

ITypeNode right

{

get;

}

}

Реализация:

public class is_node : expression_node, SemanticTree.IIsNode

{

private expression_node _left;

private type_node _right;

public is_node(expression_node left, type_node right, location loc)

: base(compiled_type_node.get_type_node(typeof(bool)), loc)

{

_left = left;

_right = right;

}

public expression_node left

{

get

{

return _left;

}

}

public type_node right

{

get

{

return _right;

}

}

SemanticTree.IExpressionNode SemanticTree.IIsNode.left

{

get

{

return _left;

}

}

SemanticTree.ITypeNode SemanticTree.IIsNode.right

{

get

{

return _right;

}

}

public override semantic_node_type semantic_node_type

{

get

{

return semantic_node_type.is_node;

}

}

public override void visit(SemanticTree.ISemanticVisitor visitor)

{

visitor.visit(this);

}

}

Как видно из кода, данный узел является потомком узла expression_node. При конструировании мы указываем, что данное выражение имеет тип bool.

6.2 Узел для представления операции sizeof

Синтаксис: sizeof(type_name)

Операция is позволяет определить размер типа type_name в байтах.

Интерфейс:

public interface ISizeOfOperator : IExpressionNode

{

ITypeNode oftype

{

get;

}

}

Реализация:

public class sizeof_operator : expression_node, SemanticTree.ISizeOfOperator

{

private type_node _oftype;

public sizeof_operator(type_node oftype, location loc)

: base(compiled_type_node.get_type_node(typeof(int)), loc)

{

_oftype = oftype;

}

public type_node oftype

{

get

{

return _oftype;

}

}

SemanticTree.ITypeNode SemanticTree.ISizeOfOperator.oftype

{

get

{

return _oftype;

}

}

public override semantic_node_type semantic_node_type

{

get

{

return semantic_node_type.sizeof_operator;

}

}

public override void visit(SemanticTree.ISemanticVisitor visitor)

{

visitor.visit(this);

}

}

Как видно из кода, данный узел также является потомком узла expression_node. При конструировании мы указываем, что данное выражение имеет тип int.

7. Семантический анализатор

Семантический анализатор - центральная часть компилятора. Семантический анализатор осуществляет перевод синтаксического дерева в семантическое. Важнейшей структурой, используемой в процессе перевода, является таблица символов. Основная часть семантического анализатора была разрботана Водолазовым Н.

7.1 Таблица символов

Таблица символов (ТС) - это таблица имен, встречающихся при компиляции. Вместе с каждым именем хранится его семантическая характеристика (тип, пространство имен, модификатор видимости и т.д.). Пространства имен (области видимости) хранятся в отдельной таблице, тесно связанной с таблицей символов.

Таблица символов используется лишь на этапе перевода синтаксического дерева в семантическое для быстрого поиска имен. Затем вся информация о переменных хранится непосредственно в семантическом дереве.

Таблица символов в данной реализации устроена как единая хеш-таблица для всех пространств имен. Вместе с каждым именем хранится список пространств имен, в которых данное имя встречается.

В качестве хеш-функции была использована:

int HashFunc(string s)

{

int n=0;

for (int i=0;i<s.Length;i++)

n=127*n+s[i]*7;

return Math.Abs(n % hash_size);

}

В качестве функции разрешения конфликтов была использована:

int GetHash(string s)

{

int hash=HashFunc(s);

int i=1;

while (hash_arr[hash]!=null)

{

if (hash_arr[hash].Name==s) return hash;

i=i*11;

hash=Math.Abs((hash + i)%hash_size);

}

return hash;

}

Эффективность рассмотренной хеш-таблицы была показана в докладе «Реализация таблицы символов компилятора» [8] (докладчик Ткачук А.В., научный руководитель Михалкович С.С.) в 2004 на студенческой конференции «Неделя науки», в секции «Теоретическое и прикладное программирование». В докладе было обосновано, что хеш-таблица с такой хеш-функцией показывает результаты производительности, близкие к теоретическим [9].

7.1.1 Дерево областей видимости

Дерево областей видимости реализовано в виде динамического массива. На следующем примере показано, как дерево областей видимости можно упаковать в одномерный массив. Красными стрелками отмечен путь по дереву областей видимости из области с номером 5 в область с номером 0:

Области видимости объединены в иерархию классов. Далее в пункте 7.1.3 мы подробно рассмотрим все типы областей видимости и алгоритмы поиска в них.

7.1.2 Общий алгоритм поиска

Рассмотрим общий алгоритм поиска в таблице символов на примере.

Поиск в таблице символов (ТС) происходит следующим образом: допустим в ТС поступает запрос: «найти имя “a” в области видимости 5 и выше». В хеш-таблице находим имя “a”. Далее смотрим, есть ли в списке областей видимости область 5. Такой нет, и мы начинаем подниматься по таблице из области 5. Рассмотрим верхнюю область 4: ее в списке тоже нет. Поднимаемся выше - там находим область 1. Она есть в списке, и мы можем дать ответ «”a” найден в области видимости 1».

7.1.3 Иерархия областей видимости

Рассмотрим все классы областей видимости и их члены.

1. BaseScope

virtual SymbolInfo Find(string name)

Базовый класс для всех областей видимости. Содержит виртуальную функцию Find.

2. Scope->BaseScope

Scope TopScope

override SymbolInfo Find(string name)

SymbolInfo FindOnlyInScope(string name)

SymbolInfo FindOnlyInType(string name)

void AddSymbol(string Name,SymbolInfo Inf)

Используется для задания простых областей видимости, имеющих только одну объемлющую область видимости. Например как область видимости подпрограммы.

3. DotNETScope->Scope

Служит для объявления области видимости класса из сборки .NET

Разработчик блока «доступ к .NET» (см. структура компилятора PascalABC.NET) Бондарев И. порождает потомка этого класса, в котором переопределяет метод Find. Процессом добавления таких областей видимости занимается блок “Интерфейс TC”. Таким образом, таблица символов не занимается поиском в сборках, а лишь знает о том, что эта область - из .NET сборки. Тем самым удалось отделить собственно поиск имен в сборках от таблицы символов.

4. UnitPartScope->Scope

Scope[] TopScopeArray

Предок для областей, которые являются частями модуля. Содержит массив областей видимости (строится по секции uses), которые по смыслу являются интерфейсными частями модулей либо сборками .NET.

5. UnitInterfaceScope->UnitPartScope

Интерфейсная часть модуля.

6. UnitImplementationScope->UnitPartScope

Часть модуля, содержащая реализации. Свойство TopScope в этом классе всегда должно иметь тип UnitInterfaceScope.

7. ClassScope->Scope

Scope BaseClassScope

Используется для задания области видимости класса. Хранит область видимости базового класса.

8. ClassMethodScope->Scope

Scope MyClass

Используется для задания области видимости метода. Хранит область видимости класса, в котором он описан.

7.1.4 Алгоритмы поиска в областях видимости

Для поиска имен в таблице символов используется свой алгоритм для каждой области видимости. Все алгоритмы поиска возвращают односвязный список элементов типа SymbolInfo (информацию о символе). Опишем алгоритм поиска для каждой области видимости.

Простая область видимости

Ищем в этой области видимости затем в объемлющей области видимости (ТоpScope).

Интерфейсная часть модуля

Ищем в области видимости интерфейса, затем ищем в массиве областей видимости (TopScopeArray) справа налево (подключенные модули).

Часть модуля, содержащая реализации

Сначала ищем в этой области видимости, затем ищем в массиве областей видимости справа налево. Далее ищем в TopScope (интерфейсной части модуля).

Область видимости класса

Ищем в классе, далее в надклассах (BaseClassScope), затем в модуле, в котором описан класс (ТоpScope), далее в подключенных модулях (ТоpScope.TopScopeArray).

Область видимости метода

Ищем в методе, ищем в классе (MyClass), далее в надклассах, затем в модуле (ТоpScope), в котором описан метод, далее в подключенных модулях (ТоpScope.TopScopeArray).

Область видимости класса в сборке

Поиск осуществляется с помощью рефлексии. При этом происходит кеширование в специальные хеш-таблицы. В зависимости от типа найденного члена класса создается специальный SymbolInfo.

7.1.5 Алгоритмы поиска в таблице символов

Первый алгоритм служит для поиска имени только в заданной области видимости.

SymbolInfo FindOnlyInScope(Scope scope,string Name)

1. Если в хеш-таблице такого имени нет то выход;

2. Список=ХешТабица[ХешТабица.ВзятьХеш(Name)].СписокОбластей;

3. Если Список.Найти(scope), то вернуть информацию;

4. Если Scope типа UnitImplementationScope то

scope=scope.TopArea;

перейти к пункту 3;

Второй алгоритм предназначен для поиска всех подходящих имен. Параметр OnlyInType определяет, нужно ли искать это имя только в классе.

SymbolInfo FindAll(Scope scope,string Name,bool OnlyInType)

1. Если OnlyIntype и не(Scope типа ClassScope) то выход;

2. ТекущаяОблась=scope;

2.1 Если ТекущаяОблась типа DotNETScope то

Результат.Добавить(ТекущаяОблась,name);

Если Результат.ЕстьХоябыОдин то вернуть Результат;

2.2 Если ТекущаяОблась типа UnitPartScope то

Если ТекущаяОблась типа UnitImplementation то

Результат.Добавить(

ПоискПоВсемМодулям(ТекущаяОблась,name));

ТекущаяОблась=ТекущаяОблась.TopScope;

Результат.Добавить(

ПоискПоВсемМодулям(ТекущаяОблась,name));

Если Результат.ЕстьХоябыОдин то вернуть Результат;

2.3 Если ТекущаяОблась типа СlassScope то

Двигаться по всей иерархии классов вверх

Результат.Добавить(ТекущаяОблась,name);

Если Результат.ЕстьХоябыОдин или OnlyInType то

вернуть Результат;

2.4 Результат.Добавить(ТекущаяОблась,name);

2.5 Если Результат.ЕстьХоябыОдин то вернуть Результат;

2.6 Если ТекущаяОблась типа СlassMethodScope то

Двигаться по всей иерархии классов вверх

Результат.Добавить(ТекущаяОблась,name);

Если Результат.ЕстьХоябыОдин то вернуть Результат;

2.7 Если ТекущаяОблась.TopArea!=null то

ТекущаяОблась=ТекущаяОблась.TopArea;
перейти к пункту 2.1;

Иначе Выход;

Выбор подходящих имен из области видимости происходит в методе

Результат.Добавить() с помощью двух проверок.

Первая проверка реализуется с помощью функции IsNormal:

private bool IsNormal(SymbolInfo to,SymbolInfo add)

{

return (((to.symbol_kind==symbol_kind.sk_none)&&(add.symbol_kind==symbol_kind.sk_none))&&(to.scope==add.scope))

||

((to.symbol_kind==symbol_kind.sk_overload_function)&&(add.symbol_kind==symbol_kind.sk_overload_function)));

}

Эта функция проверяет, подходит ли add к символу to.

С каждым символом хранится информация

enum symbol_kind {sk_none, sk_overload_function};

т.е. символ может быть обычным либо перегруженной подпрограммой

Алгоритм:

1. Если (to - обычный символ) и (add - обычный символ) и (они в одной области видимости) то разрешить. Далее конвертор дерева, получив в ответ на запрос поиска такой список, поймет, что здесь надо выдать ошибку «повторно описанный идентификатор».

2. Если (to - перегруженная подпрограмма) и (add - перегруженная подпрограмма) то разрешить. Конвертор дерева должен сам выбрать подходящую подпрограмму, исходя из анализа параметров подпрограммы.

Вторая проверка реализуется с помощью функции IsVisible:

private bool IsVisible(SymbolInfo ident, Scope fromScope)

{

if (fromScope == null)

return true;

if (FindClassScope(ident.scope) == null)

return true;

switch (ident.access_level)

{

case access_level.al_public:

case access_level.al_internal:

return true;

case access_level.al_protected:

return

IsInOneModule(ident.scope, fromScope)

||

IsInOneOrDerivedClass(ident.scope, fromScope);

case access_level.al_private:

return IsInOneModule(ident.scope, fromScope);

}

return true;

}

Эта функция проверяет, виден ли данный символ, исходя из данного контекста поиска символа. Параметр fromScope указывает, из какой области видимости осущесвляется поиск (контекст поиска).

Уровни видимости символа:

access_level.al_public - публичный, видим для всех;

access_level.al_internal - видим для всех, но после компиляции становися приватным;

access_level.al_protected - защищенный, видин только в модуле в котором определен, в классе в котором определен и в о всех потомках этого класса;

access_level.al_private - приватный, видин только в модуле в котором определен и в классе в котором определен.

Функция IsVisible реализуется с помощью нескольких вспомогательных функций:

IsInOneOrDerivedClass - определяет, находится ли область видимости IdentScope в одном классе с FromScope, либо в одном из базовых классах FromScope:

private bool IsInOneOrDerivedClass(Scope IdentScope, Scope FromScope)

{

IdentScope = FindClassScope(IdentScope);

FromScope = FindClassScope(FromScope);

while (FromScope != null)

{

if (IdentScope.ScopeNum == FromScope.ScopeNum)

return true;

FromScope = ((ClassScope)FromScope).BaseClassScope;

}

return false;

}

IsInOneModule - позволяет определить, находится ли Scope1 и Scope2 в одном модуле:

private bool IsInOneModule(Scope Scope1, Scope Scope2)

{

Scope1 = FindUnitInterfaceScope(Scope1);

Scope2 = FindUnitInterfaceScope(Scope2);

return

(Scope1 != null) && (Scope2 != null)

&& (Scope1.ScopeNum == Scope2.ScopeNum);

}

FindClassScope - находит область видимости класса, в которую вложена область видимости scope:

private Scope FindClassScope(Scope scope)

{

while (scope != null && !(scope is ClassScope))

if(scope is ClassMethodScope)

scope = ((ClassMethodScope)scope).MyClass;

else

scope = scope.TopScope;

return scope;

}

FindUnitInterfaceScope - находит интерфейсную область видимости модуля класса, в которую вложена область видимости scope:

private Scope FindUnitInterfaceScope(Scope scope)

{

while (scope!=null && !(scope is UnitInterfaceScope))

scope = scope.TopScope;

return scope;

}

7.2 Генерация узлов семантического дерева

Семантический анализатор состоит из визитора по синтаксическому дереву и нескольких вспомогательных блоков (таблица символов, таблица типов, контекст компиляции), котрые предоставляют необходимые алгоритмы для семантического анализа. Также семантический анализатор содержит реализацию всех интерфейсов семантического дерева. Семантический анализатор и большинство алгоритмов семантического анализа были разработаны Водолазовым Н. Далее будет описан процесс генерации некоторых частей семантического дерева, которые были сделаны автором.


Подобные документы

  • Рассмотрение нелинейных динамических структур данных в виде бинарного дерева. Построение дерева двоичного поиска. Реализация трех обходов дерева, выведение обходов на экран компьютера. Разработка текста программы. Симметричноправая прошивка дерева.

    контрольная работа [81,6 K], добавлен 14.12.2011

  • Сбалансированные многоходовые деревья поиска. Исследование структуры B+-дерева, её основные операции. Доказательство их вычислительной сложности. Утверждение о высоте. Поиск, вставка, удаление записи, поиск по диапазону. B+-деревья в системах баз данных.

    курсовая работа [705,5 K], добавлен 26.12.2013

  • Организация таблицы идентификаторов, ее содержание и назначение. Метод бинарного дерева и цепочек. Проектирование лексического анализатора и схема распознавателя. Построение дерева вывода, синтаксический анализатор. Анализ результатов работы программы.

    курсовая работа [1,0 M], добавлен 25.12.2014

  • Простой и быстрый переход по иерархической файловой системе. Наличие файлового менеджера. Ряд глобальных переменных. Основные пользовательские функции. Основная идея формирования дерева. Функция добавления записи в список. Обновление дерева каталогов.

    курсовая работа [243,1 K], добавлен 04.06.2011

  • Понятие и базовые свойства ориентированного дерева. Обходы (способы нумерации вершин) в глубину и ширину. Представление бинарных графов с помощью указателей и массива, скобочной записи, списком прямых предков. Сбалансированность дерева двоичного поиска.

    презентация [330,6 K], добавлен 19.10.2014

  • Организация бинарного дерева. Порядок размещения данных в нелинейных структурах. Организация пользовательского интерфейса. Симметричный обход дерева. Параллельная работа обработчиков исключений. Расширенный графический интерфейс и его возможности.

    курсовая работа [426,0 K], добавлен 24.06.2013

  • Краткая история становления языка программирования Pascal и основные понятия графики. Основные функции и процедуры работы с графикой в PascalABC. Создание графического проекта: понятие "фрактал" и реализация треугольника. Построения фрактала "Дерево".

    курсовая работа [1,4 M], добавлен 26.10.2014

  • Разработка программы на языке С#, которая будет заниматься построением бинарного дерева для исходных данных и их редактированием, поиском информации о товарах по заданному ключу. Графические схемы алгоритмов поиска и удаления элемента бинарного дерева.

    курсовая работа [796,9 K], добавлен 22.02.2016

  • Понятие семантики; обзор и анализ существующих средств семантического разбора естественно-языковых текстов. Разработка алгоритма работы системы на основе семантического анализа, его реализация на языке программирования; проектирование интерфейса системы.

    дипломная работа [1,7 M], добавлен 18.03.2012

  • Изучение структуры доменных имен и описание возможностей их системы по использованию символьных наименований узлов в IP-сетях. Записи ресурсов домена и функции сети по расширению имен и зон обратного просмотра. Делегирование ответственности за домены.

    презентация [104,2 K], добавлен 25.10.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.