수학 해석기 만들기: 파서
36822 단어 algorithmscsharp
자원
파서
파서는 인터프리터의 엔진이므로 3가지 주요 방법으로 구성됩니다.
세 가지 방법이 밀접하게 결합됩니다. Expression 메서드는 Factor 메서드를 호출하고 Factor 메서드는 Term 메서드를 호출하고 반환 값은 다시 Expression 메서드로 전달됩니다.
Parser.cs
생성부터 시작해 보겠습니다. 다음과 같이 초기화해 보겠습니다. public class Parser
{
private List<Token> TermItems = new List<Token>() { Token.ADD, Token.MINUS };
private List<Token> FactorItems = new List<Token>() { Token.MULTIPLY, Token.DIVISION };
private readonly List<Tokens> _tokens;
private int pos = 0;
private Tokens curr_token = null;
public Parser(List<Tokens> tokens)
{
this._tokens = tokens;
// set the current token
Get_Next();
}
private void Get_Next()
{
if(pos < this._tokens.Count)
{
curr_token = this._tokens[pos];
pos++;
}
}
}
파서 클래스에는 토큰 목록을 설정하고 첫 번째 토큰
curr_token
을 가져오는 생성자가 있습니다. Get_Next
메서드를 사용하면 다음 토큰을 얻을 수 있습니다.표현
ParseExp
바로 아래에 Get_Next
메서드를 추가해 보겠습니다. public AST ParseExp()
{
AST result = Factor();
while(curr_token._tokenType != Token.EOF && result != null && TermItems.Contains(curr_token._tokenType))
{
if(curr_token._tokenType == Token.ADD)
{
Get_Next();
AST rigthNode = Factor();
result = new ASTPlus(result, rigthNode);
}
else if(curr_token._tokenType == Token.MINUS)
{
Get_Next();
AST rigthNode = Factor();
result = new ASTMinus(result, rigthNode);
}
}
return result;
}
Expression 메소드는 먼저 우리가 정의할 factor 메소드를 호출합니다. while 루프는
ADD
및 MINUS
토큰을 반복합니다. if 문은 토큰 유형을 확인하고 본문은 마이너스 또는 AST 추가에 대한 중첩 개체를 생성합니다.요인
Factor
메서드 바로 아래에 Expression
메서드를 만들어 봅시다. public AST Factor()
{
AST factor = Term();
while (curr_token._tokenType != Token.EOF && factor != null && FactorItems.Contains(curr_token._tokenType))
{
if (curr_token._tokenType == Token.MULTIPLY)
{
Get_Next();
AST rigthNode = Term();
factor = new ASTMultiply(factor, rigthNode);
}
else if (curr_token._tokenType == Token.DIVISION)
{
Get_Next();
AST rigthNode = Term();
factor = new ASTDivide(factor, rigthNode);
}
}
return factor;
}
표현식 방법과 유사하게
MULTIPLY
및 DIVISION
토큰을 반복합니다. 이 메소드에서 정의할 Term
메소드도 호출합니다.용어
Term
바로 뒤에 최종 메소드Factor
를 추가합시다. public AST Term()
{
AST term = null;
if(curr_token._tokenType == Token.LBRACE)
{
Get_Next();
term = ParseExp();
if(curr_token._tokenType != Token.RBRACE)
{
throw new FormatException("Missing )");
}
}
else if(curr_token._tokenType == Token.NUMBER)
{
term = new ASTLeaf((decimal)curr_token._value);
}
Get_Next();
return term;
}
Term
메서드에서 curr_token
가 LBRACE
(왼쪽 중괄호)인지 확인합니다. 숫자 또는 다른 왼쪽 중괄호로 예상되는 다음 토큰을 얻습니다. 그런 다음 재귀적으로 ParseExp
를 호출합니다. 존재하는지 확인한 직후RBRACE
없으면 예외가 발생합니다.else if 문에서
ASTLeaf
를 만들고 다음 토큰을 가져와 결과를 반환합니다.따라서 전체
Parser.cs
는 다음과 같습니다. public class Parser
{
private List<Token> TermItems = new List<Token>() { Token.ADD, Token.MINUS };
private List<Token> FactorItems = new List<Token>() { Token.MULTIPLY, Token.DIVISION };
private readonly List<Tokens> _tokens;
private int pos = 0;
private Tokens curr_token = null;
public Parser(List<Tokens> tokens)
{
this._tokens = tokens;
// set the current token
Get_Next();
}
private void Get_Next()
{
if(pos < this._tokens.Count)
{
curr_token = this._tokens[pos];
pos++;
}
}
public AST ParseExp()
{
AST result = Factor();
while(curr_token._tokenType != Token.EOF && result != null && TermItems.Contains(curr_token._tokenType))
{
if(curr_token._tokenType == Token.ADD)
{
Get_Next();
AST rigthNode = Factor();
result = new ASTPlus(result, rigthNode);
}
else if(curr_token._tokenType == Token.MINUS)
{
Get_Next();
AST rigthNode = Factor();
result = new ASTMinus(result, rigthNode);
}
}
return result;
}
public AST Factor()
{
AST factor = Term();
while (curr_token._tokenType != Token.EOF && factor != null && FactorItems.Contains(curr_token._tokenType))
{
if (curr_token._tokenType == Token.MULTIPLY)
{
Get_Next();
AST rigthNode = Term();
factor = new ASTMultiply(factor, rigthNode);
}
else if (curr_token._tokenType == Token.DIVISION)
{
Get_Next();
AST rigthNode = Term();
factor = new ASTDivide(factor, rigthNode);
}
}
return factor;
}
public AST Term()
{
AST term = null;
if(curr_token._tokenType == Token.LBRACE)
{
Get_Next();
term = ParseExp();
if(curr_token._tokenType != Token.RBRACE)
{
throw new FormatException("Missing )");
}
}
else if(curr_token._tokenType == Token.NUMBER)
{
term = new ASTLeaf((decimal)curr_token._value);
}
Get_Next();
return term;
}
}
마무리
Program.cs
에서 다음 코드를 추가할 수 있습니다. Parser parser = new Parser(tokens);
AST astObj = parser.ParseExp();
if(astObj == null)
{
continue;
}
Console.WriteLine(">> {0}", astObj.Eval());
파서 객체를 생성한 다음
AST
를 생성합니다. 계속인 경우 null인지 확인하고 그렇지 않으면 Eval을 인쇄합니다. astObj.ToString()
를 사용하여 평가 구조를 생성할 수도 있습니다.단위 테스트
단위 테스트 프로젝트에서
ParserTest.cs
클래스를 만들고 다음 테스트를 추가해 보겠습니다. public class ParserTest
{
[Fact]
public void TestASTString()
{
string expected = "((56 - (64 / 8)) + 4)";
Lexer lexer = new Lexer("56 - 64/8 +4");
List<Tokens> tokens = lexer.Get_Tokens();
Assert.NotEmpty(tokens);
Parser parser = new Parser(tokens);
AST astObj = parser.ParseExp();
Assert.NotNull(astObj);
string actual = astObj.ToString();
Assert.Equal(expected, actual);
}
[Fact]
public void TestASTTermOperations()
{
decimal expected = 45;
Lexer lexer = new Lexer("25+25 -5");
List<Tokens> tokens = lexer.Get_Tokens();
Assert.NotEmpty(tokens);
Parser parser = new Parser(tokens);
AST astObj = parser.ParseExp();
Assert.NotNull(astObj);
decimal actual = astObj.Eval();
Assert.Equal(expected, actual);
}
[Fact]
public void TestASTFactorOperations()
{
decimal expected = 5.5m;
Lexer lexer = new Lexer("121/11*0.5");
List<Tokens> tokens = lexer.Get_Tokens();
Assert.NotEmpty(tokens);
Parser parser = new Parser(tokens);
AST astObj = parser.ParseExp();
Assert.NotNull(astObj);
decimal actual = astObj.Eval();
Assert.Equal(expected, actual);
}
[Fact]
public void TestASTBraces()
{
decimal expected = 0;
Lexer lexer = new Lexer("10-5*(25/5)+15");
List<Tokens> tokens = lexer.Get_Tokens();
Assert.NotEmpty(tokens);
Parser parser = new Parser(tokens);
AST astObj = parser.ParseExp();
Assert.NotNull(astObj);
decimal actual = astObj.Eval();
Assert.Equal(expected, actual);
}
[Fact]
public void TestASTBraceMissing()
{
Lexer lexer = new Lexer("(25/5");
List<Tokens> tokens = lexer.Get_Tokens();
Assert.NotEmpty(tokens);
Parser parser = new Parser(tokens);
Assert.Throws<FormatException>(() => parser.ParseExp());
}
}
다음 시리즈에서는 다음을 수행할 것입니다.
Reference
이 문제에 관하여(수학 해석기 만들기: 파서), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/j0nimost/making-a-math-interpreter-parser-52j8텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)