ANTLR 3.3을 사용하십니까?
ANTLR 및 C #을 시작하려고하는데 문서 / 튜토리얼이 부족하여 매우 어렵습니다. 이전 버전에 대한 반감이있는 튜토리얼을 몇 개 찾았지만 그 이후로 API에 몇 가지 주요 변경 사항이있는 것 같습니다.
누구든지 문법을 만들고 짧은 프로그램에서 사용하는 방법에 대한 간단한 예를 줄 수 있습니까?
마침내 문법 파일을 렉서와 파서로 컴파일하고 Visual Studio에서 컴파일하고 실행할 수 있습니다 (C # 바이너리도 오래된 것처럼 보이기 때문에 ANTLR 소스를 다시 컴파일해야합니다!- 소스가 몇 가지 수정없이 컴파일되지 않는다는 것은 말할 것도없고),하지만 여전히 내 파서 / 어휘 분석기 클래스로 무엇을 해야할지 모르겠습니다. 아마도 그것은 약간의 입력이 주어지면 AST를 생성 할 수 있고 ... 그런 다음 나는 그것으로 멋진 것을 할 수 있어야합니다.
다음 토큰으로 구성된 간단한 표현식을 구문 분석하려고한다고 가정 해 보겠습니다.
-
빼기 (단항);+
부가;*
곱셈;/
분할;(...)
그룹화 (서브) 표현식;- 정수 및 십진수.
ANTLR 문법은 다음과 같습니다.
grammar Expression;
options {
language=CSharp2;
}
parse
: exp EOF
;
exp
: addExp
;
addExp
: mulExp (('+' | '-') mulExp)*
;
mulExp
: unaryExp (('*' | '/') unaryExp)*
;
unaryExp
: '-' atom
| atom
;
atom
: Number
| '(' exp ')'
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
이제 적절한 AST를 만들기 output=AST;
위해 options { ... }
섹션에 추가 하고 어떤 토큰이 트리의 루트가되어야 하는지를 정의하는 문법에서 "트리 연산자"를 혼합합니다. 이를 수행하는 두 가지 방법이 있습니다.
- 추가
^
하고!
토큰 뒤에. 이로^
인해 토큰이 루트가되고!
ast에서 토큰 이 제외됩니다. - "다시 쓰기 규칙"사용 :
... -> ^(Root Child Child ...)
.
foo
예를 들어 규칙 을 사용 하십시오 .
foo
: TokenA TokenB TokenC TokenD
;
및하자 당신이 원하는 말을 TokenB
루트가 될 및 TokenA
및 TokenC
그 자녀가, 당신은 제외 할 TokenD
나무에서. 옵션 1을 사용하여 수행하는 방법은 다음과 같습니다.
foo
: TokenA TokenB^ TokenC TokenD!
;
다음은 옵션 2를 사용하여 수행하는 방법입니다.
foo
: TokenA TokenB TokenC TokenD -> ^(TokenB TokenA TokenC)
;
그래서, 여기에 트리 연산자가있는 문법이 있습니다 :
grammar Expression;
options {
language=CSharp2;
output=AST;
}
tokens {
ROOT;
UNARY_MIN;
}
@parser::namespace { Demo.Antlr }
@lexer::namespace { Demo.Antlr }
parse
: exp EOF -> ^(ROOT exp)
;
exp
: addExp
;
addExp
: mulExp (('+' | '-')^ mulExp)*
;
mulExp
: unaryExp (('*' | '/')^ unaryExp)*
;
unaryExp
: '-' atom -> ^(UNARY_MIN atom)
| atom
;
atom
: Number
| '(' exp ')' -> exp
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
Space
: (' ' | '\t' | '\r' | '\n'){Skip();}
;
또한 Space
소스 파일의 공백을 무시 하는 규칙을 추가하고 렉서 및 파서에 대한 추가 토큰 및 네임 스페이스를 추가했습니다. 순서가 중요합니다 ( options { ... }
첫 번째, tokens { ... }
마지막으로 @... {}
-namespace 선언).
그게 다야.
이제 문법 파일에서 어휘 분석기와 파서를 생성합니다.
java -cp antlr-3.2.jar org.antlr.Tool Expression.g
그리고 넣어 .cs
과 함께 파일을 프로젝트에서 C #을 런타임 DLL의 .
다음 클래스를 사용하여 테스트 할 수 있습니다.
using System;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using Antlr.StringTemplate;
namespace Demo.Antlr
{
class MainClass
{
public static void Preorder(ITree Tree, int Depth)
{
if(Tree == null)
{
return;
}
for (int i = 0; i < Depth; i++)
{
Console.Write(" ");
}
Console.WriteLine(Tree);
Preorder(Tree.GetChild(0), Depth + 1);
Preorder(Tree.GetChild(1), Depth + 1);
}
public static void Main (string[] args)
{
ANTLRStringStream Input = new ANTLRStringStream("(12.5 + 56 / -7) * 0.5");
ExpressionLexer Lexer = new ExpressionLexer(Input);
CommonTokenStream Tokens = new CommonTokenStream(Lexer);
ExpressionParser Parser = new ExpressionParser(Tokens);
ExpressionParser.parse_return ParseReturn = Parser.parse();
CommonTree Tree = (CommonTree)ParseReturn.Tree;
Preorder(Tree, 0);
}
}
}
다음과 같은 출력이 생성됩니다.
뿌리 * + 12.5 / 56 UNARY_MIN 7 0.5
다음 AST에 해당합니다.
( graph.gafol.net을 사용하여 생성 된 다이어그램 )
ANTLR 3.3은 방금 출시되었으며 CSharp 대상은 "베타 버전"입니다. 그래서 제 예에서 ANTLR 3.2를 사용했습니다.
(위의 예와 같이) 다소 단순한 언어의 경우 AST를 만들지 않고도 결과를 즉시 평가할 수 있습니다. 문법 파일에 일반 C # 코드를 포함하고 파서 규칙이 특정 값을 반환하도록하여이를 수행 할 수 있습니다.
예를 들면 다음과 같습니다.
grammar Expression;
options {
language=CSharp2;
}
@parser::namespace { Demo.Antlr }
@lexer::namespace { Demo.Antlr }
parse returns [double value]
: exp EOF {$value = $exp.value;}
;
exp returns [double value]
: addExp {$value = $addExp.value;}
;
addExp returns [double value]
: a=mulExp {$value = $a.value;}
( '+' b=mulExp {$value += $b.value;}
| '-' b=mulExp {$value -= $b.value;}
)*
;
mulExp returns [double value]
: a=unaryExp {$value = $a.value;}
( '*' b=unaryExp {$value *= $b.value;}
| '/' b=unaryExp {$value /= $b.value;}
)*
;
unaryExp returns [double value]
: '-' atom {$value = -1.0 * $atom.value;}
| atom {$value = $atom.value;}
;
atom returns [double value]
: Number {$value = Double.Parse($Number.Text, CultureInfo.InvariantCulture);}
| '(' exp ')' {$value = $exp.value;}
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
Space
: (' ' | '\t' | '\r' | '\n'){Skip();}
;
클래스로 테스트 할 수 있습니다.
using System;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using Antlr.StringTemplate;
namespace Demo.Antlr
{
class MainClass
{
public static void Main (string[] args)
{
string expression = "(12.5 + 56 / -7) * 0.5";
ANTLRStringStream Input = new ANTLRStringStream(expression);
ExpressionLexer Lexer = new ExpressionLexer(Input);
CommonTokenStream Tokens = new CommonTokenStream(Lexer);
ExpressionParser Parser = new ExpressionParser(Tokens);
Console.WriteLine(expression + " = " + Parser.parse());
}
}
}
다음 출력을 생성합니다.
(12.5 + 56 / -7) * 0.5 = 2.25
편집하다
댓글에서 Ralph는 다음과 같이 썼습니다.
Visual Studio를 사용하는 사람들을위한 팁 :
java -cp "$(ProjectDir)antlr-3.2.jar" org.antlr.Tool "$(ProjectDir)Expression.g"
빌드 전 이벤트에 같은 것을 넣은 다음 렉서 / 파서를 다시 빌드하는 것에 대해 걱정할 필요없이 문법을 수정하고 프로젝트를 실행할 수 있습니다.
Have you looked at Irony.net? It's aimed at .Net and therefore works really well, has proper tooling, proper examples and just works. The only problem is that it is still a bit 'alpha-ish' so documentation and versions seem to change a bit, but if you just stick with a version, you can do nifty things.
p.s. sorry for the bad answer where you ask a problem about X and someone suggests something different using Y ;^)
My personal experience is that before learning ANTLR on C#/.NET, you should spare enough time to learn ANTLR on Java. That gives you knowledge on all the building blocks and later you can apply on C#/.NET.
I wrote a few blog posts recently,
- http://www.lextm.com/index.php/2012/07/how-to-use-antlr-on-net-part-i/
- http://www.lextm.com/index.php/2012/07/how-to-use-antlr-on-net-part-ii/
- http://www.lextm.com/index.php/2012/07/how-to-use-antlr-on-net-part-iii/
- http://www.lextm.com/index.php/2012/07/how-to-use-antlr-on-net-part-iv/
- http://www.lextm.com/index.php/2012/07/how-to-use-antlr-on-net-part-v/
The assumption is that you are familiar with ANTLR on Java and is ready to migrate your grammar file to C#/.NET.
There is a great article on how to use antlr and C# together here:
http://www.codeproject.com/KB/recipes/sota_expression_evaluator.aspx
it's a "how it was done" article by the creator of NCalc which is a mathematical expression evaluator for C# - http://ncalc.codeplex.com
You can also download the grammar for NCalc here: http://ncalc.codeplex.com/SourceControl/changeset/view/914d819f2865#Grammar%2fNCalc.g
example of how NCalc works:
Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)");
e.Parameters["Pi2"] = new Expression("Pi * Pi");
e.Parameters["X"] = 10;
e.EvaluateParameter += delegate(string name, ParameterArgs args)
{
if (name == "Pi")
args.Result = 3.14;
};
Debug.Assert(117.07 == e.Evaluate());
hope its helpful
참고 URL : https://stackoverflow.com/questions/4396080/using-antlr-3-3
'developer tip' 카테고리의 다른 글
'빈'생성자 또는 소멸자는 생성 된 생성자와 동일한 작업을 수행합니까? (0) | 2020.10.26 |
---|---|
Chrome, Safari가 표의 최대 너비를 무시 함 (0) | 2020.10.26 |
리플렉션을 사용하여 선언 순서대로 속성 가져 오기 (0) | 2020.10.26 |
메인 콘텐츠 div를 CSS로 화면 높이를 채우는 방법 (0) | 2020.10.25 |
인스턴스 수준에서 메서드 재정의 (0) | 2020.10.25 |