La regla fuera de juego describe la sintaxis de un lenguaje de programación de computadoras que define los límites de un bloque de código mediante sangría . [1] [2]
El término fue acuñado por Peter Landin , posiblemente como un juego de palabras con la ley del fuera de juego en el fútbol asociativo .
Un lenguaje de reglas fuera de juego se contrasta con un lenguaje de forma libre en el que la sangría no tiene significado sintáctico y la sangría es estrictamente una cuestión de estilo .
También se describe que un lenguaje de regla de fuera de juego tiene una sangría significativa .
Peter Landin , en su artículo de 1966 " Los próximos 700 lenguajes de programación ", definió la regla del fuera de juego de la siguiente manera: "Cualquier ficha que no sea un espacio en blanco a la izquierda de la primera ficha en la línea anterior se considera el comienzo de una nueva declaración." [3]
El siguiente es un ejemplo de bloques de sangría en Python ; un lenguaje popular de reglas de fuera de juego. En Python, la regla se utiliza para definir los límites de las declaraciones en lugar de las declaraciones.
def es_par ( a : int ) -> bool : si un % 2 == 0 : imprimir ( '¡Incluso!' ) devolver verdadero imprimir ( '¡Extraño!' ) falso retorno
El cuerpo de la función comienza en la línea 2 ya que tiene una sangría de un nivel (4 espacios) más que la línea anterior. El if
cuerpo de la cláusula comienza en la línea 3, ya que tiene una sangría de un nivel adicional, y termina en la línea 4, ya que la línea 5 tiene una sangría de un nivel menor, también conocida como sangría.
Los dos puntos ( :
) al final de una línea de declaración de control son la sintaxis de Python; no es un aspecto de la regla del fuera de juego. La regla se puede implementar sin dicha sintaxis de dos puntos.
La regla de fuera de juego se puede implementar en la fase de análisis léxico , como en Python , donde aumentar la sangría da como resultado que el lexer genere un INDENT
token y disminuir la sangría da como resultado que el lexer genere un DEDENT
token. [4] Estos tokens corresponden a la llave de apertura {
y la llave de cierre }
en los idiomas que usan llaves para los bloques, y significa que la gramática de la frase no depende de si se usan llaves o sangría. Esto requiere que el lexer mantenga el estado, es decir, el nivel de sangría actual y, por lo tanto, pueda detectar cambios en la sangría cuando ésta cambia y, por lo tanto, la gramática léxica no está libre de contexto y depende de la información contextual del nivel de sangría anterior.INDENT
DEDENT
La principal alternativa a delimitar bloques mediante sangría, popularizada por el amplio uso y la influencia del lenguaje C , es ignorar los espacios en blanco y marcar los bloques explícitamente con llaves (es decir, {
y }
) o algún otro delimitador. Si bien esto permite una mayor libertad de formato (un desarrollador puede optar por no aplicar sangría a pequeños fragmentos de código, como las declaraciones de interrupción y continuación ), un código con sangría descuidada puede desviar al lector, como el error de ir a fallar .
Lisp y otros lenguajes basados en expresiones S no diferencian declaraciones de expresiones, y los paréntesis son suficientes para controlar el alcance de todas las declaraciones dentro del lenguaje. Al igual que en los idiomas con llaves, el lector ignora en su mayor parte los espacios en blanco (es decir, la función de lectura). Los espacios en blanco se utilizan para separar tokens. [5] La estructura explícita del código Lisp permite la sangría automática para formar una señal visual para los lectores humanos.
Otra alternativa es que cada bloque comience y termine con palabras clave explícitas. Por ejemplo, en ALGOL 60 y su descendiente Pascal , los bloques comienzan con palabra clave begin
y terminan con palabra clave end
. En algunos idiomas (pero no en Pascal), esto significa que las nuevas líneas son importantes [ cita necesaria ] (a diferencia de los idiomas con llaves), pero la sangría no lo es. En BASIC y Fortran , los bloques comienzan con el nombre del bloque (como IF
) y terminan con el nombre del bloque antepuesto END
(por ejemplo, END IF
). En Fortran , todos y cada uno de los bloques también pueden tener su propio nombre de bloque único, lo que añade otro nivel de claridad al código extenso. ALGOL 68 y el shell Bourne (sh y bash ) son similares, pero el final del bloque generalmente viene dado por el nombre del bloque escrito al revés (por ejemplo, case
inicia una instrucción de cambio y se extiende hasta la coincidencia ; condicionalesesac
similares ... . ...[ ...[ ...]] o bucles for ... ... en ALGOL68 o ... ... en bash). if
then
elif
else
fi
for
do
od
for
do
done
Una variante interesante de esto ocurre en Modula-2 , un lenguaje similar a Pascal que elimina la diferencia entre bloques de una y varias líneas. Esto permite omitir el abridor de bloque ( {
o ) para todos menos el bloque de nivel de función, lo que requiere solo un token de terminación de bloque ( o ). También arregla cosas que cuelgan . Lo personalizado es que el token se coloque en el mismo nivel de sangría que el resto del bloque, lo que proporciona una estructura de bloque que es muy legible.BEGIN
}
END
end
Una ventaja del enfoque Fortran es que mejora la legibilidad de código largo, anidado o complejo. Un grupo de sangrías o corchetes de cierre por sí solos no proporciona señales contextuales sobre qué bloques se están cerrando, lo que requiere retroceder y un escrutinio más detenido durante la depuración . Además, los lenguajes que permiten un sufijo para palabras clave similares a END mejoran aún más dichas señales, como continue
versus continue for x
y el marcador de final de bucle que especifica la variable de índice NEXT I
versus NEXT
, y bucles con nombres únicos CYCLE X1
versus CYCLE
. Sin embargo, los editores de código fuente modernos suelen proporcionar indicadores visuales, como resaltado de sintaxis , y funciones como el plegado de código para ayudar con estos inconvenientes.
En el lenguaje Scala , las primeras versiones solo permitían llaves. Scala 3 agregó una opción para usar sangría para estructurar bloques. El diseñador Martin Odersky dijo que esta fue la forma más importante en que Scala 3 mejoró su propia productividad, que hace que los programas sean más de un 10% más cortos y mantiene a los programadores "en el flujo", y recomienda su uso. [6]
#light
se especifica; en versiones posteriores, cuando #light "off"
no [7] )where
, let
, do
o case ... of
cuando se omiten las llaves)