Как создать парсер (lex / yacc)? - PullRequest
3 голосов
/ 12 марта 2011

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

--TestFile
Start ASDF123
Name "John"
Address "#6,US" 
end ASDF123

Строки, начинающиеся с --, будут рассматриваться как строки комментариев.и файл начинается с «Пуск» и заканчивается end.Строка после Start - это UserID, а затем name и address будут заключены в двойные кавычки.

Мне нужно проанализировать файл и записать проанализированные данные в XML-файл.

Таким образом, полученный файл будет выглядеть как

<ASDF123>
  <Name Value="John" />
  <Address Value="#6,US" />
</ASDF123>

, теперь я использую сопоставление с шаблоном (Regular Expressions) для разбора файла выше.Вот мой пример кода.

    /// <summary>
    /// To Store the row data from the file
    /// </summary>
    List<String> MyList = new List<String>();

    String strName = "";
    String strAddress = "";
    String strInfo = "";

Метод : ReadFile

    /// <summary>
    /// To read the file into a List
    /// </summary>
    private void ReadFile()
    {
        StreamReader Reader = new StreamReader(Application.StartupPath + "\\TestFile.txt");
        while (!Reader.EndOfStream)
        {
            MyList.Add(Reader.ReadLine());
        }
        Reader.Close();
    }

Метод : FormateRowData

    /// <summary>
    /// To remove comments 
    /// </summary>
    private void FormateRowData()
    {
        MyList = MyList.Where(X => X != "").Where(X => X.StartsWith("--")==false ).ToList();
    }

Метод : ParseData

    /// <summary>
    /// To Parse the data from the List
    /// </summary>
    private void ParseData()
    {
        Match l_mMatch;
        Regex RegData = new Regex("start[ \t\r\n]*(?<Data>[a-z0-9]*)", RegexOptions.IgnoreCase);
        Regex RegName = new Regex("name [ \t\r\n]*\"(?<Name>[a-z]*)\"", RegexOptions.IgnoreCase);
        Regex RegAddress = new Regex("address [ \t\r\n]*\"(?<Address>[a-z0-9 #,]*)\"", RegexOptions.IgnoreCase);
        for (int Index = 0; Index < MyList.Count; Index++)
        {
            l_mMatch = RegData.Match(MyList[Index]);
            if (l_mMatch.Success)
                strInfo = l_mMatch.Groups["Data"].Value;
            l_mMatch = RegName.Match(MyList[Index]);
            if (l_mMatch.Success)
                strName = l_mMatch.Groups["Name"].Value;
            l_mMatch = RegAddress.Match(MyList[Index]);
            if (l_mMatch.Success)
                strAddress = l_mMatch.Groups["Address"].Value;
        }
    }

Метод : WriteFile

    /// <summary>
    /// To write parsed information into file.
    /// </summary>
    private void WriteFile()
    {
        XDocument XD = new XDocument(
                           new XElement(strInfo,
                                         new XElement("Name",
                                             new XAttribute("Value", strName)),
                                         new XElement("Address",
                                             new XAttribute("Value", strAddress))));
        XD.Save(Application.StartupPath + "\\File.xml");
    }

Я слышал о ParserGenerator

Пожалуйста, помогите мне написать парсер с использованием lex и yacc.Причина этого в том, что мой существующий синтаксический анализатор (Pattern Matching) не гибкий, более того, он не является правильным способом (я так думаю).

Как мне использовать ParserGenerator (I 'прочитали Пример проекта кода и Пример проекта кода 2 , но я все еще не знаком с этим).Пожалуйста, предложите мне генератор парсеров, который выводит парсеры C #.

Ответы [ 2 ]

5 голосов
/ 12 марта 2011

Gardens Point LEX и Генератор синтаксического анализатора Gardens Point находятся под сильным влиянием LEX и YACC, и выводят код C #.

Ваша грамматика достаточно проста, так что я думаю, что ваш нынешний подход хорош, но слава за желание изучить "реальный" способ сделать это. :-) Итак, вот мое предложение по грамматике (только производственные правила; это далеко не полный пример. Фактический файл GPPG должен заменить код ... на C # для построения синтаксического дерева, и вам нужны объявления токенов и т. Д.). - прочитайте примеры GPPG в документации. Вам также понадобится файл GPLEX, который описывает токены):

/* Your input file is a list of "top level elements" */
TopLevel : 
    TopLevel TopLevelElement { ... }
    | /* (empty) */

/* A top level element is either a comment or a block. 
   The COMMENT token must be described in the GPLEX file as 
   any line that starts with -- . */
TopLevelElement:
    Block { ... }
    | COMMENT { ... }

/* A block starts with the token START (which, in the GPLEX file, 
   is defined as the string "Start"), continues with some identifier 
   (the block name), then has a list of elements, and finally the token
   END followed by an identifier. If you want to validate that the
   END identifier is the same as the START identifier, you can do that
   in the C# code that analyses the syntax tree built by GPPG.
   The token Identifier is also defined with a regular expression in GPLEX. */
Block:
    START Identifier BlockElementList END Identifier { ... }

BlockElementList:
    BlockElementList BlockElement { ... }
    | /* empty */

BlockElement:
    (NAME | ADDRESS) QuotedString { ... }
1 голос
/ 12 марта 2011

Сначала вам нужно определить грамматику для вашего парсера.(Часть Yacc)

Кажется, что-то вроде:

file : record file
     ;

record: start identifier recordContent end identifier {//rule to match the two identifiers}
      ;

recordContent: name value; //Can be more detailed if you require order in the fields

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

Мой ответ - черновик, я советую вам поискать в Интернете, чтобы найти более полное руководство по lex / yacc flex / bison, и вернутьсяздесь, если у вас есть более сфокусированная проблема.

Я также не знаю, есть ли реализация C #, которая позволила бы вам сохранить управляемый код.Возможно, вам придется использовать неуправляемый импорт C / C ++.

...