martes, 13 de junio de 2017

Nuevo evaluador de expresiones para PSeInt

Cuando empecé a pensar en reescribir el parser de PSeInt tenía planeado enfocarme en el tokenizado y las reglas sintácticas de cada instrucción. Pero había dos cosas que pensaba mantener del sistema viejo, al menos por ahora: la gestión de la memoria, y la evaluación de expresiones. Ahora resulta que de pronto me encuentro reescribiendo también al evaluador de expresiones.

Estas son dos partes cuyo diseño no permaneció intacto desde el comienzo de PSeInt. La gestión de la memoria sufrió varias transformaciones progresivas, y a la evaluación de expresión ya la había reescrito una vez allá por el 2010. Pero digamos que ahora ya no "calza" muy bien en la filosofía del nuevo sistema; y que al final no será mucho más trabajo rehacerlo otra vez frente a adaptar lo que hay y arrastrar o parchear sus limitaciones.

Por ejemplo, en una linea como "Escribir A+(B/2)", luego de detectar que es un "Escribir", y que tiene un solo argumento "A+(B/2)", pensaba dejar de lado la evaluación de este argumento y utilizar el evaluador actual/viejo. Su trabajo es analizar operandos y operadores, reemplazar variables, y calcular. Es decir, determinar qué operaciones involucra y cuánto da realmente una expresión en un determinado momento.

El mecanismo viejo iba descomponiendo y reemplazando variables y subexpresiones por sus valores y resultados a medida que avanzaba. Digamos que al invocar a la función evaluar con "A+(B/2)", esta detectaba el "+", mandaba a pedir el valor de "A", a evaluar recursivamente "B/2", y cuando llegaban los dos resultados hacía la suma. Así, el árbol que representa la expresión está implícito en el camino que toma la recursión, pero nunca se construye explícitamente como tal. Eso me pareció astuto en su momento, ya que me ahorraba algunos pasos, pero resultó poco reutilizable. No puedo extraer esa estructura para, por ejemplo, analizar los operadores a la hora de exportar la expresión a otro lenguaje. Y además estoy repitiendo ese análisis varias veces por ejecución.

El nuevo mecanismo separa el análisis y la evaluación de las expresiones, construyendo el 
árbol de la expresión en el primer paso y permitiendo así simplificar o variar el segundo

En el mismo sentido, cuando se me iban ocurriendo algunas ideas nuevas para mejorar la "adivinación" de tipos en base al uso de las variables en las expresiones, eran muy difíciles de probar (implementar). Más aún, la combinación parser+evaluador tenía ciertas limitaciones no deseadas, como por ejemplo la falta de soporte para constantes en notación científica, o la necesidad de paréntesis en "5 * (-3)". Corregir estos dos detalles era muy muy engorroso.

La buena noticia es que estas limitaciones ya desaparecieron. En lo poco que tengo andando del nuevo mecanismo de análisis de expresiones, estas cosas ya no son un "gran" problema. A veces sí son un "pequeño" problema, porque pueden representar casos especiales para el análisis. La notación científica es el único caso donde una letra es parte de una constante numérica, o donde incluso la constante tiene dentro un operador (ej: "1e-8"). Y sobre el segundo ejemplo deja en evidencia que hay operadores diferentes que se escriben igual: como el menos unario (-5) y el menos binario (7-5). Son detalles a tener en cuenta que obligan a complicar un poquito las cosas. Pero todavía se mantiene dentro de lo esperable.

Entonces, puedo entender expresiones que antes no y tener una mejor representación para luego aprovechar esa información para otras cosas, algunas que actualmente funcionan mal, otras que todavía ni existen. Por ejemplo, para ofrecer alguna interfaz ad-hoc para que el alumno entienda qué significa o cómo se analiza una expresión. Para eso falta mucho todavía, pero la buena noticia es que avanza... Lento, sí, pero avanza

6 comentarios:

  1. Hola. Un gusto escribirte. Empecé hace algunas semanas el Plan 111Mil. Estamos usando PSeInt como puerta hacia Java.

    ResponderEliminar
  2. Dices: hay operadores diferentes que se escriben igual: como el menos unario (-5) y el menos binario (7-5).

    Pienso que si investigas sobre NOTACIÓN POLACA INVERSA RPN o SOLO NOTACION POLACA PN, veras que esas ambigüedades se resuelven, y que por ejemplo -5 se reescribe como neg(-5) negativo o cambio de signo.

    ResponderEliminar
    Respuestas
    1. Hay muchas formas de escribir expresiones que resuelven estos problemas, pero NO son las que usa ni debería usar PSeInt. Para el pseudolenguaje, la idea es diseñar el algoritmo de interpretación/evaluación en función de cómo quiero que se vea el lenguaje para su finalidad educativa, y nunca al revés.

      Eliminar
    2. algunas reglas para tener en cuenta en la evaluación

      a-b-c <=>(a-b)-c a^b-c <=>(a^b)-c
      a-b/c <=>a-(b/c) a^-b-c <=>(a^-b)-c
      a-b/-c <=>a-(b/-c) a^b/c <=>(a^b)/c
      a-b^c <=>a-(b^c) a^-b/c <=>(a^-b)/c
      a-b^-c <=>a-(b^-c) a^b/-c <=>(a^b)/-c
      a/b-c <=>(a/b)-c a^-b/-c <=>(a^-b)/-c
      a/-b-c <=>(a/-b)-c a^b^c <=>(a^b)^c
      a/b/c <=>(a/b)/c a^-b^c <=>(a^-b)^c
      a/-b/c <=>(a/-b)/c a^b^-c <=>(a^b)^-c
      a/b/-c <=>(a/b)/-c a^-b^-c <=>(a^-b)^-c
      a/-b/-c <=>(a/-b)/-c
      a/b^c <=>a/(b^c)
      a/-b^c <=>a/-(b^c)
      a/b^-c <=>a/(b^-c)
      a/-b^-c <=>a/-(b^-c)

      Eliminar
  3. Hola

    Sería bueno liberar una versión tipo prealfa o alfa (junto con su código fuente e instrucciones de compilación) para poder ir probando PSeInt; no importa que no tenga todas las funcionalidades.
    ¿La nueva versión de PSeInt hará uso de wxWidgets?

    Gracias por su trabajo en esta aplicación.

    ResponderEliminar
  4. En un principio manejaba LPP, pero ahora conocí PSeInt y me gusta tu trabajo...

    Por si no conoces LPP esta es la pagina...
    http://mediatecnica.weebly.com/lpp.html

    ResponderEliminar