Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

September 2010 - Posts

Visual Studio debería ser gratis

Ya hace unos años (cerca de 2003) que vengo sosteniendo la postura del título. La enuncio no sostenida con un argumento irrefutable o “hard data” (datos duros, números en concreto en mano), sino basada en experiencia y algunas ideas de lo que podría hacer Microsoft para posicionarse entre los desarrolladores de software.

Quien quiera que haya trabajado con las versiones de Visual Studio, sabe que es una excelente herramienta, con muy buena experiencia de usuario, con todo integrado (conexiones de datos, diseñadores varios, etc…). En el 2003, Eclipse todavía no había llegado a ese nivel, pero se acercaba. El problema con Eclipse es que es “todo es un plugin” y cuando voy a una empresa, consultora, cada equipo tiene un Eclipse distinto: con los plugins que necesita para cada proyecto. En cambio, Visual Studio, al ser “armado” por una empresa, ha permitido que la necesidad de plugins adicionales sea relativamente baja.

Microsoft, como empresa, comenzó implementando un intérprete Basic para la ya mítica Altair del MITS de Ed Roberts (curiosidad: ¿saben por qué se llamaba Altair? Roberts buscaba un nombre para su máquina, estaba pensando en su casa, le pregunta al hijo, que miraba televisión: ¿dónde va hoy la Enterprise de Star Trek? “A Altair”). (la imagen de este artículo la tomé de la página de la Wikipedia sobre Altair BASIC).

Luego, por muchos años, los ingresos de Microsoft fueron por implementar y vender versiones de intérprete Basic para distintas máquinas (estuvo a punto de “perecer” porque una empresa compró a MITS y les hizo juicio, porque reclamaba para sí todo el trabajo derivado de Basic, pero Microsoft ganó el juicio). Con el tiempo se fue diversificando. Por ejemplo, llegó a arreglos con Lattice para vender y adaptar su compilador C. Pero todos sabemos que el real despegue de Microsoft ha sido con la aparición del DOS, gracias a la colaboración con IBM.

En los ochenta, las mejoras herramientas de desarrollo para PC venían de Borland. Recuerdo que hacia fines de los ochenta, Borland llegó a vender un Turbo C++ a 100 dólares, un precio más que accesible. A principios de los noventa, Microsoft Access llegó acá a un  precio similar. Pero ahí vino una dispersión: comenzaron a aparecer las versiones “normales”, Professional, Architect de las herramientas (no recuerdo quien fue el primero en poner eso, si Borland, Microsoft u otro). Microsoft siguió teniendo la mayor parte de los ingresos por la venta del sistema operativo, y otros productos, como Office, sistemas operativos servidores, y notablemente, juegos (en un tiempo en los noventas, los mayores ingresos de Microsoft en Argentina, fueron los juegos; claro que habría que sopersar eso con la copia ilegal de software de desarrollo que habría en esos tiempos).

En la segunda mitad de los noventas, aparece Java: uno podía desarrollar con un JDK gratuito, las IDEs de desarrollo todavía no eran gratuitas, y menos abiertas. Java y Sun atacaron un sector del mercado que va más allá de las PC: el mercado corporativo. Como reacción, para no quedar afuera de ese mercado (seguramente hubo otras causas), Microsoft abandona sus herramientas de desarrollo, y COM+, para reinventarse a sí misma (por lo menos en el ámbito de herramientas y frameworks de desarrollo), y salir a .NET (que inicialmente fue vendido como la solución de web services, el standard emergente de la mano de Microsoft e IBM (noten que Sun se resistió a subirse al vagón de web services por años)).

Microsoft pasó de ser una empresa admirada por los desarrolladores en los ochenta, a ser una empresa resistida en varios ámbitos de programación. Daría para toda una serie de post para discutir cómo pasó eso. Pero la realidad es ésta: Microsoft, para seguir alimentando todo lo que hace y vende, necesita de la comunidad de desarrolladores. Los necesita para vender desde sistema operativos, SQL Server, aplicaciones mobile, y posicionar Windows Phone 7, y set-top-box de PC. Los sigue necesitando para llegar a estar en el mercado de las “tres pantallas”: la PC, el teléfono y la TV. Los necesita para ser elegida en las empresas (PyMEs y corporaciones) como proveedor de sistema operativo, servidor de base de datos, soluciones de directorio, etc. Y los necesita para que sus ofertas cloud, Azure y S+S (prefiero SaaS) tengan algo de contenido: de nada sirven todos los datacenters del mundo, sin aplicaciones que solucionen problemas a las empresas consumidoras finales.

Vean cómo en este siglo, IBM potencia Eclipse, y su “Eclipse comercial”, el WebSphere, languidece. Vean cómo Sun, de nuevo en este siglo, trata de sacar tajada de su Netbeans (comprado en los noventa a una compañía europea), lo abre, deja aparte una versión con precio, y esta última tambié va pasando al recuerdo.

Los desarrolladores, de Java o de .NET, son los que ayudan en la aceptación de las tecnologías. Son, diríamos, “socios” de Oracle, IBM, Sun, y Microsoft.

Hoy, y desde hace unos años (creo que como reacción a las movidas Eclipse, Netbeans y al análisis de la realidad), Microsoft tiene versiones Express de sus productos de desarrollo. Pero tiene también una serie de iniciativas para hacer que estudiantes de varios paises, accedan a sus productos “full”. Reconoce, claramente, que es necesario “evangelizar” sobre sus productos de desarrollo. De hecho hay toda una división dedicada a eso.

Pero esa oferta a estudiantes, costó un montón de esfuerzo para implementarse. Y sólo llega a estudiantes. He incluso la recepción es “con pinzas”: parece transmitir, a un desarrollador: “esto es gratis, pero despues vas a tener que pagar, y por cada escritorio de desarrollo, eh!”.

Eclipse llega a todo el mundo. Y NO TRANSMITE ESO.

Aagrego: si hasta Larry Ellison (si, Larry, el que maneja aviones de combate, gana la copa America, y no deja un centavo que pase por ahí sin que lo persiga) tiene un Oracle para desarrollo de libre bajada.

Vean que no es un tema a discutir en el ámbito racional: si fuéramos vulcanos, comprenderíamos que el costo de las herramientas de desarrollo se amortizarían con nuestro primeros trabajos serios, y un estudiante tomaría la oportunidad ofrecida como una forma de llegar a eso. Pero, por lo que veo, el mensaje que llega es otro: “no hay almuerzo gratis, de alguna forma esto lo voy a tener que pagar, mientras en otras plataformas no me pasa esto”.

De aquí mi propuesta: Microsoft debería ofrecer todos sus productos de desarrollo, gratuitamente.

Esa es una movida de pensar “out-of-the-box”. Una movida que ayudaría a hacer la adopción de sus otros productos apenas un trámite. El costo de los servidores, sistemas operativos, otros productos, licencias para dispositivos móviles, aplicaciones de juegos, seguiría siendo pagado por el mercado consumer y las empresas. Pero ese grito de Ballmer “Developers, developers, developers”, debería traducirse en una frase más simple:

“Visual Studio, download here”.

¿Opiniones?

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Wed, Sep 22 2010 11:08 by lopez | 6 comment(s)

Escribiendo un intérprete en .NET (Parte 6)

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
http://twitter.com/ajlopez

Posted Tue, Sep 21 2010 10:29 by lopez | with no comments

Escribiendo un intérprete en .NET (Parte 5)

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

Posted Mon, Sep 20 2010 10:11 by lopez | with no comments

Escribiendo un intérprete en .NET (Parte 4)

En este post, agrego un analizador léxico, un lexer, para procesar texto y separar el código en tokens, las “palabras” de nuestra entrada.

La nueva solución:

Pueden bajar el código de InterpreterStep04.zip. Hice refactor de la versión anterior: ahora la class library Interpreter tiene tres directorios y namespaces: Commands, Expressions, Compiler.

El nuevo código es:

El TokenType es una enumeración:

    public enum TokenType
    {
        Name,
        String,
        Integer,
        Operator,
        Separator
    }

Token representa una “palabra” de nuestro lenguaje:

    public class Token
    {
        public TokenType TokenType { get; private set;  }
        public object Value { get; private set;  }
        public Token(TokenType type, object value)
        {
            this.TokenType = type;
            this.Value = value;
        }
    }

Value contiene el token detectado: su valor entero si era un entero, o su texto, si era un nombre o separador u operador.

Lexer está encargado de detectar el próximo token desde un TextReader o desde un string:

       public Lexer(TextReader reader)
        {
            this.reader = reader;
        }
        public Lexer(string text)
            : this(new StringReader(text))
        {
        }
        public Token NextToken()
        {
            int ch;
            for (ch = this.NextChar(); ch != -1 && char.IsWhiteSpace((char)ch); ch = this.NextChar())
                ;
            if (ch == -1)
                return null;
//...
        }

Lexer fue escrito en “baby-steps” (pasos de bebé), siguiendo la evolución de los test (recuerden, estoy usando TDD). Escribí un tests, y modifiqué el código para que pasara a verde, y así. Tests como:

        [TestMethod]
        public void ProcessNameWithWhitespaces()
        {
            Lexer lexer = new Lexer("  one  ");
            Token token = lexer.NextToken();
            Assert.IsNotNull(token);
            Assert.AreEqual(TokenType.Name, token.TokenType);
            Assert.AreEqual("one", token.Value);
            Assert.IsNull(lexer.NextToken());
        }
        [TestMethod]
        public void ProcessTwoNames()
        {
            Lexer lexer = new Lexer("one two");
            Token token = lexer.NextToken();
            Assert.IsNotNull(token);
            Assert.AreEqual(TokenType.Name, token.TokenType);
            Assert.AreEqual("one", token.Value);
            token = lexer.NextToken();
            Assert.IsNotNull(token);
            Assert.AreEqual(TokenType.Name, token.TokenType);
            Assert.AreEqual("two", token.Value);
            Assert.IsNull(lexer.NextToken());
        }
        [TestMethod]
        public void ProcessNameAndSeparator()
        {
            Lexer lexer = new Lexer("one;");
            Token token = lexer.NextToken();
            Assert.IsNotNull(token);
            Assert.AreEqual(TokenType.Name, token.TokenType);
            Assert.AreEqual("one", token.Value);
            token = lexer.NextToken();
            Assert.IsNotNull(token);
            Assert.AreEqual(TokenType.Separator, token.TokenType);
            Assert.AreEqual(";", token.Value);
            Assert.IsNull(lexer.NextToken());
        }

La versión actual (en este paso) del lexer solo reconoce nombres, algunos separadores como “;” y algún operador como “=”. No procesa strings delimitados, números reales u otros separadores y operadores. Mi idea es ir agregando más capacidades a medida que escriba más tests.

Todos los tests de la solución en verde:

Buen code coverage:

Próximos pasos: escribir un parser que reconozca expresiones, luego que procese comandos, y agregar comandos como if y for, así como definición de funciones. Esto es un intérprete, pero en una nueve serie de posts, podría tratar de compilarlo a código .NET nativo, usando Reflection.Emit or CodeDom.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Sat, Sep 18 2010 11:15 by lopez | with no comments

Escribiendo un intérprete en .NET (Parte 3)

Esta es la tercera parte de esta serie:

Writing an Interpreter in .NET (Part 1)
Escribiendo un intérprete en .NET (Parte 1)
Writing an Interpreter in .NET (Part 2)
Escribiendo un intérprete en .NET (Parte 2)

Hasta ahora, tenemos implementado IExpression, ConstantExpression and VariableExpression, usando un BindingEnvironment para almacenar un diccionaro de nombres-valores para las variables. Podría reimplementar este environment u otras partes si necesito nueva funcionalidad: usando TDD, es relativamente fácil agregar nuevas características, preservando las anteriores (eso pasó en la parte 2, donde al agregar BindingEnvironment cambió el Evaluate de las IExpressions.

En este post, quiero agregar comandos (pueden bajar el código desde InterpreterStep03.zip):

Esta interfaz ICommand es:

namespace Interpreter
{
    public interface ICommand
    {
        void Execute(BindingEnvironment environment);
    }
}

El método Execute recibe el estado actual, ahora representado por un  BindingEnvironment. El código fue desarrollado usando TDD: no necesito nada más para implementar los tests.

Primer comando para implementar: SetCommand, que pone un valor en una variable usando el nombre y una expresión a evaluar:

Primeros tests:

 

        [TestMethod]
        public void CreateSetCommand()
        {
            SetCommand command = new SetCommand("foo", new 
                ConstantExpr ession(1));
            Assert.AreEqual("foo", command.Name);
            Assert.IsInstanceOfType(command.Expression, typeof(ConstantExpression));
        }
        [TestMethod]
        public void ExecuteSetCommand()
        {
            BindingEnvironment environment = new BindingEnvironment();
            ICommand command = new SetCommand("one", new ConstantExpr ession(1));
            command.Execute(environment);
            Assert.AreEqual(1, environment.GetValue("one"));
        }

 

El código de implementación:

    public class SetCommand : ICommand
    {
        private string name;
        private IExpression expression;
        public SetCommand(string name, IExpression expression)
        {
            this.name = name;
            this.expression = expression;
        }
        public string Name { get { return this.name; } }
        public IExpression Expression { get { return this.expression; } }
        public void Execute(BindingEnvironment environment)
        {
            environment.SetValue(this.name, this.expression.Evaluate(environment));
        }
    }

Noten que el nombre de la variable y la expresión a evaluar son entregadas en el constructor.

Ahora, los tests están en verde:

Buen coverage:

Próximo paso: agregar un lexer, analizador léxico, que toma un string o un archivo, y va separando los tokens: las “palabras” de nuestro lenguaje a interpretar. Vean que podríamos implementar todo el núcleo del lenguaje (expresiones, comandos, variables…) por un lado, en un proyecto, y la sintaxis en otro (o en varios otros, usar distintos lenjuages externos, de distintas sintaxis, para construir las instancias de un árbol de evaluación). Por simplicidad, agregaré el lexer y después el parser en el mismo proyecto.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Fri, Sep 17 2010 10:30 by lopez | with no comments

Escribiendo un intérprete en .NET (Parte 2)

En mi anterior post:

Writing an Interpreter in .NET (Part 1)
Escribiendo un intérprete en .NET (Parte 1)

Presenté expresiones y la implementación de ConstantExpression. Hoy quiero implementar una expresión de variable: una expresión que dado el nombre de una variable retorna su valor asociado. El código de este paso lo pueden bajar desde InterpreterStep02.zip. Y de nuevo, lo quiero implementar usando Test-Driven Development. El código que se bajen es el FINAL del proceso, pero espero que igual sirva como ejemplo de esta forma de desarrollo.

Pueden seguir el código de esta serie y mis progresos en mi AjCodeKata Google Project, en el trunk, dentro del directorio Interpreter. Si quieren ejemplos más complejos, también armados con TDD, ver AjSharp, AjTalk, AjLisp.

La solución de este paso:

Implementé un BindingEnvironment, un lugar donde salvar los nombres de variables y sus valores asociados:

Mis tests:

    public class BindingEnvironmentTests
    {
        [TestMethod]
        public void SetAndGetValue()
        {
            BindingEnvironment environment = new BindingEnvironment();
            environment.SetValue("one", 1);
            Assert.AreEqual(1, environment.GetValue("one"));
        }
        [TestMethod]
        public void GetNullIfUndefinedName()
        {
            BindingEnvironment environment = new BindingEnvironment();
            Assert.IsNull(environment.GetValue("undefined"));
        }
    }

Noten que especifiqué que obtener un valor no definido no da error sino que retorna null. Si hubiera decidido lanzar una excepción, mi tests hubiera debido reflejar esa decisión. Quiero que al usar un lenguaje no sea necesario definir variables, sino simplemente usarlas. Si una variable no “existe”, no está inicializada, entonces es como si tuviera null.

Después, cambié IExpression.Evaluate para que acepte un  BindingEnvironment como parámetro:

    public interface IExpression
    {
        object Evaluate(BindingEnvironment environment);
    }

Escribí una nueva VariableExpression (y adapté la ya existente ConstantExpression):

Mis tests:

        [TestMethod]
        public void EvaluateIntegerVariable()
        {
            BindingEnvironment environment = new BindingEnvironment();
            IExpression expression = new VariableBLOCKED EXPRESSION;
        }

Todos los tests, ahora, están en verde:

Pero recuerden: eso es el final. Usando TDD fue pasando de rojo, a verde, a refactor.

Buen code coverage:

Próximos pasos: implementar comandos y un SetCommand: que cambie el valor de una variable. De nuevo, escribir todo eso usando TDD.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Thu, Sep 16 2010 10:41 by lopez | 1 comment(s)

Escribiendo un intérprete en .NET (Parte 1)

Hace un poco más de una semana, tuve la oportunidad de dar una charla sobre compiladores e intérpretes en .NET, en el Code Camp Buenos Aires http://www.codecamp.com.ar

Escribí varios intérpretes en .NET (AjSharp, AjTalk, AjLisp….) y sigo con ese desarrollo. Pero ahore, me gustaría ir explorando los pasos que sigo para escribir un simple intérprete, sin usar herramientas de construcción de gramáticas.

Primero, uso Driven Development. Para cada cosa que agrego en código, escribo el test, lo ejecuto en rojo, cambio el código para que quede en verde, y luego refactor. Pueden bajar el código de este primer post desde InterpreterStep01.zip). La solución es:

El punto de partida es una expression.

namespace Interpreter
{
    public interface IExpression
    {
        object Evaluate();
    }
}

Escribí una interfaz, pero podría haber escrito una primera clase concreta, y entonces, extraer la interfaz luego de tener algo andando. En este intérprete simple, una expresión es algo a ser evaludado, como

3
”foo”
3+5
fun(a)

La primera clase concreta es ConstantExpression: new ConstantBLOCKED EXPRESSION; } }

Los tests están en verde:

Buen code coverage:

Bien, es un ejemplo muy simple. Pero es un punto de partida. Próximos pasos: agregar el manejo de variables, su evaluación. Y luego, agregaré comandos, la asignaciónde valor a una variable. Podría escribir funciones, analizador léxico, parser, y más, todo usando TDD.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Wed, Sep 15 2010 11:38 by lopez | 2 comment(s)

Armando una aplicación usando AjGenesis (Parte 5)

Publiqué un nuevo paso en esta serie de post sobre cómo ir generando una aplicación con AjGenesis. Anteriores posts:

Building An Application Using AjGenesis (Part 1)
Armando una Aplicación usando AjGenesis (Parte 1)
Building An Application Using AjGenesis (Part 2)
Armando una Aplicación usando AjGenesis (Parte 2)
Building an Application Using AjGenesis (Part 3)
Armando una Aplicación usando AjGenesis (Parte 3)
Building an Application Using AjGenesis (Part 4)
Armando una Aplicación usando AjGenesis (Parte 4)
Building an Application Using AjGenesis (Part 5)

El código puede ser bajado desde AppExampleStep05. El ejemplo:

Como en los anteriores ejemplos, necesitamos los últimos binarios de AjGenesis. Podemos bajarlo desde AjGenesisTrunkBinaries.zip. (el código fuente completo está en el repositorio de AjGenesis en Codeplex). Tienen que agregar el directorio bin al path, para ejecutar el ejemplo.

En el directorio raíz, seguimos teniendo los mismos tres comando que en el anterior post.

GenerateCSharp.cmd
GenerateJava.cmd
GenerateVbNet.cmd

El modelo es el mismo. Pero se agregaron los nuevos Tasks/GenerateCSharp.ajg, Tasks/GenerateVbNet.ajg. Ahora, podemos generar una solución .NET y proyecto. Este es el nuevo GenerateCSharp.ajg:

IncludeCode "Utilities/Utilities.ajg"
IncludeCode "Utilities/DotNetUtilities.ajg"
' Create Build Directories
FileManager.CreateDirectory(Project.BuildDirectory)
FileManager.CreateDirectory("${Project.BuildDirectory}")
FileManager.CreateDirectory("${Project.BuildDirectory}/${Project.Name}.Entities")
FileManager.CreateDirectory("${Project.BuildDirectory}/${Project.Name}.Entities/Properties")
Project.Solution = CreateObject()
Project.Solution.Guid = "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"
Project.Solution.WebGuid = "E24C65DC-7377-472B-9ABA-BC803B73C61A"
Project.Solution.Projects = CreateList()
PrjEntities = CreateObject()
PrjEntities.Name = "${Project.Name}.Entities"
PrjEntities.Directory = "${Project.BuildDirectory}/${Project.Name}.Entities"
PrjEntities.Includes = CreateList()
PrjEntities.Projects = CreateList()
PrjEntities.Libraries = CreateList()
PrjEntities.Guid = CreateGuid()
PrjEntities.COMGuid = CreateGuid()
Project.Solution.Projects.Add(PrjEntities)
for each Entity in Project.Model.Entities
  TransformerManager.Transform("Templates/CSharp/EntityClass.tpl", "${Project.BuildDirectory}/${Project.Name}.Entities/${Entity.Name}.cs", Environment)
  PrjEntities.Includes.Add(CreateFileCs("${Entity.Name}"))
end for
for each CsProject in Project.Solution.Projects
  TransformerManager.Transform("Templates/CSharp/CsProject.tpl", "${CsProject.Directory}/${CsProject.Name}.csproj", Environment)
  TransformerManager.Transform("Templates/CSharp/AssemblyInfoCs.tpl", "${CsProject.Directory}/Properties/AssemblyInfo.cs", Environment)
end for
TransformerManager.Transform("Templates/CSharp/Solution.tpl", "${Project.BuildDirectory}/${Project.Name}.sln", Environment)

Muchas cosas nuevas! Pero el punto principal es: ahora, generamos un archivo de solución y un archivo de proyecto. Hay nuevos archivos de funciones utilitarios, y nuevos templates. Notemos la creación del objeto dinámico Project.Solution y de PrjEntities. Ver el CreateObject() y el CreateList() en Utilities/Utilities.ajg.

La solución generada en C#:

Una solución y proyecto es generado para VB.NET, también:

La generación de Java sigue igual que en el anterior post.

Próximos pasos: no generar de nuevo los archivos que no cambiaron, permitir archivos de código manual en los proyectos y mantenerlos luego de una nueva generación; nuevos proyectos (persistencias, interfaz de web, …)

Recuerden: hay otro camino para armar una aplicación, lo comenté en AjGenetizando una aplicación: partir de una aplícación ya armada, y comenzar a abstraer desde ahí, y hasta iterar: generar manual, abstraer, poner en generación automática, mantener lo manual, etc…. En cambio, esta serie de posts pretende ser un tutorial para quien recién se inicia con esta herramienta.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Tue, Sep 14 2010 9:57 by lopez | 1 comment(s)

Otro Code Retreat en Buenos Aires

Ya había escrito sobre una actividad anterior en:

Code Retreat en Buenos Aires

(si no conocen el término, ahí recomendaba

http://www.coderetreat.com/how-it-works.html

What Is Code Retreat

The idea for code retreat was spawned at the January, 2009, Codemash Conference by Patrick Welsh, Nayan Hajratwala and me, Corey Haines. The idea was to develop a repeatable, day-long event that was focused on practicing the fundamentals of software development. The first event was held in Ann Arbor, MI, not long after the conference.

Más información en:

http://www.coderetreat.com/
http://coderetreat.ning.com/

)

Ayer jueves me entero, por un tweet del bueno de @MartinSalias, que vuelve la actividad, mañana viernes 10 de septiembre. Se anuncia en

Segundo Code Retreat

Ahí leo:

La idea de un Code Retreat es que un grupo de programadores nos juntemos, programemos , discutamos y aprendamos unos de otros. 
Esta es la segunda realización en Buenos Aires (y Argentina por lo que sabemos!)

Cuándo: 10 de Septiembre, 14:30 a 20:00 hs

Dónde: MUG - Rivadavia 1479 Piso 1 Of. A.

Como recibimos algunas consultas, aclaramos que el evento no tiene costo para los participantes.

¡Inscribirte!

La mecánica es la siguiente: vamos a trabajar haciendo Pair Programming y TDD. Todos los pares van a atacar el mismo problema en sesiones de 40 minutos. Una vez terminada la sesión haremos una retrospectiva de unos 10-15 minutos para reflexionar sobre los problemas que encontramos. Luego de la retrospectiva, se cambian los pares, se borra el código escrito en la sesión anterior y se arranca una sesión nueva con el mismo problema que la anterior.
La idea es hacer 4 o 5 sesiones por la tarde, una retrospectiva final y, para los que se prendan, podemos terminar con alguna cerveza o agua mineral en algun bar cercano.

Los lenguajes que elegimos para este Retreat son Java y C#. Generalmente se usa un solo lenguaje por retreat, pero son lenguajes muy parecidos y nos pareció que abría un poco más la convocatoria usar los dos. 
Durante el retreat vamos a trabajar los siguientes conceptos:

- es bueno ser capaces de tirar el codigo y empezar de nuevo

- pequeños pasos en TDD, muy pequeños

- foco en el tercer paso del ciclo de TDD: calidad del codigo y refactoring

No es necesario tener experiencia en TDD ni en Pair programming.
Vamos a necesitar que todos los que puedan lleven laptops con Eclipse/Visual Studio instalados

 

Muy bueno que vuelva a realizarse, y que sea en el MUG (Microsoft User Group de Argentina). Si alguien asiste, que deje post! ;-)

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Thu, Sep 9 2010 17:49 by lopez | 1 comment(s)

El encanto del desarrollo de software: Introducción

Hace un tiempo, gracias a mi reconsolidación de libros, me reencontré con el excelente “El encanto de la física” de Sheldon Glashow. Me gustaría lejanamente inspirarme en ese libro, para ir comentando lo que veo que es el “encanto del desarrollo de software”.

Por un lado, el desarrollo de software es el “trabajo” de muchos de nosotros: es la actividad a la que nos dedicamos para obtener los ingresos de cada día, para mitigar las consecuencias de lo que llamo el “Efecto Coto”.

Pero por otro lado, para muchos de nosotros, es la actividad que nos interesa por muchos otros motivos, que disfrutamos y tratamos de entender, más allá de su aplicación inmediata.

Para mí, el desarrollo de software es lo que me permite “crear” nuevas cosas, más o menos nuevos programas, soluciones a viejos problemas. Me permite ir explorando lo abstracto y lo concreto al mismo tiempo, consiguiendo, de vez en cuando, la satisfacción de encontrar una solución sencilla y a la vez hermosa a un problema.

De alguna forma, en ese sentido, se acerca a las matemáticas. Pero por otro lado, se acerca a la ingeniería: construir cosas, nuevas o adaptadas de anteriores, que podemos compartir con otros. Gracias a la ubicuidad de Internet y de la web en particular, hoy el desarrollo de software puede ser una actividad colectiva, justamente, como las matemáticas y la ciencia fáctica.

Un desarrollador de software crea, comparte, descubre nuevas formas de resolver problemas. Un software puede contribuir a potenciar alguna actividad humana, o simplemente, aportar con su sencillez y belleza a satisfacer el anhelo estético de los que disfrutamos de esta actividad.

También destaco que esta actividad es una actividad humana que más niveles involucra: tenemos que estar atentos a la visión general, y a la vez, estar dedicados a los detalles. En ese sentido, se parece a la arquitectura de edificios, donde tanto tenemos que detenernos en la visión global de lo que queremos construir, como a los elementos minúsculos, como los materiales y la disposición de pequeñas piezas.

Con el software, construimos nuevas “máquinas”: el anhelo humano de crear lo que nunca ha sido creado, se ve potenciado en la programación de sistemas. Y en vez de trabajar en una “torre de marfil”, como la ingeniería de puentes también nosotros nos vemos enfrentados a la prueba de la realidad, a la prueba del uso y creación de valor para los usuarios de lo que creamos.

Y en los últimos tiempos, ha crecido la creación en equipo. De alguna forma, el desarrollo de software ha conservado las caracterísicas de una ingeniería, con sus reglas y mejores prácticas,  y en un arte, con la creatividad siempre bienvenida y necesaria.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Sun, Sep 5 2010 19:49 by lopez | 4 comment(s)