Sintaxis de Pascal
El propósito de esta página es proporcionar un resumen de la sintaxis,
expresada en BNF (Formas de Backus-Naur), del subconjunto del lenguaje Pascal
contemplado en la asignatura Programación I, curso
99/00.
Debe quedar claro que estas definiciones tan sólo incorporan la sintaxis
básica, y por lo tanto el que un elemento cumpla una definición no significa
necesariamente que sea una construcción correcta en Pascal. Por ejemplo, la
definición de sentencia de asignación, <Variable> :=
<Expresion> no incorpora la información de que el resultado de la
evaluación de la expresión debe ser de un tipo de datos compatible con el tipo
de la variable.
Ese tipo de información corresponde a la semántica del lenguaje, y sólo se
incorpora de manera parcial mediante construcciones del tipo Identificador
de funcion, que restringen los elementos permitidos en una parte de
la definición.
Cuando aparezca el símbolo ‡ en la parte derecha de una definición,
significa que existe un enlace a una página donde se proporciona información
adicional sobre ese elemento (significado, restricciones, etc).
Para facilitar la búsqueda de una definición concreta, elija el término en la
siguiente lista y pulse "Ver Definicion", o comienze con un elemento de alto
nivel como Programa ,
Unidad o
Tipo de
dato.
- Alternativa doble ::= if <Condicion>
then <Sentencia>
else <Sentencia>
- Alternativa multiple ::= case
<Expresion
de Tipo
ordinal> of <Caso>{;
<Caso>}
end
Alternativa multiple ::= case <Expresion
de Tipo
ordinal> of <Caso>{;
<Caso>}
else <Sentencia>
end ‡
- Alternativa simple ::= if <Condicion>
then <Sentencia>
- Bucle con numero fijo de
iteraciones ::= for <Sentencia
de asignacion> to <Expresion
de Tipo
ordinal> do <Sentencia>
Bucle con numero fijo de iteraciones ::= for <Sentencia
de asignacion> downto <Expresion
de Tipo
ordinal> do <Sentencia>
‡
- Bucle con salida al final ::=
repeat <Sentencia>{;
<Sentencia>}
until <Condicion>
- Bucle con salida al principio
::= while <Condicion>
do <Sentencia>
- Cabecera de funcion ::= function
<Identificador
de funcion> : <Identificador
de Tipo
elemental> ;
Cabecera de funcion ::= function <Identificador
de funcion> ( <Parametros
formales por valor>{; <Parametros
formales por valor>} ) : <Identificador
de Tipo
elemental> ;
- Cabecera de procedimiento ::=
procedure <Identificador
de procedimiento> ;
Cabecera de procedimiento ::=
procedure <Identificador
de procedimiento> ( <Parametros
formales>{; <Parametros
formales>} ) ;
- Cabecera de programa ::= program
<Identificador
de programa> ;
Cabecera de programa ::= program <Identificador
de programa> ( <Lista
de dispositivos> );
- Cabecera de unidad ::= unit <Identificador
de unidad> ;
- Caracter de subrayado ::= _
- Caso ::= <Rango
de literales> : <Sentencia>
- Comentario ::= { {<Caracter>}
}
Comentario ::= (* {<Caracter>}
*)
- Condicion ::= <Expresion
de tipo boolean>
- Conversion de tipo ::= <Identificador
de Tipo
ordinal> ( <Expresion
de Tipo
ordinal> )
- Cuerpo de funcion ::= <Sentencia
compuesta>
- Cuerpo de procedimiento ::= <Sentencia
compuesta>
- Cuerpo de programa ::= <Sentencia
compuesta>
- Declaracion de campos ::= <Identificador
de campo>{, <Identificador
de campo>} : <Identificador
de Tipo de
dato>
- Declaracion de constante ::= <Identificador
de constante> = <Literal>
Declaracion de constante ::= <Identificador
de constante> : <Identificador
de Tipo
elemental> = <Literal>
- Declaracion de funcion ::= <Cabecera
de funcion><Zona
de declaraciones><Cuerpo
de funcion>
- Declaracion de interfaz ::= <Cabecera
de funcion> | <Cabecera
de procedimiento>
- Declaracion de procedimiento ::=
<Cabecera
de procedimiento><Zona
de declaraciones><Cuerpo
de procedimiento>
- Declaracion de subprograma ::=
<Declaracion
de funcion> | <Declaracion
de procedimiento>
- Declaracion de variables ::= <Identificador
de variable>{, <Identificador
de variable>} : <Tipo de
dato>
- Definicion de tipo ::= <Identificador
de tipo> = <Tipo de
dato>
- Digito ::= 0 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9
- Dispositivo ::= input | output |
<Identificador
de variable tipo fichero>
- Elemento de array ::= <Variable
de Tipo
array> [ <Expresion
de Tipo
indice>{, <Expresion
de Tipo
indice>} ]
- Elemento de estructura ::= <Elemento
de array> | <Elemento
de registro>
- Elemento de registro ::= <Variable
de Tipo
registro> . <Identificador
de campo>
- Estructura alternativa ::= <Alternativa
simple> | <Alternativa
doble> | <Alternativa
multiple>
- Estructura de control ::= <Estructura
alternativa> | <Estructura
iterativa> | <Estructura
with>
- Estructura iterativa ::= <Bucle
con salida al principio> | <Bucle
con salida al final> | <Bucle
con numero fijo de iteraciones>
- Estructura with ::= with <Variable
de tipo registro> do <Sentencia>
- Expresion ::= <Literal>
| <Identificador
de constante> | <Variable>
| <Expresion
unaria> | <Expresion
binaria> | <Llamada
a funcion> | <Conversion
de tipo>
Expresion ::= ( <Expresion>
)
- Expresion binaria ::= <Expresion><Operador
binario><Expresion>
- Expresion unaria ::= <Operador
unario><Expresion>
- Identificador ::= <Letra>{<Letra> |
<Digito>}
- Letra ::= <Letra
mayuscula> | <Letra
minuscula> | <Caracter
de subrayado>
- Letra mayuscula ::= A | B |
C | D | E | F | G | H | I |
J | K | L | M | N | O | P |
Q | R | S | T | U | V | W |
X | Y | Z
- Letra minuscula ::= a | b |
c | d | e | f | g | h | i |
j | k | l | m | n | o | p |
q | r | s | t | u | v | w |
x | y | z
- Lista de dispositivos ::= <Dispositivo>{<Dispositivo>}
- Literal ::= <Literal
de tipo entero> | <Literal
de tipo real> | <Literal
de tipo caracter> | <Literal
de tipo boolean> | <Literal
de tipo string> | <Literal
de tipo conjunto> | <Literal
de tipo puntero> | <Literal
tipo enumerado>
- Literal de tipo boolean ::=
true | false ‡
- Literal de tipo caracter ::= '
<Caracter>
'
Literal de tipo caracter ::= # <Digito>{<Digito>}
‡
- Literal de tipo conjunto ::= [
]
Literal de tipo conjunto ::= [ <Rango
de valores> ]
- Literal de tipo entero ::= <Signo><Digito>{<Digito>}
- Literal de tipo puntero ::= nil
‡
- Literal de tipo real ::= <Signo><Digito>{<Digito>}
. <Digito>{<Digito>}
Literal de tipo real ::= <Signo><Digito>{<Digito>}
e <Signo><Digito>{<Digito>}
Literal de tipo real ::= <Signo><Digito>{<Digito>}
. <Digito>{<Digito>}
e <Signo><Digito>{<Digito>}
- Literal de tipo string ::= '
{<Caracter>}
'
- Literal tipo enumerado ::= <Identificador
de valor enumerado>
- Llamada a funcion ::= <Identificador
de funcion>
Llamada a funcion ::= <Identificador
de funcion> ( <Parametros
actuales por valor>{; <Parametros
actuales por valor>} )
- Llamada a procedimiento ::= <Identificador
de procedimiento>
Llamada a procedimiento ::= <Identificador
de procedimiento> ( <Parametros
actuales>{; <Parametros
actuales>} )
- Operador aritmetico entero ::=
+ | - | * | div | mod ‡
- Operador aritmetico real ::= +
| - | * | / ‡
- Operador binario ::= + | - |
* | div | mod | / | and | or |
= | <> | < | > | <= |
>= | in
- Operador de conjunto ::= + |
- | * | = | <> | <= | >=
| in ‡
- Operador de string ::= + ‡
- Operador logico ::= not | and |
or
- Operador relacional ::= = |
<> | < | > | <= | >=
- Operador unario ::= - | not
- Parametros actuales ::= <Parametros
actuales por valor> | <Parametros
actuales por variable>
- Parametros actuales por valor
::= <Expresion>
- Parametros actuales por
variable ::= <Variable>
- Parametros formales ::= <Parametros
formales por valor> | <Parametros
formales por variable>
- Parametros formales por valor
::= <Identificador
de variable>{, <Identificador
de variable>} : <Identificador
de tipo>
- Parametros formales por
variable ::= var <Identificador
de variable>{, <Identificador
de variable>} : <Identificador
de tipo>
- Programa ::= <Cabecera
de programa><Zona
de declaracion de unidades><Zona
de declaraciones><Cuerpo
de programa> .
- Rango de literales ::= <Literal de
Tipo
ordinal>
Rango de literales ::= <Literal de
Tipo
ordinal> .. <Literal de
Tipo
ordinal> ‡
Rango de literales ::= <Rango
de literales>{, <Rango
de literales>}
- Rango de valores ::= <Expresion
de Tipo
ordinal>
Rango de valores ::= <Expresion
de Tipo
ordinal> .. <Expresion
de Tipo
ordinal> ‡
Rango de valores ::= <Rango
de valores>{, <Rango
de valores>}
- Sentencia ::= <Sentencia
de asignacion> | <Sentencia
compuesta> | <Llamada
a procedimiento> | <Estructura
de control>
- Sentencia compuesta ::= begin
<Sentencia>{;
<Sentencia>}
end
- Sentencia de asignacion ::= <Variable>
:= <Expresion>
‡
- Signo ::= | + | -
- Tipo array ::= <Identificador
de tipo array>
Tipo array ::= array[ <Tipo
indice>{, <Tipo
indice>} ] of <Tipo de
dato>
- Tipo conjunto ::= <Identificador
de tipo conjunto>
Tipo conjunto ::= set of <Tipo
ordinal> ‡
- Tipo de dato ::= <Tipo
simple> | <Tipo
estructurado> ‡
- Tipo elemental ::= <Tipo
simple> | <Tipo
string>
- Tipo entero ::= integer | longint ‡
- Tipo enumerado ::= <Identificador
de tipo enumerado>
Tipo enumerado ::= ( <Literal
tipo enumerado>{, <Literal
tipo enumerado>} )
- Tipo estructurado ::= <Tipo
array> | <Tipo
registro> | <Tipo
fichero> | <Tipo
conjunto> | <Tipo
string>
- Tipo fichero ::= <Identificador
de tipo fichero>
Tipo fichero ::= text
Tipo fichero ::=
file of <Tipo de
dato> ‡
- Tipo indice ::= <Tipo
ordinal>
- Tipo ordinal ::= <Tipo
ordinal predefinido> | <Tipo
enumerado> | <Tipo
subrango>
- Tipo ordinal predefinido ::= <Tipo
entero> | char | boolean
- Tipo puntero ::= <Identificador
de tipo puntero>
Tipo puntero ::= ^ <Identificador
de Tipo de
dato> ‡
- Tipo real ::= real ‡
- Tipo registro ::= <Identificador
de tipo registro>
Tipo registro ::= record <Declaracion
de campos>{; <Declaracion
de campos>} end
- Tipo simple ::= <Tipo
ordinal> | <Tipo
real> | <Tipo
puntero>
- Tipo string ::= <Identificador
de tipo string>
Tipo string ::= string[ <Literal
de tipo entero> ] ‡
- Tipo subrango ::= <Identificador
de tipo subrango>
Tipo subrango ::= <Literal de
Tipo
ordinal> .. <Literal de
Tipo
ordinal>
- Unidad ::= <Cabecera
de unidad><Zona
de interfaz><Zona
de implementacion> end.
- Variable ::= <Identificador
de variable> | <Elemento
de estructura> | <Variable
dinamica>
- Variable dinamica ::= <Variable
de Tipo
puntero> ^ ‡
- Zona de declaracion de
constantes ::=
Zona de declaracion de constantes ::=
const <Declaracion
de constante>{; <Declaracion
de constante>} ;
- Zona de declaracion de
interfaces ::= <Declaracion
de interfaz>{; <Declaracion
de interfaz>} ;
- Zona de declaracion de
subprogramas ::=
Zona de declaracion de subprogramas ::= <Declaracion
de subprograma>{; <Declaracion
de subprograma>} ;
- Zona de declaracion de
unidades ::=
Zona de declaracion de unidades ::= unit
<Identificador
de unidad>{, <Identificador
de unidad>} ;
- Zona de declaracion de
variables ::=
Zona de declaracion de variables ::= var
<Declaracion
de variables>{; <Declaracion
de variables>} ; ‡
- Zona de declaraciones ::= <Zona
de declaracion de constantes><Zona
de definicion de tipos><Zona
de declaracion de subprogramas><Zona
de declaracion de variables> ‡
- Zona de definicion de tipos ::=
Zona de definicion de tipos ::= type <Definicion
de tipo>{; <Definicion
de tipo>} ;
- Zona de implementacion ::=
implementation <Zona
de declaracion de subprogramas>
- Zona de interfaz ::= interface <Zona
de declaracion de unidades><Zona
de declaracion de constantes><Zona
de definicion de tipos><Zona
de declaracion de interfaces>
Elementos de semántica de Pascal
case <Expresion de Tipo ordinal> of
<Caso>{; <Caso>} else <Sentencia> end
Este tipo de estructura tiene las siguientes características:
- La expresión se evalúa una única vez, antes de comenzar la estructura
- Los casos deben ser excluyentes entre sí, y por lo tanto no es posible que
un valor pertenezca a más de un caso.
- Cuando se ejecuta la sentencia asociada a un caso, la estructura termina.
- La sentencia asociada a la parte opcional else sólo se ejecuta si
el resultado de la expresión no pertenece a ningún caso. En esta última
situación, si no existiera la parte else, la estructura terminaría sin
ejecutar ninguna sentencia.
for <Variable de control del bucle> := <Expresion
1> to <Expresion 2> do <Sentencia>
Este tipo de estructura tiene las siguientes características:
- La variable de control del bucle no puede ser modificada en la(s)
sentencia(s) que forman parte del bucle.
- Las expresiones 1 y 2 se evaluan una única vez, antes de comenzar el
bucle.
- Los resultados de las expresiones 1 y 2 deben garantizar como mínimo una
ejecución del bucle. En concreto, el valor de la expresión 1 debe ser menor o
igual que el de la expresión 2 en un bucle to, y mayor o igual en un
bucle downto.
- La variable de control del bucle toma todos los valores comprendidos entre
entre el valor inicial (el resultado de la evaluacion de la expresion 1) y el
valor final (el resultado de la evaluacion de la expresion 2), ambos
inclusive.
- Tras la ejecución del bucle, el valor almacenado en la variable de control
del bucle es desconocido.
- Si N1 y N2 representan los resultados de la
evaluación de las expresiones 1 y 2, respectivamente, entonces el bucle se
repite (N2 - N1 + 1) veces en el caso de un bucle
to, y (N1 - N2 + 1) veces en el caso de un bucle
downto.
El valor false tiene asociado un valor ordinal de 0, y el valor
true un valor ordinal de 1. Al comparar valores de tipo boolean se cumple
que false < true.
La notación #<Número> representa al carácter que se encuentra en
esa posición de la tabla de caracteres. Es equivalente, por tanto, a
chr(Número).
Comparar caracteres es comparar las posiciones que ocupan en la tabla de
caracteres del sistema. No existe (todavía) una tabla de caracteres universal,
por lo que sólo podemos suponer las siguientes características, comunes a la
mayoria de tablas existentes:
- Los caracteres correspondientes a dígitos se encuentran antes que los
correspondientes a letras mayúsculas, que a su vez son anteriores a las letras
minúsculas. Cuando se habla de letras se debe entender que nos referimos al
alfabeto anglosajón, y por lo tanto no se incluyen la letra eñe, las vocales
acentuadas, ni otros símbolos particulares de nuestro alfabeto.
- Los grupos de caracteres 0..9, A..Z, a..z ocupan posiciones consecutivas,
y siguen el orden alfabetico normal. Nota: Los grupos generalmente no
son consecutivos, es decir al carácter 9 no le sigue el caráter A.
El literal nil es compatible con todos los tipos puntero. Como la
dirección representada por nil no puede corresponder a la de ninguna
variable dinámica, asignar a un puntero este valor sirve para indicar que el
puntero no almacena una dirección válida.
Los tipos enteros sirven para representar valores numéricos sin parte
decimal. Tienen una limitación de rango, es decir sólo pueden representar
valores dentro de unos límites determinados. Si alguna operación aritmetica
produce un valor fuera de esos limites, se produce un error de
desbordamiento. Los límites, para la versión de Turbo-Pascal que
utilizamos, son:
- Tipo integer: -(Maxint+1) .. Maxint
- Tipo longint: -231 .. (231-1)
Maxint es una constante predefinida que indica el valor máximo que se
puede representar en un valor de tipo integer.
Nota: Maxint = 215-1 = 32.767, 231 =
2.147.483.648. Los valores se codifican internamente en complemento a dos.
Los operadores aritmeticos enteros operan sobre valores de tipo
integer y longint. Si un operando es de tipo integer y el
otro de tipo longint, el operando de tipo integer se convierte
automáticamente a un valor de tipo longint y se pasa a calcular el
resultado, que será de tipo longint.
En el caso de los operadores cociente (div) y resto (mod), se
cumplen las siguientes relaciones: Dados c := a div b, r := a mod
b, se tiene que:
- (1) a = c*b + r
- (2) |r| < |b|
- (3) |c| = |a| div |b|
- (4) |r| = |a| mod |b|
Las ecuaciones (3) y (4) indican que cuando el dividendo y/o el divisor son
negativos, los valores absolutos del cociente y el resto proporcionados son
iguales a los obtenidos cuando el dividendo y el divisor son positivos, y los
signos del cociente y el resto se eligen de manera que se cumpla la ecuación
(1).
Los tipos reales sirven para representar valores numéricos con parte decimal.
La representación de valores tiene las siguientes limitaciones:
- El desbordamiento superior sucede cuando el valor a representar es
mayor, en terminos absolutos (sin tener en cuenta el signo), que el valor
máximo representable. En Turbo-Pascal este máximo vale aproximadamente
1,7*1038. Cualquier operación en la que se obtenga un resultado
absoluto mayor da un error.
- El desbordamiento inferior sucede cuando el valor a representar
esta más cerca de cero que un determinado límite. En Turbo-Pascal este límite
vale aproximadamente 2,9*10-38. Cualquier operación en la que se
obtenga un resultado absoluto menor da como resultado cero (no se produce
error).
- La perdida de precisión sucede cuando se necesita representar un
valor con más dígitos significativos (precisión) de la que es capaz el método
de codificación. En Turbo-Pascal los valores de tipo real almacenan los 11
dígitos decimales más significativos (es decir, sin tener en cuenta los ceros
iniciales y/o finales).
Esta última limitación indica que, salvo casos especiales, los valores de
tipo real no son una representación exacta del valor real, sino tan solo una
aproximación. Por lo tanto, es necesario evitar en lo posible
comparaciones de igualdad y desigualdad con valores de tipo real, ya que pueden
dar lugar a resultados inesperados. Por ejemplo, la comparación 3*(1/3) = 1.0 da
como resultado false.
Los operadores suma, resta y multiplicación proporcionan un resultado de tipo
real cuando alguno de sus operandos es de ese tipo. Si un operando es de
tipo integer o longint, se convierte automáticamente a un valor de
tipo real y se pasa a calcular el resultado.
El operador de división (/), sin embargo, proporciona siempre un
resultado de tipo real, incluso cuando ambos operandos son enteros y la
división tiene un resultado exacto.
En la siguiente tabla se muestran los operadores de conjuntos, el tipo de sus
argumentos y de su resultado:
Operador |
Significado |
Operando 1 |
Operando 2 |
Resultado |
+ |
Unión |
Conjunto tipo base T1 |
Conjunto tipo base T2 |
Conjunto tipo base
T3 |
* |
Intersección |
Conjunto tipo base T1 |
Conjunto tipo base T2 |
Conjunto tipo base
T3 |
- |
Diferencia |
Conjunto tipo base T1 |
Conjunto tipo base T2 |
Conjunto tipo base
T3 |
= |
Igualdad |
Conjunto tipo base T1 |
Conjunto tipo base T2 |
Boolean |
<> |
Desigualdad |
Conjunto tipo base T1 |
Conjunto tipo base T2 |
Boolean |
<= |
Subconjunto de |
Conjunto tipo base T1 |
Conjunto tipo base T2 |
Boolean |
in |
Pertenencia |
Valor de tipo T1 |
Conjunto tipo base T2 |
Boolean |
Los tipos de datos T1 y T2 deben cumplir alguna de las
siguientes condiciones:
- Ser el mismo tipo. En este caso el tipo T3 será el mismo que
T1 y T2.
- Que T1 sea un subrango de T2. El tipo resultado
T3 será igual a T2.
- Que T2 sea un subrango de T1. El tipo resultado
T3 será igual a T1.
- Que T1 y T2 sean subrangos del mismo tipo,
posiblemente con límites distintos. En este caso T3 será el menor
subrango cuyos límites incluyen a los de T1 y T2. Por
ejemplo, si T1 = 1..5 y T2 = 4..10, T3 sería
1..10.
El operador de concatenación de cadenas de caracteres (+) tiene la siguiente
sintaxis:
<Expresión de tipo T1> + <Expresión de tipo
T2>
Los tipos de datos T1 y T2 pueden ser cualquier tipo
string (independientemente de su número máximo de caracteres) o char. El
resultado es un valor de tipo string[n], donde n es el
tamaño mínimo del string que puede almacenar la cadena resultante.
En Turbo-Pascal, si la cadena resultante tiene más de 255 caracteres, no se
tienen en cuenta aquellos situados tras la posición 255.
La definición de un rango de valores se utiliza en la sentencia case
(los límites son literales) y en el constructor de conjuntos (los limites son
expresiones). Suponiendo la siguiente definición común a ambos casos:
<Valor N1>..<Valor N2>
Se deben cumplir las siguientes condiciones:
- N1 debe ser del mismo tipo que N2.
- N1 debe ser menor o igual que N2.
- En Turbo-Pascal, en el caso de construcción de conjuntos, se tiene la
limitación de que los valores no pueden ser negativos.
Nota: Los valores N1 y N2 pertenecen al rango
resultante.
<Variable de tipo T1> := <Expresión de tipo
T2>
La ejecución de una sentencia de asignación comprende los siguientes
pasos:
- Se evalua la expresión, obteniendose, si no se producen errores, un valor
de tipo T2. (Para mas detalles, ver Evaluación de
expresiones)
- Se comprueba que es posible asignar a la variable de tipo T1 un
valor de tipo T2. Para que sea posible, el valor de tipo
T2 debe estar dentro del rango de valores del tipo T1, y
se debe cumplir alguna de las siguientes condiciones:
- T1 y T2 son el mismo tipo, el cual no es fichero o
un tipo estructurado donde algun elemento sea de tipo fichero.
- T1 es longint y T2 es integer.
- T1 es real y T2 es integer o
longint.
- T1 y T2 son cadena de caracteres.
- T1 es cadena de caracteres y T2 es char.
- T1 y T2 son conjuntos cuyos tipos base son
compatibles.
- T1 es puntero y el resultado de la expresión es el valor
nil.
- Se asigna el valor a la variable.
Para ampliar información, vease Tipos de
datos compatibles.
Todos los tipos de datos son compatibles consigo mismos. Dos tipos distintos
son compatibles entre sí cuando cumplen alguna de las siguientes
condiciones:
- Un tipo es integer y el otro longint.
- Un tipo es un subrango del otro.
- Ambos son subrangos del mismo tipo.
- Ambos son conjuntos cuyos tipos base son compatibles entre sí.
- Ambos son cadenas de caracteres (string).
- Un tipo es cadena de caracteres y el otro es char.
El orden de evaluación de los elementos de una expresión sigue las siguientes
normas:
- Cualquier elemento encerrado entre paréntesis se evalúa primero.
- Si la expresión es una llamada a función, se evaluan los parámetros
actuales por valor, se asignan esos valores a las variables parámetros
formales de la función, se ejecuta la funcíon y la expresión se resuelve con
el ultimo valor asignado a la variable asociada a la función.
- Si en la expresión aparecen varios operadores, se ejecuta primero el
operador con mayor precedencia.
En el caso de operadores con la misma precedencia, se ejecuta primero el
operador situado más a la izquierda en la expresión.
Para que una expresión pueda evaluarse se deben dar las siguientes
condiciones:
- En el caso de operadores, los operandos deben ser del tipo requerido y/o
cumplir las reglas de compatibilidad
entre tipos.
- En el caso de llamadas a funciones, el tipo de los parametros actuales
(valores que se envián) deben ser compatibles
según las normas de la asignación con el tipo de los parámetros formales
(variables de la función que reciben los valores).
- No se produzcan problemas de desbordamiento
u operaciones no permitidas (dividir por cero, por ejemplo).
Nota: En el caso de parámetros por variable, el tipo de datos de la
variable parámetro actual y el tipo de datos de la variable parámetro formal
debe ser exactamente el mismo.
Los niveles de precedencia de los operadores de Pascal son los
siguientes:
Nivel |
Tipo de operador |
Operadores |
4 |
Operadores unarios |
not, - |
3 |
Operadores multiplicativos |
*, /, div, mod,
and |
2 |
Operadores aditivos |
+, -, or |
1 |
Operadores relacionales |
=, <>, <,
>, <=, >=,
in |
El tipo de datos conjunto tiene las siguientes características
especiales:
- El tipo base debe ser un ordinal, y en Turbo-Pascal existe la limitación
de que su cardinalidad (número de valores distintos que pertenecen al tipo)
debe ser menor de 256, y en el caso de tipos base numéricos no están
permitidos los valores negativos.
- Existen literales de tipo conjunto (ver Literal
de tipo conjunto).
- Existen operadores que se aplican a conjuntos (ver Operadores
de conjuntos).
- Son compatibles los valores conjunto cuyo tipo base sea compatible, aunque
su definición de tipo sea distinta.
- A diferencia del resto de tipos estructurados, los conjuntos no almacenan
valores del tipo base, sino la información sobre si un valor, en un momento
determinado, pertenece o no al conjunto.
El tipo de datos cadena de caracteres (string) es una especialización basada
en los arrays unidimensionales de caracteres a la que se añade funcionalidad
para facilitar la representación de texto. Entre sus características especiales
destacan:
- Comparte las características de un array de caracteres indexado desde 1
hasta el tamaño máximo indicado en su definición.
- No todos los elementos (caracteres) de un string se considera que forman
parte de la cadena de caracteres en un momento dado: Sólamente aquellos cuyo
índice esté comprendido entre 1 y la longitud actual del string. La
información referente a la longitud se obtiene mediante la función predefinida
length, y no puede cambiarse directamente, sino que se adapta
automaticamente en las operaciones de asignación entre cadenas de caracteres y
al usar los operadores, funciones y procedimientos predefinidos que actuan
sobre cadenas de caracteres.
- Sólo puede accederse, en un momento dado, a aquellos elementos
(caracteres) de un string cuyo índice se encuentre comprendido entre 1 y la
longitud actual del string (es decir, a aquellos caracteres que forman parte
de la cadena). Se debe tener en cuenta, además, que una cadena de caracteres
puede estar vacía (su longitud sea igual a cero).
- Existen literales de tipo cadena de caracteres.
- Se pueden definir funciones que devuelvan un valor de tipo cadena de
caracteres.
- Existen operadores, funciones y procedimientos predefinidos que se aplican
a cadenas de caracteres (ver Operadores
de string y Subprogramas
predefinidos que se aplican a string).
- Se pueden leer y escribir directamente en dispositivos de tipo texto.
- En la versión de Turbo-Pascal que estamos usando no se pueden definir
tipos string cuyo tamaño máximo supere los 255 caracteres.
Los ficheros, al representar datos externos al programa y tener un acceso
secuencial, tienen un tratamiento distinto al de los otros tipos estructurados.
Algunas de esas características especiales son:
Definición de
ficheros:
- Las variables tipo fichero (denominadas ficheros lógicos, en
contraposición con los ficheros físicos, los ficheros reales) no
almacenan los datos del fichero, sino que sirven de intermediario para
poder acceder a un determinado fichero físico.
- El tipo base de un fichero no puede ser a su vez un tipo fichero, puesto
que no tiene sentido almacenar ficheros lógicos.
- No se puede asignar a una variable tipo fichero el contenido de otra
variable tipo fichero. No tiene sentido el tener dos variables con la misma
información de acceso a un fichero físico. Consecuentemente, tampoco es
posible que varias variables tipo fichero tengan asignado y abierto,
simultaneamente, el mismo fichero físico.
- Debido a lo anterior, al pasar un fichero como parámetro a un subprograma
siempre es necesario hacerlo por variable (un paso por valor es
equivalente a una asignación).
Uso de ficheros:
- El acceso a los datos de un fichero se debe hacer mediante una serie fija
de etapas (asignación de un fichero físico a un fichero lógico, apertura del
fichero para lectura o creación-escritura, comprobación de fin de
fichero-lectura de datos o escritura de datos, y cierre del fichero) que
requieren la utilizacion de subprogramas
predefinidos para acceso a ficheros.
- Un fichero sólo se puede abrir o bien para lectura o bien para
creación-escritura. No se puede leer y escribir simultaneamente en un fichero.
- Abrir un fichero para escritura supone crear un fichero físico nuevo y
vacío. Si previamente existía un fichero físico con el mismo nombre, los datos
que contenía se pierden al abrirlo para escritura. Por lo tanto no es posible
añadir datos nuevos al final de un fichero abriendolo para escritura, es
necesario usar otros medios (usar ficheros auxiliares, por ejemplo).
- No es posible conocer anticipadamente el número de elementos de un fichero
físico. Por lo tanto, antes de cualquier lectura de un dato es necesario haber
comprobado que realmente quedan datos por leer en el fichero (mediante la
fúncion eof).
- Cualquier fichero puede estar vacío, por lo tanto no es posible aplicar
directamente tecnicas de lectura adelantada sobre ficheros.
- Cualquier modificación del contenido de un fichero físico requiere la
utilización de ficheros auxiliares.
La tabla siguiente muestra características de los tipos de datos predefinidos
de Turbo-Pascal:
Tipo de dato |
Simple |
Ordinal |
Elemental |
Imprimible |
Literales |
Acceso |
Entero |
Si |
Si |
Si |
Si |
Si |
- |
Real |
Si |
No |
Si |
Si |
Si |
- |
Carácter |
Si |
Si |
Si |
Si |
Si |
- |
Lógico |
Si |
Si |
Si |
No |
Si |
- |
Enumerado |
Si |
Si |
Si |
No |
Si |
- |
Subrango |
Si |
Si |
Si |
(1) |
Si |
- |
Puntero |
Si |
No |
Si |
No |
No |
- |
Array |
No |
No |
No |
No |
No |
Por índice |
Registro |
No |
No |
No |
No |
No |
Por campo |
Fichero |
No |
No |
No |
No |
No |
Secuencial |
Conjunto |
No |
No |
No |
No |
Si |
(2) |
String |
No |
No |
Si |
Si |
Si |
Por
índice |
- Igual que el tipo de datos del que es subrango.
- Un conjunto tan solo permite preguntar si un valor pertenece o no en un
momento dado al conjunto.
Significado de las columnas:
- Elemental: Se pueden definir funciones que devuelvan este tipo de
datos.
- Imprimible: Se puede leer o escribir valores de este tipo de datos
directamente en dispositivos de tipo texto (usando read, readln,
write o writeln).
- Literales: Se pueden especificar directamente valores de este tipo
de datos.
- Acceso: Para tipos estructurados, la estrategia de acceso a sus
elementos.
A la hora de trabajar con variables dinámicas es necesario asegurarse de que
la dirección almacenada en la variable puntero corresponde realmente a la de una
variable dinámica, puesto que Pascal no verifica que sea así: Si usamos una
dirección incorrecta, podemos inadvertidamente estar cambiando el valor de otras
variables dinámicas, estáticas o incluso el propio código de nuestro programa o
el de cualquier otro que se esté ejecutando en el ordenador.
Para que esto no ocurra, es necesario asegurarse de que al puntero usado para
acceder a la variable dinámica le ha sido asignado una dirección válida mediante
una llamada al procedimiento new o por una asignación de un puntero que
almacenaba una dirección válida.
Tras la llamada al procedimiento dispose, la dirección que almacena el
puntero proporcionado deja de ser válida (la memoria que ocupaba la variable
dinámica situada en esa dirección se libera y pasa a estar disponible para otros
usos).
La definición de un tipo puntero tiene dos partes diferenciadas:
Tipo puntero ::= ^ <Tipo de dato asociado>
- El acento circumflejo (^) significa que las variables de este tipo de
datos son punteros, y por lo tanto almacenarán direcciones de memoria.
- El tipo de datos asociado no tiene nada que ver el tipo puntero que se
esta definiendo: Tan sólo sirve para indicar el tipo de datos que almacenaría
una hipotetica variable dinámica cuya dirección estuviera almacenada en una
variable de este tipo puntero.
Las variables de tipo puntero almacenan direcciones de memoria. No es
necesario que conozcamos qué es exactamente una dirección de memoria, sino tan
solo que mediante esos valores es posible hacer referencia a variables
dinámicas.
La dirección almacenada en un puntero puede ser, en un momento dado,
válida o no válida. Es válida cuando represente la dirección de
una variable dinámica existente.
Una variable dinámica existe cuando ha sido creada mediante una
llamada al procedimiento new, y todavia no ha sido destruida mediante una
llamada al procedimiento dispose.
Es necesario tener en cuenta que los valores de tipo puntero son un mero
intermediario para permitir el trabajar con variables dinámicas, y que no
guardan más relación con ellas que las de almacenar su dirección.
Turbo-Pascal permite que escribamos las zonas de declaraciones en cualquier
orden, e incluso repitamos zonas en sitios distintos. Para los programas
desarrollados en el marco de la asignatura se va a imponer, sin embargo, la
siguiente restricción: Sólo debe existir una zona de declaración de variables
y debe estar situada despues de cualquier declaración de subprogramas.
De esta forma se garantiza que ningún subprograma puede hacer uso de
variables globales a él, cumpliendo la regla de la programación modular que
establece que los módulos deben ser independientes (respecto a los datos que
manejan) entre sí.
Operaciones matemáticas:
Subprograma |
Tipo |
Parámetros |
Resultado |
abs(x) |
Función |
x de tipo entero o real |
El valor absoluto de x |
odd(x) |
Función |
x de tipo entero |
true si x es impar, false si
es par |
random(x) |
Función |
x de tipo entero |
Un entero entre 0 y x - 1, escogido al
azar |
sin(x) |
Función |
x de tipo real |
El seno de x (x son
radianes) |
cos(x) |
Función |
x de tipo real |
El coseno de x (x son
radianes) |
arctan(x) |
Función |
x de tipo real |
El arcotangente de x, expresado en
radianes |
ln(x) |
Función |
x de tipo real |
El logaritmo neperiano de
x |
exp(x) |
Función |
x de tipo real |
La exponencial de x
(ex) |
Operaciones de conversión entre tipos:
Subprograma |
Tipo |
Parámetros |
Resultado |
chr(x) |
Función |
x de tipo entero |
El carácter en la posición x de la tabla de
caracteres |
trunc(x) |
Función |
x de tipo real |
La parte entera de x |
round(x) |
Función |
x de tipo real |
El valor entero más cercano a
x |
Operaciones ordinales:
Subprograma |
Tipo |
Parámetros |
Resultado |
ord(x) |
Función |
x de tipo ordinal |
La posición del valor x en su tipo de datos
(1) |
succ(x) |
Función |
x de tipo ordinal |
El valor siguiente de su mismo
tipo |
pred(x) |
Función |
x de tipo ordinal |
El valor anterior de su mismo
tipo |
(1) En el caso de argumentos enteros, ord devuelve su mismo valor. En
el resto de casos, devuelve la posición del valor comenzando a contar por cero.
Nota: La función ord no tiene en cuenta si el argumento proviene o
no de una variable de tipo subrango.
Subprograma |
Tipo |
Parámetros |
Resultado |
length(c) |
Función |
c valor de tipo string |
Longitud de la cadena |
copy(c,i,n) |
Función |
c valor de tipo
string i,n de tipo entero |
La subcadena de c que comienza a
partir de la posición i y tiene longitud
n |
insert(s,c,i) |
Procedimiento |
s valor de tipo
string c variable de tipo
string i de tipo entero |
Inserta la subcadena s en la cadena
c a partir de la posición i |
delete(c,i,n) |
Procedimiento |
c variable de tipo
string i,n de tipo entero |
Elimina de c la subcadena que
comienza a partir de la posición i y tiene longitud
n |
pos(s,c) |
Función |
s, c valor de tipo
string |
La primera posición de c donde
aparece la subcadena s, o cero si no
aparece |
Subprograma |
Tipo |
Parámetros |
Resultado |
assign(f,c) |
Procedimiento |
f de tipo fichero c de tipo
string |
Asigna un fichero físico a un fichero
lógico |
reset(f) |
Procedimiento |
f de tipo fichero |
Abre un fichero para lectura |
rewrite(f) |
Procedimiento |
f de tipo fichero |
Abre un fichero para
creación-escritura |
eof(f) |
Función |
f de tipo fichero de datos |
false si quedan datos por
leer true si no quedan datos por leer |
eof(f) |
Función |
f de tipo fichero de texto |
false si quedan lineas true si no
quedan lineas |
eoln(f) |
Función |
f de tipo fichero de texto |
false si quedan caracteres en la linea
actual true si no quedan caracteres en la linea
actual |
read(f,v) |
Procedimiento |
f de tipo fichero de
datos v variable de tipo base |
Lee un dato del fichero y lo asigna a la
variable |
read(f,v) |
Procedimiento |
f de tipo fichero de
texto v variable de tipo imprimible |
Lee caracteres, los traduce a un
valor del tipo de la variable y se lo asigna |
readln(f,v) |
Procedimiento |
f de tipo fichero de
texto v variable de tipo imprimible |
Igual que read, y además pasa a la
siguiente linea ignorando los caracteres que quedan en la linea
actual |
readln(f) |
Procedimiento |
f de tipo fichero de texto |
Pasa a la siguiente linea ignorando
los caracteres que quedan en la linea actual |
write(f,v) |
Procedimiento |
f de tipo fichero de
datos v variable de tipo base |
Escribe en el fichero el
dato almacenado en la variable |
write(f,e) |
Procedimiento |
f de tipo fichero de
texto e expresión de tipo imprimible |
Traduce el valor resultante de la expresión a
una secuencia de caracteres y la escribe en el
fichero |
writeln(f,e) |
Procedimiento |
f de tipo fichero de
texto e expresión de tipo imprimible |
Igual que write, y además escribe
los caracteres que indican el final de
linea |
writeln(f) |
Procedimiento |
f de tipo fichero de texto |
Escribe los caracteres que indican el final de
linea |
close(f) |
Procedimiento |
f de tipo fichero |
Cierra un
fichero |