domingo, febrero 27, 2005

 

Un mar de configuraciones

Ates: link a la version en MSWord de este blog:
http://www.geocities.com/jahazhn/cmpl/un_mar_de_configuraciones.doc

La plataforma eclipse puede ser un poco confusa. Son muy agradables toda la gama de posibilidades y facilidades que posee, sin embargo, por ser un IDE muy apartado de lo común requiere bastante tiempo para adaptarse a él.
Uno de los prejuicios más grandes que tengo, es por no tener obvio el comando “compilar”, eso es lo que opina un fanático de DEV C++ 4, donde en el centro de la barra de herramientas aparecen los tres botones: “Compilar”, “Correr”, o para ir de un solo al mandado, “Compilar y correr”.
Hasta hace unos días encontré el comando “Construir” (Build).
El otro detalle que me ha llevado hasta el borde de la desesperación, es el casi infinito mar de configuraciones, un cuadro de diálogo tras otro, y en cada cuadro , “ene” posibilidades... hablando concretamente, en las configuraciones para el comando “Run...”, olvidé ese paso, por eso, no importaba cuantas veces creara un nuevo proyecto, o incluso un nuevo espacio de trabajo, el dichoso IDE seguía buscando el método main de la primera cosa que hice con eclipse, de modo que nunca encontraba los nuevos archivos.
Ese es el tipo de cosas que deja la sensación de no saber si el de ‘la falla’ es el usuario o el que diseñó el programa. Por convención, suele ser el usuario, ¿verdad?.

Una vez superadas esas dificultades con el IDE, el proyecto se pone en marcha luego de un poco de observación de la gramática en la definición de micro-C.

Aquí muestro el código del lexer

class lexico extends Lexer;
options{ k=4; }

TOK_IF: "if";
TOK_ELSE: "else";
TOK_RETURN: "return";
TOK_WHILE: "while";
TOK_BREAK: "break";
TOK_CONTINUE: "continue";
TOK_TYPE_INT: "int";
TOK_TYPE_CHAR: "char" ;
TOK_VOID: "void";

ASSIGN:('=');

ADD_OP:('+''-');

MUL_OP:('*''/');

EQ_OP:("==""!=");

REL_OP:("<="">=");

REL_OP2:("<"">");


OPEN_PAR:('(');

CLOSE_PAR: (')');

OPEN_BRACE:('{');

CLOSE_BRACE: ('}');

COMMA: (',');

SEMICOLON: (';');

SUSPENSIVE: "...";

SPACE: (' ''\r''\n'){$setType(Token.SKIP); } ;

IDENTIFIER:
(LETTER)(LETTERDIGIT)*
;

INTEGER_CONSTANT:
(ZERO)(NONZERO)(DIGIT)*
;

protected ZERO:'0';
protected NONZERO:('1'..'9');
protected DIGIT:(ZERONONZERO);

protected LETTER:('a'..'z''A'..'Z''_') ;

CHAR_CONSTANT: ('\'')(LETTERDIGIT)('\''); //aun falta

STRING_CONSTANT: ('\"')()('\"'); //aun falta

protected COMMENT: "/*"()"*/";

Las definiciones de los operadores y palabras reservadas no dan mayor problema, solamente hay que fijarse de definirlas antes que los identificadores, para que el “no-determinismo” no sea una dificultad extra.

Los elementos más complejos han resultado ser la constante de caracter y la constate de cadena. En realidad, solamente se trata de analizar con calma elcaso en particular.

Continuamos con el Parser
El parser es sin duda la parte más emocionante d este proyecto, hasta ahora. Resulta sorprendente que la “Teoría de la computación” sirva para algo, (al cursar esa clase, esa es la interrogante más popular, pero la única que el maestro no contesta... ¿porqué nos hace eso? ).
Al definir la gramática del lenguaje a nivel de tokens, se logra ver con toda claridad la estructura del programa, y como es de esperar, hay que tener ciertas delicadezas con esa gramática.
El primer problema que se detecta al introducir una gramática y compilar el código de ANTLR para el parser es el de “Infinite recursion...”. La recursión izquierda, como la conocemos en español, es el primer detalle a tratar.

Eliminando recursión izquierda
A continuación muestro las transformaciones que realicé a algunas de las producciones de la gramática provista por el profesor:
( léase el signo ‘&’ como ‘lamda’)

translation_unit -> external_declaration translation_unit external_declaration
--------------------------------------------------------------------------------
translation_unit -> external_declaration translation_item
translation_item -> external_declaration translation_item &


parameter_def_list -> type identifier parameter_def_list "," type identifier;
-------------------------------------------------------------------------------
parameter_def_list -> type identifier parameter_item
parameter_item -> "," type identifier parameter_item &


declarations -> declarations declaration &
---------------------------------------------
declarations -> declarative
declarative -> declaration declarative &


parameter_decl_list -> parameter_decl_list "," parameter_decl_spec parameter_decl_spec
----------------------------------------------------------------------------------------
parameter_decl_list -> parameter_decl_spec parameter_declarative
parameter_declarative -> "," parameter_decl_spec parameter_declarative &


statement_list -> statement_list statement statement
------------------------------------------------------
statement_list -> statement statement_item
statement_item -> statement statement_item &


simple_expression -> simple_expression add_op term term
---------------------------------------------------------
simple_expression -> term simple_expression_
simple_expression_ -> add_op term simple_expression_ &


term -> term mul_op factor factor
-----------------------------------
term -> factor term_
term_ -> mul_op factor term_ &


expression_list -> expression_list "," expression expression
--------------------------------------------------------------
expression_list -> expression expression_list_
expression_list_ -> "," expression expression_list_ &


identifier_list -> identifier_list "," identifier identifier
---------------------------------------------------------------
identifier_list -> identifier identifier_list_
identifier_list_ -> "," identifier identifier_list_ &

Este procedimiento resulta relativamente sencillo, ya que la transformación implica solamente aplicar una fórmula de dos pasos.

Producciones no deterministas
Un segundo problema resulta el no-determinismo de algunas de las producciones, generalmente las de la forma A -> B BC. En algunos casos, no basta para ANTLR aplicar factorización izquierda, por ejemplo, si hacemos A -> BH, H -> C ‘lambda’, ANTLR reportará no-determinismo en la producción H.
Todavía no he encontrado la manera de solucionar este problema; mencionando que para estos casos estoy utilizando el operador ‘?’, que significa “cero o una vez”, es decir H -> C?.

A continuación muestro la gramática para el Parser, en formato de ANTLR, encontrará marcadas con un ‘!!’ al inicio d la línea, aquellas producciones en las cuales he encontrado el problema arriba descrito.

class parsico extends Parser;

startRule: translation_unit;

translation_unit : external_declaration translation_item;

translation_item : (external_declaration translation_item)?;

!!external_declaration : function_definition declaration;

function_definition : function_def_header function_body;

function_def_header : return_type IDENTIFIER (OPEN_PAR) parameters_def (CLOSE_PAR);

return_type : type (TOK_VOID);

parameters_def : parameter_def_list (TOK_VOID);

parameter_def_list : type IDENTIFIER parameter_item;

parameter_item : ((COMMA) type IDENTIFIER parameter_item)?;

function_body : (OPEN_BRACE) declarations statement_list (CLOSE_BRACE);

declarations : declarative;

declarative : (declaration declarative)?;

!!declaration : variable_declaration function_declaration;

variable_declaration : type identifier_list (SEMICOLON);

function_declaration : return_type IDENTIFIER (OPEN_PAR)

!!parameters_decl (CLOSE_PAR) (SEMICOLON);

parameters_decl : parameter_decl_list parameter_decl_list (COMMA) (SUSPENSIVE) (TOK_VOID);

parameter_decl_list : parameter_decl_spec parameter_declarative;

!!parameter_declarative : (COMMA parameter_decl_spec parameter_declarative)?;

parameter_decl_spec : type IDENTIFIER;

statement_list : statement statement_item;

statement_item : (statement statement_item)?;

!!statement : expression (SEMICOLON)
(TOK_RETURN) statement_
(TOK_WHILE) (OPEN_PAR) expression (CLOSE_PAR) statement
(TOK_IF) (OPEN_PAR) expression (CLOSE_PAR) statement //else_statement
(TOK_IF) (OPEN_PAR) expression (CLOSE_PAR) statement (TOK_ELSE) statement
(OPEN_BRACE) statement_list (CLOSE_BRACE)
// (TOK_RETURN) (SEMICOLON)
(TOK_BREAK) (SEMICOLON)
(TOK_CONTINUE) (SEMICOLON);

statement_: expression SEMICOLON SEMICOLON;

//else_statement : (TOK_ELSE statement)?;

expression : equality_expression ((ASSIGN) equality_expression)?;

equality_expression : relational_expression ((EQ_OP) relational_expression)?;

relational_expression : simple_expression ((REL_OP) simple_expression)?;

simple_expression : term simple_expression_;

simple_expression_ : ((ADD_OP) term simple_expression_)?;

term : factor term_;

term_ : (MUL_OP factor term_)?;

factor :
constant
(ADD_OP) factor
IDENTIFIER ((OPEN_PAR) factor_)?
(OPEN_PAR) expression (CLOSE_PAR);

factor_ : (CLOSE_PAR) expression_list (CLOSE_PAR);

constant : STRING_CONSTANT numeric_constant CHAR_CONSTANT;

numeric_constant : INTEGER_CONSTANT;

expression_list : expression expression_list_;

expression_list_ : ((COMMA) expression expression_list_)?;

identifier_list : IDENTIFIER identifier_list_;

identifier_list_ : ((COMMA) IDENTIFIER identifier_list_)?;

type : (TOK_TYPE_INT) (TOK_TYPE_CHAR);

La mente se consuela con un clásico “un poco más de análisis bastará para hallar la solución...”

Este es el contenido de uno de los archivos que he probado con el “compilador”:
int uno(int x, char c){
int y, z, w;
char r, c;
if(c == c){break;}
return x + 3;
}


A estas alturas del proyecto, me siento más realizado si el código generado “acepta” el input, que si le encuentra errores, porque mientras no corrija el no-determinismo de la gramática, no se logra reconocer código válido, como en:

if(c == c){break;}else{continue;}.

Este tipo de input no es aceptada aún por la gramática expuesta más arriba.

Ahora viene la etapa de reportar errores. Aquí es donde “la mula botó a Genaro”.

miércoles, febrero 09, 2005

 

Un reconocedor de expresiones a la brava( continuacion )

En la publicación anterior, olvidé comentar otros problemas que tuve con ese reconocedor.
La primera de ellas es que para respetar el orden estándar de operaciones, la cadena re recorre al revés, por ejemplo en 1 + 2 + 3, si se recorre al revés, el primer operador reconocido es el ‘+’ que se encuentra entre el ‘2’ y el ‘3’.
Esa fue la única solución que se me ocurrió para que esa operación quedase mas profunda en el árbol construido.
Otra observación ( si no han compilado el código) es que tuve que utilizar un treeview, así que necesitarán el código del .vbp para que VB sepa que tiene que incluir el OCX adecuado.
Aquí les va el texto. Igual que antes: Copy & Paste, salvar con editor de texto plano con CualquierNombre.vbp.
Al abrirlo, asegurarse que el archivo incluido es el que hayan salvado de la publicación anterior, y prueben a correrlo.
Lamento tener que poner el texto aquí en el blog, ya se que es mas difícil leerlo asi, pero estoy teniendo problemas de log in con la página web a la cual pude haber subido todo en un simple .zip. [ cookies rejected ]
------------- archivo: Proyecto.vbp ---------------------
Type=Exe
Form=Pharser01.frm
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\WINDOWS\System32\stdole2.tlb#OLE Automation
Object={831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0; MSCOMCTL.OCX
IconForm="Pharser01"
Startup="Pharser01"
HelpFile=""
Command32=""
Name="Pharser"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="Familia Lara"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1

[MS Transaction Server]
AutoRefresh=1
------------------------ fin de archivo --------------------

 

Un reconocedor de expresiones a la brava

Uno de mis intereses desde que aprendi a programar ha sido el de generar gráficas de funciones matemáticas. Esa parte es sencilla, si la función a graficar se coloca en el Código fuente. Por supuesto, una mejor implementación permitirá al usuario introducir la ecuación que desee. La primera solución que se viene a la mente – si no se tiene conocimiento sobre compiladores - es crear una estructura fija, que el usuario pueda llenar, evitando al máximo la cantidad de errores de input. Esto limita la capacidad del programa, y requiere un cambio de la interfase se desea ampliar la funcionalidad del programa.
La mejor alternativa: que el usuario pueda introducir la expresión tal y como se escribe en notación matemática. Aquí es donde viene la parte interesante.
Como construir un reconocedor reexpresiones matemáticas sin conocimiento de herramientas para crear compiladores, y aún sin saber aplicar la teoría de autómatas?Aquí presento un primer intento. Un programa en Visual Basic.

La idea básica para reconocer era: Dado que la mayoría de las expresiones matemáticas son binarias, encontrar un operador, subdividir el input (cadena de entrada) en tres partes, izquierda, operador, derecha.
El operador se convierte en la raíz de un árbol, y se llama recursivamente este procedimiento para poblar los hijos de este arbol.
Las dificultades relativas a la precedencia se resolvieron haciendo un exceso d pasadas, ya que en una primera pasada se buscan los operadores con mayor precedencia, como la exponenciación, en una segunda los ‘por’ y ‘entre’, y en otra más, los operadores de suma y resta, y considerando la recursión, la cadena completa se recorre hasta tres veces por cada operador.
La precedencia con los paréntesis se resolvía “bloqueando” el encuentro de operadores si se estaba en un nivel de paréntesis distinto de cero, y enviando luego la cadena entre paréntesis (removiendo los mismos) al procedimiento antes descrito.
He aquí el código para examinarlo y si lo desea, compilarlo. Fue creado usando VB 6.0.
Por favor disculpar el “abuso” con el nombre, en ese entonces no sabía exactamente de que se trataba...
Instrucciones: Copy & Paste en cualquier editor de texto plano, salvar como Pharser01.frm, crear un nuevo proyecto de VB, e incluir este formulario en el.
“Hay que caerse antes de aprender a caminar”
---------------------- Archivo: pharser01.frm -------------------
VERSION 5.00
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX"
Begin VB.Form Pharser01
BorderStyle = 1 'Fixed Single
Caption = "Pharser01"
ClientHeight = 7335
ClientLeft = 45
ClientTop = 435
ClientWidth = 10470
BeginProperty Font
Name = "MS Sans Serif"
Size = 12
Charset = 0
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
LinkTopic = "Form1"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 7335
ScaleWidth = 10470
StartUpPosition = 3 'Windows Default
Begin VB.ListBox varList
Height = 5760
Left = 7560
Sorted = -1 'True
TabIndex = 6
Top = 840
Width = 2655
End
Begin VB.CommandButton btnSolve
Caption = "&Solve"
Height = 375
Left = 8880
TabIndex = 5
Top = 0
Width = 1335
End
Begin MSComctlLib.TreeView Tree
Height = 5895
Left = 0
TabIndex = 2
Top = 840
Width = 7530
_ExtentX = 13282
_ExtentY = 10398
_Version = 393217
HideSelection = 0 'False
Indentation = 706
LabelEdit = 1
LineStyle = 1
Style = 4
Appearance = 1
End
Begin VB.CommandButton btnProccess
Caption = "&Proccess"
Height = 375
Left = 7560
TabIndex = 1
Top = 0
Width = 1335
End
Begin VB.TextBox txtPharse
Height = 420
Left = 0
TabIndex = 0
Top = 0
Width = 7575
End
Begin VB.Label Label3
Caption = "Variables:"
Height = 255
Left = 7560
TabIndex = 7
Top = 600
Width = 2655
End
Begin VB.Label Label2
BorderStyle = 1 'Fixed Single
Height = 615
Left = 0
TabIndex = 4
Top = 6720
Width = 10455
End
Begin VB.Label Label1
Caption = "Arbol de expresión:"
Height = 255
Left = 0
TabIndex = 3
Top = 600
Width = 4095
End
End
Attribute VB_Name = "Pharser01"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Function MenosUnario(Expre As String, i As Integer) As Boolean
If i = 1 Then MenosUnario = True: Exit Function
If esOper(Mid(Expre, i - 1, 1)) Then MenosUnario = True: Exit Function
'if i > 2 and esOper(Mid(Expre, i - 2, 2)) Then MenosUnario = True: Exit Function
End Function

Sub Arbol(Expre As String, Optional index As Integer)
If Expre = "" Then Exit Sub
Dim aux As Node
Dim i As Integer, par As Integer: par = 0
Dim ParMid As Boolean
'si la cadena empieza y termina en parentesis, y no hay parentesis intermedios
'removemos los extremos.
For i = 2 To Len(Expre) - 1
If Mid(Expre, i, 1) = "(" Or Mid(Expre, i, 1) = ")" Then ParMid = True: Exit For
Next
If Not ParMid Then
If Mid(Expre, 1, 1) = "(" And Mid(Expre, Len(Expre), 1) = ")" Then Expre = Mid(Expre, 2, Len(Expre) - 2)
End If
If Len(Expre) = 0 Then Exit Sub
'Operadores Logicos( &: AND, : OR, !:NOT, ?:XOR
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "&" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "&")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "&")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
If Mid(Expre, i, 1) = "!" And par = 0 Then
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "?" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
Next
'Signos de Comparacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If i > 1 Then
If Mid(Expre, i - 1, 2) = ">=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i - 1, 2) = "<=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "=")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = ">" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "<" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If

Next
'signos de suma ( +, -)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "+" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "+")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "+")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "-" And par = 0 Then
If Not MenosUnario(Expre, i) Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
Else 'menos unario
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
End If
Next
'Signos de multiplicacion ( /, *)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "*" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "*")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "*")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "/" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "/")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "/")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'Signo de exponenciacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "^" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "^")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "^")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'si no habia signos en la cadena:
If index = 0 Then
Tree.Nodes.Add , , , Expre
Else
Tree.Nodes.Add index, tvwChild, , Expre
End If
End Sub
Sub Arbol2(Expre As String, Tree As TreeView, Optional index As Integer)
If Expre = "" Then Exit Sub
Dim aux As Node
Dim i As Integer, par As Integer: par = 0
Dim ParMid As Boolean
'si la cadena empieza y termina en parentesis, y no hay parentesis intermedios
'removemos los extremos.
For i = 2 To Len(Expre) - 1
If Mid(Expre, i, 1) = "(" Or Mid(Expre, i, 1) = ")" Then ParMid = True: Exit For
Next
If Not ParMid Then
If Mid(Expre, 1, 1) = "(" And Mid(Expre, Len(Expre), 1) = ")" Then Expre = Mid(Expre, 2, Len(Expre) - 2)
End If
If Len(Expre) = 0 Then Exit Sub
'Operadores Logicos( &: AND, : OR, !:NOT, ?:XOR
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "&" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "&")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "&")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
If Mid(Expre, i, 1) = "!" And par = 0 Then
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "?" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
Next
'Signos de Comparacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If i > 1 Then
If Mid(Expre, i - 1, 2) = ">=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i - 1, 2) = "<=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "=")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = ">" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "<" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If

Next
'signos de suma ( +, -)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "+" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "+")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "+")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "-" And par = 0 Then
If Not MenosUnario(Expre, i) Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
Else 'menos unario
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
End If
Next
'Signos de multiplicacion ( /, *)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "*" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "*")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "*")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "/" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "/")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "/")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'Signo de exponenciacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "^" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "^")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "^")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'si no habia signos en la cadena:
If index = 0 Then
Tree.Nodes.Add , , , Expre
Else
Tree.Nodes.Add index, tvwChild, , Expre
End If
End Sub

Private Function esOper(str As String) As Boolean
Select Case str
Case "+": esOper = True: Exit Function
Case "-": esOper = True: Exit Function
Case "_": esOper = True: Exit Function
Case "*": esOper = True: Exit Function
Case "/": esOper = True: Exit Function
Case "^": esOper = True: Exit Function

Case "=": esOper = True: Exit Function
Case ">": esOper = True: Exit Function
Case "<": esOper = True: Exit Function
Case ">=": esOper = True: Exit Function
Case "<=": esOper = True: Exit Function

Case "&": esOper = True: Exit Function
Case "": esOper = True: Exit Function
Case "!": esOper = True: Exit Function
Case "?": esOper = True: Exit Function
End Select
esOper = False
End Function

Function palabra(signo As String) As String
Select Case palabra
Case "+": signo = "MAS"
Case "-": signo = "MENOS"
Case "_": signo = "NEG"
Case "*": signo = "POR"
Case "/": signo = "ENTRE"
Case "^": signo = "ELEVADO A"
Case "=": signo = "IGUAL A"
Case ">": signo = "MAYOR QUE"
Case "<": signo = "MENOR QUE"
Case ">=": signo = "MAYOR O IGUAL QUE"
Case "<=": signo = "MENOR O IGUAL QUE"
Case "&": esOper = "AND"
Case "": esOper = "OR"
Case "!": esOper = "NOT"
Case "?": esOper = "XOR"
End Select
End Function

Private Function Simetrical(Tree As TreeView) As String
Dim temp As String
If Tree.Nodes.Count = 0 Then
Simetrical = "NULL"
Exit Function
Else
Simetrical = SimOrder(Tree.Nodes(1))
End If
End Function
Function SimOrder(NODO As Node) As String
If NODO.Children = 0 Then
SimOrder = " " & NODO.Text & " "
Else
If NODO.Children = 2 Then
SimOrder = "(" & SimOrder(NODO.Child) & NODO.Text & SimOrder(NODO.Child.Next) & ")"
Else
SimOrder = "(" & NODO.Text & SimOrder(NODO.Child) & ")"
End If
End If
End Function
Function Solve(NODO As Node) As Double
If NODO.Children = 0 Then
If IsNumeric(NODO.Text) Then
Solve = CDbl(NODO.Text)
Else
Solve = Variable(NODO.Text)
End If
Else
Select Case NODO.Text
Case "+": Solve = Solve(NODO.Child) + Solve(NODO.Child.Next)
Case "-": Solve = Solve(NODO.Child) - Solve(NODO.Child.Next)
Case "_": Solve = -Solve(NODO.Child)
Case "*": Solve = Solve(NODO.Child) * Solve(NODO.Child.Next)
Case "/": Solve = Solve(NODO.Child) / Solve(NODO.Child.Next)
Case "^": Solve = Solve(NODO.Child) ^ Solve(NODO.Child.Next)
Case "=":
If Solve(NODO.Child) = Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case ">":
If Solve(NODO.Child) > Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "<":
If Solve(NODO.Child) < Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case ">=":
If Solve(NODO.Child) >= Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "<=":
If Solve(NODO.Child) <= Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "&":
If Solve(NODO.Child) And Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "":
If Solve(NODO.Child) Or Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "!":
If Solve(NODO.Child) = 0 Then
Solve = 1
Else
Solve = 0
End If
Case "?": 'XOR
If Solve(NODO.Child) Xor Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
End Select
End If
End Function

Private Function VarDefn(var As String) As Boolean
'Averigua si una cadena corresponde con una variable ya definida
Dim i As Integer
For i = 0 To varList.ListCount - 1
If InStr(1, varList.List(i), var) <> 0 Then VarDefn = True: Exit Function
Next
VarDefn = False
End Function

Private Function Variable(var As String) As Double
'Busca en la lista de variables la definición y devuelve el valor asociado
Dim i As Integer
For i = 0 To varList.ListCount - 1
If InStr(1, varList.List(i), var) <> 0 Then
Variable = CDbl(Right(varList.List(i), Len(varList.List(i)) - InStr(1, varList.List(i), "=", vbTextCompare)))
Exit Function
End If
Next
End Function

Private Sub btnProccess_Click()
'Preproceso de la expresión
'Construir el arbol de expresión
If txtPharse.Text <> "" Then Tree.Nodes.Clear: Arbol2 txtPharse.Text, Tree
'Revisa el arbol:
Dim i As Integer
For i = 1 To Tree.Nodes.Count
Tree.Nodes(i).Expanded = True 'Expande todos los nodos para hacerlos visibles
Tree.Nodes(i).Text = Trim(Tree.Nodes(i).Text) 'Elimina los espacios antes y despues de la cadena
'Si un nodo contiene texto no numérico, no es operador ni variable definida, entonces:
If Not IsNumeric(Tree.Nodes(i).Text) And Not esOper(Tree.Nodes(i).Text) And Not VarDefn(Tree.Nodes(i).Text) Then
'Define la variable con el valor por defecto 0.0
varList.AddItem Tree.Nodes(i).Text & " = 0.0"
End If
Next
End Sub

Private Sub btnSolve_Click()
'Muestra la expresión que fue reconocida y la solución de la misma
Label2.Caption = Simetrical(Tree) & " = " & CStr(Solve(Tree.Nodes(1)))
End Sub

Private Sub txtPharse_KeyDown(KeyCode As Integer, Shift As Integer)
'Reacciona a la tecla <>
If KeyCode = vbKeyReturn Then btnProccess_Click
End Sub

Private Sub varList_DblClick()
'Edicion de variable
Dim str As String, igual As Integer
str = varList.List(varList.ListIndex)
igual = InStr(1, str, "=", vbTextCompare)
str = Mid(str, 1, igual) & " " & InputBox("Definir variable" & vbNewLine & Mid(str, 1, igual), , Mid(str, igual + 1, Len(str) - igual))
If IsNumeric(Mid(str, igual + 1, Len(str) - igual)) Then varList.List(varList.ListIndex) = str
End Sub

---------------- fin de archivo ---------------------------

This page is powered by Blogger. Isn't yours?