it-swarm.com.ru

Как написать парсер в C #?

Как мне написать парсер (рекурсивный спуск?) На C #? Сейчас я просто хочу простой синтаксический анализатор, который анализирует арифметические выражения (и читает переменные?). Хотя позже я намереваюсь написать парсер xml и html (в учебных целях). Я делаю это из-за широкого спектра вещей, в которых полезны парсеры: веб-разработка, интерпретаторы языков программирования, встроенные инструменты, игровые движки, редакторы карт и плиток и т.д. Итак, какова основная теория написания парсеров и как мне это сделать? реализовать один в C #? Является ли C # подходящим языком для синтаксических анализаторов (однажды я написал простой арифметический синтаксический анализатор на C++, и он был эффективным. Будет ли компиляция JIT одинаково хорошей?). Любые полезные ресурсы и статьи. И, что лучше всего, примеры кода (или ссылки на примеры кода). 

Примечание: из любопытства кто-нибудь, отвечая на этот вопрос, когда-либо реализовывал парсер в C #?

59
ApprenticeHacker

Я реализовал несколько синтаксических анализаторов в C # - рукописных и сгенерированных инструментов.

Очень хорошим вводным уроком по синтаксическому анализу в целом является Давайте построим компилятор - он демонстрирует, как построить анализатор рекурсивного спуска; и концепции легко переводятся с его языка (я думаю, что это был Паскаль) на C # для любого компетентного разработчика. Это научит вас, как работает парсер рекурсивного спуска, но совершенно непрактично писать полный парсер языка программирования вручную.

Вы должны изучить некоторые инструменты для генерации кода для вас - если вы решили написать классический парсер рекурсивного спуска ( TinyPG , Coco/R , Irony ). Имейте в виду, что сейчас есть другие способы написания синтаксических анализаторов, которые обычно работают лучше - и имеют более простые определения (например, синтаксический анализ TDOP или монадический синтаксический анализ ).

На тему того, подходит ли C # для этой задачи - C # имеет одни из лучших текстовых библиотек. Многие парсеры сегодня (на других языках) имеют непристойное количество кода для работы с Unicode и т.д. Я не буду комментировать слишком много кода JITted, потому что он может стать довольно религиозным - однако у вас должно быть все в порядке. IronJS является хорошим примером синтаксического анализатора/среды выполнения в CLR (даже если он написан на F #), а его производительность просто не соответствует Google V8.

Side Примечание: Парсеры разметки - совершенно разные звери по сравнению с парсерами языков - в большинстве случаев они пишутся от руки - и на уровне сканера/парсера очень просты; они обычно не являются рекурсивным спуском - и особенно в случае XML, лучше, если вы не пишете парсер рекурсивного спуска (чтобы избежать переполнения стека, и потому что «плоский» парсер может использоваться в режиме SAX/Push).

79
Jonathan Dickinson

Sprache - это мощная, но легкая среда для написания парсеров в .NET. Существует также Sprache NuGet пакет . Чтобы дать вам представление о структуре, вот один из samples , который может анализировать простое арифметическое выражение в дереве выражений .NET. Довольно удивительно, я бы сказал.

using System;
using System.Linq.Expressions;
using Sprache;

namespace LinqyCalculator
{
    static class ExpressionParser
    {
        public static Expression<Func<decimal>> ParseExpression(string text)
        {
            return Lambda.Parse(text);
        }

        static Parser<ExpressionType> Operator(string op, ExpressionType opType)
        {
            return Parse.String(op).Token().Return(opType);
        }

        static readonly Parser<ExpressionType> Add = Operator("+", ExpressionType.AddChecked);
        static readonly Parser<ExpressionType> Subtract = Operator("-", ExpressionType.SubtractChecked);
        static readonly Parser<ExpressionType> Multiply = Operator("*", ExpressionType.MultiplyChecked);
        static readonly Parser<ExpressionType> Divide = Operator("/", ExpressionType.Divide);

        static readonly Parser<Expression> Constant =
            (from d in Parse.Decimal.Token()
             select (Expression)Expression.Constant(decimal.Parse(d))).Named("number");

        static readonly Parser<Expression> Factor =
            ((from lparen in Parse.Char('(')
              from expr in Parse.Ref(() => Expr)
              from rparen in Parse.Char(')')
              select expr).Named("expression")
             .XOr(Constant)).Token();

        static readonly Parser<Expression> Term = Parse.ChainOperator(Multiply.Or(Divide), Factor, Expression.MakeBinary);

        static readonly Parser<Expression> Expr = Parse.ChainOperator(Add.Or(Subtract), Term, Expression.MakeBinary);

        static readonly Parser<Expression<Func<decimal>>> Lambda =
            Expr.End().Select(body => Expression.Lambda<Func<decimal>>(body));
    }
}
17
Martin Liversage

C # - почти достойный функциональный язык, поэтому не так уж и сложно реализовать в нем что-то вроде Parsec. Вот один из примеров того, как это сделать: http://jparsec.codehaus.org/NParsec+Tutorial

Также возможно реализовать Packrat на основе комбинатора, очень похожим образом, но на этот раз где-то хранить глобальное состояние синтаксического анализа вместо того, чтобы делать чисто функциональные вещи. В моей (очень простой и специальной) реализации это было достаточно быстро, но, конечно, генератор кода вроде this должен работать лучше.

3
SK-logic

Я знаю, что немного опоздал, но я только что опубликовал библиотеку парсера/грамматики/генератора AST с именем Ve Parser. Вы можете найти его по адресу http://veparser.codeplex.com или добавить в свой проект, введя «Install-Package veparser» в консоли диспетчера пакетов. Эта библиотека является своего рода анализатором рекурсивного спуска, который должен быть простым в использовании и гибким. Поскольку его источник доступен вам, вы можете узнать из его исходных кодов. Я надеюсь, что это помогает.

2
000

На мой взгляд, есть лучший способ реализовать синтаксические анализаторы, чем традиционные методы, что приводит к более простому и легкому для понимания коду и особенно облегчает расширение любого языка, который вы анализируете, просто подключив новый класс к самому объекту. ориентированный путь. Одна статья из большой серии, которую я написал, посвящена этому методу синтаксического анализа, и полный исходный код включен для синтаксического анализатора C # 2.0: http://www.codeproject.com/Articles/492466/Object-Oriented- Parsing-Break-With-Tradition-Pa

1
Ken Beckett

Ну ... с чего начать ....

Прежде всего, написание парсера, это очень широкое утверждение, особенно с вопросом, который вы задаете.

Ваше вступительное заявление состояло в том, что вы хотели простой арифметический «парсер», технически это не парсер, а лексический анализатор, аналогичный тому, который вы можете использовать для создания нового языка. ( http://en.wikipedia.org/wiki/Lexical_analysis ) Однако я точно понимаю, откуда может возникнуть путаница в том, что они одно и то же. Важно отметить, что лексический анализ - это то же, что вы захотите понять, если вы тоже собираетесь писать парсеры языка/скрипта, это строго не синтаксический анализ, потому что вы интерпретируете инструкции, а не используете их.

Вернуться к разбору вопроса ....

Это то, что вы будете делать, если вы берете жестко заданную файловую структуру для извлечения информации из нее.

В общем, вам действительно не нужно писать анализатор для XML/HTML, потому что их уже существует масса, и более того, если ваш синтаксический анализ XML производится во время выполнения .NET, то вам даже не нужно разобрать, вам просто нужно "сериализовать" и "десериализовать".

Однако в интересах изучения синтаксический анализ XML (или чего-либо подобного html) в большинстве случаев очень прост.

если мы начнем со следующего XML:

    <movies>
      <movie id="1">
        <name>Tron</name>
      </movie>
      <movie id="2">
        <name>Tron Legacy</name>
      </movie>
    <movies>

мы можем загрузить данные в XElement следующим образом:

    XElement myXML = XElement.Load("mymovies.xml");

затем вы можете получить корневой элемент 'movies', используя 'myXML.Root'

Более интересно, однако, вы можете легко использовать Linq, чтобы получить вложенные теги:

    var myElements = from p in myXML.Root.Elements("movie")
                     select p;

Даст вам вар XElements, каждый из которых содержит один «...», который вы можете получить, используя что-то вроде:

    foreach(var v in myElements)
    {
      Console.WriteLine(string.Format("ID {0} = {1}",(int)v.Attributes["id"],(string)v.Element("movie"));
    }

Для чего-то еще, кроме XML, таких как структуры данных, тогда я боюсь, что вам придется начать изучать искусство регулярных выражений, такой инструмент, как "тренер регулярных выражений", поможет вам очень ( http://weitz.de/regex-coach/ ) или один из более современных аналогичных инструментов.

Вам также необходимо ознакомиться с объектами регулярных выражений .NET, ( http://www.codeproject.com/KB/dotnet/regextutorial.aspx ) должно дать вам хороший старт.

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

Хороший бесплатный источник форматов файлов практически для всего, что вы можете себе представить, можно найти по адресу ( http://www.wotsit.org/ )

0
shawty

Для записи, я реализовал генератор синтаксических анализаторов в C # только потому, что я не смог найти ни одного работающего должным образом или похожего на YACC (см .: http://sourceforge.net/projects/naivelangtools/ ).

Однако после некоторого опыта с ANTLR я решил пойти с LALR вместо LL. Я знаю, что теоретически LL проще реализовать (генератор или анализатор), но я просто не могу жить со стеком выражений, просто чтобы выразить приоритеты операторов (например, * идет перед + в "2 + 5 * 3"). В LL вы говорите, что mult_expr встроен в add_expr, что не кажется мне естественным.

0
greenoldman