martes, 25 de diciembre de 2012

JMagick

Hola amigos, como he visto que existen un poco de problemas a la hora de instalar y usar ImageMagick para java, vamos a ver una sencilla guía para instalarlo en eclipse.

Lo primero que tenemos que hacer es bajar los DLL de ImageMagick, recordemos que en un principio está diseñado para tratamiento de imágenes en C, y lo único que lograremos con JMagick es poder usar ciertas funciones de esas librerías, en lenguaje Java.

¿Por qué usar JMagick? Porque el formato que puede leer ImageMagick en imágenes es muy basto, sirve para PNG, JPG, incluso PGM, todo lo pasa a una matriz de píxeles con el espacio de color RGB, en caso de que sean blanco y negro, los 3 canales tienen la misma intensidad.

Lo que tenemos que descargar es lo siguiente:


El paquete de DLL's
http://downloads.jmagick.org/6.3.9/ImageMagick-6.3.9-0-Q16-windows-dll.exe

La interfaz de JMagick
http://downloads.jmagick.org/6.3.9/jmagick-win-6.3.9-Q16.zip

Ojo que tienen que checar que la versión de DLL corresponda con la versión de JMagick, la última que encontré fue la 6.3.9

Ahora, en eclipse, simplemente tenemos que agregar en el build path el jar que viene de JMagick.

Esto es:
En el proyecto actual, click derecho y luego propiedades.
Cliqueamos en Java Build Path y damos add external JAR.
Buscamos el jar que bajamos de Jmagick, de preferencia pongan el jar en el classpath de su JDK.

El mío es por ejemplo:

C:\Program Files (x86)\Java\jre7\lib

Ahí adentro aventamos el archivo de jmagick.jar

Ahora, el DLL jmagick.dll del comprimido se manda a la carpeta de binarios, en mi caso:

C:\Program Files (x86)\Java\jre7\bin

Ahora, después de agregarlo en el proyecto, le vamos a picar, a la flechita que saldrá de despliegue, teniendo seleccionado el jmagick.jar en la misma ventana de propiedades sale una barrita que dice Native Libraries Location, en esa damos doble clic y donde está el boton external folder, vamos a buscar donde descomprimimos el contenido de los DLL de ImageMagick, en mi caso lo aventé en C:/Windows/System32/ para que no haya broncas.

Finalmente damos aceptar.

Listo ya tenemos funcionando el JMagick.
Pronto les dejaré una pequeña clase que transforma una imagen leída de imagemagick a un bufferedImage de java para que lo puedan usar en etiquetas o lo que les dé en gana.

miércoles, 19 de diciembre de 2012

Continuación de la Quinta Fase...

Continuamos otra vez con el proyecto del ensamblador después de jugar un poco con el Jmagick...

Pues bien, para la quinta fase requerimos saber las direcciones de memoria de las variables, así como de las instrucciones de código, calcular desplazamientos, etc.

Nos valdremos de la clase TablaSimbolos y de su método getPropiedad para obtener estos datos.

Para calcular los desplazamientos requerimos saber el tamaño de variable, es decir DB o DW y los elementos declarados, para las cadenas se multiplica la longitud de la cadena [sin contar las comillas dobles o simples que pudiera tener].

Agregado a esto, tenemos la nueva restricción de validar las etiquetas, que deberán ser definidas antes de ser usadas, esto con el objetivo de agregarlas a la tabla de símbolos.

Así pues, la tabla contendrá un nuevo tipo de dato que será "etiqueta".

Quedando los tipos como "constante" "variable" y "etiqueta".

Además, aumentamos que se puede especificar una cantidad para dup en hexadecimal, binario o decimal.

Todas estas modificaciones aumentan la complejidad del analizador de instrucciones, ya que ahora no sólo necesitaremos saber si la instrucción es correcta, sino qué tipo de instrucción se analizó y varios datos que son de interés.

Crearemos una nueva clase llamada Instruccion, que contendrá métodos para:
Saber si es válida o inválida.
actualizar estado en caso de requerir buscar una variable o etiqueta.
Obtener el tamaño de sus  elementos.
Obtener los elementos.
Obtener el elemento en espera.

Esto no es más que un modelo para las instrucciones y sus elementos son seteados por el controlador cuando no han podido ser seteados en el constructor, como es el caso de tener que buscar una variable o una etiqueta en la tabla de símbolos.

Sólo resta realizar la sexta y última fase que consiste en codificar las instrucciones...

viernes, 7 de diciembre de 2012

Cuarta y quinta fase

Pues como ya no he escrito avances, este será uno corto que tratará de explicar cómo hemos ido avanzando.

Pues bien, hasta ahora tengo una clase que se llama Separador simple que no hace más que separar líneas en un vector de elementos.

Ya que tenemos elementos separados, los podemos pasar por la clase Identificador, esta clase tiene métodos para identificar si un string dado es un registro, símbolo, etiqueta, etc...

Otra de las clases es TamanoDato, la cual se encarga de decir si un string tiene un tamaño DB o DW, los strings pueden incluir cadenas de texto debidamente formateadas, enteros, caracteres, etc.

Por otro lado, tenemos una clase llamada tabla de símbolos, la cual se encarga de guardar todas las variables que estén debidamente codificadas, obviamente ya han sido comprobadas por el identificador en orden y sintaxis. Esta clase, tiene métodos para acceder a los atributos de cada una de las variables, y además es capaz de generar un vector de vectores, simulando una matriz.

Esto es:

Vector.get(0) nos devolverá un vector con los atributos de la primera variable.

Para obtener por ejemplo del nombre de la tercera variable tendríamos...

VectorSimbolos().get(2).get(0);

Y así... todo esto está oculto en el método getPropiedad, el cual funciona enviándole un nombre de variable en lugar de un index, porque es más cómodo decir "dame el tamaño de la variable pancho" que decir, dame el tamaño de la variable 4...

En base a la clase de TablaSimbolos tenemos a la VentanaTablaSimbolos, que no es nada más que la vista del modelo contenido en la clase TablaSimbolos, la ventana simplemente recibe un vector de vectores creado a través del método  crearVectorSimbolos() de tablaSimbolos.

Esto es debido al uso de DefaultTableModel que requiere vectores de filas para trabajar.

Finalmente tenemos el comprobador de instrucciones del cual hablamos en la publicación pasada, que simplemente le pregunta a las demás clases si los elementos recibidos son correctos.
Algunas comprobaciones que hace son con elementos de etiquetas, registros, variables en memoria y su tamaño, y además constantes numéricas.

Pues bien, creo que va quedando bastante bien el ensamblador, al final del semestre subiré todo el código fuente para que puedan entender mejor las ideas que quedan volando.

sábado, 1 de diciembre de 2012

Tercera fase del ensamblador

En la tercera fase tenemos que analizar el segmento de datos.
Las especificaciones son las siguientes:


La Fase 3 del proyecto cubre la fase del análisis sintáctico y semántico de los segmentos de datos y pila, es decir, analizar los elementos que integran las líneas del programa fuente que se encuentran en las definiciones de los segmentos de datos y pila para verificar que son los correctos y que están en el orden correcto, conforme a la siguiente sintaxis.

data segment (pseudoinstrucción que identifica el inicio de la definición del segmento de datos)
símbolo db constante caracter
símbolo db constante numérica byte
símbolo db constante numérica dup (constante caracter byte)
símbolo db constante numérica dup (constante numérica byte)
símbolo dw constante numérica palabra
símbolo dw constante numérica palabra dup(constante numérica palabra)
símbolo equ constante numérica palabra
ends (pseudoinstrucción que identifica el fin de la definición de un segmento)

stack segment (pseudoinstrucción que identifica el inicio de la definición del segmento de pila)
dw constante numérica palabra dup(constante numérica palabra)
ends (pseudoinstrucción que identifica el fin de la definición de un segmento)

Esta fase del proyecto debe cubrir la característica de desplegar la línea del programa fuente analizada y frente a ella el resultado de la verificación con la frase Correcta o Incorrecta, según sea el caso.

En esta fase del proyecto se debe comenzar a llenar la tabla de símbolos (considerar el llenado de los cambos símbolo, tipo, valor y tamaño).

Esta fase es relativamente fácil...

Lo único que tenemos que hacer es:

Leer cada una de las líneas del archivo.
Analizar la secuencia arriba mostrada, y determinar si es correcta o incorrecta.

En un principio parecía que las fases no iban a servir demasiado... Pero no es así, bien podemos darle uso.

¿Recuerdan el Separador Simple y nuestro mapa de Strings? Pues simplemente vamos a utilizar los elementos que regrese la función del separador [en mi caso un vector de Strings] y compararlos con el mapa de strings.

Vamos a plantear una bandera que determine cuándo es que se abre y cuándo se cierra un segmento dado, ya sabemos que no puede haber más de 1 definición de cada uno así que podemos controlarla con un simple entero.

0 es que no ha habido ninguna definición, 1 que ya se inicio el segmento de datos, 2 que ya se cerró, 3 que inicio segmento de pila, 4 que se cerró, 5 que inició segmento de código y 6 que se cerró.

Esto corresponde al ciclo del data segment....

En pseudocódigo...

Si línea leída es "data segment"
    estado=1
Si no
    seguir leyendo líneas.

Si estado=1 y lineaLeida != vacío.
    si primer elemento es símbolo.
        si segundo elemento es db
            si tercer elemento es un número entero o una serie de caracteres...
                imprime linea es correcta.
                si es un entero y después existe un dup(...)
                    si el dup está bien formulado.
                        escribe linea es correcta
                    si no
                        imprime linea es incorrecta
            si no
                imprime linea es incorrecta
        si es dw
            si la definicion del elemento es correcta
                imprime linea es correcta.
            si no
                imprime linea es incorrecta.
        otro
            imprime linea es incorrecta

Verificar si es db implica comprobar tanto que el número de veces que se desea duplicar un elemento es válido, como los elementos que se desean duplicar.

    VAR5 db 5 dup('(')
Es correcta

    VAR5 db a dup('(')
Es incorrecta

Bien, el diseño de esto es simple, vamos a crear una clase que reciba un elemento y nos devuelva su tamaño...

Es decir, si enviamos "Ya voy amigos!" nos devuelva DB, y si le enviamos 50000 nos devuelva DW...
Con esto podemos comprobar dos cosas, la primera es que cuando queramos hacer un DUP se verifique que el tamaño del interior corresponda a la definición de la variable, y además, que podamos usarla con los operadores que nos asignarán en las siguientes fases del proyecto.

Nos podemos ayudar de los comprobadores de tamaño hexadecimal y decimal, ya que el ejemplo que puse antes era con signo o sin signo, podemos cambiar ese parámetro a 1 para db o 2 para dw... dependiendo cómo definimos nuestras variables estáticas en la clase del identificador, y saber si corresponde al tamaño.

jueves, 22 de noviembre de 2012

Fase 2... Análisis léxico del ensamblador

Pues bien, después de un largo rato de no hacer nada más que jugar League of Legends, decidí empezar con la fase 2 del proyecto que consiste en:

Identificar todos y cada uno de los elementos del programa fuente como elementos validos del lenguaje (pseudoinstrucción, instrucción, registro, símbolo, constante [numérica decimal, numérica hexadecimal, numérica binaria, caracter]). Esta fase del proyecto debe cubrir la característica de desplegar frente a cada elemento de la lista resultado de la fase 1, el tipo de elemento de que se trate. De no identificar a un elemento como válido, se debe desplegar e mensaje "Elemento no identificado" o "Elemento invalido".

Los subconjuntos de doce instrucciones a identificar por cada equipo son los siguientes. Recuerden que es restricción del proyecto el que únicamente identifiquen las instrucciones asignadas y se tomará como símbolos las instrucciones no asignadas.


EquipoInstrucciones a identificar
1AAALAHFPOPFSTOSBINTPUSHCMPTESTJAEJLJOLOOPZ
2AADCMPSBPUSHASTOSWLDSDECMOVXCHGJBJLEJNGJA
3AAMCMPSWLAHFXLATBLEADIVORXORJBEJMPJNGEJP
4AASCWDLODSBPUSHFLESIDIVRCLADCJCJNAJNLJS
5CBWDAALODSWRETDECIMULRORADDJCXZJNAEJNLEJZ
6CLCDASMOVSBSCASWDIVMULSARANDJEJNBJNOLOOP
7CLDHLTMOVSWSTCIDIVNEGADCCMPJGJNBEJNPLOOPE
8CLIINTONOPSTDIMULNOTADDSHLJGEJNCJNSLOOPNE
9CMCIRETPOPASTIINCPOPANDSUBJAJNEJNZLOOPNZ



Pues bien, vamos a suponer que somos el equipo 7, porque dicen que el 7 trae buena suerte.

Esta fase no es nada complicada y sólo tenemos que analizar el elemento que nos ha devuelto la fase 1.

Para esta fase, podemos hacer un HashMap de strings.
Esta clase de java nos permite manejar conjuntos de objetos como si fuera un vector, pero en lugar de índices numéricos, podemos usar palabras.

La idea es hacer una lista como por ejemplo:

CLD:INSTRUCCIÓN
HLT:INSTRUCCIÓN
MOVSW:INSTRUCCIÓN
AX:REGISTRO
BX:REGISTRO
...

Y así, por lo que cuando busquemos por una instrucción válida, nos regresará el string instrucción o registro según corresponda.

Si el mapa no devuelve ninguna de estas cosas, aplicaremos la expresión regular para los símbolos...


if(linea.matches("[a-zA-Z][\\w]*")){


Algo así... donde tamano es el tamaño que queremos aceptar como máximo para nuestros símbolos, no es posible poner variables en las expresiones regulares, por lo tanto o lo hardcodean, o hacen un if con un length del string y una variable int estática...


HashMap<String, Integer> tipoElementos = new HashMap<String, Integer>();
tipoElementos.put("CLD",Identificador.INSTRUCCION);

tipoElementos.put("AX",Identificador.REGISTRO);

Así quedaría el mapa, obviamente con más elementos...


Finalmente reconocer las constantes numéricas y demás, es sencillo, dejo uno para identificar números hexadecimales con o sin signo acá...


private static boolean isHex(String elemento, boolean signo) {
elemento=elemento.replace("h", "");
elemento=elemento.replace("H", "");

try{
if(signo==true){
if( (Integer.parseInt(elemento,16) > -129) && (Integer.parseInt(elemento,16)<128) ){
return true;
}
}
else{
if( (Integer.parseInt(elemento,16) > -1) && (Integer.parseInt(elemento,16)<256) ){
return true;
}
}
}
catch(Exception e){
return false;
}
return false;
}

jueves, 15 de noviembre de 2012

Expresiones regulares en java

En la materia de ensambladores nos piden armar un ensamblador de juguete [aunque "funcional" en realidad no sirve para nada], pero bueno, el chiste es que se realiza en fases.

La fase 1 contempla simplemente separar los elementos de un archivo de texto y ordenarlos en una lista.


En el moodle dice textualmente:




CONSIDERACIONES:
  1. Los comentarios que comienzan con punto y coma no se deben considerar, por lo que se deben eliminar.
  2. Los separadores de elementos son: el espacio, los dos puntos y la coma.
  3. Los siguientes elementos son compuestos: data segment, code segment, stack segment, dup(xxx), [xxx], "xxxx", 'xxxx'. Estos no se deben separar y deben mostrarse de esa manera en la lista que despliega el programa.
Así pues, la tarea es relativamente sencilla.

Si nos ponemos analizar cada carácter, nos vamos a meter en muchos problemas al darnos cuenta que las restricciones de que abra y cierre corchetes, comillas dobles o simples, que si la cadena tiene un espacio en el medio pero empieza por data, code o stack debemos juntarla con lo que sigue... Que si los punto y coma se deben eliminar... Pues bueno, ya me entienden los que hayan tratado de hacer algo parecido.

Así pues me di a la tarea de buscar cómo hacerlo más sencillo.
Basado en el curso de compiladores y las utilidades de herramientas como flex y bison que usan expresiones regulares, pues seguramente debería haber una forma de usarlas en un lenguaje como java.

Las expresiones regulares no son más que formas de "buscar" o decidir si una cadena cumple con una restricción que nosotros querramos... Asumiré que para cuando necesiten esta información ya sabrán cómo funcionan.estas cosas.

Con esta idea, podemos realizar expresiones regulares que nos agrupen elementos, que nos separen las palabras y que reemplacen los comentarios.

Lo primero es separar los elementos "compuestos"

Lo que necesitamos es que si encontramos "data" o code, o stack, siga un espacio [ya sea tabulador o espacio] y después segment, y si se lo desea, pues evitar que haya algo más que no sean espacios.

Pondré entre paréntesis las cosas para separarlas, esto no es código, sino mi forma de decir "esto y luego aquello" (esto)(aquello). Espero darme a entender.

Y para decir esto o aquello usaré la barra vertical.
esto|aquello

Sería algo como

(data|code|stack)(segment)

Obviamente en lenguaje ensamblador no importa si son mayúsculas o minúsculas, debe poder aceptarse igual.

Bien, pues empecemos con lo bueno.

En java, las expresiones regulares les llaman REGEX, así pueden buscarlo en google por mayor información.

Comencemos por decir qué es un patrón.

Un patrón no es más que una expresión regular que podemos usar con otras instrucciones.
Su declaración es la siguiente:

Pattern patronOro = Pattern.compile("dame oro");

Los patrones siempre van acompañados de un "matcher" que es el encargado de aplicar la expresión regular a algo.

Matcher matcherOro = patronOro.matcher(linea);

El método para crearlo recibe como parámetro un string, en este caso se llama línea, que contiene el texto a evaluar.

Ahora, para probar que está bien, hagamos una cadena y también un pequeño if...

String linea = "dame oro";

if(matcherOro.matches()){
   System.out.println("Te doy oro :D");
}

Verán que imprime "Te doy oro" en la consola.
Cambien el contenido de la cadena y verán que no entra.

Ahora, ¿qué pasa si queremos que ignore mayúsculas o minúsculas?
Lo de menos es usar el método toLowerCase() para pasarlo todo a minúsculas y luego cuadrarlo, pero las expresiones regulares tienen modificadores para eso.

El modificador para ignorar mayus, es (?i), se identifican por estar encerrados en paréntesis, empezar con un signo de interrogación y luego una letra, existen otros, pero en esta publicación sólo usaremos este.

Cambiemos el patrón a este...
Pattern patronOro = Pattern.compile("(?i)dame oro");

Cambien el string de prueba y verán que sigue cumpliendo.

Hasta ahora no nos sirve de nada esto, podemos hacerlo con un compareToIgnoreCase() verdad?

Hagámoslo más interesante, qué tal que estamos en una partida de Age of Empires, y no sólo necesitamos oro, sino también alimento porque somos persas...

Los Regex tienen operadores lógicos como el or y el and... etc, igualito que los if, usan la barra |, el & etc...

Cambiemos el patrón a:

Pattern patronOro = Pattern.compile("(?i)(dame oro)|(dame piedra)");

Como verán  encerramos las cadenas que queremos comprobar entre paréntesis, esto sirve para dos cosas... Para separar una cadena de otra, y para usar el concepto de "grupos".

Va, ya puedo decir dame oro y dame piedra, en mayus, minus, mezclado etc...
¡Pero mi compañero siempre me da oro! ¿Si le pido piedra, cómo puedo hacer que él me de piedra y no oro? [Aunque con el oro podría comprarla... Pero ese no es el punto]

Pues bien, mencionando los grupos, podemos saber qué cosa cumple con una condición haciendo uso de ellos.

La clase Matcher tiene otro método que se llama group()

Este método nos regresa el último grupo que cumplió con la condición...
Modificando el código anterior un poco, escribamos en el if, la nueva instrucción..

if(matcherOro.matches()){
System.out.println(matcherOro.group());
}

Como vemos, aparece en pantalla el string que cumplió con la condición, cambien en el string línea a su gusto y verán que aparece lo mismo.

Bueno, ya sabemos identificar grupos e ignorar mayúsculas, y podemos actuar en consecuencia.

Esto de los grupos nos sirve para poder "guardar" alguna parte de interés de la cadena, ya sea para reemplazar algo, para copiarlo... No sé...

Bueno, pero hasta ahora, las cadenas de prueba están casi hardcodeadas... ¡Yo quiero que sean flexibles!
Vamos a suponer que en el nuevo Age of Empires, no nos sabemos los recursos existentes... puede ser cualquier nombre... Conozcamos otra forma de decir "or"

En los REGEX de java, podemos agrupar cosas entre corchetes para decir "OR"

Cambiemos el patrón a...

Pattern patronOro = Pattern.compile("(?i)(dame) [abc]");

Nótese que hay un espacio entre el dame y los corchetes... Ese espacio es de hecho el espacio que queremos entre frases... "dame" "a" o "dame" "b"

Los corchetes nos dicen "a" o "b" o "c"...

Cambiemos la cadena de prueba a ...
String linea = "DaMe a";
Luego a...
String linea = "DaMe B";
Y así... noten que cuando ponen: 
String linea = "DaMe AB";

No será cierta la expresión regular... Esto es porque dijimos a, b ó c, pero sólo 1 vez alguna de ellas...
Para definir cuántas veces queremos que aparezcan, tenemos varias formas, la primera, son los cuantificadores.

Estos son según la API de Java 1.4.2:

Donde X es una parte de la expresión regular.


Entonces...


Pattern patronOro = Pattern.compile("(?i)(dame) [abc]*");

Nos permitirá cualquier cantidad de a,b y c juntas...

desde la cadena vacía hasta abcbacbacbacbbbbbcccaaa... cualquiera cantidad.


Pattern patronOro = Pattern.compile("(?i)(dame) [abc]{1,2}");

Al menos 1 vez y máximo 2... es decir, puede ser "dAme ab" o "dame bc" o "DaMe ac" pero no "DAME abc" porque ya son 3 letras...



Pattern patronOro = Pattern.compile("(?i)(dame) (abc)*");

Nos aceptará "dame abcabcabcabc" y "dame " [con el espacio...] Pero no "dame abcbca"


Podemos también concatenar cosas...



Pattern patronOro = Pattern.compile("(?i)(dame) [a][b][c]");

Es lo mismo que:



Pattern patronOro = Pattern.compile("(?i)(dame) abc");

Porque dice "a" seguido de "b" seguido de "c"

Pero si ponemos



Pattern patronOro = Pattern.compile("(?i)(dame)[ab]c");
Estamos diciendo "a" o "b" seguido de c



Supongamos que el nuevo recurso del age no se sabe qué letras tiene, por lo que tenemos que poner todo el abecedario...
¡Imagien poner de la a a la z!


Pattern patronOro = Pattern.compile("(?i)(dame) [abcdefghijklmnopqrstuvwxyz]*");

Bueno, ya no lo imagen, ya lo puse...
Eso está muy feo verdad?
Bueno, pues los REGEX nos permiten poner rangos...


La declaración anterior es igual a esta...
Pattern patronOro = Pattern.compile("(?i)(dame) [a-z]");

Y podemos incluir otras cosas, suponiendo que no nos gusta el  modificador (?i)

Podríamos escribir...


Pattern patronOro = Pattern.compile("(dame) [a-zA-Z]*");

Esto es... de "a" a "z" minus ó de "A" a "Z" mayus... [Obviamente el dame sí tendría que estar en minúsculas]

Esto también aplica a números, por lo que podemos poner 0-9
Java tiene un pequeño problema a la hora de poner
[a-z0-9] ya que una z seguida de un 0 no sería válido... Cosas del lenguaje.
La solución es poner un guión bajo..
[a-z_0-9]

Aún así, es muy complicado poner todos esos rangos, ¿no?

Java tiene otras formas de escribirlos, que son las siguientes:

[Me da flojera traducirlo, creo que se entiende con mínimos conocimientos de inglés...]


.
Punto, cualquier carácter incluyendo signos, enter, fin de archivo...
\dA digit: [0-9]
\D A non-digit: [^0-9]
\sA whitespace character: [ \t\n\x0B\f\r]
\SA non-whitespace character: [^\s]
\wA word character: [a-zA-Z_0-9]
\WA non-word character: [^\w]



La línea...
Pattern patronOro = Pattern.compile("(dame) [a-zA-Z_0-9]*");

Es equivalente a:



Pattern patronOro = Pattern.compile("(dame) \\w*");
Y si queremos quitar el espacio hardcodeado...

Pattern patronOro = Pattern.compile("(dame)\\s\\w*");
Eso es la palabra "dame" seguida de un espacio [ya sea tab o espacio normal] y una serie de letras o números.

Nótese que agregamos una barra antes de la barra w, esto es para decirle a java que queremos usar un conjunto predefinido...


También podemos negar cosas y hacer intersecciones,  etc...




[abc] a, b, or c (simple class)
[^abc] Any character except a, b, or c (negation)
[a-zA-Z] a through z or A through Z, inclusive (range)
[a-d[m-p]] a through d, or m through p: [a-dm-p] (union)
[a-z&&[def]] d, e, or f (intersection)
[a-z&&[^bc]] a through z, except for b and c: [ad-z](subtraction)
[a-z&&[^m-p]] a through z, and not m through p: [a-lq-z]subtraction)


Y... ¿cómo le hacemos si queremos usar signos?
Pues les ponemos doble barra,  como a los conjuntos.

Por ejemplo

String linea = "DaMe (abc)";

Para poder verificar eso...
Pattern patronOro = Pattern.compile("(?i)(dame) \\(abc\\)");

Y para String linea = "DaMe (a" seguido de  b o c y cerrar paréntesis...
Por ejemplo algo del tipo: "dame (ab)"
"dame (ac)"

Pattern patronOro = Pattern.compile("(?i)(dame) \\(a[bc]\\)");

Existen también conjuntos para especificar signos y caracteres...

Usando POSIX...

POSIX character classes (US-ASCII only)
\p{Lower}A lower-case alphabetic character: [a-z]
\p{Upper}An upper-case alphabetic character:[A-Z]
\p{ASCII}All ASCII:[\x00-\x7F]
\p{Alpha}An alphabetic character:[\p{Lower}\p{Upper}]
\p{Digit}A decimal digit: [0-9]
\p{Alnum}An alphanumeric character:[\p{Alpha}\p{Digit}]
\p{Punct}Punctuation: One of !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
\p{Graph}A visible character: [\p{Alnum}\p{Punct}]
\p{Print}A printable character: [\p{Graph}]
\p{Blank}A space or a tab: [ \t]
\p{Cntrl}A control character: [\x00-\x1F\x7F]
\p{XDigit}A hexadecimal digit: [0-9a-fA-F]
\p{Space}A whitespace character: [ \t\n\x0B\f\r]


En fin, pueden jugar con ello las veces que quieran...



Como última nota, hay que recordar que los REGEX de java son de tipo greedy, es decir, aplica la expresión regular a toda la cadena.

Por lo tanto...

Si ponemos
".*d"

Nos dará cualquier cadena que termine con d, aunque d esté incluído en el punto que es "cualquier cosa".

Lo mismo si ponemos
"a\\w*a"

Esto es todas las cadenas que empiecen y terminen con a, sin importar que a está incluído en las letras del conjunto \w...

Ahora ya podemos aplicarlo a nuestro proyecto sabiendo qué es un grupo y cómo identificamos un elemento u otro.

Para el ejemplo de los segmentos...

La expresión regular vendría a ser...

Pattern patronSegmentos = Pattern.compile("(?i)(data segment)|(code segment)|(stack segment)");


Recordemos que no podemos poner [data segment][code segment][stack segment]

Porque esto nos daría a escoger en el primer caso "d" o "a" o "t" o "s" o "g"... etc...

Tampoco podemos hacer

[data segment|code segment]

Así mismo, agrupamos las cadenas con paréntesis, que nos servirá para 2 cosas:

Agrupar, y saber cuál de las palabras se leyó...

String linea = "data segment";

Pattern patronSegmentos = Pattern.compile("(?i)(data segment)|(code segment)|(stack segment)");
Matcher matcherSegmentos = patronSegmentos.matcher(linea);

if(matcherSegmentos.matches()){

System.out.println(matcherSegmentos.group());
}

Si vemos, la palabra segment se repite en todas las declaraciones por lo que podemos reescribirlo así:




Pattern patronSegmentos = Pattern.compile("(?i)((data)|(code)|(stack))\\ssegment");

Hay que poner atención que estamos agrupando data code y stack en un paréntesis externo, esto es porque de poner esto... [sin los paréntesis exteriores]

Pattern patronSegmentos = Pattern.compile("(?i)((data)|(code)|(stack))\\ssegment");

Java tomaría como que (stack)\\ssegment es una declaración completa, por lo que sólo funcionaría en el caso de stack segment.


Bueno, ya tenemos la parte de identificar los elementos "compuestos".


Supongamos que queremos ahora identificar los elementos que estén entre corchetes, porque si hay espacios dentro de ellos no debemos separarlos...


Por ejemplo [ax,bx], debemos mostrarlo así y no

[ax
bx]

La expresión regular para identificar esto es:



Pattern patronCorchetes = Pattern.compile("\\s*\\[([\\w\\s\\p{Punct}]*)\\]");

Lo que quiere decir... Si viene uno o más espacios en blanco, seguido de un corchete que abre, agrupoa lo que está dentro de los corchetes... y lo de dentro de los corchetes debe cumplir que sea, o un caracter, o un espacio, o un signo de puntuación una o más veces...

La parte  \\p{Punct}   es la expresión para decir "cualquier signo de puntuación" está un poco más arriba donde vimos los conjuntos de caracteres, dígitos etc...

Pues bien, ya sólo queda ponernos a chambear y separar los elementos que nos piden...

Espero que les haya sido de ayuda esto.

Para complicarlo un poco más...

La siguiente expresión valida cosas entre corchetes, o, entre comillas dobles o entre comillas simples...


Pattern.compile("[\\w\\s]*((\\[[\\w\\s\\p{Punct}]*\\])|(\'[\\w\\s\\p{Punct}]*\')|(\"[\\w\\s\\p{Punct}]*\"))[\\w\\s]*");


Esto es:
Cualquier cantidad de espacios y letras hasta encontrar un corchete que abre o comillas simples o comillas dobles con letras, espacios o signos dentro... seguido de letras o espacios...

Como pueden notar, se repiten los patrones del medio de espacios en blanco, puntuación y palabras... ¿Por qué no juntar los | con corchetes? 



Pattern.compile("[\\w\\s]*([\\[\'\"][\\w\\s\\p{Punct}]*[\\[\'\"])[\\w\\s]*");



Bueno, esto nos hace más pequeño el código, pero trae consigo otro problema, qué tal que el usuario nos da algo como esto:

"press any key...$\'

Que comienza con dobles y termina con simples...
Pues bien, el código pequeño lo aceptaria, por lo que optamos por dejar el anterior que aunque repetía patrones, nos aseguraba que empezara y terminara con el mismo carácter.

Y para identificar una cadena antes de los elementos " o ' o [

Pattern inicio = Pattern.compile("([\\w\\s]*)((\\[|\"|\\').*)");
matcherInicio.matches();
String nueva = matcherInicio.group(1);

Eso hará el trabajo.


¡Saludos y nos vemos en la siguiente situación problemática!


El comienzo

Muchas veces pasa que aprendemos algo que necesitamos, y cuando ya está terminada la tarea, terminamos olvidando el proceso de la solución del problema, o las instrucciones para realizar ese programa que dejaron para la mañana siguiente, o los errores que presentaba un circuito que funcionaba 1 hora antes de la revisión y después ya no.

Así pues, este blog servirá para recordarme [aunque espero que también le sirva a alguien más] cómo es que se hacen  las cosas que he necesitado realizar en la carrera de Ingeniería en Computación, plan F2, en la Facultad de Ingeniería de la Universidad Autónoma del Estado de México, desde algoritmos, utilidades en diferentes lenguajes, librerías, juegos, circuitos, hasta cualquier otra vacilada que nos pidan...

Con estra breve introducción, y esperando que no me dé flojera seguir con este blog, paso a desearme la mejor de las suertes ;D

Saludos a todos los que por error pudieran caer en este pequeño espacio de Internet, disfruten su estancia.