Создание синтаксического анализатора с помощью программы GPPG

Материал из Вики ИТ мехмата ЮФУ
Версия от 11:21, 17 августа 2014; Admin (обсуждение | вклад) (Комментарии к файлу SimpleLex.lex)

Перейти к: навигация, поиск

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

Общие комментарии

  • В данном проекте мы создадим front-end компилятор с помощью генератора парсеров gppg.
  • Комплект для практического занятия скачиваем отсюда.
  • Для автоматического создания парсера создаются файлы SimpleLex.lex (описание лексического анализатора) и SimpleYacc.y (описание синтаксического анализатора).
  • Код лексического и синтаксического анализаторов создаются на C# запуском командного файла generateParserScanner.bat со следующим содержимым:
gplex.exe /unicode SimpleLex.lex
gppg.exe /no-lines /gplex SimpleYacc.y

Параметр /gplex здесь означает, что gppg работает в связке с gplex.

Файл синтаксического анализатора SimpleYacc.y

%{
// Эти объявления добавляются в класс GPPGParser, представляющий собой парсер, генерируемый системой gppg
    public Parser(AbstractScanner<int, LexLocation> scanner) : base(scanner) { }
%}

%output = SimpleYacc.cs

%namespace SimpleParser

%token BEGIN END CYCLE INUM RNUM ID ASSIGN SEMICOLON  

%%

progr   : block
	;

stlist	: statement 
	| stlist SEMICOLON statement 
	;

statement: assign
	| block  
	| cycle  
	;

ident 	: ID 
	;
	
assign 	: ident ASSIGN expr 
	;

expr	: ident  
	| INUM 
	;

block	: BEGIN stlist END 
	;

cycle	: CYCLE expr statement 
	;
	
%%

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

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

Комментарии к файлу SimpleYacc.y

  • Файл синтаксического анализатора значительно проще и понятнее рукописного файла синтаксического анализатора. Он не содержит кода и содержит лишь спецификацию грамматики.
  • Секция правил представляет собой описание грамматики языка. Пример правильной программы, удовлетворяющей этой грамматике:
begin
  b := 2;
  a := b;
  cycle 3
  begin
    a := c;
    c := 1
  end
end
  • Определение последовательности операторов - леворекурсивно:
stlist	: statement 
	| stlist SEMICOLON statement 
	;

В отличие от создания синтаксического анализатора методом рекурсивного спуска, где запрещаются леворекурсивные грамматики, в генераторе парсеров gppg именно леворекурсивные грамматики предпочтительны. Это связано с тем, что gppg содержит восходящий алгоритм разбора и разбирает класс грамматик LR.

  • В секции определений содержится описание всех терминалов:
%token BEGIN END CYCLE INUM RNUM ID ASSIGN SEMICOLON

По этому определению в файле SimpleYacc.cs генерируется перечислимый тип

public enum Tokens {
    error=1,EOF=2,BEGIN=3,END=4,CYCLE=5,INUM=6,
    RNUM=7,ID=8,ASSIGN=9,SEMICOLON=10};

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

Файл лексического анализатора SimpleLex.lex

%using SimpleParser;
%using QUT.Gppg;
%using System.Linq;

%namespace SimpleScanner

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

%%

{INTNUM} { 
  return (int)Tokens.INUM; 
}

{REALNUM} { 
  return (int)Tokens.RNUM;
}

{ID}  { 
  int res = ScannerHelper.GetIDToken(yytext);
  return res;
}

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

[^ \r\n] {
  LexError();
}

%{
  yylloc = new LexLocation(tokLin, tokCol, tokELin, tokECol); // позиция символа (терминального или нетерминального), возвращаемая @1 @2 и т.д.
%}

%%

public override void yyerror(string format, params object[] args) // обработка синтаксических ошибок
{
  var ww = args.Skip(1).Cast<string>().ToArray();
  string errorMsg = string.Format("({0},{1}): Встречено {2}, а ожидалось {3}", yyline, yycol, args[0], string.Join(" или ", ww));
  throw new SyntaxException(errorMsg);
}

public void LexError()
{
  string errorMsg = string.Format("({0},{1}): Неизвестный символ {2}", yyline, yycol, yytext);
  throw new LexException(errorMsg);
}

class ScannerHelper 
{
  private static Dictionary<string,int> keywords;

  static ScannerHelper() 
  {
    keywords = new Dictionary<string,int>();
    keywords.Add("begin",(int)Tokens.BEGIN);
    keywords.Add("end",(int)Tokens.END);
    keywords.Add("cycle",(int)Tokens.CYCLE);
  }
  public static int GetIDToken(string s)
  {
    if (keywords.ContainsKey(s.ToLower())) // язык нечувствителен к регистру
      return keywords[s];
    else
      return (int)Tokens.ID;
  }
}

Комментарии к файлу SimpleLex.lex

  • Тип Tok из предыдущего лексического анализатора заменен на тип Tokens, автоматически генерируемый синтаксическим анализатором.

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

using System;
using System.IO;
using System.Collections.Generic;
using SimpleScanner;
using SimpleParser;

namespace SimpleCompiler
{
    public class SimpleCompilerMain
    {
        public static void Main()
        {
            string FileName = @"..\..\a.txt";
            try
            {
                string Text = File.ReadAllText(FileName);

                Scanner scanner = new Scanner();
                scanner.SetSource(Text, 0);
            
                Parser parser = new Parser(scanner);
                      
                var b = parser.Parse();
                if (!b)
		     Console.WriteLine("Ошибка");
                else Console.WriteLine("Программа распознана");
            }
            catch (FileNotFoundException)
            {
                Console.WriteLine("Файл {0} не найден", FileName);
            }
            catch (LexException e)
            {
                Console.WriteLine("Лексическая ошибка. " + e.Message);
            }
            catch (SyntaxException e)
            {
                Console.WriteLine("Синтаксическая ошибка. " + e.Message);
            }

            Console.ReadLine();
        }

    }
}

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

Основные задания