I.2.3. Procesos Occam I.2.3.1. Construcciones del lenguaje Hemos considerado que los procesos van a ser de una de las tres clases primitivas: procesos de asignaci¢n, entradas y salidas. Se puede combinar un n£mero de procesos para formar una construcci¢n. Una construcci¢n tambi‚n es un proceso y puede utilizarse en otras construcciones. Cada proceso componente de una construcci¢n se escribe dos espacios a la derecha del margen izquierdo, de esta forma se indica que es parte de la construcci¢n. En nuestra notaci¢n un proceso puede escribirse como: process = SKIP | STOP | action | construction SKIP comienza un proceso, no hace nada y termina. STOP comienza un proceso y nunca termina. SKIP puede considerarse como un proceso que no hace nada, de esta forma puede utilizarse en partes de un programa que no deben estar escritas pero que por la situaci¢n en que se encuentran no puede permitirse que no haya nada. De esta forma se utiliza en muchas construcciones para satisfacer ciertas condiciones. STOP representa un proceso que no hace nada. Es importante darse cuenta de que un proceso parado no acaba nunca. Por ejemplo, un proceso puede llegar a pararse esperando por una entrada que nunca se producir  y de aqu¡, el proceso se bloquea. Es muy importante que sea correcta la terminaci¢n de procesos concurrentes. La diferencia entre los procesos SKIP y STOP puede ilustrarse de la siguiente forma: SEQ Keyboard ? char SKIP screen ! char SEQ Keyboard ? char STOP screen ! char En el primer caso la secuencia es tal que se produce una entrada a la variable llamada char v¡a el canal Keyboard. Cuando termina este proceso, se ejecuta el proceso SKIP, que no desarrolla acciones. A este le sigue la salida del car cter char a trav‚s de la pantalla. En el segundo caso, la secuencia empieza de nuevo con la entrada de la variable char. Luego se ejecuta el proceso STOP que no termina, por lo que la sentencia "screen ! char" no se ejecutar  nunca. Se pueden combinar varios procesos en un proceso m s largo especificando como se van a desarrollar los procesos. Por ejemplo, la especificaci¢n podr¡a ser tal que se necesitara que los procesos se ejecutaran uno detr s de otro, esto es, en secuencia, o que todos se ejecutaran al mismo tiempo, en paralelo. La definici¢n de construcci¢n puede representarse de la siguiente forma: construction = sequence | parallel | conditional | loop | alternation | selection Las clases de construcciones vienen dadas por: - SEQ secuencial - PAR paralela - IF condicional - WHILE bucle - ALT alternativa I.2.3.1.1. SEQ secuencial La podemos representar por: sequence = SEQ {process} La construcci¢n SEQ viene seguida de uno o m s procesos que vienen identados por dos espacios. Por ejemplo: SEQ P1 P2 P3 ...... significa que los procesos P1, P2, P3, ... se ejecutan uno despu‚s de otro. Cada proceso empieza s¢lo cuando el anterior ha terminado, en este caso el orden de los procesos es P1, P2, P3, ... La secuencia acaba cuando el £ltimo proceso ha terminado. Una secuencia sin componentes actuar  como un SKIP. I.2.3.1.2. PAR paralela Una construcci¢n paralela puede representarse por: parallel = PAR {process} La construcci¢n PAR viene seguida de cero o m s procesos identados a dos espacios. Por ejemplo: PAR P1 P2 P3 ..... significa que los procesos P1, P2, P3, ... empiezan simult neamente y evolucionan concurrentemente, por lo que deben ser independientes entre s¡. La construcci¢n termina s¢lo despu‚s de que todos los procesos componentes hayan acabado, pero no hay orden fijo en el que los procesos acaban. Una construcci¢n paralela sin componentes actuar  como un SKIP. Hay varias restricciones importantes en la construcci¢n paralela relacionadas con la independencia de los procesos que la constituyen: - En un proceso no pueden usarse variables que han sido cambiadas por asignaci¢n o por entradas en otros procesos de la misma construcci¢n. - En la misma construcci¢n no se puede usar un canal para la entrada en m s de un proceso componente y no se puede usar un canal para salida en m s de un proceso componente. Una construcci¢n paralela es inv lida a menos que se satisfagan estas condiciones de no interferencia. La sucesi¢n: PAR c.1 ? x c.2 ! y permite que las comunicaciones de entrada en el canal "c.1" de la variable "x" y la salida en el canal "c.2" de la variable "y", tengan lugar al mismo tiempo, esto es, concurrentemente. Este paralelismo est  altamente optimizado de cara a minimizar el proceso de planificaci¢n. I.2.3.1.3. PLACED PAR Una vez un programa se ha desarrollado y verificado entonces cada uno de los procesos componentes puede ejecutarse en un procesador individual. Una variante de la construcci¢n PAR llamada PLACED PAR, se usa para asignar un proceso que va a ejecutarse en un determinado procesador. La sintaxis para esta construcci¢n es: parallel = PLACED PAR {placement} | PLACED PAR replicator placement placement = PROCESSOR expression process Por ejemplo: PLACED PAR PROCESSOR 1 P1 PROCESSOR 2 P2 har  que los procesos P1 y P2 se sit£en en los procesadores enumerados como 1 y 2 respectivamente. I.2.3.1.4. IF condicional Una construcci¢n condicional puede representarse como: conditional = IF {choice} choice = guarded.choice | conditional guarded.choice = boolean process boolean = expression Una selecci¢n (choice) puede ser una selecci¢n protegida u otro condicional. Una selecci¢n protegida es un valor del tipo boolean seguido de un proceso identado a dos espacios. Por ejemplo: condici¢n1 P1 condici¢n2 P2 ........ es tal que, si "P1" y "P2" son procesos y "condicion1" y "condicion2" representan condiciones cuyos valores pueden ser TRUE o FALSE, entonces P1 se ejecutar  si la "condici¢n1" es TRUE, por otro lado, se ejecutar  P2 si la "condici¢n2" es TRUE y as¡ sucesivamente con el resto de las condiciones. Solo se ejecuta uno de los procesos y despu‚s la construcci¢n acaba. N¢tese la identaci¢n de los procesos Pi. Un condicional act£a como el proceso asociado a la primera de las condiciones que verifique, o como STOP si no verifica ninguna de ellas. Por ejemplo: IF x = 0 y := y + 1 x <> 0 SKIP produce un incremento de "y", si y s¢lo si "x" = 0. Es necesario incluir la opci¢n x<>0 y para ello haremos uso del comando SKIP puesto que, en una construcci¢n condicional si no se verifica ninguna de las condiciones se produce una parada (STOP). Por lo tanto es conveniente hacer un chequeo de todas las situaciones. De esta forma, en el siguiente ejemplo la constante booleana "TRUE" es siempre cierta y se utiliza como una condici¢n otherwise (en otro caso) cuando x=0. Por ejemplo: IF x < 0 z := y-1 x > 0 z := y+1 TRUE z := y I.2.3.1.5. WHILE bucle La construcci¢n bucle se puede representar por: loop = WHILE boolean process El comando WHILE viene seguido, a su derecha, de una expresi¢n booleana, y en la l¡nea siguiente por un proceso identado dos espacios. Por ejemplo: WHILE (x-5)<0 x := x-5 deja en x el valor x-5 si (x-5) es negativo. I.2.3.1.6. ALT alternativa Representamos una construcci¢n alternativa por: alternation = ALT {alternative} alternative = guarded.alternative | alternation guarded.alternative = guard process guard = input | boolean & input | boolean & SKIP La construcci¢n ALT viene seguida de uno o m s procesos alternativos identados dos espacios. Una alternativa puede ser o una "alternativa protegida" u otra "alternativa". Una alternativa protegida es, o una entrada o una expresi¢n booleana a la izquierda del "ampersand" (&), con una entrada o un SKIP a la derecha. SKIP puede tomar el lugar de una entrada en una protecci¢n que incluya una expresi¢n booleana. Por ejemplo: ALT input1 P1 input2 P2 input3 P3 ......... donde P1, P2 y P3 ... son procesos e "input1", "input2", "input3", ... se refieren a procesos de entrada (aunque la sintaxis del lenguaje permite que puedan ser procesos de salida, Occam2 no lo permite). Si "input1" est  preparado primero, entonces se ejecutar  el proceso "P1", si "input2" est  preparado primero entonces se ejecutar  P2 y as¡ sucesivamente. S¢lo se ejecutar  uno de los procesos y, la construcci¢n termina cuando el proceso elegido termina. Una protecci¢n act£a como STOP si su booleano es FALSO y como la entrada o SKIP en otro caso. Un ALT sin componentes alternativas se comporta como un STOP. I.2.3.1.7 CASE Las construcciones IF, WHILE y ALT ofrecen un tipo de selecci¢n entre procesos que dependen de criterios de selecci¢n condicionales. Existe otra construcci¢n que ofrece otro tipo de forma de selecci¢n, la construcci¢n CASE. Puede representarse por: selection = CASE selector {option} option = {1, case.expression} process | ELSE process selector = expression case.expression = expression La palabra CASE viene seguida de una o m s opciones identadas dos espacios. Cada opci¢n empieza con una lista de "expresiones case" o la palabra ELSE. Sin embargo, cada selecci¢n s¢lo puede tener una opci¢n ELSE. A la opci¢n le sigue un proceso identado dos espacios. Toda expresi¢n case usada en una selecci¢n debe tener valores constantes distintos, esto es, no debe haber ambiguedad entre los valores usados en la selecci¢n. El selector y la expresi¢n case, han de tener el mismo tipo, que debe ser o INT o BYTE. En este caso el selector se eval£a, y su valor se usa para optar por una de las selecciones componentes. Si el valor del selector es el mismo que uno de los valores de las expresiones, entonces se selecciona la expresi¢n, y la construcci¢n CASE act£a como el proceso asociado en esta selecci¢n. De otra forma la selecci¢n se comporta como el proceso que est‚ en la selecci¢n ELSE. Si no hay opci¢n ELSE y, CASE no selecciona ninguno de los procesos, la construcci¢n se comporta como un STOP. En el siguiente ejemplo: CASE numero '1','3','5','7','9' odd := TRUE la construcci¢n CASE es tal que si "n£mero" tiene el car cter de valor 1,3,5,7,9 se devuelve la variable "odd" con valor TRUE, en cualuqier otro casomodo la selecci¢n se comporta como el proceso primitivo STOP. Como se ha discutido, es conveniente tener una opci¢n que permita cubrir todas las posibles selecciones. El programa anterior se puede modificar de la siguiente forma: CASE numero '1','3','5','7','9' odd := TRUE ELSE odd := FALSE de esta forma ELSE ser  efectivo solamente cuando no se satisfacen otros selectores. I.2.3.2. Replicantes Hasta el momento se han discutido las construcciones SEQ, PAR, IF, WHILE y ALT. Existe una construcci¢nque permite repetir un proceso un n£mero determinado de veces. En general, si X representa una de las construcciones SEQ, PAR, IF, o ALT, Z(n) un proceso y, A y B son expresiones de tipo INT con valores a y b, entonces la forma de la construcci¢n replicante es la siguiente: X n = A FOR B Z(n) El significado del replicante puede explicarse como: Si B = 0 SEQ n = A FOR 0 [-------------L SKIP Z(n) PAR n = A FOR 0 [-------------L SKIP Z(n) IF n = A FOR 0 [-------------L STOP Z(n) ALT n = A FOR 0 [-------------L STOP Z(n) Si B > 0 entonces: X n = A FOR B Z(n) se comporta como: X Z(A) Z(A+1) Z(A+2) ......... Z(A+B-1) Si B < 0 entonces: X n = A FOR B Z(n) no es v lido. I.2.3.2.1. SEQ replicado La sintaxis de la construcci¢n SEQ replicado extiende a la sintaxis de la construcci¢n SEQ que ya se ha discutido. Esta sintaxis se puede representar por: sequence = SEQ replicator process replicator = name = base FOR count base = expression count = expression En esta construcci¢n, el SEQ y el replicante, que aparece a la derecha de SEQ, vienen seguidos por un proceso identado dos espacios. El replicante especifica el nombre para el ¡ndice que no necesita estar declarado en ninguna parte. El valor del ¡ndice para la primera replicaci¢n es el valor de la expresi¢n base y el n£mero de veces que se replica es el valor de la expresi¢n count al comienzo de la secuencia. El ¡ndice, que tiene un valor de tipo INT, puede usarse en la expresi¢n pero no puede ser asignado ni mediante una entrada ni mediante una asignaci¢n. Las expresiones "base" y "count" son tambi‚n de tipo INT. Un valor para "count" que sea menor que 0 es inv lido, y el caso en que "count" es 0 significa que la construcci¢n SEQ replicado act£a como SKIP. La construcci¢n SEQ replicada es equivalente a un bucle enumerado. Por ejemplo: SEQ i = 0 FOR n P1 causa que el proceso P1 se repita n veces. I.2.3.2.2. PAR replicado La sintaxis para la construcci¢n PAR replicado es similar a la descrita para la construcci¢n SEQ replicado: parallel = PAR replicator process replicator = name = base FOR count base = expression count = expression En esta construcci¢n, el PAR y el replicante que aparecen a la derecha de PAR, vienen seguidos por un proceso identado dos espacios. El replicante especifica el nombre para el ¡ndice que no necesita estar declarado en ninguna parte. El valor del ¡ndice para la primera replicaci¢n es el valor de la expresi¢n "base" y el n£mero de veces que se replica es el valor de la expresi¢n "count" al comienzo de la secuencia. El ¡ndice, que tiene un valor de tipo INT, se puede usar en la expresi¢n pero no se puede asignar ni mediante una entrada ni mediante una asignaci¢n. Las expresiones "base" y "count" son tambi‚n de tipo INT. No es v lido dar un valor menor que cero a "count" y el caso en que count es 0 significa que PAR replicado act£a como SKIP. La construcci¢n PAR replicado produce un array de procesos paralelos de estructura similar. Adem s tiene un  mplio rango de aplicaciones, y ofrece una forma sencilla de realizar conceptos tales como colas, "buffers" y "pipelines". En general, para la construcci¢n PAR replicado podr¡amos tener: PAR i = 0 FOR n Pi que construye un array de "n" procesos similares, P0, P1 , ..., Pn-1. Como ilustraci¢n de la construcci¢n, consideraremos la implementaci¢n de una cola simple. Las colas se usan a menudo como una forma de comunicaci¢n as¡ncrona entre procesos, operando cuando el rango de oferta y el rango de demanda no es el mismo y los datos necesitan almacenarse temporalmante antes de procesarse. Este efecto puede simularse f cilmente con una cadena de "slots" en el programa. Los "slots" forman un array de procesadores que en paralelo pasan datos entre ellos. [20] CHAN OF INT slot: PAR i := 0 FOR 19 WHILE TRUE INT y: SEQ slot[i] ? y slot[i+1] ! y En este caso hay diecinueve procesos paralelos activos que transfieren datos entre los "slots" de la cola. La cola est  representada mediante un array de veinte canales. N¢tese que la sincronizaci¢n entre los slots sucesivos se alcanza en la construcci¢n SEQ. Sin embargo, el segmento de c¢digo anterior s¢lo puede considerarse como una parte del programa porque por s¡ mismo, ‚ste no ofrece una fuente inicial de datos en el "slot[0]" o una salida de datos desde el "slot[20]". Consideraremos ahora un nuevo problema. Queremos construir procesos "filtros" en "pipeline", que reciben y env¡an datos, como se ilustra en la figura: Cada elemento del pipeline debe estar en uno de los estados: - Contar un ¡tem. - Pasar un ¡tem. - Pasar el ¡tem contador a un proceso receptor y terminar. Asumimos las siguientes declaraciones globales: VAL scan.values IS [ ... ] VAL max.items IS SIZE scan.values: [max.items]CHAN OF INT in: [max.items + 1]CHAN OF INT collect: "scan.values" es un array de ¡tems de datos a buscar, "in" es un array de canales que permite el flujo de datos hacia dentro y hacia fuera del estado del "pipeline" y "collect" es un array de canales para recolectar los ¡tems contados. El "pipeline" lo podemos escribir en Occam2 de la siguiente forma: VAL INT terminate IS -1: PAR ix = 0 FOR (SIZE scan.values -1) INT item: INT count: BOOL running: VAL input IS in[ix] VAL output IS in[ix+1] VAL myvalue IS scan.values[ix]: SEQ running := TRUE count := 0 WHILE running SEQ input ? item IF item = terminate SEQ output ! terminate collect[ix] ! count running := FALSE item = myvalue count := count + 1 TRUE output ! item I.2.3.2.3. IF replicado La sintaxis para la construcci¢n IF replicado es: conditional = IF replicator choice replicator = name = base FOR count base = expression count = expression En esta construcci¢n, el IF y el replicante, que aparece a la derecha de IF, vienen seguidos por un proceso identado dos espacios. El replicante especifica el nombre para el ¡ndice, que no necesita estar declarado en ninguna parte. El valor del ¡ndice para la primera replicaci¢n es el valor de la expresi¢n "base", y el n£mero de veces que se replica es el valor de la expresi¢n "count" al comienzo de la secuencia. El ¡ndice, que tiene un valor de tipo INT, puede usarse en la expresi¢n pero no se le pueden asignar valores ni mediante una entrada ni mediante una asignaci¢n. Las expresiones "base" y "count" son tambi‚n de tipo INT. Un valor para count que sea menor que 0 es inv lido. El caso en que "count" es 0 significa que la construcci¢n IF replicado act£a como STOP. La construcci¢n IF replicado, produce una construcci¢n condicional con un n£mero de elecciones que est n estructuradas de manera similar. Por ejemplo: IF i=0 FOR 3 array[i] = 0 array[i] := 1 chequear  los tres elementos del array buscando un cero. El primer elemento que se encuentre que sea cero, se reemplazar  por un uno. Uno de los problemas de este fragmento de programa es que si no se encuentran elementos con valor cero, el programa para. Nos gustar¡a concluir la replicaci¢n con un TRUE SKIP, pero como se ha comentado, no se puede ejecutar parcialmente una construcci¢n replicaci¢n. Una soluci¢n al problema puede ser el concepto de anidamiento de la contrucci¢n IF replicado con otra construcci¢n IF, en el ejemplo: IF IF i=0 FOR 3 array[i] = 0 array[i+1] := 1 TRUE SKIP se ejecutar  el SKIP si no se encuentran ceros. El programa continuar  despu‚s de la replicaci¢n si no se encuentran ceros en el array. Un examen de la definici¢n formal de la sentencia IF replicada revelar  que evalua sus condiciones en secuencia, buscando la primera que sea cierta. Esto sugiere una soluci¢n elegante a un problema com£n en programaci¢n, conocido como el problema de encontrar el primer elemento de un array que obedece a alguna regla. Por ejemplo, sup¢ngase que queremos encontrar el primer elemento distinto de cero en un array de enteros, llamado item: []INT items: -- array de enteros INT first.non.zero: IF IF i=0 FOR SIZE items items[i] <> 0 first.non.zero := i TRUE first.non.zero := -1 Aqu¡, simplemente creamos un IF replicado sobre los elementos del array. Si hay un ¡tem no cero, su ¡ndice se devolver  en "first.non.zero". Si el array es todo cero, la bifurcaci¢n TRUE devolver  el ¡ndice -1. N¢tese que si no estuviera la bifurcaci¢n TRUE y el array fuera nulo entonces el IF replicado ser¡a equivalente a un STOP. I.2.3.2.4. ALT replicado La sintaxis del ALT replicado es: alternation = ALT replicator alternative replicator = name = base FOR count base = expression count = expression En esta construcci¢n, el ALT y el replicante, que aparecen a la derecha de ALT, vienen seguidos por un proceso identado dos espacios. El replicante especifica el nombre para el ¡ndice, que no necesita estar declarado en ninguna parte. El valor del ¡ndice para la primera replicaci¢n es el valor de la expresi¢n "base" y el n£mero de veces que se replica es el valor de la expresi¢n "count" al comienzo de la secuencia. El ¡ndice, que tiene un valor de tipo INT, puede usarse en la expresi¢n pero no puede ser asignado ni meidante una entrada ni mediante una asignaci¢n. Las expresiones "base" y "count" son tambi‚n de tipo INT. Un valor para "count" que sea menor que 0 es inv lido, y el caso en que "count" sea 0 significar  que la contrucci¢n ALT replicado actuar  como un STOP. Una contrucci¢n ALT replicado consta de un n£mero de alternativas id‚nticamente estructuradas, cada una de ellas se dispara por una entrada desde un canal. Por ejemplo, el siguiente fragmento de programa actuar  como un multiplexor: [40] CHAN OF INT in: CHAN OF INT out: PAR ... procesos que ofrecen datos a los canales in WHILE TRUE INT y: ALT i = 0 FOR 40 in[i] ? y out ! y ... procesos que toman datos del canal out esto monitoriza canales de entrada cuando alguno de ellos tiene datos, el dato pasa al canal de salida. De esta forma las comunicaciones de los 40 canales, se fusionan en el canal de salida. Esta ilustraci¢n de multiplexamiento de datos forma la base de muchas aplicaciones en el control y conmutaci¢n de redes y, la monitorzaci¢n de equipos. I.2.3.3. Prioridades I.2.3.3.1. PRI PAR La construcci¢n PAR es tal que permite generar un n£mero de procesos concurrentes. EL tiempo total que los procesos alcanzan dependen del algoritmo de planificaci¢n que se utilice. Esto significa que la construcci¢n PAR depende de la implementaci¢n. A veces es de utilidad poder especificar la distribuci¢n del comienzo de una serie de procesos concurrentes, de manera que haya una medida en lo que se refiere a la prioridad. De esta forma, un proceso con alta prioridad tendr  preferencia sobre un proceso de baja prioridad siempre que se ejecuten sobre el mismo procesador. La sintaxis para la ejecuci¢n prioritaria de procesos paralelos es: parallel = PRI PAR {process} | PRI PAR replicator process donde las palabras PRI PAR vienen seguidas de cero o m s procesos identados dos espacios. El proceso puede replicarse como se ha descrito en la secci¢n anterior. Por ejemplo: PRI PAR P1 -- Proceso con prioridad m s alta PAR -- Dos procesos con igual prioridad P2 P3 P4 -- Proceso con prioridad m s baja El orden de prioridad se toma del orden en que se escriben los procesos, de esta forma, en el ejemplo anterior el proceso P1 tiene la prioridad m s alta y el proceso P4 la m s baja. Si los procesos van a tener la misma prioridad, entonces pueden introducirse de una construcci¢n paralela dentro de la construcci¢n PRI PAR, de esta forma, en el ejemplo anterior los procesos P2 y P3 tienen la misma prioridad, en un nivel intermedio entre la prioridad alta de P1 y la prioridad baja de P4. La construcci¢n PRI PAR es bastante £til en aplicaciones de tiempo real donde podemos asignar la prioridad m s alta al proceso que tenga m s responsabilidad y, el programador pueda estar seguro de que los otros procesos no se ejecutr n hasta que se hayan realizado los sucesos de tiempo real. En un transputer s¢lo se soportan dos niveles de prioridad y, en general el hecho de que los sistemas tengan que soportar en tiempo de ejecuci¢n distintas colas para cada prioridad, significa que esta construcci¢n depende de la implementaci¢n. La sentencia PRI PAR puede utilizarse para dar respuestas r pidas a eventos urgentes que ocurran en cualquier parte. Por ejemplo, podr¡amos escribir: [3]CHAN OF INT priority: PRI PAR i=0 FOR SIZE priority INT value: SEQ priority[i] ? value -- c¢digo para manejar el mensaje de prioridad i en este caso v‚ase que cada proceso manejador se basar  en el mismo c¢digo Occam, que se ejecutar  independientemente. Si hubi‚semos querido que cada nivel de prioridad hubiese sido manejado de diferente manera, hubi‚ramos tenido que escribir: PRI PAR INT value: SEQ priority[0] ? value -- c¢digo para manejar el mensaje de prioridad 0 INT value: SEQ priority[1] ? value -- c¢digo para manejar el mensaje de prioridad 1 INT value: SEQ priority[2] ? value -- c¢digo para manejar el mensaje de prioridad 2 donde 0 es el nivel de prioridad m s alta y 2 es el nivel m s bajo. Consideremos otro ejemplo; deseamos responder a "timeouts" muy r pidamente mientras se manejan otros mensajes de manera m s despreocupada, proponemos el siguiente segmento de c¢digo: CHAN OF INT request: PRI PAR TIMER clock: VAL INT delay IS t: INT time: SEQ -- espera por timeouts clock ? time WHILE TRUE SEQ time := time PLUS delay clock ? AFTER time --respuesta del timeout INT value: SEQ -- espera por el requisito de prioridad -- m s baja request ? value -- respuesta al requisito I.2.3.3.2. PRI ALT Con la construcci¢n PRI ALT tenemos la posibilidad de dar prioridad en ejecuci¢n a una serie de alternativas. La sintaxis para esta construcci¢n viene dada por: alternation = PRI ALT replicator alternative donde la palabra PRI ALT viene seguida de cero o m s procesos identados dos espacios. Por ejemplo: PRI ALT stream ? blocks SKIP TRUE & SKIP P1 este proceso recibe "blocks" si est  preparada una entrada en "stream", en otro caso, como booleano TRUE es v lido, se ejecuta el proceso P1. El uso de la protecci¢n TRUE & SKIP tiene muchos usos en la construcci¢n PRI ALT. En algunos sentidos, la construcci¢n PRI PAR aparece como una alternativa a la sentencia PRI ALT. Sin embargo, tienen sem nticas muy diferentes. La sentencia PRI PAR produce un n£mero de procesos independientes corriendo con varios niveles de prioridad; la sentencia PRI ALT produce un simple proceso que espera por eventos de entrada con varios niveles de prioridad. Como la construcci¢n ALT descrita anteriormente, la alternativa puede ser replicada. Por ejemplo, consideremos lo siguiente: [3]CHAN OF INT priority: PRI ALT i = 0 FOR SIZE priority INT value: SEQ priority[i] ? value -- C¢digo que maneja el mensaje Comparando este segmento de c¢digo con el ejemplo de la secci¢n anterior puede observarse que aqu¡ tenemos un proceso que espera por un n£mero de canales simult neamente. I.2.3.4. Protocolos I.2.3.4.1. Protocolos simples Ya se ha descrito c¢mo puede utilizarse un canal para comunicar dos procesos concurrentes. El formato y el tipo de datos del canal se especifican mediante el protocolo del canal. Este protocolo se define en la declaraci¢n del canal. La definici¢n tiene que ser tal que, las entradas y salidas que utilicen ese canal han de ser compatibles con el protocolo de canal definido para ‚l, de esta forma los protocolos de canales permiten al compilador chequear el uso correcto de los canales. La sintaxis de un protocolo simple es la siguiente: simple.protocol = type | primitive.tipe :: []type input = channel ? input.item input.item = variable | variable :: variable output = channel ! output.item output.item = variable | expression :: expression variable = element protocol = simple.protocol De esta forma un protocolo simple es un tipo de dato o un array numerado especificado por el tipo de dato del contador, que puede ser un entero o un "byte", seguido por `::`, corchetes y un especificador indicando el tipo de componente. Por ejemplo: CHAN OF INT ::[] BYTE mail: declara un canal llamado "mail" cuya salida debe ser, primero un entero que especifica el n£mero de elementos de un array que aparece a continuaci¢n. Si una salida por este canal viniera dada por: mail ! 11 :: "La m quina necesita..." el efecto ser¡a la salida de los 11 primeros caracteres del mensaje, esto es, "La m quina ". Es conveniente dar un nombre a los protocolos, esto se consigue con la definici¢n de protocolos. Su sintaxis es: definition = PROTOCOL name IS simple.protocol : | PROTOCOL name IS sequential.protocol : protocol = name De esta forma se define nombre como lo que aparece a la derecha de IS. Por ejemplo: PROTOCOL CHAR IS BYTE: permite declarar un canal con protocolo CHAR de la siguiente forma: CHAN OF CHAR mail: I.2.3.4.2. Protocolos secuenciales Una vez establecido el concepto de protocolo simple, se puede definir el protocolo secuencial como una sucesi¢n de protocolos simples. La sintaxis es: sequential.protocol = {1; simple.protocol} input = channel ? {1; input.item} output = channel ! {1; output.item} input.item = variable | variable :: variable output = channel ! output.item output.item = variable | expression :: expression protocol = simple.protocol Un protocolo secuencial consta de, uno o m s protocolos separados por punto y coma. Las comunicaciones en un canal son v lidas siempre que se tenga en cuenta la compatibilidad de las correspondientes componentes del canal. Por ejemplo: PROTOCOL COORDENADA IS REAL32;REAL32;REAL32 permite declarar canales que pasar n los datos en grupos de tres. La definici¢n : CHAN OF COORDENADA dato: permitir¡a una entrada de datos de la forma: dato ? xvalue;yvalue;zvalue I.2.3.4.3. Protocolos con variante Aunque se ha hecho ‚nfasis en la necesidad de que los datos que se transmiten v¡a un canal sean del mismo tipo que la definici¢n del canal por el que van a transmitirse, a veces es necesario transmitir datos de diferentes formatos a trav‚s de un canal simple de datos. Esta facilidad nos la ofrecen los protocolos con variante; nos permiten definiciones de protocolos que especifican un n£mero determinado de formatos que pueden usarse en las comunicaciones a trav‚s de un canal. La definici¢n de un protocolo con variante es distinta para salidas que para entradas. La sintaxis del protocolo con variante para salidas es: definition = PROTOCOL name CASE {tagged.protocol} : tagged.protocol = tag | tag; sequential.protocol tag = name output = channel | tag; {1; output.item} output.item = variable | expression :: expression La definici¢n del nombre del protocolo con variante aparece a la derecha de la palabra PROTOCOL, seguida de la palabra CASE (identada dos espacios) y seguida de una serie de protocolos etiquetados (tagged.protocol)tambi‚n identados. La definici¢n acaba con el car cter `:` al mismo nivel de identaci¢n que la palabra PROTOCOL. Un protocolo etiquetado es o una etiqueta en s¡ misma o una etiqueta seguida de un punto y coma y un protocolo secuencial. Las etiquetas deben tener nombres distintos. La salida a trav‚s de un canal de este tipo ser  v lida s¢lo si la etiqueta y los items de salida asociados coinciden con los protocolos asociados en la definici¢n. Por ejemplo, consid‚rese la posibilidad de enviar datos tanto enteros como reales: PROTOCOL INT.OR.REAL CASE Fixed ; INT Floating ; REAL32 : CHAN OF INT.OR.REAL chanvalue : PAR SEQ chanvalue ! Fixed; I -- Integer chanvalue ! Floating; R -- Real de esta forma el canal "chanvalue" puede usarse para enviar enteros o reales. Para entradas la situaci¢n del protocolo con variante es m s complicada dado que el proceso de lectura al no saber que tipo de objeto se est  transmitiendo, debe realizar una serie de posibles acciones de lectura dependiendo del tipo de dato que venga, una para cada etiqueta. La sintaxis para el protocolo con variante en este caso es: case.input = channel ? CASE {variant} variant = tagged.list process | specification variant tagged.list = tag | tag; {1; input.item} input.item = variable | variable :: variable process = case.input input = channel ? CASE tagged.list Una entrada selectiva recibe una etiqueta del canal que se nombra a la izquierda de los s¡mbolo `? CASE` y, esta etiqueta se utiliza para seleccionar de una de las variantes. Estas variantes aparecen en las siguientes l¡neas identadas dos espacios. Cuando se recibe una etiqueta si la variante con esa etiqueta est  presente, el proceso asociado recibe el resto de la lista y desarrolla la acci¢n adecuada. Si no se encuentra ninguna variante que coincida con esta etiqueta se produce un proceso de parada. El protocolo con variante para la entrada del ejemplo anterior ser¡a: PROTOCOL INT.OR.REAL CASE Fixed ; INT Floating ; REAL32 : CHAN OF INT.OR.REAL chanvalue: PAR -- Proceso para salidas SEQ chanvalue ! Fixed ; I -- Entero chanvalue ! Floating ; R -- Real SEQ -- Proceso para la entrada chanvalue ? CASE Fixed ; J This.is.an.integer := TRUE Floating ; S This.is.an.integer := FALSE el proceso recibir  en "J" un entero v¡a el canal "chanvalue" y pondr  la variable "This.is.an.integer" a TRUE o recibir  en "S" un real a trav‚s del mismo canal haciendo la asignaci¢n a FALSE. Si no se hubiera encontrado la etiqueta adecuada se hubiera producido un STOP. Hay ocasiones en las que se necesita un canal en el que el formato no est  definido, este es el caso t¡pico de los dispositivos externos, tales como terminales o impresoras. Esto significa que el compilador no debe realizar chequeos para evitar el mal uso del canal. En este caso la definici¢n del protocolo del canal es: CHAN OF ANY: Por ejemplo: CHAN OF ANY terminal: PLACE terminal AT 1: esta es la definici¢n que se ha usado para enviar datos a la pantalla. No es recomendable el uso de esta definici¢n. I.2.4. Expresiones En su forma m s simple, una expresi¢n se ha concebido para desarrollar una evaluaci¢n y producir un resultado. El resultado de una expresi¢n tiene un valor y un tipo de dato. Las expresiones m s simples son los literales y las variables, se pueden construir expresiones m s complejas de operandos, operadores y par‚ntesis. Una expresi¢n tambi‚n puede ser un operando en una expresi¢n. La sintaxis de esta representaci¢n es: expression = monadic.operator operand | operand dyadic.operator operand | conversion | operand operand = element | literal | table | (expression) de esta forma un operando es un elemento de tipo dato, un literal, una tabla u otra expresi¢n encerrada entre par‚ntesis. Ya hemos apuntado que todas las variables deben definirse y asociarse con un £nico tipo de dato. Todos los tipos primitivos, aparte de CHAN y TIMER, tienen definido el operador de asignaci¢n. Los literales, que son representaciones textuales de un valor conocido, tambi‚n se pueden utilizar con todo tipo de datos. La sintaxis de los literales es: literal = integer | byte | integer(type) | byte(type) | real(type) | string | TRUE | FALSE integer = digits | #hex.digits byte = 'character' real = digits.digits | digits.digitsEexponent exponent = +digits | -digits digits = {digit} hex.digits = {hex.digit} digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 hex.digit = digit | A | B | C | D | E | F Todos los caracteres se codifican de acuerdo al c¢digo ASCII. Un valor encerrado entre comillas simples 'B' es un valor Byte, a menos que su tipo se establezca expl¡citamente de otra forma despu‚s del valor. Un string literal es una sucesi¢n de caracteres encerrada entre comillas dobles. Cada componente de un string se representa por el c¢digo ASCII para ese car cter particular, de esta forma "today" viene representado por los valores 116,111,100,97,121. Veamos algunos ejemplos de literales: J := 6 literal entero Running := TRUE literal booleano Char := 'b' literal byte Mail := "today" literal string Pi := 3.1416 literal real Inc := 0.3 E+2 literal real, valor 30.0 Delta := 1.4 E-2 literal real, valor 0.014 De cara a clarificar el tipo del literal, su tipo puede a¤adirse entre par‚ntesis despu‚s del literal. Por ejemplo: e := 2.718(REAL32) es v lido. Esto puede extenderse a una forma £til de ofrecer la posibilidad de reespecificar el tipo de un literal. Por ejemplo: k := 'b'(INT) significa que el literal car cter que es de tipo Byte pueda interpretarse como un valor de tipo entero. Volviendo a la naturaleza de las expresiones, son v lidas las siguientes expresiones: 2.718(REAL32) valor literal x variable 3+2 suma de dos operandos literales x-y resta de dos operandos variables NOT FALSE expresi¢n booleana Una expresi¢n puede ser tambi‚n un operando o una expresi¢n, y de esta forma construir expresiones muy largas, por ejemplo: (x * y) + z multiplica la variable "x" por la variable "y" y le suma la variable "z". Al contrario de otros lenguajes, en Occam no existe precedencia de operadores y la estructura jer rquica de las operaciones debe mostrarse mediante el uso de par‚ntesis, como en el caso anterior. N¢tese tambi‚n que a excepci¢n de las operaciones de cambio de tipo, el tipo de datos de una expresi¢n di dica debe ser el mismo. En una asignaci¢n el valor de la expresi¢n tiene que ser del mismo tipo que la variable a la que se va a asignar. Una tabla constituye un array de valores. Cada componente de la tabla es el valor de la correspondiente expresi¢n en un conjunto de expresiones. Los valores deben de ser todos del mismo tipo. La sintaxis para la tabla es: table = table [subscript] | [{1, expression}] | [table FROM subscript FOR count] subscript = expression count = expression de esta forma una tabla es una o m s expresiones, de datos del mismo tipo separadas por comas y encerradas entre corchetes. Por ejemplo: [a,b,c] es una tabla de tres valores ['d','o','g'] es una tabla de tres bytes equivalente a "dog" Uno de los mayores usos de las tablas est  en asignar valores a arrays, por ejemplo: [8]INT a.1,a.2: INT i.1,i.2,i.3,i.4: SEQ a.1 := [0,1,0,1,0,1,0,1] [a.2 FROM 0 FOR 4] := [0,1,0,1] [i.1,i.2,i.3,i.4]:= [a.1 FROM 3 FOR 4] ser¡an todas sentencias de asignaci¢n v lidas. I.2.4.1. Operadores aritm‚ticos Como ya se ha descrito, las operaciones eval£an operandos y producen un resultado. Para el caso de operaciones aritm‚ticas, existen los siguientes operadores: + suma - resta * multiplicaci¢n / divisi¢n REM resto \ resto en m¢dulo PLUS suma en m¢dulo MINUS resta en m¢dulo TIMES multiplicaci¢n en m¢dulo MOSTPOS mayor positivo MOSTNEG menor negativo Las operaciones de suma, resta, multiplicaci¢n, divisi¢n y resto, desarrollan operaciones sobre operandos del mismo tipo, a saber, enteros o reales pero de tipo boolean ni de tipo byte. El resultado de una operaci¢n produce un valor del mismo tipo de dato que los operandos. Si el tipo de dato del resultado no es compatible con el tipo de dato de los operandos, la operaci¢n es inv lida. Por ejemplo, si la multiplicaci¢n de dos enteros excede el valor entero positivo mayor, entonces la operaci¢n no es v lida. La divisi¢n por cero tambi‚n es una operaci¢n inv lida. Una expresi¢n est  definida para ser un operando seguido de un operador seguido de un operando. Un operando puede ser una variable u otra expresi¢n. Sin embargo, si un operando es otra expresi¢n entonces debe estar contenido entre par‚ntesis, as¡ una expresi¢n como: A+B+C escrita de esta forma, ser¡a inv lida. S¢lo podr  evaluarse si se escribe de alguna de las siguientes maneras: (A+B)+C o A+(B+C) por lo tanto, con el uso de par‚ntesis se elimina la necesidad de definir un orden de precedencia. Ejemplos del uso de operaciones aritm‚ticas son: 48 + 6 resultado 54 48 - 6 " 42 3 * 4 " 12 12 / 3 " 4 14 REM 6 " 2 14 \ 3 " 2 'REM' y '\' producen el resto de la divisi¢n de dos valores. El signo del resto es el mismo que el del lado izquierdo de la expresi¢n. Cuando el tipo de los datos para la divisi¢n es entero entonces la operaci¢n '/' produce truncamiento del resultado y no redondeo. Por ejemplo: 13 / 3 da como resultado 4 Los resultados de operaciones aritm‚ticas con n£meros reales se redondean a los valores m s cercanos que puedan representarse con esos tipos de datos. Debe tenerse cuidado al considerar los efectos de redondeo en los resultados de la divisi¢n de n£meros reales. Cuando se usa el operador '/' con datos reales el resultado se redondea y no se trunca, en contra de lo que ocurre con datos enteros. Es posible encontrarse con un resto negativo al hacer la operaci¢n con n£meros reales. Por ejemplo: 2.4(REAL32) REM 3.0(REAL32) da como resultado (-0.6) Las reglas para redondeo que se usan son las definidas por ANSII/IEEE est ndar 754-1985. En este caso, si tomamos una expresi¢n general de lo anterior, a REM b donde a y b son n£meros reales, entonces el resultado est  definido por: (a-(b*n)) donde "n" es el entero resultante de dividir "a" entre "b" y redondear a cero decimales. As¡ pues, en el ejemplo , a = 2.4 dividido por b = 3.0 da 0.8 y, al redondear al entero m s cercano da el valor 1. De esta forma la expresi¢n se evalua como (2.4 -(3.0 * 1)) que es igual a (-0.6). Las operaciones aritm‚ticas en m¢dulo PLUS, MINUS y TIMES, desarrollan operaciones entre datos enteros del mismo tipo. Estas operaciones no son v lidas, con datos de otros tipos(real, bool o byte). Las operaciones son similares, en efecto, a las operaciones aritm‚ticas ya descritas. Sin embargo, no se efect£an chequeos de desbordamiento y, de esta forma, los valores son c¡clicos. Antes de ilustrar este efecto, vamos a introducir las operaciones MOSTPOS y MOSTNEG. MOSTPOS produce el mayor valor positivo del tipo entero y MOSTNEG produce el menor valor negativo del tipo entero. La sintaxis para estos operadores es: expression = MOSTPOS type | MOSTNEG type donde la palabra MOSTPOS o MOSTNEG aparece a la izquierda del tipo. Por ejemplo: MOSTPOS INT16 produce el valor 32767 MOSTNEG INT16 produce el valor -32768 Volviendo ahora a ilustrar la diferencia entre las operaciones aritm‚ticas y las operaciones aritm‚ticas en m¢dulo, podemos considerar: 32767(INT16) +1 (INT16) produce desbordamiento (inv lido) 32767(INT16) PLUS 1 (INT16) produce el valor -32768 (-32768(INT16)) -1(INT16) produce desbordamiento (inv lido) (-32768(INT16)) MINUS 1(INT16) produce el valor 32767 10000(INT16) * 4(INT16) produce desbordamiento (inv lido) 10000(INT16) TIMES 4(INT16) produce el valor -25535 I.2.4.2. Operaciones con bits (bitwise) y desplazamientos La configuraci¢n de los bits del valor de un dato de tipo entero puede operarse mediante operaciones sobre bits. Las operaciones posibles son: /\ AND \/ OR >< OR Exclusivo ~ NOT El resultado de una operaci¢n sobre bits es del mismo tipo que los operandos. En algunas implementaciones en que hay un conjunto reducido de caracteres, las palabras BITAND, BITOR y BITNOT son equivalentes a /\, \/ y ~ respectivamente. Los resultados de las operaciones correspondientes son: AND OR OR Exclusivo NOT 0 /\ 0 = 0 0 \/ 0 = 0 0 >< 0 = 0 ~ 0 = 1 0 /\ 1 = 0 0 \/ 1 = 1 0 >< 1 = 1 ~ 1 = 0 1 /\ 0 = 0 1 \/ 0 = 1 1 >< 0 = 1 1 /\ 1 = 1 1 \/ 1 = 1 1 >< 1 = 0 Por ejemplo, si tomamos los siguientes valores, todos del tipo INT16, patron1 #C3C3 es decir, 1100001111000011 patron2 #9B9B es decir, 1001101110011011 patron3 #0404 es decir, 0000010000000100 patron4 #FFFF es decir, 1111111111111111 entonces para las siguientes operaciones: patron1 \/ patron2 se tiene #BDDB, es decir, 1100001111000011 OR 1001101110011011 [------------------] #DBDB 1101101111011011 patron1 >< patron2 se tiene #5858, es decir, 1100001111000011 XOR 1001101110011011 [------------------] #5858 0101100001011000 patron4 /\ patron3 se tiene #0404, es decir, 1111111111111111 AND 0000010000000100 [------------------] #0404 0000010000000100 este ejemplo de la funci¢n AND ilustra la posibilidad de ofrecer una m scara para conocer la composici¢n en un patr¢n de bits. patron1 se tiene #3C3C es decir, 1100001111000011 [------------------] NOT #3C3C 0011110000111100 aunque la operaci¢n anterior da el complemento del valor, podr¡amos alcanzar el mismo resultado utilizando el OR exclusivo con el valor #FF. Por ejemplo: patron1 >< patron4 se tiene #3C3C, es decir, 1100001111000011 XOR 1111111111111111 [------------------] #3C3C 0011110000111100 Adem s de estas operaciones sobre bits, existe tambi‚n operaciones que permiten desplazar a la izquierda o a la derecha un determinado n£mero de lugares. Los desplazamientos s¢lo son posibles sobre valores de datos de tipo entero y, la operaci¢n viene dada de la forma: value << count , desplaza a la izquierda "count" lugares value >> count , desplaza a la derecha "count" lugares La operaci¢n desplazamiento no es c¡clica y no hay acarreo, por lo que los bits desplazados hacia fuera desde el bit m s significativo, en el desplazamiento hacia la izquierda, se pierden y se a¤aden ceros al bit menos significativo en cada desplazamiento. En el caso del desplazamiento a la derecha, los bits desplazados hacia fuera desde el bit menos significativo se pierden, y se a¤aden ceros en el bit m s significativo en cada desplazamiento. Si el valor es de tipo INT16 y el valor llamado x es #C39B, tendr¡amos: x << 3 #C39B 1100001110011011 desplaza tres lugares a la izquierda 0001110011011000 y se obtiene #1CD8. Para el caso de: x >> 4 #C39B 1100001110011011 desplaza cuatro lugares a la derecha 0000110000111001 el resultado es #0C39. Si "count" es 16, entonces para enteros del tipo INT16, el resultado es cero si el desplazamiento es hacia la izquierda o derecha. El resultado es el mismo si el tipo entero es INT32, s¢lo que "count" deber¡a ser 32 para que el valor sea puesto a cero. En los casos en los que "count" es mayor que 16 para INT16 o "count" es mayor que 32 para INT32, los desplazamientos son inv lidos. Los desplazamientos tampoco son v lidos si "count" es negativo. Una aplicaci¢n de los operadores sobre bits es la siguiente: Si un dispositivo de entrada/salida tiene un registro de estados del tama¤o INT16 donde los 8 bits m s significativos indican un valor del estado y los 8 bits menos significativos indican un n£mero de dispositivo, podr¡amos hacer lo siguiente: INT16 register.values: INT16 status.bits: INT16 device.id: SEQ registro.value := Register status.bits := (Register AND #FF00) >> 8 device.id := (Register AND #00FF) Si queremos construir un valor para escribir en un registro de entrada/salida, podr¡amos invertir el proceso. INT16 register.value: INT16 status.bits: INT16 device.id: SEQ -- asignaci¢n device.id -- asignacion status.bits register.value := (status.bits << 8) OR device.id I.2.4.3. Operaciones booleanas Los operadores booleanos combinan operandos de tipo booleano, los operandos posibles son: AND and booleano OR or booleano NOT not booleano Se producen los siguientes resultados: - AND falso AND cierto = falso falso AND falso = falso cierto AND falso = falso cierto AND cierto = cierto - OR falso OR falso = falso falso OR cierto = cierto cierto OR falso = cierto cierto OR cierto = cierto - NOT NOT falso = cierto NOT cierto = falso El proceso de evaluaci¢n es el siguiente: Durante la evaluaci¢n, se eval£a el operando de la izquierda del operador, si el resultado de esta operaci¢n y la naturaleza del operador es tal que el resultado est  determinado, entonces, se detiene la evaluaci¢n en ese punto y no se eval£a el operando de la derecha. Por ejemplo, para la expresi¢n: IF (ch >= 'd') AND (ch <= 'x') si la condici¢n ch >= 'd' es falsa no hay necesidad de evaluar la condici¢n de ch <= 'x' porque el resultado es falso. Si la expresi¢n anterior se modifica de la siguiente forma a: IF (ch >= 'd') OR (ch <= 'x') si la condici¢n ch >= 'd' es cierta no hay necesidad de evaluar la condici¢n de ch <= 'x' porque el resultado es cierto. I.2.4.4. Operaciones relacionales Los operadores relacionales llevan a cabo una comparaci¢n de operandos y producen un resultado de tipo bool. Los operadores relacionales permitidos son: = igual <> distinto < menor que > mayor que <= menor o igual >= mayor o igual En el caso de expresiones relacionales que usan los operadores '=' y '<>' los operandos pueden ser de cualquier tipo primitivo. Sin embargo para expresiones que utilicen cualquier otro operando, ya sean, '<', '>', '<=', '>=' entonces deben utilizarse solamente junto con operandos de tipo entero, byte o real y nunca con operandos de tipo bool. Se pueden combinar operadores relacionales con operadores y variables booleanas para calcular condiciones complejas. Por ejemplo, para decidir, si un car cter de control, est  fuera de los caracteres imprimibles puede utilizarse: BYTE ch: BOOL is.control: SEQ is.control := ((ch < ' ') OR (ch > '~')) I.2.4.5. Otras operaciones Adem s de las operaciones que ya se han tratado, hay otras que vale la pena mencionar. Introducimos a continuaci¢n dos de tales operaciones, AFTER y SIZE. El operador AFTER se ha usado en conexi¢n con el uso de TIME. AFTER realiza la comparaci¢n de si uno de los operandos ocurre despu‚s del otro y devuelve un valor TRUE o FLASE. Si b y c representan dos operandos, entonces la expresi¢n: (b AFTER c) devolver  cierto si la operaci¢n de b est  en un ciclo posterior a la operaci¢n de c. A pesar de que este operador se introdujo en el contexto de los "timers" y relojes, se puede utilizar para cualquier objeto con un tipo que venga representado como una secuencia c¡clica. Si el camino m s corto del primer operando al segundo es en el sentido de las agujas del reloj, entonces el resultado es cierto, pero si el camino m s corto es en sentido contrario al de las agujas del reloj, el resultado es falso. De esta forma, para una serie de operandos c¡clicos, las expresiones de: (b AFTER c) y (a MINUS c) > 0 producir n el mismo resultado. El operador SIZE act£a sobre un solo operando que tiene que ser de tipo array y produce un valor de tipo INT que representa el n£mero de elementos del array. Por ejemplo, si b es un array de tipo [16]INT, entonces: SIZE b producir  como resultado, el valor de 16. Consideremos una extensi¢n del ejemplo anterior a una definici¢n de array m s compleja, donde b es un array de tipo [16][8]INT, entonces: SIZE b da el resultado 16 como antes SIZE b[1] da el resultado 8 Una vez m s, t‚ngase en cuenta que los arrays en Occam se indexan desde cero. Esto significa que sentencias como: []INT a.1: SEQ ix = 0 FOR SIZE a.1 -- Procesos el ¡ndice "ix" va desde cero hasta (SIZE a.1-1). I.2.4.6. Conversi¢n de tipo de datos Para expresiones l¢gicas de desplazamiento, el n£mero de bits a desplazar debe ser de tipo INT, as¡ como para otras expresiones los operandos deben ser del mismo tipo. Para los operandos es posible convertir su tipo expl¡citamente, lo que permite a un valor de tipo primitivo (que no sea del tipo array), ser convertido a un valor num‚rico similar de tipo diferente del tipo primitivo. La sintaxis para conversi¢n de tipo de datos es: conversion = primitive operand | primitive.type ROUND operand | primitive.type TRUNC operand El dato, que aparece a la izquierda del operando, debe ser un dato de tipo primitivo. Para el caso de la conversi¢n del tipo de dato se produce redondeo (ROUND) cuando se redondea al valor m s cercano del tipo especificado. En la situaci¢n donde los dos valores son igualmente cercanos, el valor se redondea al n£mero par m s cercano. Por ejemplo, consideremos la conversi¢n de los siguientes reales a enteros: INT32 ROUND 0.8(REAL32) da el valor 1 INT32 ROUND 0.2(REAL32) da el valor 0 Si b y c son n£meros reales, con valores 5.5 y 4.5 respectivamente, entonces obtenemos lo siguiente de la conversi¢n a enteros: INT16 ROUND b da el valor 6, redondeo par INT32 ROUND c da el valor 4, redondeo par Para el caso de la conversi¢n de tipo TRUNC, se produce un valor, que es el truncado a un valor del tipo especificado, donde truncamiento significa redondeo hacia cero. Por ejemplo, el correspondiente efecto a los casos anteriormente considerados, producir¡an: INT32 TRUNC 0.8(REAL32) da el valor 0 INT32 TRUNC 0.2(REAL32) da el valor 0 Usando los mismos valores para los n£meros reales b y c se obtiene: INT16 TRUNC b da el valor 5 INT32 TRUNC c da el valor 4 INT16 TRUNC (b/c) da el valor 1 (INT ROUND b)*(INT ROUND c) da el valor 24 Una conversi¢n entre cualquiera de los tipos enteros y conversiones entre esos tipos y el tipo BYTE s¢lo es v lida si el valor que se produce est  en el rango del tipo de dato receptor. En particular, los tipos BYTE y entero pueden convertirse a cualquier otro si su valor es uno o cero. Entonces: BOOL 1 da TRUE BOOL 0 da FALSE INT TRUE da 1 INT FALSE da 0 I.2.5. Procedimientos y funciones I.2.5.1. Abreviaciones Las abreviaciones las podemos utilizar para especificar un nombre para una expresi¢n o para especificar un nombre para un elemento. El nombre que se especifica en la abreviaci¢n, se usa como un alias para la expresi¢n o el elemento. La sintaxis que indica el uso de una abreviaci¢n para especificar el nombre de una expresi¢n es: abbreviation = specifer name IS element: | name IS element : | VAL specifer name IS expression: | VAL name IS expression: specificier = primitive.type | []specificier | [expression]specificier La abreviaci¢n de un valor empieza con la palabra VAL. A su derecha aparece un especificador opcional que indica el tipo de dato de la abreviaci¢n, seguido de la palabra IS. La expresi¢n aparece a la derecha de IS. Se permiten l¡neas de continuaci¢n despu‚s del IS. El tipo de dato de la expresi¢n debe ser el mismo que el del especificador, puede omitirse el tipo del especificador asumiendo que va a ser el mismo que el de la expresi¢n. Un especificador []tipo simplemente define que la abreviaci¢n va a ser un array de determinado tipo. La expresi¢n abreviada debe ser una expresi¢n v lida, as¡ pues, no deben producirse desbordamientos y todos los sub¡ndices deben estar en el rango definido. Las variables que se usan en una expresi¢n abreviada no pueden asignarse ni mediante una entrada ni mediante una sentencia de asignaci¢n en el  mbito de la abreviaci¢n, esto es, en la regi¢n de programa en que el nombre es v lido. Por ejemplo: VAL REAL32 y IS (m*x)+c: significa que no se pueden hacer asignaciones a los par metros m, x ¢ c . Pueden hacerse abreviaciones, tales como el nombre de un elemento, para el nombre de variables, canales, timers o arrays que ya existan. Por ejemplo: INT b IS c: especificar¡a que b es el nuevo nombre para el elemento c. INT x IS a.1[1] especificar¡a que x es el nuevo nombre del segundo elemento del array a.1. Debe probarse que dos abreviaciones que identifican segmentos de un mismo array no se solapen, pues esto ser¡a incorrecto. I.2.5.2. Ambito Aunque nos hemos referido al  mbito en conexi¢n con las abreviaciones el concepto no es realmente nuevo. Podemos resumir la sintaxis como sigue: process = specification process choice = specification choice option = specification option alternative = specification alternative variant = specification variant valof = specification valof specification = declaration | abbreviation | definition Esta sintaxis indica los puntos de un programa en los que pueden aparecer una declaraci¢n, abreviaci¢n o una definici¢n. La especificaci¢n se puede hacer antes de un proceso, una selecci¢n, una opci¢n, una alternativa, una variante o antes de la palabra reservada VALOF. La regi¢n del programa en la que tal especificaci¢n es v lida, incluye cualquier otra especificaci¢n que se haga con el mismo nivel de identaci¢n, y el correspondiente proceso, elecci¢n, opci¢n, alternativa o VALOF. Por ejemplo: INT y: -- variable entera "y" SEQ --  mbito input ? y -- ALT REAL32 y: -- variable real "y" que tapa a la variable -- entera "y" chan ? y: --  mbito En este ejemplo se ve el aparente conflicto en la especificaci¢n de la variable "y", por l¡mites del  mbito de cada especificaci¢n. Debido al cambio de nivel de identaci¢n necesario en la construcci¢n ALT la segunda declaraci¢n tiene el efecto de tapar o esconder a la primera mientras dure su  mbito. Todos los nombres en un  mbito en Occam deben ser distintos. La asociaci¢n de un nombre con un  mbito dado puede ser local o libre de asociaciones locales, por ejemplo, el programa anterior podr¡a haberse escrito: INT y: SEQ input ? y ALT REAL32 x: chan ? x I.2.5.3. Procedimientos En Occam la operaci¢n b sica es el proceso. Los procedimientos en Occam ofrecen la definici¢n de un nombre para un proceso. Por ejemplo: PROC decremento(INT y) SEQ y := y - 1 : da el nombre `decremento` al proceso Occam que desarrolla las operaciones entre el nombre y los dos puntos finales. En este caso el proceso es la simple operaci¢n y := y - 1. El par metro 'y' en el caso anterior es el par metro formal del proceso y se especifica entre par‚ntesis despu‚s del nombre del procedimiento. Una vez definido el procedimiento se pueden hacer llamadas a ‚l. Por ejemplo: INT x: SEQ ......... decremento(x) ......... La sintaxis para la definici¢n de un procedimiento es: definition = PROC name ({0, formal}) procedure.body : formal = specifier{1, name} | VAL specificier{1, name} procedure.body = process La palabra clave PROC, el nombre del procedimiento y una lista de parametros formales encerrada entre par‚ntesis y el cuerpo del proceso identado a dos espacios. La definici¢n del procedimiento termina con el car cter `:` que aparece en una l¡nea nueva al mismo nivel que el comienzo de la palabra PROC. La sintaxis de la llamada a un procedimiento desde el programa es: instance = name({0, actual}) actual = element | expression Viene a ser el nombre del procedimiento seguido de una lista de cero ¢ m s par metros actuales entre par‚ntesis. Un par metro actual es un elemento o una expresi¢n. La lista de par metros actuales debe corresponderse directamente con la lista de par metros formales usados en la definici¢n del procedimiento, es decir, deben tener el mismo n£mero de par metros y adem s coincidir en clase y tipo. Debe hacerse notar que los procedimientos Occam no son recursivos. Un par metro de tipo canal canal o canal libre s¢lo puede usarse para entradas o salidas en el procedimiento, pero no para ambas cosas. Las reglas para los par metros usados en un procedimiento son las mismas que para las abreviaciones. I.2.5.4. Funciones Las funciones en Occam se refieren a una clase especial de procesos, aquellos que eval£an procesos. La sintaxis para una funci¢n es: value.process = valof valof = VALOF process RESULT expression.list | especification valof operand = (value.process ) expression.list = (value.process ) definition = {1, primitive type} FUNCTION name ({0, formal}) funtion.body : function.body = value.process operand = name ({0, expression}) expression.list = name ({0, expression}) definition = {1, primitive.type} FUNCTION name ({0, formal}) IS expression.list : Un proceso valor consiste en cero o m s especificaciones que preceden a la palabra VALOF, seguida de un proceso identado dos espacios y la palabra RESULT al mismo nivel que VALOF. RESULT viene seguida de una lista de expresiones en la misma l¡nea. La lista de expresiones puede partirse despu‚s de una coma o en otro punto v lido en la expresi¢n, tal y como se ha discutido en el contexto general de l¡neas de continuaci¢n. Un operando de una expresi¢n puede ser un proceso valor encerrado entre par‚ntesis. La cabecera de una definici¢n de funci¢n consiste en la palabra FUNCTION precedida del tipo (o tipos) del resultado (o resultados) de la funci¢n. A la palabra funci¢n le sigue el nombre de la funci¢n y una lista de par metros formales entre par‚ntesis. El proceso valor que constituye el cuerpo de la funci¢n debe comenzar en la siguiente l¡nea con una identaci¢n de dos espacios. La definici¢n de la funci¢n se completa con el car cter ':' en una nueva l¡nea al mismo nivel de identaci¢n que el comienzo de la definici¢n de la funci¢n. Una definici¢n alternativa a la descrita podr¡a ser, una definici¢n de funci¢n como la anterior seguida por la palabra IS y, una lista de expresiones seguida del car cter ':' en la misma l¡nea. Finalmente, cuando se define una funci¢n con cero par metros, debe incluirse un par de par‚ntesis vacios y, cuando los par metros que definen tienen el mismo tipo puede utilizarse un solo especificador. El proceso valor especificado por la funci¢n produce un resultado, que es de un tipo de datos primitivo y no de tipo array, que puede utilizarse en una expresi¢n. Los procesos valor pueden producir m s de un resultado que pueden utilizarse en m s de una asignaci¢n m£ltiple. Es £til tener en cuenta que la expresi¢n VALOF puede utilizarse en un programa Occam para devolver un valor, su uso no est  restringido a funciones. Por ejemplo, ser¡a v lido escribir en un programa Occam: [10]REAL32 A: ....... i := REAL32 tmp VALOF SEQ tmp := REAL32 0.0 SEQ ix = 0 FOR SIZE A tmp := tmp + A[ix] RESULT tmp Hay una serie de restricciones al tipo de contrucci¢n que puede aparecer en el contexto de un VALOF: - Las variables que est n asignadas deben declararse inmediatamente antes al VALOF o en el cuerpo del VALOF. - No puede haber contrucciones PAR. - No puede haber constructor ALT. - No puede haber operaciones con canales. Al igual que los procedimientos, las funciones se utilizan a menudo para separar secciones de c¢digo Occam que se van a utilizar frecuentemente. Sin embargo, al contrario que los procedimientos, las funciones devuelven uno o varios valores, m s que tener efecto en el entorno del proceso. Por ejemplo, una funci¢n que calcule la media de un array podr¡a escribirse como sigue: REAL32 FUNCTION average( VAL []REAL32 values) REAL32 sum: VALOF SEQ sum := REAL32 0.0 SEQ ix = 0 FOR SIZE values sum := sum + values[ix] RESULT sum / (REAL32 ROUND SIZE values) : Pueden utilizarse asignaciones multiples para escribir funciones como la siguiente: REAL32,REAL32 FUNCTION average.and.max(VAL []REAL32 values) REAL32 sum, max: VALOF SEQ sum, max := REAL32 0.0, values[0] SEQ ix = 0 FOR SIZE values sum := sum + values[ix] IF values[ix] > max max := values[ix] TRUE SKIP RESULT sum / (REAL32 ROUND SIZE values), max : A pesar de que en este ejemplo los datos devueltos son del mismo tipo (REAL32), no tiene que ser necesariamente as¡.