¿Cómo hacer un intérprete de ecuaciones matemáticas en QuickBASIC? Parte 4

¿Cómo hacer un intérprete de ecuaciones matemáticas en QuickBASIC? Parte 4


Imagen generada usando Bing Image Creator


Para ver la tercera parte de este artículo puedes usar este enlace: Parte 3


Introducción

En la parte anterior de este texto, terminamos de definir la gramática de una expresión matemática de una manera lo suficientemente detallada, como sabemos un paso importante para implementar a partir de ella un intérprete de ecuaciones matemáticas, y por tanto en esta parte del texto podemos empezar a escribir código del intérprete usando el lenguaje de programación QuickBASIC.

El parser de ecuaciones resolverá las ecuaciones matemáticas especificadas en la forma de una cadena de texto a través de una serie de pasos como los descritos a continuación:

Paso

Descripción

1

Entrada (ecuación como cadena de texto)

2

Tokenización (números, operadores, paréntesis, etc.)

3

Análisis sintáctico (orden de operaciones, jerarquía)

4

Evaluación (cálculo numérico)

5

Salida (resultado de la ecuación)

En esto un intérprete de ecuaciones también se parece a un intérprete de un lenguaje de programación donde del mismo modo se sigue un proceso parecido con una serie de pasos definidos para interpretar código de programación.

En esta parte del tutorial empezaremos por implementar el analizador lexicográfico (paso de Tokenización) del intérprete de ecuaciones, un paso encargado de reconocer los distintos símbolos válidos con los cuales se describe una ecuación matemática tal como lo define la gramática de una expresión matemática, así como los distintos tokens o lexemas conformados a partir de esos símbolos o caracteres aislados.


El analizador lexicográfico

El analizador lexicográfico, como hemos visto, utiliza la gramática definida para reconocer los distintos símbolos o caracteres válidos para la conformación de los distintos tokens o lexemas (símbolos o conjuntos de otros símbolos con significado específico); y en adición también usa la gramática para identificar esos lexemas creando una tabla de símbolos usada por los pasos posteriores como el analizador sintáctico, con lo cual se consigue detectar posibles errores de sintaxis o del órden correcto en esos lexemas en un texto fuente de programa.

En el caso del análisis lexicográfico en un intérprete de ecuaciones resulta innecesario tener presentes todos los detalles más propios del analizador lexicográfico del intérprete de un lenguaje de programación, y por eso nosotros no necesitamos crear una tabla de símbolos y podemos conformarnos con un analizador bastante simplificado.

En un intérprete de ecuaciones matemáticas, el analizador sintáctico también se sirve del analizador lexicográfico para obtener un lexema detrás de otro, sin necesidad de lidiar con la secuencia de caracteres sin identificar de un programa fuente (ecuación matemática en este caso).

El analizador lexicográfico deberá utilizar una serie de subrutinas y funciones destinadas a llevar a cabo su proceso de reconocimiento de los distintos elementos considerados válidos en el texto fuente.

La siguiente tabla lista algunas de estas subrutinas y funciones:

Nombre

Propósito

GetCharacter()

Obtener un carácter de la secuencia de caracteres.

IsAlpha%(c As String)

Comprueba "c" y devuelve true si es alfabético.

IsDigit%(c As String)

Combrueba "c" y devuelve true si es un dígito.

IsWhite%(c As String)

Comprueba "c" y devuelve true si es un espacio.

IsAddOp%(c As String)

Comprueba "c" y devuelve true si es "+" o "-".

IsMulOp%(c As String)

Comprueba "c" y devuelve true si es "*" o "/".

IsParentheses(c As String)

Comprueba "c" y devuelve true si es "(" o ")".

El contenido de la tabla anterior nos enseña como las subrutinas o funciones usadas por el analizador lexicográfico reflejan lo definido en la gramática de una expresión matemática, puesto como se puede notar muchas de las funciones consisten en detectar si se trata de un carácter válido, aun cuando como es fácil suponer todavía nos faltarían algunas subrutinas y funciones más, las cuales no listaremos pero si deben ser implementadas de modo dicho analizador pueda reconocer todos los símbolos válidos definidos por la gramática.

Los interesados en revisar todas las funciones del analizador lexicográfico podrán descargar su código fuente más adelante, sin embargo, es necesario mencionar que dado esta es una primera aproximación, la versión final del código del intérprete de ecuaciones tendrá algunos cambios.

Los cambios mencionados serán necesarios para relacionar de una forma más adecuada las subrutinas y funciones del analizador lexicográfico con las necesidades del analizador sintáctico y el resto del intérprete de ecuaciones matemáticas.


Implementación de las subrutinas y funciones

La subrutina GetCharacter podría implementarse como sigue:

Sub GetCharacter()
  If Position < Len(Expression) Then
    Position = Position + 1
    Character = UCase$(Mid$(Expression, Position, 1))
  End If
End Sub

En esta subrutina hacemos uso de una variable global Integer de nombre Position, en la cual se guarda, como su nombre lo indica, la posición del carácter a ser leído dentro de la cadena de caracteres de entrada. La cadena de entrada o ecuación matemática estará guardada a su vez en la variable global String de nombre Expression. Por fin, el carácter es leído por medio de la función Mid$ del BASIC y es guardado en una variable global String de nombre Character de donde será tomado cuando nos veamos en la necesidad de hacerlo.

En esta subrutina también se hace uso de la función UCase$ de QuickBASIC de modo nuestro intérprete de ecuaciones no distinga entre mayúsculas y minúsculas en los nombres de variables.

Por su parte, las demás funciones listadas son casi iguales entre ellas, salvo por reconocer el elemento indicado por su nombre (Letra, Dígito, Espacio en blanco, etc.).

Las líneas de código siguientes muestran como sería la función IsAlpha destinada a reconocer una letra o carácter alfabético:

Function IsAlpha%(c As String)
  IsAlpha = (Asc(c) >= 65) And (Asc(c) <= 90)
End Function

En este caso usamos la función BASIC Asc para obtener el código ASCII del elemento en la variable "c" usada como parámetro, y comparamos ese código para ver si se trata de una letra de la "A" a la "Z" (los códigos ASCII del rango de letras de la "A" a la "Z" se corresponden con los números de 65 a 90).

En general como se ha mencionado necesitaremos funciones básicas como estas de modo podamos detectar todos los distintos símbolos legales en la secuencia de entrada.

Es decir, debe ser evidente la necesidad de una función para reconocer un "." puesto un número real lo posee, tal como lo expresa la gramática de un número, y así mismo se necesitan otras funciones para detectar cada caracter válido según lo expresado en la gramática.

En resumen, la intención de esto es mostrarles sin lugar a dudas cómo el analizador lexicográfico se basa en los símbolos o caracteres válidos definidos en la gramática de una expresión matemática y depende de ella.

En forma de adelanto podemos comentar como las funciones mencionadas nos van a servir como base para hacer otras funciones o subrutinas de reconocimiento de orden superior como comentamos seguidamente.


La subrutina SkipWhite para ignorar espacios en blanco

La subrutina SkipWhite puede servirnos de demostración de lo dicho:

Sub SkipWhite()
  While IsWhite(Character)
    GetCharacter
  Wend
End Sub

La subrutina SkipWhite, como su nombre lo indica bastante bien, será utilizada por el analizador lexicográfico para saltarse todos los espacios en blanco en la secuencia de entrada, puesto estos no tienen significado para el analizador sintáctico.

Nota: En un analizador lexicográfico de un lenguaje de programación donde el código se escribe en muchas líneas, SkipWhite también deberá saltarse los retornos del carro y los caracteres de nueva línea.

Pero vamos a dar un salto y ver la función principal del analizador lexicográfico, la cual se pudiera llamar GetToken, porque de todos modos en este instante debería haberse entendido la idea, conociendo la función del analizador lexicográfico de reconocer e ir entregando todos los lexemas válidos también conocidos como tokens según estos son descritos por la gramática.


La función GetToken

La función GetToken utiliza las subrutinas y funciones más básicas comentadas antes para llevar a cabo su tarea de ir obteniendo cada token o lexema en la secuencia de entrada.

Function GetToken$()
  SkipWhite
  If IsAlpha(Character) Then
    GetToken = GetIdentifier    
  ElseIf IsDigit(Character) Then
    GetToken = GetNumber
  ElseIf IsOperator(Character) Or IsParenthesis(Character) Or IsComma(Character) Or IsTerminator(Character) Then
    GetToken = Character
    GetCharacter
  Else
    Error 255
  End If
End Function

La función GetToken debería devolver un lexema con cada llamada a ella, o emitir un error en caso de encontrarse con un carácter no válido como parte de la secuencia de entrada; se trata de una función bastante simplificada, puesto probablemente no sea usada en ese mismo estado más adelante, para lograr una mejor integración entre el analizador lexicográfico y el analizador sintáctico.

En esta función hacemos uso de otras funciones como las mencionadas de reconocimiento de distintos símbolos o caracteres, y de otras como GetIdentifier, destinada a obtener un identificador (nombre de variable o función), o GetNumber, destinada a reconocer un número.

Las funciones GetIdentifier y GetNumber, como sucede con algunas de las otras, no serán listadas en este texto, no obstante, cada cual podrá verlas si descarga el fuente del programa donde están implementadas.

En particular, la función GetNumber debería ser capaz de reconocer un número entero o un número real, así como un número en notación científica o exponencial expresado como se lo suele hacer en los sistemas informáticos (como en 1.36e-14) según fue definido en la gramática de una expresión matemática.


Resultados

El código a continuación nos puede servir para probar lo hecho hasta ahora:

Position =  0
Expression = "4/3*Pi*Pow(r,3);"

GetCharacter

While Position < Len(Expression)
  Print GetToken
Wend
Print GetToken

Las líneas de código anteriores deben reconocer y mostrar en pantalla todos los lexemas existentes en la secuencia de caracteres en la variable Expression.

En la captura de pantalla a continuación se puede ver este resultado donde en cada línea se lista un token válido:


Código fuente

Los interesados pueden descargar el fuente de la implementación del analizador lexicográfico básico por medio de este enlace: 🔗 Lex.zip

En todo caso, como deben de recordar, esto se trata nada más de una aproximación inicial a un analizador lexicográfico para el intérprete de ecuaciones. En una próxima parte comenzaremos con la implementación del analizador sintáctico y reutilizaremos casi todo esto si bien como se ha comentado con algunas modificaciones para relacionar mejor los analizadores antes mencionados.

Nota: El código del programa no ha utilizado códigos de error diferenciados dado esto se está haciendo sólo con fines educativos, todos los errores lanzados desde dentro de las distintas subrutinas y funciones tienen un código común número 255.


¿Qué te ha parecido esta cuarta parte?

Tú participación aportando ideas, dando sugerencias, o expresando tu crítica constructiva a través de tus comentarios, es importante y fundamental para permitirnos crecer y crear contenidos con cada vez más calidad.

También es importante tu voto si crees vale la pena concederlo.

¡Nos vemos en la siguiente parte de este artículo tutorial!



0
0
0.000
3 comments
avatar

Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!

Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).

Consider setting @stemsocial as a beneficiary of this post's rewards if you would like to support the community and contribute to its mission of promoting science and education on Hive. 
 

0
0
0.000
avatar

Congratulations @retrotech-cuba! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You received more than 200 upvotes.
Your next target is to reach 300 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

0
0
0.000