Created
June 27, 2014 09:25
-
-
Save lnicola/96fda75078ede992d1b7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
namespace Parser | |
{ | |
enum TokenType | |
{ | |
Add, | |
Sub, | |
Mul, | |
Div, | |
Identifier, | |
Number, | |
LBracket, | |
RBracket, | |
Eof | |
} | |
class Token | |
{ | |
public TokenType TokenType { get; private set; } | |
public string Identifier { get; private set; } | |
public Token(TokenType tokenType, string identifier) | |
{ | |
TokenType = tokenType; | |
Identifier = identifier; | |
} | |
} | |
abstract class Node | |
{ | |
} | |
class BinOpNode : Node | |
{ | |
public TokenType TokenType { get; private set; } | |
public Node Left { get; private set; } | |
public Node Right { get; private set; } | |
public BinOpNode(TokenType tokenType, Node left, Node right) | |
{ | |
TokenType = tokenType; | |
Left = left; | |
Right = right; | |
} | |
} | |
class IdentifierNode : Node | |
{ | |
public string Identifier { get; private set; } | |
public IdentifierNode(string identifier) | |
{ | |
Identifier = identifier; | |
} | |
} | |
class ConstantNode : Node | |
{ | |
public string Constant { get; private set; } | |
public ConstantNode(string constant) | |
{ | |
Constant = constant; | |
} | |
} | |
class Scanner | |
{ | |
private TextReader textReader_; | |
private Token token_; | |
public Scanner(TextReader textReader) | |
{ | |
textReader_ = textReader; | |
GetToken(); | |
} | |
public Token PeekToken() | |
{ | |
return token_; | |
} | |
public Token GetToken() | |
{ | |
var token = token_; | |
token_ = ReadToken(); | |
return token; | |
} | |
private Token ReadToken() | |
{ | |
var c = textReader_.Peek(); | |
if (c == -1) | |
return new Token(TokenType.Eof, null); | |
var ch = (char)c; | |
while (Char.IsWhiteSpace(ch)) | |
{ | |
textReader_.Read(); | |
ch = (char)textReader_.Read(); | |
} | |
switch (ch) | |
{ | |
case '+': | |
textReader_.Read(); | |
return new Token(TokenType.Add, ch.ToString()); | |
case '-': | |
textReader_.Read(); | |
return new Token(TokenType.Sub, ch.ToString()); | |
case '*': | |
textReader_.Read(); | |
return new Token(TokenType.Mul, ch.ToString()); | |
case '/': | |
textReader_.Read(); | |
return new Token(TokenType.Div, ch.ToString()); | |
case '(': | |
textReader_.Read(); | |
return new Token(TokenType.LBracket, "("); | |
case ')': | |
textReader_.Read(); | |
return new Token(TokenType.RBracket, ")"); | |
} | |
var sb = new StringBuilder(); | |
if (Char.IsDigit(ch)) | |
{ | |
while (Char.IsDigit(ch)) | |
{ | |
sb.Append(ch); | |
textReader_.Read(); | |
ch = (char)textReader_.Peek(); | |
} | |
return new Token(TokenType.Number, sb.ToString()); | |
} | |
if (Char.IsLetter(ch)) | |
{ | |
while (Char.IsLetter(ch)) | |
{ | |
sb.Append(ch); | |
textReader_.Read(); | |
ch = (char)textReader_.Peek(); | |
} | |
return new Token(TokenType.Identifier, sb.ToString()); | |
} | |
throw new Exception(String.Format("Unexpected character: {0}", ch)); | |
} | |
} | |
class Parser | |
{ | |
private Scanner scanner_; | |
private Token token_; | |
public Parser(TextReader textReader) | |
{ | |
scanner_ = new Scanner(textReader); | |
} | |
// F = E | N | I | |
public Node ParseFactor() | |
{ | |
var token = scanner_.GetToken(); | |
if (token.TokenType == TokenType.LBracket) | |
{ | |
var expression = ParseExpression(); | |
token = scanner_.GetToken(); | |
if (token.TokenType != TokenType.RBracket) | |
throw new Exception(String.Format("Expected ')', found {1}", token.TokenType)); | |
return expression; | |
} | |
if (token.TokenType == TokenType.Number) | |
return new ConstantNode(token.Identifier); | |
if (token.TokenType == TokenType.Identifier) | |
return new IdentifierNode(token.Identifier); | |
throw new Exception(String.Format("Unexpected token: {0}", token.TokenType)); | |
} | |
// T = F [*|/ T] | |
public Node ParseTerm() | |
{ | |
var factor = ParseFactor(); | |
var token = scanner_.PeekToken(); | |
if (token.TokenType == TokenType.Mul || token.TokenType == TokenType.Div) | |
{ | |
scanner_.GetToken(); | |
return new BinOpNode(token.TokenType, factor, ParseTerm()); | |
} | |
return factor; | |
} | |
// E = T [+|- E] | |
public Node ParseExpression() | |
{ | |
var term = ParseTerm(); | |
var token = scanner_.PeekToken(); | |
if (token.TokenType == TokenType.Add || token.TokenType == TokenType.Sub) | |
{ | |
scanner_.GetToken(); | |
return new BinOpNode(token.TokenType, term, ParseTerm()); | |
} | |
return term; | |
} | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
using (var textReader = new StringReader("100 * 2 + 4 + 5")) | |
{ | |
var parser = new Parser(textReader); | |
var expression = parser.ParseExpression(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment