C #에서 파서를 작성하는 방법?
C #으로 파서 (재귀 하강?)를 작성하려면 어떻게해야합니까? 지금은 산술 표현식을 구문 분석하고 변수를 읽는 간단한 파서를 원합니다. 나중에 학습 목적으로 xml 및 html 파서를 작성하려고합니다. 웹 개발, 프로그래밍 언어 인터프리터, 사내 도구, 게임 엔진,지도 및 타일 편집기 등 파서가 유용한 광범위한 항목 때문에이 작업을 수행하고 있습니다. 파서 작성의 기본 이론은 무엇이며 어떻게해야합니까? C #에서 구현 하시겠습니까? C #이 파서에 적합한 언어입니까 (한때 C ++로 간단한 산술 파서를 작성했고 효율적이었습니다. JIT 컴파일이 똑같이 좋은 것으로 입증됩니까?). 유용한 리소스 및 기사. 그리고 무엇보다도 코드 예제 (또는 코드 예제에 대한 링크)가 있습니다.
참고 : 호기심으로이 질문에 답한 사람이 C #에서 파서를 구현 한 적이 있습니까?
저는 C #으로 여러 파서를 구현했습니다-손으로 작성하고 도구를 생성했습니다.
일반적으로 구문 분석에 대한 아주 좋은 입문 자습서는 Let 's Build a Compiler입니다 . 재귀 하강 구문 분석기를 빌드하는 방법을 보여줍니다. 그리고 개념은 유능한 개발자를 위해 그의 언어 (파스칼이라고 생각합니다)에서 C #으로 쉽게 번역됩니다. 이것은 재귀 적 하강 파서가 어떻게 작동하는지 가르쳐 줄 것이지만 전체 프로그래밍 언어 파서를 직접 작성하는 것은 완전히 비현실적입니다.
코드를 생성 할 수있는 몇 가지 도구를 살펴 봐야합니다. 기존의 재귀 파서 ( TinyPG , Coco / R , Irony ) 를 작성하기로 결정 했다면 . 쉽게 정의 (예를 들어,이 - 일반적으로 더 나은 수행하는 것이, 쓰기 파서에 다른 방법이 지금 있다는 것을 명심 TDOP 구문 분석 또는 단항 구문 분석을 ).
C #이 작업에 적합한 지 여부에 대한 주제-C #에는 최고의 텍스트 라이브러리가 있습니다. 오늘날 (다른 언어로 된) 많은 파서들은 유니 코드 등을 다루기위한 음란 한 양의 코드를 가지고 있습니다. JITted 코드는 상당히 종교적이 될 수 있기 때문에 너무 많이 언급하지는 않겠습니다.하지만 괜찮습니다. IronJS 는 CLR에서 파서 / 런타임의 좋은 예이며 (F #으로 작성 되었음에도 불구하고) 성능은 Google V8에 미치지 못합니다 .
참고 : 마크 업 파서는 언어 파서와 비교할 때 완전히 다른 짐승입니다. 대부분의 경우 손으로 작성되며 스캐너 / 파서 수준에서는 매우 간단합니다. 일반적으로 재귀 하강이 아닙니다. 특히 XML의 경우 재귀 하강 파서를 작성하지 않는 것이 좋습니다 (스택 오버플로를 방지하고 '플랫'파서를 SAX / 푸시 모드에서 사용할 수 있기 때문에).
Sprache 는 .NET에서 파서를 작성하기위한 강력하면서도 가벼운 프레임 워크입니다. 도있다 Sprache NuGet 패키지 . 프레임 워크에 대한 아이디어를 제공하기 위해 간단한 산술 표현식을 .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# is almost a decent functional language, so it is not such a big deal to implement something like Parsec in it. Here is one of the examples of how to do it: http://jparsec.codehaus.org/NParsec+Tutorial
It is also possible to implement a combinator-based Packrat, in a very similar way, but this time keeping a global parsing state somewhere instead of doing a pure functional stuff. In my (very basic and ad hoc) implementation it was reasonably fast, but of course a code generator like this must perform better.
I know that I am a little late, but I just published a parser/grammar/AST generator library named Ve Parser. you can find it at http://veparser.codeplex.com or add to your project by typing 'Install-Package veparser' in Package Manager Console. This library is kind of Recursive Descent Parser that is intended to be easy to use and flexible. As its source is available to you, you can learn from its source codes. I hope it helps.
In my opinion, there is a better way to implement parsers than the traditional methods that results in simpler and easier to understand code, and especially makes it easier to extend whatever language you are parsing by just plugging in a new class in a very object-oriented way. One article of a larger series that I wrote focuses on this parsing method, and full source code is included for a C# 2.0 parser: http://www.codeproject.com/Articles/492466/Object-Oriented-Parsing-Breaking-With-Tradition-Pa
Well... where to start with this one....
First off, writing a parser, well that's a very broad statement especially with the question your asking.
Your opening statement was that you wanted a simple arithmatic "parser" , well technically that's not a parser, it's a lexical analyzer, similar to what you may use for creating a new language. ( http://en.wikipedia.org/wiki/Lexical_analysis ) I understand however exactly where the confusion of them being the same thing may come from. It's important to note, that Lexical analysis is ALSO what you'll want to understand if your going to write language/script parsers too, this is strictly not parsing because you are interpreting the instructions as opposed to making use of them.
Back to the parsing question....
This is what you'll be doing if your taking a rigidly defined file structure to extract information from it.
In general you really don't have to write a parser for XML / HTML, beacuse there are already a ton of them around, and more so if your parsing XML produced by the .NET run time, then you don't even need to parse, you just need to "serialise" and "de-serialise".
In the interests of learning however, parsing XML (Or anything similar like html) is very straight forward in most cases.
if we start with the following XML:
<movies>
<movie id="1">
<name>Tron</name>
</movie>
<movie id="2">
<name>Tron Legacy</name>
</movie>
<movies>
we can load the data into an XElement as follows:
XElement myXML = XElement.Load("mymovies.xml");
you can then get at the 'movies' root element using 'myXML.Root'
MOre interesting however, you can use Linq easily to get the nested tags:
var myElements = from p in myXML.Root.Elements("movie")
select p;
Will give you a var of XElements each containing one '...' which you can get at using somthing like:
foreach(var v in myElements)
{
Console.WriteLine(string.Format("ID {0} = {1}",(int)v.Attributes["id"],(string)v.Element("movie"));
}
For anything else other than XML like data structures, then I'm afraid your going to have to start learning the art of regular expressions, a tool like "Regular Expression Coach" will help you imensly ( http://weitz.de/regex-coach/ ) or one of the more uptodate similar tools.
You'll also need to become familiar with the .NET regular expression objects, ( http://www.codeproject.com/KB/dotnet/regextutorial.aspx ) should give you a good head start.
Once you know how your reg-ex stuff works then in most cases it's a simple case case of reading in the files one line at a time and making sense of them using which ever method you feel comfortable with.
A good free source of file formats for almost anything you can imagine can be found at ( http://www.wotsit.org/ )
For the record I implemented parser generator in C# just because I couldn't find any working properly or similar to YACC (see: http://sourceforge.net/projects/naivelangtools/).
However after some experience with ANTLR I decided to go with LALR instead of LL. I know that theoretically LL is easier to implement (generator or parser) but I simply cannot live with stack of expressions just to express priorities of operators (like *
goes before +
in "2+5*3"). In LL you say that mult_expr is embedded inside add_expr which does not seem natural for me.
참고URL : https://stackoverflow.com/questions/7377344/how-to-write-a-parser-in-c
'programing' 카테고리의 다른 글
서비스 계층이 MVC 애플리케이션에 대한 뷰 모델을 반환해야합니까? (0) | 2020.12.01 |
---|---|
명령 줄에서 log4j 수준을 어떻게 설정합니까? (0) | 2020.12.01 |
0으로 평가되지 않은 나눗셈은 정의되지 않은 동작입니까? (0) | 2020.11.30 |
C # 리틀 엔디안 또는 빅 엔디안? (0) | 2020.11.30 |
Emacs Org-Mode 내보내기에서 밑줄-구독 비활성화 (0) | 2020.11.30 |