Continuando con esta serie de posts, esta vez agregaré un parser sencillo. La nueva solución:

Pueden bajar el código desde InterpreterStep05.zip. La nueva clase agregada es:

El parse puede ser construido usando un TextReader o un string:
public class Parser
{
private Lexer lexer;
public Parser(Lexer lexer)
{
this.lexer = lexer;
}
public Parser(TextReader reader)
: this(new Lexer(reader))
{
}
public Parser(string text)
: this(new Lexer(text))
{
}
//...
}
Tengo sólo dos Expr essions por ahora (ConstantExpr ession y VariableExpr ession). El método público ParseExpr ession ahora reconoce enteros y nombres(el lexer todavía no reconoce strings delimitados):
public IExpr ession ParseExpr ession()
{
Token token = this.NextToken();
if (token == null)
return null;
if (token.TokenType == TokenType.Integer)
return new ConstantExpr ession(token.Value);
if (token.TokenType == TokenType.String)
return new ConstantExpr ession(token.Value);
if (token.TokenType == TokenType.Name)
return new VariableExpr ession((string) token.Value);
throw new InvalidDataException(string.Format("Unexpected token '{0}'",
token.Value));
}
El parser usa al lexer que comenté en el anterior post de la serie. Como antes, el nuevo Parser y el método ParseExpr ession fueron escritos usando TDD. Algunos tests:
[TestMethod]
public void ParseIntegerExpr ession()
{
Parser parser = new Parser("1");
IExpr ession Expr ession = parser.ParseExpr ession();
Assert.IsNotNull(Expr ession);
Assert.IsInstanceOfType(Expr ession, typeof(ConstantExpr ession));
Assert.AreEqual(1, Expr ession.Evaluate(null));
Assert.IsNull(parser.ParseExpr ession());
}
[TestMethod]
public void ParseVariableExpr ession()
{
Parser parser = new Parser("one");
IExpr ession Expr ession = parser.ParseExpr ession();
Assert.IsNotNull(Expr ession);
Assert.IsInstanceOfType(Expr ession, typeof(VariableExpr ession));
VariableExpr ession varexpr = (VariableExpr ession)Expr ession;
Assert.AreEqual("one", varexpr.Name);
Assert.IsNull(parser.ParseExpr ession());
}
Todos los tes están en verde:
Buen code coverage:
Noten que usando TDD puedo agregar características nuevas en “baby steps” (pasos de bebé), evitando tiempos de depuración o agregar grandes fragmentos de código de una vez. Y siempre tengo al retroalimentación “verde”: cada cosa nueva que se agrega se prueba compatible con el anterior código.
Próximos pasos: agregar más expresiones (aritmética, operadores, ..) y comandos (como if, for, …), “entrenando” al parser en reconocerlas,. Luego puedo agregar al lexer el proceso de strings delimitados por dobles comillas.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez