пятница, 24 сентября 2010 г.

Простейший компилятор простейшего языка C в Assembler. Часть 1. Транслитератор

Два слова в общем

Для общего развития, никогда не помешает написать компилятор языка. Я не ставлю перед собой задачу написать компилятор c++ или c#, задачу которую я перед собой ставлю - это необходимый минимум для ознакомления с теорией компиляции.

Входной язык будет достаточно простой, урезанный по самое не могу, C. А выходной язык будет - язык Ассемблера.

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

Наш простейший компилятор языка C (далее TheSimpliestCCompiler - tsico), будет разбит на четыре блока или более: transliteration unit, lexical unit, syntax unit, generation unit. Получатся четырех-проходной компилятор. Чем же занимаются эти блоки?

Transliteration Unit


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

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

Lexical Unit

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

Syntax Unit 

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

Generation Unit

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

Транслитератор

Сегодня, мы приступим к транслитератору. Сразу оговорюсь, язык на котором, мы будем писать транслитератор - C#.

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

Определимся с символьными классами для простейшего прототипа языка C:

  • letter - [A-Za-z]
  • digit - [0-9]
  • whitespace - ' '
  • newline - '\n'
  • compare - [><=!]
  • arithmetic - [+-/*]
  • ( - '('
  • ) - ')'
  • ; - ';'
  • { - '{'
  • } - '}'
  • . - '.'
  • : - ':'

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

Я не буду приводить весь код транслитератор. Покажу лишь фрагмент:

class TransliterationUnit
{

    // ... 

    public void Transliterate(
        string input, string output)
    {
        // don't do this at production, 
        // reading string to memory can be 
        // so dangerous on files with larger size
        string code = File.OpenText(input).ReadToEnd().
                           Replace("\r", "").
                           Replace("\t", "").Trim().
                           ClearExtraWhitespaces();
        List<Symbol> symbols = new List<Symbol>();

        foreach (char c in code)
        {
            
            if (GetSymbolClass(c) != "other")
            {
                symbols.Add(new Symbol() { 
                   Value = c, 
                   Class = GetSymbolClass(c)});
            }
            else
            {
                _logger.Log(String.Format(
                    "Unexpected symbol - '{0}'", c));
            }
        }

        SaveSymbols(output, symbols);
    }

    // ... 
}

Если у вас есть желание ознакомиться с полной работающей версии транслитератора компилятора tsico (я упоминал о названии выше), то вы можете скачать проект с исходным кодом для Visual Studio 2010.

Литература

В завершение я бы хотел посоветовать вам книгу "Альфред В. Ахо, Моника С. Лам, Рави Сети, Джеффри Д. Ульман: Компиляторы. Принципы, технологии и инструментарий".

Комментариев нет:

Отправить комментарий