sábado, 21 de octubre de 2017

HISTORIA LENGUAJE PROGRAMACION PL/I

HISTORIA LENGUAJE PL/I


PL/I, acronimo de Programing Language I, fue propuesto por IBM hacia el ano 1970 para responder simultaneamente a las necesidades de las aplicaciones cientificas y comerciales.

Este lenguaje tenia muchas de las caracteristicas que mas adelante adoptaria el lenguaje C y algunas versiones de lenguaje C++. 

Por desgracia, IBM registra el nombre del lenguaje como forma de mantener control sobre su desarrollo,   lo que disuadio a otras empresas de dar ese nombre a sus instalaciones.

Sus innovaciones incluian:
  1.  Multiprocesamiento.
  2. Recursion.
  3. Estructuras de control modernas.
  4. Facilidades para la puesta a punto. 
  5. Asignacion dinamica de espacio para estructuras de datos.
  6. Procedimientos genericos.
 PL/I fue probablemente el primer lenguaje comercial cuyo compilador estaba escrito en el lenguaje que compilaba.

Su desarrollo fue llevado a cabo en los laboratorios HURSLEY, ubicados en el Reino Unido y propiedad de IBM.

Lenguajes de programacion

EJEMPLO 1: LEER Y ESCRIBIR PL/I

Ejemplo 1: Leer de SYSIN y escribir en SYSPRINT (pl/i).

En PL/I hay que diferenciar entre los programas que acceden a DB2 y los que no, pues se compilarán de maneras diferentes y se ejecutarán de forma diferente.
Enpezaremos por ver el programa sin DB2 más sencillo:

El programa más sencillo es aquel que recibe datos por SYSIN del JCL y los muestra por SYSPRINT.

JCL:

//PROG1 EXEC PGM=PRUEBA1
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
JOSE LOPEZ VAZQUEZ  HUGO CASILLAS DIAZ
JAVIER CARBONERO    PACO GONZALEZ
JESUS IGLESIAS      RICARDO MONTES
/*


donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSPRINT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSPRINT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa

Fijaos en las posiciones de los nombres de la SYSIN, para entender bien el programa:

----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
JOSE LOPEZ VAZQUEZ  HUGO CASILLAS DIAZ
JAVIER CARBONERO    PACO GONZALEZ
JESUS IGLESIAS      RICARDO MONTES


Como veis, hemos dividido la información en 2 trozos de 20 posiciones, cada uno con un nombre. Además hemos escrito varias líneas de SYSIN, pues como vimos en el artículo de Ficheros en PL/I, podemos recuperar varias líneas (en COBOL esto no es posible).

PROGRAMA:

PLIPRU1: PROCEDURE OPTIONS (MAIN);
/* PROGRAMA QUE LEE DE SYSIN(GET EDIT)*/
/* Y ESCRIBE EN SYSPRINT (PUT EDIT) */
/*DEFINIMOS SYSIN*/
DCL SYSIN FILE STREAM INPUT;
/*DEFINIMOS SYSPRINT*/
DCL SYSPRINT FILE PRINT;
/*DECLARACION DE VARIABLES DEL PROGRAMA*/
DCL 1 TABLA_NOMBRES,
      2 NOMBRE(6) CHAR(20);
DCL 1 LINEA_SYSIN,
      2 NOMBRE_SYSIN1 CHAR(20),
      2 NOMBRE_SYSIN2 CHAR(20);
DCL CONT_NOMBRE DEC FIXED (2);
DCL FIN CHAR(1) INIT ('0');
/*CONTROL FIN SYSIN*/
ON ENDFILE(SYSIN) BEGIN;
   FIN = '1';
END;
/*PROCESO DEL PROGRAMA*/
GET FILE(SYSIN) EDIT(LINEA_SYSIN)(A(40));

CONT_NOMBRE = 1;

DO WHILE (FIN = '0');
   CALL MOVER_A_TABLA;

   GET FILE(SYSIN) EDIT(LINEA_SYSIN)(A(40));
END;

CALL PINTAR_NOMBRES;

/*PARRAFO QUE GUARDA LOS NOMBRES RECUPERADOS EN TABLA_NOMBRES*/
MOVER_A_TABLA: PROC;
NOMBRE(CONT_NOMBRE) = NOMBRE_SYSIN1;

CONT_NOMBRE = CONT_NOMBRE + 1;

NOMBRE(CONT_NOMBRE) = NOMBRE_SYSIN2;

CONT_NOMBRE = CONT_NOMBRE + 1;

END MOVER_A_TABLA;

/*PARRAFO QUE ESCRIBE EN EL SYSPRINT LOS NOMBRES DE LA SYSIN*/
PINTAR_NOMBRES: PROC;
CONT_NOMBRE = 1;

DO WHILE CONT_NOMBRE < 7 

   PUT EDIT('NOMBRE: ', NOMBRE(CONT_NOMBRE)) (SKIP,A,A); 

CONT_NOMBRE = CONT_NOMBRE + 1; 
END;

END PINTAR_NOMBRES; 
END PLIPRU1;


En el programa podemos ver las siguientes sentencias:
ON ENDFILE(SYSIN): controla el final de la SYSIN (como el fin fichero pero para la SYSIN).
GET FILE: lee del fichero STREAM SYSIN.
DO WHILE: es un bucle. Las instrucciones de dentro del bucle se harán MIENTRAS se cumpla la condición indicada.
CALL: llamada a párrafo
PUT EDIT: escribe en el fichero STREAM SYSPRINT.
SKIP: indica salto de línea.
END PLIPRU1: indica fin del programa.


Descripción del programa:
Al inicio, declararemos los ficheros y las variables que utilizaremos a lo largo del programa.
En el proceso principal, leemos el primer registro de la SYSIN y ponemos a 1 el contador CONT_NOMBRE y montamos un bucle para recuperar todos los registros.

En el párrafo MOVER_A_TABLA guardamos los 2 nombres recuperados de la SYSIN en diferentes filas de la tabla TABLA_NOMBRES. Para ello indicamos entre paréntesis el número de la fila donde se va a guardar. El máximo de filas de la tabla es 6 (es el número indicado entre paréntesis al lado del campo NOMBRE).

En el párrafo PINTAR_NOMBRES escribiremos en el SYSPRINT todos los nombres guardados en nuestra TABLA_NOMBRES.
Informamos el campo CONT_NOMBRE con un 1, pues vamos a utilizar los campos de la tabla interna:
Para utilizar un campo que pertenezca a una tabla interna, debemos acompañar el campo de un "índice" entre paréntesis. De tal forma que indiquemos a que fila de la tabla nos estamos refiriendo. Por ejemplo, NOMBRE(1) sería el primer nombre guardado (JOSE LOPEZ VAZQUEZ).
Como queremos displayar todas las filas de la tabla, haremos que el índice sea una variable que va aumentando.

A continuación montamos un bucle (DO WHILE) con la condición CONT_NOMBRE menor de 7, pues el máximo es 6. La primera vez CONT_NOMBRE valdrá 1, y necesitamos que el bucle se repita 6 veces:
CONT_NOMBRE = 1: NOMBRE(1) = JOSE LOPEZ VAZQUEZ
CONT_NOMBRE = 2: NOMBRE(2) = HUGO CASILLAS DIAZ
CONT_NOMBRE = 3: NOMBRE(3) = JAVIER CARBONERO
CONT_NOMBRE = 4: NOMBRE(4) = PACO GONZALEZ
CONT_NOMBRE = 5: NOMBRE(5) = JESUS IGLESIAS
CONT_NOMBRE = 6: NOMBRE(6) = RICARDO MONTES
CONT_NOMBRE = 7: salimos del bucle porque ya no se cumple CONT_NOMBRE menor que 7


NOTA: el índice de una tabla interna NUNCA puede ser cero, pues no existe la fila cero. Si no informásemos CONT_NOMBRE con 1, el PUT de NOMBRE(0) nos daría un estupendo OFFSET.

RESULTADO:
NOMBRE: JOSE LOPEZ VAZQUEZ
NOMBRE: HUGO CASILLAS DIAZ
NOMBRE: JAVIER CARBONERO
NOMBRE: PACO GONZALEZ
NOMBRE: JESUS IGLESIAS
NOMBRE: RICARDO MONTES


He de decir que no me ha dado tiempo a ejecutar este ejemplo, en cuanto tenga un rato lo pruebo y corrijo si es necesario.

FICHEROS EN PL/I


Seguimos adentrándonos en el mundo del PL/I. En este artículo veremos los tipos de ficheros con los que se puede trabajar y como defenirlos. Los usos los iremos viendo en futuros ejemplos.

FICHEROS RECORD
Como vimos en artículos anteriores, un programa PL/I no tiene divisiones como el COBOL, por lo que podremos definir el fichero en cualquier punto del programa. Para poner orden, lo definiremos al principio del procedimiento principal.
Los ficheros de tipo RECORD deben declararse explícitamente.

PRUEBA1: PROCEDURE OPTIONS(MAIN);

DCL FICHERO1 FILE INPUT;


Donde FICHERO1 será el nombre lógico del fichero, que se corresponderá con el nombre que se le haya dado en el JOB.
FILE nos indica que se trata de un fichero.
INPUT es el atributo, y nos indica que se trata de un fichero de entrada. Otros posibles atributos son:
OUTPUT: fichero de salida.
UPDATE: fichero de entrada y salida (para modificaciones; DISP=MOD).
SEQUENTIAL: fichero secuencial.
DIRECT: acceso directo.
INDEXED: fichero indexado.
KEYED: para acceder al fichero se utilizarán claves.
ENVIROMENT: describe el conjunto de datos externos asociados al fichero (ej: ENV(VSAM)).
EXTERNAL: el fichero es un elemento externo al programa.
RECORD: fichero tipo record.


Apertura/Cierre
Forma explícita:
OPEN FILE (FICHERO1); / CLOSE FILE (FICHERO1);


Podemos añadir atributos a continuación del nombre, siempre que no se contradigan con lo declarado en la definición.

Forma implícita:
Si se ejecuta una instrucción de entrada/salida sin haber abierto el fichero con la sentencia OPEN, el sistema abrirá el fichero de forma implícita y se le asignarán los atributos que tenga en la declaración.
Si no hacemoe el CLOSE del fichero, el sistema lo cerrará al final de la ejecución.
OJO! En los ficheros de salida, importante hacer OPEN. Si por casuística no se escribe sobre ellos, el sistema intentará cerrarlos al final de la ejecución, y al no haberlos abierto previamente dará un error.

Lectura
Dependerá del tipo de acceso:
SEQUENTIAL(secuencial)
READ FILE (FICHERO1) INTO (REG_ENTRADA);


Donde REG_ENTRADA es la variable donde se guardará la información del registro.

DIRECT(directo)
READ FILE (FICHERO1) INTO (REG_ENTRADA) KEY(CLAVE);


Donde KEY indica que se accederá por clave, y CLAVE es una variable donde estará el valor de la clave por la que vamos a acceder.

Escritura
Dependerá del tipo de acceso.
SEQUENTIAL(secuencial)
WRITE FILE (FICHERO2) FROM (REG_SALIDA);


Donde REG_SALIDA es la variable que contiene la información que escribiremos en el fichero.

DIRECT(directo)
WRITE FILE (FICHERO2) FROM (REG_SALIDA) KEYFROM (CLAVE);


Donde KEYFROM indica que se accederá por clave, y la varriable CLAVE llevará el valor de la clave.

Modificación/Sobreescritura
Dependerá del tipo de acceso.
SEQUENTIAL(secuencial)
REWRITE FILE (FICHERO3) FROM (REG_ENTSAL);


Donde REWRITE indica sobreescritura.

DIRECT(directo)
REWRITE FILE (FICHERO3) FROM (REG_ENTSAL) KEY(CLAVE);


Borrado
Dependerá del tipo de acceso.
SEQUENTIAL(secuencial)
Todo borrado debe estar precedido por una operación de lectura.
DELETE FILE (FICHERO4);


Donde DELETE indica que debe borrarse el último registro leído del fichero especificado.

DIRECT(directo)
DELETE FILE (FICHERO4) KEY (CLAVE);


Donde DELETE indica que debe borrarse el registro del fichero y clave especificados.

FICHEROS STREAM
El acceso siempre es de tipo SEQUENTIAL(secuencial). Los datos se guardan como una cadena de caracteres.
Existen 3 tipos de declaración para estos ficheros:
1. Explícita.
DCL SYSIN FILE STREAM INPUT;
DCL SYSPRINT FILE PRINT;


Donde STREAM indica que el fichero se procesará por flujo de caracteres.
PRINT indica que el fichero es de tipo impresora.

Estos dos ejemplos se refieren a la SYSIN de un JOB y a la cola del SYSPRINT. En la SYSIN escribiremos los datos que va a leer el programa, y en ek SYSPRINT escribiremos datos de salida.
El resto de posibles atributos son los mismos que para los ficheros RECORD.

2. Implícita.
Si omitimos el nombre del fichero en una operación de lectura (READ), el sistema supondrá que se trata de un fichero STREAM INPUT (SYSIN). En caso de la escritura (WRITE) se asumirá STREAM OUTPUT (SYSPRINT).

3. Por defecto.
Si se realiza una operación de entrada/salida contra un fichero que no tenga atributos se considerará STREAM por defecto.

Apertura/Cierre
Es el mismo caso que para los ficheros RECORD.

Lectura
GET FILE (SYSIN) LIST/DATA/EDIT;


Donde GET indica orden de lectura.
SYSIN indica que se trata del fichero del sistema. Si no se indica nombre asume SYSIN.
LIST/DATA/EDIT: son las opciones de lectura que se pueden indicar.
LIST: requiere que los datos de entrada estén en formato dato1,...,datoN. Entre cada dato debe haber una coma o un espacio en blanco. Cada dato se leerá sobre la variable indicada.
SYSIN DD *
datos1 datos2 datos3
/*

GET FILE LIST(variable_1, variable_2, variable_3);


DATA: requiere que la entrada esté en el formato var_1 = dato_1. Entre und ato y la variable siguiente debe haber una coma o un espacio en blanco. Cada dato se copiará sobre su variable correspondiente.
SYSIN DD *
var1=datos1 var2=datos2 var3=datos3
/*

GET FILE DATA (variable_1,variable_2,variable_3);


EDIT: los datos están dispuestos igual que en la opción LIST, pero podemos añadir el formato en que los leemos.
SYSIN DD *
campo1 campo2 campo3
/*

GET FILE EDIT (variable1,variable2,variable3) (A,A,A);


Los formatos que se pueden indicar son:
SKIP(n): salta n líneas antes de leer.
COL(n): se posiciona en la columna n antes de leer.
A: datos alfanuméricos.
P'formato PIC': datos numéricos. Ej P'999'.
X(n): salta n posiciones antes de leer.

Vamos a fijarnos en la opción SKIP.
Pensad que estamos leyendo de la SYSIN, y con SKIP le estamos indicando que salte a la siguiente línea de la SYSIN:
SYSIN DD *
campo1
campo2
campo3
/*


Esto en COBOL no sería posible, pues la SYSIN tiene una única línea que se recupera mediante un ACCEPT.
Sin embargo en PL/I podemos introducir mucha más información a través de la SYSIN.

Escritura
PUT FILE (SYSPRINT) LIST/DATA/EDIT;


Donde PUT indica orden de escritura.
Si no se indica nombre del fichero el sistema asume SYSPRINT.
LIST/DATA/EDIT son las mismas opciones que para el GET, pero referido a los datos que vamos a escribir. En EDIT se ñade la opción de "PAGE" para hacer salto de página al escribir.

"Los PUT" en PL/I son el equivalente a los DISPLAYs en COBOL. Cuando necesitemos "displayar" alguna variable para controles de errores, buscar un fallo en el programa, etc. lo haremos con un PUT y la información saldrá en el SYSPRINT en lugar de salir en la SYSOUT.
Tanto para programa ONLINE como BATCH, podremos ver los "PUTs" en el SYSPRINT.


En próximos artículos veremos ejemplos concretos del uso de estos ficheros. Hasta entonces, cualquier duda preguntad sin problema : )

PROCESO PROGRAMACION PL/I

Proceso de un programa PL/I

El proceso del programa comienza en donde nosotros queramos, aunque por poner un poco de orden, podemos decir que empazará después de que acabemos de definir todas las variables que vayamos a necesitar.

INFORMANDO VARIABLES

En PL/I la manera de informar variables es "asignándoles" un valor:
DCL CAMPO1 CHAR(3) INIT ('123');
DCL CAMPO2 CHAR(3);

CAMPO2 = CAMPO1;


Ahora CAMPO2 valdrá '123'.

Vamos a ver algunos ejemplos de asignaciones denominadas "simples", es decir, la asignación se hace entre variables del mismo tipo (numéricos con numéricos, alfanuméricos con alfanuméricos).

Ejemplo 1: Campos numéricos.
DCL CAMPO_NUM1 DEC FIXED (3,2) INIT (0);
DCL CAMPO_NUM2 DEC FIXED (6,3) INIT (0);

CAMPO_NUM1 = 1.23;
CAMPO_NUM2 = CAMPO_NUM1;


Ahora CAMPO_NUM2 valdrá 001.230.

CAMPO_NUM2 = 456.789;
CAMPO_NUM1 = CAMPO_NUM2;


Ahora CAMPO_NUM1 valdrá 6.78.
Como podéis ver, mantiene la posición del punto decimal.

En las asginaciones de campos numéricos también podremos guardar operaciones artiméticas:

CAMPO_NUM1 = CAMPO_NUM2/2 + CAMPO_NUM3 * (CAMPO_NUM4 - CAMPO_NUM5) ** 2;


Y se aplican las mismas reglas de prioridades que en matemáticas (el doble asterisco es la potencia).

Ejemplo 2: campos alfanuméricos.
DCL ALFANUM1 CHAR (5)  INIT(' ');
DCL ALFANUM2 CHAR (11) INIT (' ');

ALFANUM1 = 'COBOL';
ALFANUM2 = ALFANUM1;


Ahora ALFANUM2 valdrá 'COBOL       '.

ALFANUM2 = 'CONSULTORIO';
ALFANUM1 = ALFANUM2;


Ahora ALFANUM1 valdrá 'CONSU'.

Usando el operador de concatenación || podemos guardar varios campos en una variable:
DCL CAMPO1 CHAR(10) INIT ('COMO MOLA ');
DCL CAMPO2 CHAR(20) INIT ('EL CONSULTORIO COBOL');
DCL CAMPO3 CHAR(30);

CAMPO3 = CAMPO1||CAMPO2;


Ahora CAMPO3 contendrá 'COMO MOLA EL CONSULTORIO COBOL' :D

Ejemplo 3: matrices (tablas internas).
DCL TABLA(3) CHAR(1);


Para utilizar un elemento de una matriz tendremos que indicar el índice del elemento entre paréntesis.

TABLA(1) = 'A';
TABLA(2) = 'B';
TABLA(3) = 'C';


Ahora el elemento 1 de la tabla valdrá 'A', el elemento 2 valdrá 'B', y el elemento 3 valdrá 'C'.

Para tablas de 2 dimensiones se indicarán los dos índices entre paréntesis.
DCL TABLA_DOBLE(2,3) CHAR(1);

Y en las asignaciones también se indicarán los dos índices:
TABLA_DOBLE(1,1) = 'A';


INICIALIZANDO VARIABLES

La inicialización de variables en PL/I se hace en el momento de declararlas, con la sentencia INIT que vimos en el artículo del "Esquema de un programa pl/i".

Vamos a ver algunos ejemplos:
DCL CAMPO_ALFANUM CHAR(10)  INIT(' '); Se rellena con espacios. Para campos alfanuméricos el valor se indica entre comillas simples ''.
DCL CAMPO_ALFANUM CHAR(100) INIT((100)'-'); Se rellena con 100 guiones.
DCL CAMPO_NUM PIC 10(9)        INIT(0); Se rellena con ceros. Para campos numéricos, el valor se indica sin comillas.
DCL CAMPO_DEC DEC FIXED (10,3) INIT(0); Se rellena con ceros.

DCL TABLA1 DEC FIXED(2,2) INIT(1,2,3,4);
Asigna valores a cada elemento de la tabla:
TABLA1(1,1) = 1
TABLA1(1,2) = 2
TABLA1(2,1) = 3
TABLA1(2,2) = 4

DCL TABLA1 DEC FIXED(2,2) INIT(4(0));
Asigna ceros a los 4 elementos de la tabla.



CONDICIONES

Para expresar condiciones en PL/I se utilizará el IF/ELSE de toda la vida, pero con sus particularidades.

Ejemplo 1: IF.
IF CAMPO1 = CAMPO2
   THEN CAMPO1 = 2;


Al poner el ; finalizamos la sentencia IF (no hay que cerrarla con un END-IF como en cobol).

Ejemplo 2: IF/ELSE.
IF CAMPO1 = CAMPO2
   THEN CAMPO1 = 2;
ELSE CAMPO1 = 1;


La condición finaliza con el ; de la parte del ELSE.
Puede darse el caso de que no queramos realizar ninguna acción para la condición del IF, sino sólo en el caso contrario (ELSE):

IF CAMPO1 = CAMPO2 THEN;
ELSE CAMPO1 = 1;


Ejemplo 3: IF/ELSE con DO.
IF CAMPO1 = CAMPO2
   THEN DO;
          CAMPO1 = 2;
          CAMPO2 = 1;
        END;
ELSE DO;
       CAMPO1 = 1;
       CAMPO2 = 2;
     END;


Cuando dentro de un IF o un ELSE hay más de una expresión, todas ellas se incluirán dentro de la sentencia DO/END.

Los operadores que se utilizan en las condiciones serán:
= : igual a
¬= : distinto
& : y
| : o
< : menor que
> : mayor que
<= : menor o igual
>= : mayor o igual
¬< : no menor que
¬> : no mayor que

Otra manera de incluir una condición es con la sentencia SELECT. Viene a ser el equivalente al EVALUATE de Cobol, pero hay que tener cuidado porque no funcionan exactamente igual.

Si queremos poner como condición el valor que tome una variable:
SELECT(CAMPO_1);
  WHEN(1);
  WHEN(2)   CAMPO_2 = 2;
  OTHERWISE CAMPO_2 = 0;
END;


Si queremos validar que se cumple una condición:
SELEC;
  WHEN(CAMPO_1 = CAMPO_2);
  WHEN(CAMPO_1 < CAMPO_2) CAMPO_3 = 2;
  OTHERWISE CAMPO_3 = 0;
END;


Fijaos en el primer WHEN de los dos ejemplos. Cuando un WHEN finaliza con ; significa que para ese caso no haremos nada. Sería el equivalente a poner un CONTINUE en Cobol.
MUY IMPORTANTE no confundir con el caso del EVALUATE, donde al poner dos WHEN seguidos significa que el código que venga a continuación se realizará para los dos WHEN:

EVALUATE WX-CAMPO1
  WHEN 1
  WHEN 2
       DISPLAY 'CAMPO1 ES 1 o 2'
  WHEN OTHER
       CONTINUE
END-EVALUATE


El display se mostrará para WX-CAMPO1 = 1 y WX-CAMPO1 = 2.

En el caso del SELECT, se indicarán los dos valores en el mismo WHEN:

SELEC CAMPO_1;
  WHEN(1,2) PUT EDIT ('CAMPO_1 ES 1 o 2') (A);
  OTHERWISE;
END;


Para terminar decir que en caso de que dentro de un WHEN queramos escribir más de una instrucción, tendremos que usar el DO/END como en el caso del IF.

BUCLES

En PL/I los bucles se codifican con la sentencia DO/END. Existen varias opciones para acompañar al DO:

DO WHILE
En este caso las sentencias entre el DO y el END se ejecutarán "mientras que" se cumpla una condición, es decir, mientras esa condición sea cierta.

DO WHILE CAMPO1 ¬= ' ';
sentencias a ejecutar;
END;


Si tuviésemos más de una condición, deberá ir entre paréntesis.
DO WHILE (CAMPO1 ¬= ' ' & CAMPO2 = 1);
sentencias a ejecutar;
END;


DO UNTIL
En este caso las sentencias entre el DO y el END se ejecutarán "hasta que" se cumpla la condición especificada.

DO UNTIL CAMPO_ANO = 2012;
sentencias a ejecutar;
END;


Si tuviésemos más de una condición, deberá ir entre paréntesis.

DO + TO
En este caso las sentencias entre el DO y el END se ejecutarán hasta que se cumpla la condición especificada.

DO INDICE = 1 TO 10;
sentencias a ejecutar;
END;


Las sentencias a ejecutar se harán 10 veces (desde INDICE = 1 hasta 10, de 1 en 1).

Para retorcerlo un poco más podemos usar la sentencia BY:
DO INDICE = 1 TO 10 BY 2
sentencias a ejecutar;
END;


Las sentencias a ejecutar se harán 5 veces (desde INDICE = 1 hasta 10, de 2 en 2).

También se puede combinar el DO/TO con la opción WHILE:
DO INDICE = 1 TO 10 WHILE CAMPO1 = ' ';
sentencias a ejecutar;
END;

En este caso, además de controlar el valor de INDICE, la expresión del WHILE debe ser cierta.

QUE ES PL/I

¿Qué es PL/I?

PL/I (Programming Language 1) es un lenguaje de programación de IBM (al igual que COBOL) que os podréis encontrar en muchos entornos HOST. Incluso si trabajáis en COBOL, es probable que alguna aplicación tenga código en PL/I y seguro que tenéis acceso al compilador.

Veamos las principales diferencias con COBOL:
  • No existe área A y área B. Existe un único área desde la columna 2 a la 72.
  • No hay divisiones ni secciones.
  • Acepta caracteres alfabéticos de la A a la Z (solo en mayúsuculas). Acepta los caracteres '@', '$', '#' ó 'Ñ' (según la instalación). Los números del 0 al 9. No acepta el guión '-' pero sí el guión bajo '_'.
  • Operadores lógicos: negación (¬), igualdad (=), conjunción (&), disyunción (|), mayor que (>), menor que (<).

  • No es necesario declarar las variables (aunque recomendable).

  • En pl/i no existen las palabras reservadas, sólo palabras CLAVE pero que pueden utilizarse como variables.

  • El final de una sentencia se termina con punto y coma (;) en lugar de con un punto (.), y además debe finalizarse cada sentencia, no vale con poner un ; al final de un procedimiento (como en COBOL que poníamos un . al final de párrafo).


Estas son a grandes rasgos las principales diferencias entre los dos lenguajes. En próximos artículos iremos viendo como codificar un programa PL/I, su estructura, como informar variables, como hacer bucles, etc.

ESQUEMA DE PROGRAMACION PL/I

ESQUEMA DE UN PROGRAMA PL/I

Un programa PL/I no tiene una estructura "fija", es decir, el programa compilará aunque no sigas un esquema determinado. Pero para facilitar la comprensión del código, siempre es mejor seguir algunas pautas : )

INICIO DEL PROGRAMA


Todo programa principal empezará con la sentencia:


PRUEBA1: PROCEDURE OPTIONS(MAIN);

Donde:


PRUEBA1 es el identificador del programa. No es necesario que coincida con el nombre externo del programa. Tendrá una longitud máxima de 7 caracteres.


PROCEDURE o PROC es la palabra clave que le indica al compilador que el programa es un procedimiento.


OPTIONS(MAIN): son las palabras clave que indican al compilador que se trata de un programa principal.


Los comentarios empezarán con /* y terminarán con */ y podrán codificarse en cualquier parte del código. No hay que colocar el inicio en ninguna columna en concreto.

PROCESO DEL PROGRAMA.


En el proceso del programa incluiremos tanto la declaración de variables, como llamadas a otros procedimientos, como la funcionalidad del programa en sí.


Para las declaraciones utilizaremos la sentencia DECLARE o DCL para abreviar.

Declaración de funciones del sistema: 


DCL (DATE,TIME) BUILTIN;

Donde DATE y TIME serían la fecha y hora del sistema respectivamente.

Declaración de ficheros:   


DCL SYSPRINT FILE PRINT;
DCL SYSIN    FILE STREAM INPUT;
DCL SALIDA   FILE STREAM OUTPUT;
DCL SALIDA   FILE RECORD OUTPUT;
DCL REG_SALIDA CHAR(100);

Donde:
FILE indica que se trata de un fichero.
STREAM indica que los datos se guardarán como una cadena de caracteres. El acceso siempre será secuencial.
INPUT/OUTPUT indica que es un fichero de entrada/salida respectivamente.
RECORD indica que los datos se guardarán en un formato especifico (en nuestro caso REG_SALIDA). Existen diferentes tipos de acceso que iremos viendo en futuros ejemplos.

Declaración de variables:

 
Para entenderlo mejor, veamos un cuadro con la equivalencia entre declaraciones COBOL y PL/I:

01 ALFAN PIC X(N) VALUE SPACES.DCL ALFAN CHAR (N) INIT ' ';
01 NUMER PIC 9(N) VALUE ZEROS.DCL NUMER PIC '(N)9' INIT (0);
01 COMPR PIC S9(N)V9(M) COMP-3.DCL COMPR DEC FIXED (N+M,M);
01 HEXA1 PIC S9(4) COMP.DCL HEXA1 BIN FIXED (15) INIT(0);
01 HEXA2 PIC S9(9) COMP.DCL HEXA2 BIN FIXED (31) INIT (0);
01 ESTRUCTURA.DCL 1 ESTRUCTURA,
   05 ALFANUM PIC X.      5 ALFANUM CHAR,
   05 NUMERICO PIC 9(2).      5 NUMERICO PIC '(2)9',
   05 COM PIC 9(7)V9(2) COMP-3.      5 COM DEC FIXED (9,2),
   05 BINHEXA PIC S9(4) COMP.      5 BINHEXA BIN FIXED (15),
   05 BINHEXA2 PIC S9(9) COMP.      5 BINHEXA2 BIN FIXED (31);
01 EDITADO PIC ZZZ9,99.DCL EDITADO PIC 'ZZZ9V,99';
01 SWITCH PIC X VALUE 'S'.DCL BOOLEAN BIT;
   88 VERDAD    VALUE 'S'.DCL VERDAD  BIT INIT('1'B);
   88 FALSO     VALUE 'N'.DCL FALSO   BIT INIT('0'B);
01 TABLA PIC X OCCURS 3 TIMES.DCL TABLA(3) CHAR(1);


Donde:
CHAR indica campo alfanumérico.
INIT indica el valor que toma la variable (equivale a nuestro VALUE).
PIC (N)9 indica campo numérico.
DEC FIXED indica campo comprimido (equivale a COMP-3). En los campos con decimales hay que tener en cuenta que si en cobol indicábamos 9(N)V9(M), en pl/i se indica (N+M,M).
BIN FIXED indica campo binario (hexadecimal).
BIT indica variable booleana que tomará los valores de verdadera '1'B (SET campo a TRUE) y falsa '0'B (SET campo a FALSE).
Para definir tablas se indicará el número de filas entre paréntesis; como el ejemplo TABLA(3). Si fuese una tabla de dos dimensiones escribiríamos primero el número de filas y después el número de comumnas:
DCL TABLA_DOBLE(2,3) CHAR(3);

En PL/I no estamos obligados a declarar las variables, aunque es recomendable. Veamos que pasa cuando no declaramos variables, o no las declaramos "del todo":

Si sólo declaramos la base:
DEC = DEC FLOAT (6)
BIN = BIN FLOAT (21)

Si sólo declaramos la precisión:
FIXED = DEC FIXED (5,0)
FLOAT = DEC FLOAT (6)

Si declaramos base + precisión:
BIN FIXED = BIN FIXED (15,0)
DEC FIXED = DEC FIXED (5,0)
BIN FLOAT = BIN FLOAT (21)
DEC FLOAT = DEC FLOAT (6)

Sin declarar nada de nada:
COMIENZA POR: I-N                          = BIN FIXED (15,0)
COMIENZA POR: A-H O-Z Y CARACT. ESPECIALES = DEC FLOAT(6)


CALCULO DE LONGITUDES

Numéricos y alfanuméricos. 


Lo que venga indicado en el PIC (N)9 o en el CHAR(N). 


DCL ALFANUM CHAR(5);
DCL NUMERICO PIC (5)9;

longitud = 5

Comprimidos.


Si tenemos un campo DEC FIXED (N,M), la longitud será N+1/2. Si el resultado tiene decimales redondeamos hacia arriba. El máximo permitido es N = 15.
DCL COMPRIMIDO DEC FIXED (7,2);

longitud = 7+1/2 = 4

Binarios  


Si tenemos un campo BIN FIXED (N,M), el número de bytes que se reservan en memoria para cada campo es:
2 posiciones para N entre 1 y 15.
4 posiciones para N ente 16 y 31(máximo permitido).

FINAL DEL PROGRAMA

 
Para finalizar el programa escribiremos la sentencia:


END PRUEBA1;

Donde PRUEBA1 es el nombre del proceso principal que habíamos definido al inicio del programa.