Занятие 3 по курсу МПК — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Компиляция проекта)
(Lex-файл SimpleLang.lex)
Строка 34: Строка 34:
 
====Lex-файл SimpleLang.lex ====
 
====Lex-файл SimpleLang.lex ====
  
<source lang="csharp">%namespace SimpleScanner
+
<source lang="csharp">%using ScannerHelper;
 +
%namespace SimpleScanner
  
 
Alpha [a-zA-Z_]
 
Alpha [a-zA-Z_]
Строка 42: Строка 43:
 
REALNUM {INTNUM}\.{INTNUM}
 
REALNUM {INTNUM}\.{INTNUM}
 
ID {Alpha}{AlphaDigit}*  
 
ID {Alpha}{AlphaDigit}*  
 +
 +
// Здесь можно делать описания типов, переменных и методов - они попадают в класс Scanner
 +
%{
 +
  public int LexValueInt;
 +
  public double LexValueDouble;
 +
%}
  
 
%%
 
%%
 
{INTNUM} {  
 
{INTNUM} {  
   Console.WriteLine("IntNum "+yytext);
+
   LexValueInt = int.Parse(yytext);
 +
  return (int)Tok.INUM;
 
}
 
}
  
 
{REALNUM} {  
 
{REALNUM} {  
   Console.WriteLine("RealNum "+yytext);
+
   LexValueDouble = double.Parse(yytext);
 +
  return (int)Tok.RNUM;
 
}
 
}
  
 
begin {  
 
begin {  
   Console.WriteLine("Key: begin");  
+
   return (int)Tok.BEGIN;
 
}
 
}
  
 
end {  
 
end {  
   Console.WriteLine("Key: end");
+
   return (int)Tok.END;
 +
}
 +
 
 +
cycle {
 +
  return (int)Tok.CYCLE;
 
}
 
}
  
 
{ID}  {  
 
{ID}  {  
   Console.WriteLine("ID "+yytext);
+
   return (int)Tok.ID;
 
}
 
}
  
 
":" {  
 
":" {  
   Console.WriteLine("COLON");
+
   return (int)Tok.COLON;
 
}
 
}
  
 
":=" {  
 
":=" {  
   Console.WriteLine("ASSIGN");
+
   return (int)Tok.ASSIGN;
 
}
 
}
  
 
";" {  
 
";" {  
   Console.WriteLine("SEMICOLON");
+
   return (int)Tok.SEMICOLON;
 
}
 
}
  
 
%%
 
%%
  
// Здесь можно делать описания переменных и методов - они попадают в класс Scanner
+
// Здесь можно делать описания переменных и методов - они тоже попадают в класс Scanner
 
public int Sum = 0;</source>
 
public int Sum = 0;</source>
  

Версия 11:48, 16 августа 2014

К основной странице курса

Разработка лексических анализаторов с помощью программы GPLex

GPLex - программа для автоматической генерации сканеров (лексических анализаторов)

Комплект для практического занятия скачиваем отсюда. Состав:

  • LexProjects.sln - файл решения, содержащее проект Lex1.csproj
  • Lex1.csproj - файл демонстрационного проекта для GPLex
  • gplex.exe - исполняемый файл генератора сканеров
  • mymain.cs - основная программа, содержащая создание сканера и сканирование всех лексем в файле
  • ShiftReduceParserCode.cs - файл проекта, необходимый для работы лексического анализатора
  • SimpleLex.lex - файл, содержащий правила для генерации лексического анализатора
  • generateScanner.bat - командный файл. Содержит одну команду: gplex.exe /noparser SimpleLex.lex
  • SimpleLex.cs - файл проекта, автоматически создаваемый при запуске generateScanner.bat
  • a.txt - файл программы, подаваемой на вход сгенерированному лексеру

Компиляция проекта и выполнение программы

  • Выполняем команду gplex.exe /noparser SimpleLex.lex, запустив командный файл generateScanner.bat
При этом генерируется файл SimpleLex.cs, содержащий код лексера. Ключ /noparser означает, что генерируется лексер без парсера.
  • Открываем и компилируем .sln
  • Меняем содержимое a.txt, запускаем проект и анализируем результаты

Формат .lex-файла

Определения
%%
Правила
%%
Пользовательский код

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

Lex-файл SimpleLang.lex

%using ScannerHelper;
%namespace SimpleScanner

Alpha 	[a-zA-Z_]
Digit   [0-9] 
AlphaDigit {Alpha}|{Digit}
INTNUM  {Digit}+
REALNUM {INTNUM}\.{INTNUM}
ID {Alpha}{AlphaDigit}* 

// Здесь можно делать описания типов, переменных и методов - они попадают в класс Scanner
%{
  public int LexValueInt;
  public double LexValueDouble;
%}

%%
{INTNUM} { 
  LexValueInt = int.Parse(yytext);
  return (int)Tok.INUM;
}

{REALNUM} { 
  LexValueDouble = double.Parse(yytext);
  return (int)Tok.RNUM;
}

begin { 
  return (int)Tok.BEGIN;
}

end { 
  return (int)Tok.END;
}

cycle { 
  return (int)Tok.CYCLE;
}

{ID}  { 
  return (int)Tok.ID;
}

":" { 
  return (int)Tok.COLON;
}

":=" { 
  return (int)Tok.ASSIGN;
}

";" { 
  return (int)Tok.SEMICOLON;
}

%%

// Здесь можно делать описания переменных и методов - они тоже попадают в класс Scanner
public int Sum = 0;

Регулярные выражения, используемые в секции определений

. один символ кроме '\n'
* ноль или более повторений
+ одно или более повторений
 ? ноль или одно повторение
[] класс символов, обозначающий любой символ внутри []
^ при использовании внутри [] обозначает отрицание. При использовании вне [] обозначает, что шаблон начинается с начала строки
\ начало esc-последовательности (например, \n)

Внутри [] можно использовать: - для задания диапазона, например [0-9] ^ в начале для задания отрицания, например [^"\n] (не кавычки и не символ перехода на новую строку)

Примеры регулярных выражений

[0-9]
[0-9]+
[0-9]*\.[0-9]+    \. здесь обозначает точку, т.к. просто . имеет другое значение
[+-]?[0-9]+
#.* комментарий, начинающийся с #, после которого идет ноль или более символов до конца строки
\"[^"\n]*["\n] кавычка, после которой идет любое количество не кавычек и не символов перехода на новую строку, после которых идет кавычка или переход на новую строку - так может задаваться литеральная строка. Здесь вне [] кавычка предваряется \, поскольку символ " имеет самостоятельный смысл; в [] кавычку можно писать без \, поскольку внутри [] кавычка не имеет самостоятельного смысла
[^ \n\t]+ любой символ, не являющийся пробелом, переходом на новую строчку, табулостопом, повторяющийся 1 или более раз
^[ \t]*\n строка из whitespace - пробелы или знаки табуляции, начинающиеся с начала строки и повторенные ноль или более раз, после которых идет символ перехода на новую строку

Класс Scanner

  • Основной метод - int yylex() - возвращает код следующей лексемы (токена)
  • Свойства
string yytext - текст лексемы
int yyline - номер строки лексемы
int yycol - номер столбца лексемы
int yyleng - длина лексемы

Основная программа

using System;
using System.IO;
using SimpleScanner;

namespace Main
{
    class mymain
    {
        static void Main(string[] args)
        {
            Scanner scanner = new Scanner(new FileStream(@"..\..\a.txt", FileMode.Open));

            int tok = 0;
            do {
                tok = scanner.yylex();
            } while (tok != (int)Tokens.EOF);
        }
    }
}

Комментарии к программе

Лексический анализатор (сканер) содержит главный метод - yylex() - который возвращает следующую лексему, точнее, ее тип, приведенный к целому типу.

В основной программе мы в цикле вызываем метод scanner.yylex() пока не будет достигнут конец потока

Задания

  1. Откомпилировать лексический анализатор и запустить его для файла a.txt
  2. Сделать имя файла, обрабатываемого лексическим анализатором, параметром командной строки
  3. Подсчитать количество, среднюю, минимальную и максимальную длину всех идентификаторов
  4. Найти сумму всех целых и сумму всех вещественных в файле
  5. Дана программа, в которой встречаются ключевые слова begin end. Определить, правильно ли они расставлены
  6. По данному тексту слов составить таблицу
слово   список его вхождений в текст в формате (строка,столбец), (строка,столбец)