Как написать парсер на C#? [закрытый]
Как мне написать парсер (рекурсивный спуск?) в C#? Сейчас мне просто нужен простой парсер, который анализирует арифметические выражения (и читает переменные?). Хотя позже я намерен написать XML и html парсер (для учебных целей). Я делаю это из-за широкого спектра вещей, в которых Парсеры полезны: Веб-разработка, интерпретаторы языков программирования, внутренние инструменты, игровые движки, редакторы карт и плиток и т. д. Итак, какова основная теория написания парсеров и как мне это сделать реализовать один в C#? Является ли C# правильным языком для парсеров (я когда-то написал простой арифметический парсер на C++, и это было эффективно. Будет ли компиляция JIT одинаково хороша?). Любые полезные ресурсы и статьи. И лучше всего, примеры кода (или ссылки на примеры кода).
Примечание: из любопытства, кто-нибудь отвечает на этот вопрос когда-либо реализовывал парсер в C#?
7 ответов:
я реализовал несколько парсеров в C# - рукописный и инструмент генерируется.
очень хороший вводный учебник по разбору в целом является давайте построим компилятор - он демонстрирует, как построить рекурсивный парсер спуска; и понятия легко переводятся с его языка (я думаю, что это был Паскаль) на C# для любого компетентного разработчика. Это научит вас, как работает рекурсивный парсер спуска, но совершенно непрактично писать полное Программирование язык парсер вручную.
вы должны изучить некоторые инструменты для создания кода для вас - если вы решили написать классический рекурсивный спуск парсер (TinyPG,Coco / R,Ирония). Имейте в виду, что теперь есть другие способы написания парсеров, которые обычно работают лучше - и имеют более простые определения (например, TDOP разбор или Монадическом Разбора).
по теме ли В C# для этой задачи - C# имеет некоторые из лучших текстовых библиотек там. Многие Парсеры сегодня (на других языках) имеют непристойное количество кода для работы с Unicode и т. д. Я не буду слишком много комментировать JITted-код, потому что он может стать довольно религиозным - однако вы должны быть в порядке. IronJS является хорошим примером парсера / среды выполнения на CLR (хотя его написано в F#), и его производительность просто стесняется Google V8.
Примечание: разметка Парсеры-это совершенно разные звери по сравнению с языковыми парсерами - они в большинстве случаев написаны вручную - и на уровне сканера / парсера очень просты; они обычно не являются рекурсивным спуском - и особенно в случае XML лучше, если вы не пишете рекурсивный парсер спуска (чтобы избежать переполнения стека, и потому что "плоский" парсер можно использовать в режиме SAX/push).
Sprache это мощный, но легкий фреймворк для написания парсеров в. NET. There также Sprache NuGet package. Чтобы дать вам представление о структуре здесь является одним из образцы который может анализировать простое арифметическое выражение в дереве выражений .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)); } }
C# - это почти приличный функциональный язык, поэтому реализовать в нем что-то вроде Parsec не так уж и сложно. Вот один из примеров того, как это сделать:http://jparsec.codehaus.org/NParsec + учебник
также можно реализовать комбинатор на основе Packrat, очень похожим образом, но на этот раз сохраняя глобальное состояние разбора где-то вместо того, чтобы делать чистый функциональный материал. В моей (очень простой и специальной) реализации это было достаточно быстро, но, конечно, генератор кода, как этой должны работать лучше.
Я знаю, что немного опоздал, но я только что опубликовал библиотеку parser/grammar/AST generator с именем ve Parser. вы можете найти его по адресу http://veparser.codeplex.com или добавьте в свой проект, введя "Install-Package veparser" в консоли диспетчера пакетов. Эта библиотека является своего рода рекурсивным парсером спуска, который призван быть простым в использовании и гибким. Поскольку его источник доступен для вас, вы можете узнать из его исходных кодов. Надеюсь, это поможет.
на мой взгляд, есть лучший способ реализовать Парсеры, чем традиционные методы, что приводит к более простому и легкому пониманию кода, и особенно облегчает расширение любого языка, который вы анализируете, просто подключив новый класс очень объектно-ориентированным способом. Одна статья из большой серии, которую я написал, посвящена этому методу анализа, и полный исходный код включен на C# 2.0 синтаксический анализатор: http://www.codeproject.com/Articles/492466/Object-Oriented-Parsing-Breaking-With-Tradition-Pa
хорошо... с чего начать с этого....
во-первых, написание парсера, ну это очень широкое заявление, особенно с вопросом, который вы задаете.
ваше вступительное заявление было то , что вы хотели простой арифметический "парсер", ну технически это не парсер, это лексический анализатор, подобный тому, что вы можете использовать для создания нового языка. ( http://en.wikipedia.org/wiki/Lexical_analysis ) я понимаю, однако, где именно путаница в том, что это одно и то же, может возникнуть. Важно отметить, что лексический анализ также является тем, что вы хотите понять, если вы собираетесь писать синтаксические анализаторы языка/скрипта, это строго не разбор, потому что вы интерпретируете инструкции, а не используете их.
вернемся к вопросу разбора....
это то, что вы будете делать, если вы берете жестко определенную файловую структуру для извлечения информации из нее.
In вообще вам действительно не нужно писать парсер для 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");затем вы можете добраться до 'фильмы' корневой элемент с помощью ' myXML.Корень'
более интересно однако, вы можете использовать Linq легко получить вложенные теги:
var myElements = from p in myXML.Root.Elements("movie") select p;даст вам var XElements, каждый из которых содержит один '...- который вы можете получить, используя что-то вроде:
foreach(var v in myElements) { Console.WriteLine(string.Format("ID {0} = {1}",(int)v.Attributes["id"],(string)v.Element("movie")); }для всего остального, кроме XML, как структуры данных, то я боюсь, что вам придется начните изучать искусство регулярных выражений, такой инструмент, как "регулярное выражение тренер" поможет вам imensly ( http://weitz.de/regex-coach/) или один из более uptodate подобных инструментов.
вам также необходимо ознакомиться с объектами регулярных выражений .NET, ( http://www.codeproject.com/KB/dotnet/regextutorial.aspx) должен дать вам хорошую фору.
как только вы знаете, как работает ваш reg-ex материал, то в большинстве случаев это простой случай, когда вы читаете файлы по одной строке за раз и понимаете их, используя какой-либо метод, с которым вы чувствуете себя комфортно.
хороший бесплатный источник форматов файлов для почти всего, что вы можете себе представить можно найти на ( http://www.wotsit.org/ )
для записи я реализовал генератор парсеров в C# только потому, что я не мог найти ни одного работающего должным образом или похожего на YACC (см.:http://sourceforge.net/projects/naivelangtools/).
однако после некоторого опыта работы с ANTLR я решил пойти с LALR вместо LL. Я знаю, что теоретически LL легче реализовать (генератор или парсер), но я просто не могу жить со стеком выражений только для выражения приоритетов операторов (например,
*идет перед+in "2+5*3"). В LL вы говорите, что mult_expr встроен в add_expr, который не кажется естественным для меня.
Comments