Как создать парсер (lex/yacc)?

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

--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 (Я читал Пример кода проекта 1 и Пример кода 2, но я все еще не знаком с этим). Пожалуйста, предложите мне генератор парсеров, который выводит парсеры C#.

2 ответа

LEX и YACC Gardens Point LEX и генератор синтаксических анализаторов Gardens Point находятся под сильным влиянием выходного кода 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 { ... }

Сначала вам нужно будет определить грамматику для вашего парсера. (Часть 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/yacc flex/bison и вернуться сюда, если у вас есть более сфокусированная проблема.

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

Другие вопросы по тегам