Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 14-11-2005
bustio bustio is offline
Miembro
 
Registrado: oct 2003
Posts: 231
Poder: 21
bustio Va por buen camino
Contar Lineas de Codigo Logicas y Fisicas

Hola a la comunidad:

Estoy tratantdo de hacer un programa que cuente las Lineas de Codigo(Lines Of Code) de mis aplicaciones. Para ello disenne un Estandar para Contar Lineas de Codigo; y basandome en el es qeu hago el conteo de mis fuentes. Pero mis errores vienen a raiz de que no veo claro el algoritmo que tengo que seguir para hacer el conteo.
Lo primero que hice fue eliminar la indentacion de la Unit que le entro al programa. Una vez hecho esto, voy linea por linea(almacene la Unit en un componente SynEdit, que es iden al memo, lo unico que hace el realce de la sintaxis.. por lo demas es lo mismo que el memo) analizando que los dos primeros caracteres sean '//', y si lo son pues los marco con el simbolo '*' y continuo. Una vez llegado al final de la Unit en el SynEdit los comentarios simples eliminados.
Luego, y mediante el mismo proceso elimino los espacios en blanco; marcandolos tambien con '*'. Despues recorreo el codigo para eliminar los comentarios '{......}'; que es un poco mas complejo. Aqui presento el segmento para que me lo critiquen:

Código Delphi [-]
procedure TLOCounter.DelMultipleComments;
var
  i, k:  Integer;
  Token: String;
  Line:  String;
  Trash: TTrashLine;//Clase para almacenar el numero de las lineas donde hay comentarios y el tipo de comentario que es.
begin
  TrimL;//Metodos de la clase TLOCounter que elimina los espacios en blanco a la derecha y a la izquierda de las lineas del codigo que se analiza.
  TrimR;

  for i := 0 to LinesOfCode.Count - 1 do
  begin
    Line := LinesOfCode.Strings[i];
    Token := Copy(Line,1,1);//token es el primer caracter de la linea a procesar.
    If Token = '{' then
    begin
      Trash := TTrashLine.Create;
      Trash.LineNumber := i;
      Trash.TrashType := 'MC';//MC = MultiComentario
      FTrashList.Add(Trash);

      if Line[length(Line)] <> '}' then
      begin
        k := i;
        repeat
          inc(k);
          Line := LinesOfCode.Strings[k];
          Token := Copy(Line,length(Line),1);
          if Token <> '}' then
          begin
            Trash := TTrashLine.Create;
            Trash.LineNumber := k;
            Trash.TrashType := 'MC';
            FTrashList.Add(Trash);//Lista con las lineas que no me interesa analizar.
          end;
        until (Token = '}') or (k = LinesOfCode.Count - 1);
        Trash := TTrashLine.Create;
        Trash.LineNumber := k;
        Trash.TrashType := 'MC';
        FTrashList.Add(Trash);
      end;
    end;
  end;
end;
.

Ahhh!!! y una duda.. por que cuando pongo FTrash.Destroy al terminar de trabajar con el me da error? Lo qeu sucede es que almacena en la lista cualquier valor y no es el que yo almaceno.

COn todo esto hecho, solo me quedara en el SynEdit las lineas que me interesa contar; las demas estaran marcadas con '*'. Y es ahora que viene la parte mas dura... hay Lineas De Codigo Logicas y Lineas de Codigo Fisicas. A que llamo Lineas De Codigo Logicas y Fisicas? Fisicas son todas las lineas... pero logicas son las que encierran una accion completa.

Por Ejemplo:

1 TClase(MiObjeto.MiFuncion[6].Propiedad1).Elemento[3].Text :=
2 'VALOR STRING';

En este caso, 1 y 2 son Lineas De Codigo Fisicas(pues existen), pero solo hay 1 Linea De Codigo Logica, que sera 1 + 2(la 1 o la 2 por separados no son Lineas de Codigo Logicas) ya que ambas encierran una orden completa. Este es solo un ejemplo hipotetico pues en la realidad hay muchas variantes de Lineas de Codigo Logicas.
Ahora la pregunta es... como hago un algoritmo para determinar si dado un String constituye una Linea de Codigo Logica o no? Y en caso de que no lo sea.. como puedo obtener la linea de codigo logica completa?

He pensado en esa solucion desde hace tiempo y no logro armar el algoritmo. Todo lo que se me ocurre da algun error por alguna parte y es mas tarbajo ponerle el parche que escribir un codigo eficiente de verdad.

Agradezco desde ya cualquier ayuda o sugerencia.
__________________
Muchas Gracias...
Responder Con Cita
  #2  
Antiguo 14-11-2005
Avatar de Lepe
[Lepe] Lepe is offline
Miembro Premium
 
Registrado: may 2003
Posts: 7.424
Poder: 28
Lepe Va por buen camino
Te doy la enhorabuena por lo bien que has redactado el problema, cuando es
dificil de explicarlo desde cero.

Trash
-------
Fijate que estas usando la variable Trash para crear 3 objetos distintos
Código Delphi [-]
   If Token = '{' then
    begin
      Trash := TTrashLine.Create;

tambien:
Código Delphi [-]
    until (Token = '}') or (k = LinesOfCode.Count - 1);
        Trash := TTrashLine.Create;
Código Delphi [-]
if Token <> '}' then
          begin
            Trash := TTrashLine.Create;
Esto significa que al crear el segundo, ya no podrás acceder al primero,
porque se pierde la referencia.

Deberías tener 3 variables declararadas, algo así como :
TrashOpen para hacer referencia al parentesis abierto
TrashClose idem para el cerrado.
TrashCount para el último (que no sé bien para qué se usa)

Lineas Físicas Vs Lógicas.
-------------------------
Podrías ver que la linea termine en punto y coma, es la única forma de ver
que una linea lógica ha acabado.


Por cierto, muy interesante el proyecto, ¿será Open Source?


saludos
__________________
Si usted entendió mi comentario, contácteme y gustosamente,
se lo volveré a explicar hasta que no lo entienda, Gracias.
Responder Con Cita
  #3  
Antiguo 14-11-2005
Avatar de Lepe
[Lepe] Lepe is offline
Miembro Premium
 
Registrado: may 2003
Posts: 7.424
Poder: 28
Lepe Va por buen camino
Se me olvidaba:

No uses nunca Objeto.Destroy, llama siempre a Objeto.Free, o incluso a FreeAndNil(Objeto) que es más "seguro" que el Free.

saludos
__________________
Si usted entendió mi comentario, contácteme y gustosamente,
se lo volveré a explicar hasta que no lo entienda, Gracias.
Responder Con Cita
  #4  
Antiguo 14-11-2005
bustio bustio is offline
Miembro
 
Registrado: oct 2003
Posts: 231
Poder: 21
bustio Va por buen camino
Mas para LEPE

Creo que no entendi muy bien lo que me tratas de decir con lo de destruir mi instancia de Trash... y aprovecho para comentarte un poco mas mi algoritmo de antes.

Código Delphi [-]
{Procedimiento que marca como malas las lineas que estan dentro de llaves,
pues son todas comentarios.}
procedure TLOCounter.DelMultipleComments;
var
  i, k:  Integer;
  Token: String;
  Line:  String;
  Trash: TTrashLine;//Clase para almacenar el numero de las lineas donde hay 
comentarios y el tipo de comentario que es.
begin
  TrimL;//Metodos de la clase TLOCounter que elimina los espacios en blanco a la derecha y a la izquierda de las lineas del codigo que se analiza.
  TrimR;

  for i := 0 to LinesOfCode.Count - 1 do
  begin
    Line := LinesOfCode.Strings[i];
    Token := Copy(Line,1,1);//token es el primer caracter de la linea a 
procesar.
    If Token = '{' then
    begin
      Trash := TTrashLine.Create;
      Trash.LineNumber := i;
      Trash.TrashType := 'MC';//MC = MultiComentario
      FTrashList.Add(Trash);
      //Si ya termine de utilizar la variable TRASH, por que al decirle 
TRASH.Destroy en la lista se almacenan valores distintos a los que le entre?
      if Line[length(Line)] <> '}' then
      begin
        k := i;
        repeat
        {si el primer caracter es "{" quiere decir que a partir de ahi lo que 
vienen son comentarios, por lo tanto, marco desde ese simbolo hasta que 
encuentre "}"}
          inc(k);//voy a la proxima linea para analizarla...
          Line := LinesOfCode.Strings[k];
          Token := Copy(Line,length(Line),1);
          if Token <> '}' then
          begin
            Trash := TTrashLine.Create;
            Trash.LineNumber := k;
            Trash.TrashType := 'MC';
            FTrashList.Add(Trash);//Lista con las lineas que no me interesa 
analizar.
          end;
        until (Token = '}') or (k = LinesOfCode.Count - 1);//repit hasta que el
ultimo carater de la linea analizada sea "}"
{pero como en ese momento termine el ciclo, no he almacenado la linea
ultima, y es por eso que fuera del ciclo la guardo.}
        Trash := TTrashLine.Create;
        Trash.LineNumber := k;
        Trash.TrashType := 'MC';
        FTrashList.Add(Trash);
      end;
    end;
  end;
end;

Hasta ahora eso es loq ue tengo hecho. Pero que sucede.. que una linea logica puede ser dividida en 2 o mas lineas fisicas cuando pasa esto:

1.- Se hace referencia a una instruccion muy larga(ejemplo antes citado, Por ejemplo esto:
TClase(MiObjeto.MiFuncion[6].Propiedad1).Elemento[3].Text := TObjeto.Propiedad1.Parametro[Objeto.Propiedad2.Index].Value;
)
2.- Sentencia IF con condiciones muy largas. La solucion que propongo es buscar la palabra THEN y contar todo lo anterior como una sola linea.

P.Ej:

If (TClase(OBjeto).propiedad1.parametro = valor) and (TClase(Objeto).propiedad2.valor = valor) and (Objeto.propiedad3 = 3) then....

3.- EN una linea hay parentesis o cualquiero otro signo que tiene igual uncion que el parentesis o "[" con muchos parametros dentro.

4.- un for, while o repeat... until con muchas condicionales y muy largo.

5. - declaracion de USes muy largas.

Estos son mi sproblemas!!!!
__________________
Muchas Gracias...
Responder Con Cita
  #5  
Antiguo 15-11-2005
Avatar de Lepe
[Lepe] Lepe is offline
Miembro Premium
 
Registrado: may 2003
Posts: 7.424
Poder: 28
Lepe Va por buen camino
En principio, lo que necesitas en un analizador léxico y un analizador
sintáctico. Esto se da en la asignatura de Compiladores.


El léxico se encarga de encontrar los tokens, y se los pasa al sintáctico para
que vea el orden en el que se ha detectado, en base a eso, y según las
expresiones regulares que se hayan proporcionado al sintáctico, se bifurca en
una ejecución u otra.

No sé que tal andas en estos temas, pero existe el Lex y Yacc para delphi
hecho en delphi; es open Source:

Cita:
Empezado por TP
TP Lex and Yacc { The Compiler Writer's Tools for Turbo Pascal
Version 4.1 User Manual
Albert Graf
Department of Musicinformatics
Johannes Gutenberg-University Mainz

http://www.musikwissenschaft.uni-mainz.de/~ag/tply
For information about the Free Pascal Compiler, please refer to:
http://www.freepascal.org
De hecho, es lo que se usa para estos casos.

Vamos con el tema del Trash.
No sé si ya has trabajado con punteros anteriormente, así que te digo las
bases.

En realidad Trash es una variable de 32 bits que es un puntero al objeto.
¿Que significa eso?, que Trash guarda la dirección de memoria donde ese
objeto reside, en este ejemplo el 100


Código:
 Trash                                              Memoria Ram
 -------                          Dirección    -----------------------------
|  100 |				0     |                            |
 -------				100   |aqui está el objeto Trash   |
					200   | y puede que ocupe mucha ram|
					300   |Otro objeto Trash empieza aq|
					      |y ocupa el mismo espacio |
					       -----------------------------
De hecho, al porner
Código Delphi [-]
   var Trash :TTrash;
Simplemente se reservan 32 bits en la memoria ram ( lo que está en color
azul).
Cuando haces Trash := TTrash.Create;
es cuando se reserva el espacio en memoria ram y se crea el objeto (lo
puesto en rojo). y la variable trash, se le asigna la direccion de la ram donde
está ese objeto, en este caso 100.

Cuando por segunda vez haces Trash := TTrash.Create;
Se crea el objeto que he puesto en verde, y a la variable Trash, (el azul) se
le pone 300 en este caso.

Bien, como ves la variable Trash (en azul) ahora tiene la dirección 300, por
tanto, el primero que se creó (el rojo), ya no puedes acceder a él. De ahora
en adelante Trash, hace referencia al verde, el rojo se queda en Memoria
Ram:
- sin poder acceder a él
- Sin poder liberar la memoria

Es decir, el objeto rojo se queda en el limbo.

Esto es lo que te ocurre con los 2 primeros Trash que creas, y solo puedes
acceder al último Trash, en concreto:
Código Delphi [-]
until (Token = '}') or (k = LinesOfCode.Count - 1);
        Trash := TTrashLine.Create;
        Trash.LineNumber := k;
        Trash.TrashType := 'MC';
        FTrashList.Add(Trash);
Los 2 Trash anteriores se quedan en el limbo.

Por eso decía que pongas 3 variables Trash.

Edito: Pon la implementación de FTrashList y como creas FTrashList y como la destruyes. Sin eso no puedo ayudarte.

Espero que se entienda.
__________________
Si usted entendió mi comentario, contácteme y gustosamente,
se lo volveré a explicar hasta que no lo entienda, Gracias.

Última edición por Lepe fecha: 15-11-2005 a las 15:30:34.
Responder Con Cita
  #6  
Antiguo 15-11-2005
Avatar de Lepe
[Lepe] Lepe is offline
Miembro Premium
 
Registrado: may 2003
Posts: 7.424
Poder: 28
Lepe Va por buen camino
Aunque lo que te he comentado es bueno, realmente no es así, es decir, no
se quedan ninguno en el Limbo, ninguno de los tres. ¿Por qué?

Porque una linea de código se me escapó a la vista:
Código Delphi [-]
   FTrashList.Add(Trash);

La variable Trash, si se queda apuntando al tercer Trash creado, pero los 2
anteriores, se añaden a FTrashList, realmente no se quedan en el limbo, pero
que conste, en esa rutina tienes 3 objetos Trash distintos. ¿hasta ahí de
acuerdo?

El error que tienes ahora, es que estas
Cita:
//Si ya termine de utilizar la variable TRASH, por que al decirle
TRASH.Destroy en la lista se almacenan valores distintos a los que le entre?
1º - No veo en ningún sitio que llames a Destroy.
2º- Como te dije, no debes usar Destroy, usa FreeAnnil(Trash).
3º- Has dejado de usar la variable Trash (el puntero de color azul), pero el
objeto realmente (color rojo o verde) sigue en memoria, y puedes acceder a
él a traves de FTrashList.
4º- No puedes ni debes usar Trash.Free, porque destruirías el objeto de la
memoria RAM. Si liberas el objeto con Trash.Free, y despues intentas acceder
desde FTrashList.Items[i] a ese mismo objeto, tendrás un Access Violation.

No conozco realmente la implementación de FTrashList; por el nombre parece
una lista de objetos, y si es así, en realidad es una lista de punteros.

Solo puedes destruir el Trash, desde un sólo sitio, yo lo haría al liberar el FTrashList.

Edito: Pon la implementación de FTrashList, cuando lo creas y como lo destruyes.

saludos
__________________
Si usted entendió mi comentario, contácteme y gustosamente,
se lo volveré a explicar hasta que no lo entienda, Gracias.

Última edición por Lepe fecha: 15-11-2005 a las 15:32:27.
Responder Con Cita
  #7  
Antiguo 14-11-2005
bustio bustio is offline
Miembro
 
Registrado: oct 2003
Posts: 231
Poder: 21
bustio Va por buen camino
Respondiendo a Lepe

Cita:
Empezado por Lepe

Por cierto, muy interesante el proyecto, ¿será Open Source?
Hola Lepe!!! Muchas gracias por tomarte el tiempo de leer mi duda. Este proyecto es una tarea de la Universidad; la que por cierto me trae loco pues hace mas de 15 dias qeu ando dandole cabeza y no logro dar con la solucion.
Mi solucion tiene varios errores, a mi entender; y el mas grave es qeu recorro el codigo fuente de la Unit que entro varias veces para identificar distintos elementos cada vez; lo que a mi entender seria mejor recorrerlo todo una vez y en esa sola vez analizar todos los problemas que aparezcan y listo.

Open source?? Pues como te digo.. es una tarea de la escuela. Pense en algo de eso mientras ideaba como solucionarlo y a mi siempre me ha parecido buena la idea de que el conocimiento sea de todos y no de quien pueda pagarlo. Si alguien me ayudara a hacer de esta herramienta algo provechoso, util y bien hecho pues no tengo ningun inconveniente en que sea Open Source.. todo lo contrario.. solo que en el estado actual de desarrollo en que lo tengo no creo que sirva para nada!!!

El problema creo que es algo valido.... recuerden todos aquellos que utilizamos la gestion de software mas o menos frecuentemente el enorme trabajo que nos ahorrariamos de solo entrarle un .DPR a este programa y que el solito vaya adentrandose por dentro de cada una de las Units que conforman el proyecto contando las lineas de codigo que hemos puesto dentro de el... y que eso ayude a la hora de estimar costos, esfuerzos y todas las cosas para las qeu hace falta conocer las LOC y esas cosas.... a mi me parece muy interesante. Incluso, tal vez hasta pudiera hacerse algo para qeu no solo cuente las lineas de codigo .PAS, sino las generadas por java, C#, Kiliz o cualquier otro... Me parece algo muy interesante... pero por desgracia me encuentro varado en ese problema de las lineas de codigo logicas y fisicas.

Lo que me dices del ";" no seria una buena opcion.. recuerda que en el Interface de las Form autogeneradas por el IDE de Delphi cuando hay un evento, por ejemplo, que lleve muchos parametros(OnKeyPress, por ejemplo) el IDE divide la linea en uno de los ";" de los parametros del evento.. ya por ahi nuestro programa daria un BUG pues en realidad no es una linea logica completa.

Lo mismo sucede con los IF..Then..Else.. cuando las condiciones son muy randes.. o con otras estructuras.
En lo particular, ahora estoy tratando de resolver el caso de que sea una sola linea con muchos parametros, y bueno.. tal y como se ve en el ejemplo de mi pregunta. Eso por ahora.

Espero que pronto surjan otros interesados en esta idea y que entre todos, podamos hacer algo util para todos.... LIBRE de costos y para el uso de todos.. y en especial, darle un merito a este nuestro Foro.
__________________
Muchas Gracias...
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 16:22:44.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi