Delphi RTL incluye un motor de expresión muy potente, que es uno de los fundamentos de la arquitectura Live Bindings, pero se puede utilizar como un motor independiente para procesar expresiones. Esta publicación de blog ofrece una introducción rápida al tema.
Hay muchas gemas ocultas en Delphi RTL. Uno de ellos es el motor de expresión. Recientemente tuve algunas conversaciones con un desarrollador de Delphi desde hace mucho tiempo que estaba buscando una característica similar, sin darse cuenta de que había estado en el producto durante muchos años. Sabía que había escrito algunas demostraciones y documentación, así que pasé un poco de tiempo buscándolas y encontré algunas. Ahora bien, este tema es muy complejo y no podré cubrirlo en su totalidad, pero para escenarios simples realmente necesita muy poco código para analizar y procesar una expresión.
Antes de comenzar con el tema real, también quería mencionar que recientemente hemos presentado esta función en el nuevo componente VCL NumberBox (agregado en Delphi 10.4.2, consulte, por ejemplo, https://blog.marcocantu.com/blog/2021-february -nuevo-vcl-controles-1042.html ). Este componente permite a los usuarios finales ingresar una expresión y reemplazarla con el valor resultante. No es de extrañar que haya utilizado el motor de expresión existente y lo hace invocando un método de clase simplificado de
1 2 3 4 5 6 7 8 9 |
var LExpression: TBindingExpression; begin LExpression := TBindings.CreateExpression([], LText); try Value := LExpression.Evaluate.GetValue.AsExtended; finally LExpression.Free; end; |
En este fragmento de código, LText es una cadena con la expresión y Value es el resultado de punto flotante. El método de la clase TBindings.CreateExpression es un atajo que puede simplificar el código, pero prefiero guiarte a más detalles.
Table of Contents
Conceptos clave de las expresiones de enlace
El código anterior crea un objeto TBindingExpression, la clase principal de este motor. Como su nombre lo indica, esto va más allá de ser un evaluador de expresiones puro, pero puede “vincular” o asociar la expresión a objetos externos, utilizando RTTI para la integración.
Otro concepto clave de las expresiones vinculantes es que no evalúan la entrada de una manera completamente dinámica, sino que requieren una operación de análisis (llamada Compilar) que procesa el texto y una operación de evaluación que realiza el procesamiento final. Por supuesto, si no cambia el texto de la expresión, puede compilar una vez y evaluar la expresión varias veces con diferentes valores para los objetos asociados.
Vayamos a una demostración
Para mi primera demostración, he creado un formulario con dos controles Memo, uno para escribir la expresión y el segundo para mostrar el resultado. El objetivo aquí es procesar cadenas, no valores numéricos. El código del único botón utiliza directamente los objetos de expresión de enlace, como se muestra a continuación:
1 2 3 4 5 6 7 8 9 10 |
procedure TForm1.btnEvalClick(Sender: TObject); var bindExpr: TBindingExpression; begin bindExpr := TBindingExpressionDefault.Create; bindExpr.Source := MemoExpr.Lines.Text; BindExpr.Compile(); MemoOut.Lines.Add (BindExpr.Evaluate.GetValue.ToString); bindExpr.Free; end; |
No es que sea muy útil, ya que las únicas operaciones predefinidas para cadenas es la concatenación, por lo que puede escribir la entrada:
1 |
"Hello " + " world" |
y obtenga Hello world como resultado. ¡Observe las comillas dobles en la entrada!
Vincular un objeto
Donde las cosas comienzan a ponerse interesantes es si vincula la expresión a un objeto. Para esto, he creado una clase simple como la siguiente (aquí he omitido los campos y métodos privados):
1 2 3 4 5 6 |
type TPerson = class public property Name: string read FName write SetName; property Age: Integer read FAge write SetAge; end; |
Ahora puedo cambiar el código para agregar un enlace a la expresión, agregando al método Compile la asociación de un objeto con un nombre simbólico (puede considerarlo como el nombre del objeto para el motor de expresión):
1 2 |
pers := TPerson.Create; BindExpr.Compile([TBindingAssociation.Create(pers, 'person')]); |
Con esta extensión, ahora puedo usar este objeto en una expresión como. Por ejemplo, después de asignar ‘John’ al nombre del objeto pers en el código, la expresión:
1 |
person.name + " is happy" |
resultaría en que John esté feliz . Pero también puedo usar la expresión:
1 |
person.name + " is " + person.age + " years old." |
que va a hacer la transformación de tipo de Integer a string.
Funciones vinculantes
Las expresiones de enlace se limitan a los objetos, pero además de acceder a las propiedades, también pueden ejecutar métodos. Por lo tanto, puede crear una clase con varios métodos para usar en una expresión:
1 2 3 4 5 6 |
type TMyFunct = class public function Double (I: Integer): Integer; function ToStr (I: Integer): string; end; |
Para invocar estos métodos, debe vincular un objeto de este tipo:
1 2 3 |
BindExpr.Compile([ TBindingAssociation.Create(pers, 'person'), TBindingAssociation.Create(myFunct, 'fn')]); |
Ahora puedes escribir expresiones como:
1 |
"At double age " + person.name + " will be " + fn.ToStr(fn.Double(person.age)) + " years old" |
resultando en: Al doble de edad, John tendrá 66 años
La interfaz de usuario de demostración
No es tan elegante, pero esta es la demostración en acción:
Solo tocando la superficie
Esta introducción solo toca la superficie, ya que la expresión vinculante permite controles vinculantes y también permite registrarse para notificaciones (una especie de mecanismo de devolución de llamada). Encontré algunas demostraciones más que intentaré publicar en un blog sobre ellas pronto.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition