Ahora, en este paso, he agregado: procesamiento de string, expresiones binarias, expresiones aritméticas, reconocimiento en el parser de esas expresiones, usando precedencia y paréntesis.
El código puede bajarse desde InterpreterStep06.zip.
Procesamiento de Strings
El lexer ahora puede procesar strings, delimitados con dobles comillas, y reconoce el caracter de escape. Soporta strings multilineas. Para eso, fui escribiendo tests, al comienzo en rojo, y luego fui implementando la funcionalidad en el código. Algunos tests (hay más en código):
[TestMethod] public void ProcessSimpleString() { Lexer lexer = new Lexer("\"foo\""); Token token = lexer.NextToken(); Assert.IsNotNull(token); Assert.AreEqual(TokenType.String, token.TokenType); Assert.AreEqual("foo", token.Value); Assert.IsNull(lexer.NextToken()); } [TestMethod] [ExpectedException(typeof(LexerException), "Unclosed string")] public void RaiseIfStringIsUnclosed() { Lexer lexer = new Lexer("\"foo"); Token token = lexer.NextToken(); }Expresiones aritméticas y binarias
Agregué una nueva clase abstracta BinaryExpr ession (derivada luego de tener los tests sobre una concreta, BinaryArithmeticExpr ession):
![]()
BinaryExpr ession evalua dos expresiones, izquierda y derecha, y aplica la operación abstracta:
public abstract class BinaryExpr ession : IExpr ession { IExpr ession leftExpr ession; IExpr ession rightExpr ession; public BinaryExpr ession(IExpr ession leftExpr ession, IExpr ession rightExpr ession) { this.leftExpr ession = leftExpr ession; this.rightExpr ession = rightExpr ession; } public IExpr ession LeftExpr ession { get { return this.leftExpr ession; } } public IExpr ession RightExpr ession { get { return this.rightExpr ession; } } public object Evaluate(BindingEnvironment environment) { object leftValue = this.leftExpr ession.Evaluate(environment); object rightValue = this.rightExpr ession.Evaluate(environment); return Apply(leftValue, rightValue); } public abstract object Apply(object leftValue, object rightValue); }Definí operadores aritméticos, que tengo que extender en el futuro:
public enum ArithmeticOperator { Add, Subtract, Multiply, Divide }BinaryArithmeticExpr ession usa Microsoft.VisualBasic.CompilerServices.Operators. Estos métodos ayudantes, que agregué referenciando a la librería de VB.NET que viene con el framework, pueden sumar, restar, multiplicar, dividir dos objetos, detectando si son números:
public class BinaryArithmeticExpr ession : BinaryExpr ession { private ArithmeticOperator @operator; public ArithmeticOperator Operator { get { return this.@operator; } } public BinaryArithmeticExpr ession(IExpr ession leftExpr ession, IExpr ession rightExpr ession, ArithmeticOperator @operator) : base(leftExpr ession, rightExpr ession) { this.@operator = @operator; } public override object Apply(object leftValue, object rightValue) { switch (this.@operator) { case ArithmeticOperator.Add: return Operators.AddObject(leftValue, rightValue); case ArithmeticOperator.Subtract: return Operators.SubtractObject(leftValue, rightValue); case ArithmeticOperator.Multiply: return Operators.MultiplyObject(leftValue, rightValue); case ArithmeticOperator.Divide: return Operators.DivideObject(leftValue, rightValue); } throw new InvalidOperationException(string.Format("Unknow operator '{0}'", this.@operator)); } }Algunos tess (más en código):
[TestMethod] public void EvaluateAddExpr ession() { ConstantExpr ession leftExpr ession = new ConstantExpr ession(1); ConstantExpr ession rightExpr ession = new ConstantExpr ession(2); BinaryArithmeticExpr ession Expr ession = new BinaryArithmeticExpr ession(leftExpr ession, rightExpr ession, ArithmeticOperator.Add); Assert.AreEqual(3, Expr ession.Evaluate(null)); } [TestMethod] public void EvaluateSubtractExpr ession() { ConstantExpr ession leftExpr ession = new ConstantExpr ession(1); ConstantExpr ession rightExpr ession = new ConstantExpr ession(2); BinaryArithmeticExpr ession Expr ession = new BinaryArithmeticExpr ession(leftExpr ession, rightExpr ession, ArithmeticOperator.Subtract); Assert.AreEqual(-1, Expr ession.Evaluate(null)); }Mejoras en el Parser
El Parser tiene nuevos métodos privados ParseSimpleExpr ession, ParseFactorExpr ession:
![]()
Son las implementaciones que surgieron al satisfacer los tests (más tests en el código):
[TestMethod] public void ParseAndEvaluateAddAndMultiplyExpr ession() { Parser parser = new Parser("1+2*3"); IExpr ession Expr ession = parser.ParseExpr ession(); Assert.IsNotNull(Expr ession); Assert.IsInstanceOfType(Expr ession, typeof(BinaryArithmeticExpr ession)); Assert.AreEqual(7, Expr ession.Evaluate(null)); Assert.IsNull(parser.ParseExpr ession()); } [TestMethod] public void ParseAndEvaluateAddAndMultiplyExpr essionWithParenthesis() { Parser parser = new Parser("(1+2)*3"); IExpr ession Expr ession = parser.ParseExpr ession(); Assert.IsNotNull(Expr ession); Assert.IsInstanceOfType(Expr ession, typeof(BinaryArithmeticExpr ession)); Assert.AreEqual(9, Expr ession.Evaluate(null)); Assert.IsNull(parser.ParseExpr ession()); }De esta manera, puedo reconocer operadores binarios, procesarlos según su precedencia, y usar paréntesis para alterar el orden de evaluación.
Todos los tests en verde:
![]()
Buen code coverage:
![]()
Próximos pasos
Me gustaría agregar proceso de números reales, definiciones de funciones, más operadores, comandos if, for.
Keep tuned!
Angel “Java” Lopez
http://www.ajlopez.com