Oberón | |
---|---|
clase de idioma | imperativo , estructurado , modular |
Apareció en | 1986 |
Autor | niklaus wirth |
sistema de tipos | estático , fuerte |
sido influenciado | Módulo-2 , Pascal |
influenciado | Oberon activo , componente Pascal , Go , Java [1] [2] , Modula-3 , Oberon-2 , Zonnon , Nim |
Licencia | BSD |
Sitio web | proyectooberon.com |
Oberon es un lenguaje de programación de alto nivel diseñado por Niklaus Wirth para ejecutar programas en el sistema operativo del mismo nombre , creado por Niklaus Wirth y Jürg Gutknecht .
Los programas escritos en el lenguaje de programación Oberon requieren algún tipo de soporte de tiempo de ejecución: necesitan un cargador dinámico y un recolector de basura automático ejecutado centralmente, para lo cual los programas Oberon necesitan un entorno operativo especial. La forma habitual de implementarlo es agregar al sistema un conjunto de bibliotecas que implementen los componentes necesarios, aunque, en general, el entorno operativo no necesita necesariamente un sistema operativo separado: puede ser en sí mismo un sistema operativo. Estos son los sistemas Native Oberon para el Oberon original y A2 para el Active Oberon . Por el momento, hay compiladores de Oberon para el código de bytes de la máquina virtual de Java y una CLI para la máquina virtual de .NET .
Los sistemas operativos y entornos de ejecución de programas en lenguajes de la familia Oberon que evolucionaron del Oberon System original son ETH Oberon, BlackBox Component Builder , WinOberon , A2 , etc.
Oberon-0, Oberon-X y otros proyectos se desarrollaron sobre la base de Oberon [3] . La simplicidad de Oberon y la disponibilidad de los códigos fuente de la implementación original facilitan su adaptación para clases especiales de problemas. Pero todos estos Oberon están muy cerca unos de otros, ya que el Oberon original es muy simple.
El lenguaje de programación Oberon fue creado por Niklaus Wirth en 1988 basándose en los lenguajes de programación Modula-2 , Pascal y Algol-60 [4] .
Según Wirth, inicialmente querían escribir el sistema directamente en el Módulo, pero llegaron a la conclusión de que necesitaba ser refinado y reducido, lo que llevó a la aparición de Oberon [5] .
El objetivo del proyecto ( Eng. Project Oberon ) de Niklaus Wirth y Jürg Gutknecht en 1986-1989 [6] era crear desde cero un sistema operativo visible y confiable para una estación de trabajo de un solo usuario.
Para implementar este proyecto, en 1988 Niklaus Wirth diseñó un lenguaje de programación de alto nivel de propósito general, también llamado Oberon [7] .
En 1989, se lanzó la primera implementación de Oberon para la familia de procesadores NS32000 en ETH Zurich (ETH) . Fue creado como un componente del entorno operativo Oberon. Este compilador requiere menos de 50 KB de memoria, consta de 6 módulos con un volumen total de aproximadamente 4000 líneas y se compila en 15 segundos en una computadora con un procesador NS32532 (frecuencia de reloj: 25 MHz).
Es simplemente imposible agradecer a todos aquellos que, de una forma u otra, alimentaron con sus ideas lo que ahora se llama Oberón. La mayoría de las ideas surgieron del uso y aprendizaje de lenguajes existentes como Modula-2, Ada, Smalltalk y Cedar, que muchas veces nos advertían sobre lo que no debíamos hacer.Niklaus Wirth [5]
El lenguaje retuvo las características principales de la sintaxis de Modula y fue una extensión de objeto . Esto hizo posible abandonar el mecanismo de los módulos de registros variantes , que son un retiro del tipado estático fuerte original , lo que hizo posible introducir un mecanismo automático de gestión de memoria - recolección de basura : la posibilidad de liberar memoria asignada dinámicamente utilizando un operador especial se excluyó del lenguaje y, en cambio, el propio tiempo de ejecución contiene un módulo A que devuelve la memoria no utilizada al sistema. La gestión automática de la memoria es un medio para mejorar la fiabilidad de los programas con estructuras de datos dinámicas, ya que elimina los errores humanos, que son típicos, por ejemplo, de lenguajes como C / C++ .
Para lograr la mayor confiabilidad y rendimiento de la traducción, se acometió una importante simplificación del lenguaje eliminando características que se consideraban innecesarias (basado en la experiencia en el desarrollo, implementación y uso de otros lenguajes), o bien complicaban al compilador sin justificación suficiente en términos de rendimiento, o eran lo suficientemente complejos como para enviarse a bibliotecas externas, o no eran compatibles con la modularidad y los mecanismos automáticos de gestión de memoria: registros variantes , tipos enumerados, tipos de rango , conjuntos genéricos , tipo entero sin signo, módulos locales, módulos de definición, exportación Lists, operador for, la versión anterior de la instrucción with, una sintaxis especial para definir un programa principal. Los medios rudimentarios de admitir la programación paralela que estaban disponibles en el Módulo 2 no ingresaron al lenguaje, ya que servía a un sistema operativo de un solo usuario. El manejo de excepciones se ha descartado por simplicidad .
La descripción de los arreglos se ha simplificado (los índices de los arreglos solo pueden ser números enteros y siempre comienzan desde cero, como el lenguaje C), el uso de punteros es limitado: los punteros solo pueden existir en registros y arreglos, solo el módulo importado se especifica en importación listas, y cuando se utilizan nombres importados, una calificación obligatoria (indicación explícita del nombre del módulo exportador). En el artículo "De Modula a Oberon" [5] , Wirth explicaba en detalle los motivos de la eliminación de cada uno de los elementos.
Por razones de “mínimo suficiente”, los métodos (procedimientos y funciones asociados a un tipo) no se incluyeron en el lenguaje como un concepto sintáctico explícito, ya que este mecanismo en su forma más general es fácil de modelar creando campos de tipo procedimental en objetos (registros en el lenguaje Oberón) y asignándoles procedimientos correspondientes a los métodos. Por lo tanto, Oberon admite la programación orientada a objetos con medios mínimos para simplificar el proceso de traducción de código y acelerar este proceso.
Gracias a los cambios realizados, Oberon se ha vuelto sintácticamente más simple. La descripción de su sintaxis cabe en una página, la descripción completa del lenguaje ocupa unas 20 páginas, que es la mitad que la descripción de Modula-2 . Oberon es, si no mínimo, al menos uno de los lenguajes de programación universales de alto nivel más pequeños.
La declaración de Einstein fue elegida como epígrafe de la descripción del Oberón original: "Hazlo lo más simple posible, pero no más simple que eso " .
Definido en las siguientes propuestas RBNF [8] :
Módulo = ID de MÓDULO ";" [ ImportList ] Último declarado [ BEGIN Últimas declaraciones ] END id "." . ImportList = IMPORTAR [ id ":=" ] id { "," [ id ":=" ] id } ";" . LastDeclared = { CONST { DeclaredConst ";" } | TIPO { declaración de tipo ";" } | VAR { VarDeclarado ";" }} { ProcDeclarado ";" | Reenviar declarado ";" }. DeclaredConst = IdentDef "=" ConstExpression . TypeDeclare = IdentDef "=" Tipo . VarDeclarada = ListIdentifier ":" Tipo . DeclaredProc = PROCEDIMIENTO [ Receptor ] IdentDef [ FormalParam ] ";" Última declaración [ BEGIN Últimas declaraciones ] END Ident . ForwardDeclare = PROCEDIMIENTO "^" [ Receptor ] IdentDef [ FormalParam ]. FormalParam = "(" [ Sección FP { ";" Sección FP }] ")" [ ":" SpecIdent ]. SectionFP = [ VAR ] id { "," id } ":" Tipo . Receptor = "(" [ var ] id ":" id ")" . Tipo = ID de calidad | ARRAY [ ConstExpression { "," ConstExpression }] OF Tipo | RECORD [ "(" QualIdent ")" ] FieldList { ";" Lista de campos } FIN | PUNTERO A Tipo | PROCEDIMIENTO [ FormalParam ]. FieldList = [ ListIdent ":" Tipo ]. AfterOperators = Operador { ";" El operador }. Operador = [ Notación ":=" Expresión | Notación [ "(" [ ExpresiónDeLista ] ")" ] | IF Expr THEN Sentencia Seq { ELSIF Expr THEN Sentencia Seq } [ ELSE Sentencia Seq ] FIN | CASO Expresión OF Opción { "|" Variante } [ ELSE SentenciaSeq ] FIN | WHILE Express DO Instrucción Seq END | REPETIR SentenciaSeq HASTA Expresión | LOOP AfterStatements FIN | CON Guard DO Instrucción Seq FIN | SALIR | RETORNO [ Expreso ] ]. Opción = [ Etiquetas de opción { "," Etiquetas de opción } ":" Última declaración ]. VariantLabels = ConstExpression [ ".." ConstExpression ]. Guard = SpecId ":" SpecId . ConstExpression = Express . Expresión = ExpresiónSimple [ Relación ExpresiónSimple ]. ExpresiónSimple = [ "+" | "-" ] Plazo { Término de OperSlog }. Término \ u003d Multiplicador { OperMul Multiplier }. Multiplicador = Notación [ "(" [ ExpresiónDeLista ] ")" ] | número | símbolo | cadena | NULO | Conjunto | "(" Expresión ")" | " ~ " Multiplicador . Conjunto = "{" [ Elemento { "," Elemento }] "}" . Elemento = Expresar [ ".." Expresar ]. Relación = "=" | "#" | "<" | "<=" | ">" | ">=" | EN | ES . OperSlog = "+" | "-" | O . OperUmn = "*" | "/" | DIV | MOD | "&" . Designación = Calificador { "." identificación | "[" ExpresiónLista "]" | "^" | "(" QualIdent ")" }. ListExpr = Expresión { "," Express }. ListIdent = IdentDef { "," IdentDef }. QualID = [ identificador "." ] identificación . IdentDef = ident [ "*" | "-" ].El programa Oberon es un conjunto de módulos. En general, el módulo se parece a:
MÓDULO Nombre ; IMPORT ImportList ; Definiciones ; BEGIN Sentencias END Nombre .La lista de importación determina de qué módulos se importarán los nombres externos . Las definiciones incluyen definiciones de tipos, procedimientos, funciones, variables, constantes. En este caso, las definiciones de nombres marcados con asterisco son exportados por este módulo, es decir, serán visibles para otros módulos que importen este. En Oberon-2 también es posible marcar los nombres con un signo menos, en cuyo caso se exportan en modo de solo lectura.
El cuerpo del módulo se ejecuta cuando se carga. En Component Pascal, dentro del cuerpo de un módulo (en la sección BEGIN..END) ahora es posible agregar una sección CLOSE:
Sentencias BEGIN CLOSE Sentencias END Nombre .Aquí, las declaraciones entre BEGINy CLOSEse ejecutan cuando se carga el módulo, y las declaraciones entre CLOSEy se ejecutan cuando END se descarga de la memoria. Tal extensión se ha encontrado útil para los programas de componentes que cargan y descargan módulos dinámicamente .
Los tipos de datos creados por el programador se limitan al siguiente conjunto: tipos de matriz ARRAY , tipos de registro RECORD , tipos de procedimiento, tipos PROCEDURE de puntero POINTER . Un puntero solo se puede declarar a una matriz o registro.
La sintaxis de la parte interna del programa es bastante tradicional y simple. El lenguaje soporta el conjunto tradicional de construcciones: operador condicional, operador IFde selección CASE, ciclos (con precondición - WHILE, con postcondición REPEAT..UNTIL, incondicional - LOOP). Al igual que en el Módulo 2, se distinguen las letras mayúsculas y minúsculas en los identificadores, todas las palabras reservadas se escriben en mayúscula. Todas las construcciones del lenguaje, excepto un bucle REPEAT..UNTIL, terminan con una palabra clave ENDy permiten anidar dentro de varias declaraciones sin usar una declaración compuesta BEGIN..END. Naturalmente, como en el Módulo 2, no hay saltos incondicionales.
El paradigma de la programación orientada a objetos se apoya en el mecanismo de extensión de registros (el lenguaje no tiene una palabra clave separada para describir clases, como "clase" u "objeto", se considera que el concepto habitual de "tipo de registro" es bastante suficiente). Esencialmente, cada tipo de registro es una descripción de la clase y los campos del registro son miembros de datos de la clase.
En el Oberon original, no hay métodos (procedimientos y funciones asociados con la clase ) en absoluto. El mecanismo de método se puede utilizar declarando campos de tipo procedimental en el registro, a los que se les asignan procedimientos específicos cuando se crea una instancia de la clase. La llamada a dichos procedimientos se realiza de la forma tradicional de acceder al campo de registro, por defecto el procedimiento no conoce la instancia de la clase para la que fue llamado (no existe un mecanismo similar thisen C++ o Java), y si tal Para ello es necesaria información, la referencia a la instancia debe pasarse de forma explícita (por ejemplo, a través del parámetro ). La falta de métodos descritos explícitamente fue una de las cualidades del Oberon original, lo que provocó críticas de los programadores acostumbrados a los lenguajes híbridos tradicionales. Por otro lado, el mecanismo propuesto por Oberon le permite implementar todo lo que se puede implementar por los medios tradicionales de los lenguajes con métodos, y aún más: en Oberon, cada instancia de una clase puede tener su propia versión de un método ( el valor de un campo de tipo procedimental), mientras que cuando se describen métodos como parte de una clase, todas las instancias operan en una única variante de método. En Oberon 2, todavía se introdujeron métodos. Los métodos se describen por separado del tipo de registro, indicando el tipo con el que están asociados.
Un nuevo tipo de registro se puede declarar como una extensión de uno existente. En este caso, el tipo que se expande se especifica en la descripción de la entrada entre paréntesis después de la palabra clave RECORD. Un tipo extendido recibe automáticamente todos los campos del tipo extendido y (en Oberon 2) está asociado con todos los procedimientos asociados con el tipo extendido. Los procedimientos asociados con el nuevo tipo pueden tener la misma firma que los procedimientos asociados con el tipo que se está ampliando; esto garantiza que se anulen los métodos de los tipos derivados. En Component Pascal , para proporcionar un control estático completo sobre la coherencia de las jerarquías de herencia (y, por lo tanto, restaurar el principio de tipificación estática total que distinguía al Oberon original), los registros no son extensibles de forma predeterminada y los métodos no se pueden anular. Las palabras clave especialmente introducidas se utilizan para controlar la expansión de registros y la anulación de métodos EXTENSIBLE, ABSTRACT, LIMITED, EMPTY. En este caso, los métodos recién introducidos deben marcarse con una palabra clave NEW(cf. la definición obligatoria de variables recién introducidas).
Oberon está dirigido al desarrollo de software orientado a componentes [9] . La encapsulación se admite exclusivamente a nivel de módulo: todos los tipos declarados dentro del módulo son absolutamente transparentes entre sí. Lo que está disponible de otros módulos es lo que se declara como exportable en la definición.
El polimorfismo lo proporciona el mecanismo del método (tanto los campos de procedimiento en Oberon como los métodos en Oberon-2 se comportan como virtuales , en la terminología de la mayoría de los lenguajes híbridos orientados a objetos), así como una construcción CON extendida que le permite ejecutar diferentes grupos de declaraciones, dependiendo de a cuál de los tipos extendidos pertenece su argumento.
No hay un mecanismo constructor especial en el lenguaje. El método recomendado para crear e inicializar objetos es la descripción de la generación de módulos y procedimientos (fábrica en la terminología tradicional de programación orientada a objetos).
Un programa en esta tecnología es un conjunto de componentes relativamente independientes (en este caso, módulos) que tienen una estructura interna oculta al mundo exterior y una interfaz claramente definida. Los módulos se pueden cargar y descargar de forma dinámica mientras se ejecuta el programa, el sistema proporciona herramientas avanzadas de verificación de tipos en tiempo de ejecución que le permiten escribir algoritmos de procesamiento de datos universales que no dependen de los tipos específicos de estos datos (por ejemplo, una biblioteca para trabajar con un DBMS puede proporcionar métodos que escriben el resultado de una consulta de la base de datos en un registro de una estructura arbitraria, si el conjunto y tipos de campos en este registro corresponden al conjunto y tipos de campos en la base de datos).
En el paradigma del componente, se considera una decisión arquitectónica desafortunada asociada con el uso generalizado de la herencia de implementación de los tipos declarados en otro componente, ya que esto conduce a un fenómeno conocido como "fragilidad del tipo base", después de que una gran cantidad de tipos derivados se derivan de el tipo base (y algunos de ellos pueden incluso ser desconocidos para el desarrollador del tipo base), cualquier cambio en la implementación del tipo base se vuelve extremadamente riesgoso, ya que puede afectar a los tipos descendientes de manera impredecible.
Se sabe que uno de los problemas del uso de la programación orientada a objetos en la programación de sistemas es la necesidad de tener grupos de clases pequeñas que puedan interactuar sin sobrecarga adicional. Oberon no tiene este problema: todos los tipos definidos en un módulo se ven entre sí, y esto no crea problemas de confiabilidad, ya que el módulo aún se desarrolla, prueba y mantiene como un todo.
Un sistema típico desarrollado en Oberon es un conjunto de módulos con interfaces de procedimiento a través de las cuales los módulos intercambian datos, incluidos objetos. Al mismo tiempo, todas las herramientas de encapsulación funcionan solo en interacción entre módulos, lo que hace conveniente la programación del sistema usando objetos.
Programación orientada a objetosLas herramientas de programación de objetos se interpretan en Oberon como un desarrollo natural de las herramientas para trabajar con registros en un sistema modular, más precisamente, como un conjunto de herramientas técnicas para resolver un problema arquitectónico específico: asegurar una “división del trabajo” efectiva entre diferentes módulos cuando se trabaja. con tipos dinámicos y estructuras de datos: por ejemplo, el trabajo con punteros en la lista se puede ocultar (junto con los campos correspondientes) en un módulo, y la definición y el trabajo con el "relleno" específico de los elementos de la lista se pueden especificar en otro (o, más a menudo, otros). En este sentido, la tecnología de programación de objetos de Oberon está subordinada al concepto de modularidad: aquí es más un medio para describir datos que un medio para construir una arquitectura de aplicaciones como un todo.
Según Wirth [10] , los desarrolladores del lenguaje Java , varios años antes de su creación, “estudiaron los códigos fuente de Oberon y, en particular, los códigos fuente de los recolectores de basura de Oberon. Luego confundieron a Oberon con la sintaxis C y lo llamaron Java". Aunque no se puede exigir precisión absoluta en la redacción de una presentación oral, en cualquier caso, la indudable similitud de las ideologías de Oberon y Java (el deseo de minimalismo y tipeo fuerte, la restricción de la herencia múltiple, la gestión automática de la memoria) sugiere que hay cierto consenso sobre qué herramientas deben formar el núcleo de un lenguaje de programación moderno de propósito general. Sin embargo, si bien el minimalismo permanece a la vanguardia en Oberon y sus sucesores directos, los desarrolladores de Java han tomado el camino de desarrollar ampliamente las capacidades del lenguaje.
La propia familia de lenguajes Oberon también incluye Oberon-07 , Oberon-2 , Component Pascal ( Component Pascal ), Active Oberon , OberonScript , etc.
La versión original de Oberon ("Oberón clásico") es la más concisa, con la menor cantidad de palabras clave y construcciones sintácticas. Se utilizó como base para crear una familia de lenguajes, cada uno de los cuales amplía el clásico en alguna dirección o difiere de él en algunos detalles.
En 1992, Niklaus Wirth y su alumno Hanspeter Mössenböck son ahora profesores de la Universidad. Johannes Kepler en Linz - publicó una descripción de una versión aumentada de Oberon, llamada Oberon-2 . Es una versión refinada del clásico Oberón. Las adiciones hechas a Oberon 2, y hechas con mucha moderación, son las siguientes:
A pesar de la expansión del lenguaje, el volumen de la descripción formal de la sintaxis de Oberon-2 es menor que el del Oberon clásico debido a la optimización de la descripción sintáctica. Existe un compilador optimizador XDS [13] para Oberon-2; también hay un compilador [14] para el código de bytes de Java .
ETH Oberon , cuyas implementaciones están disponibles para muchas plataformas informáticas.
Oberon SA es una versión del lenguaje Oberon desarrollado por N. Wirth para el procesador Strong-ARM utilizado en un helicóptero no tripulado .
Basado en la experiencia de desarrollo de Oberon SA, en 2007 N. Wirth preparó cambios y adiciones al clásico Oberon [15] [16] para un soporte más estricto de programación estructurada que, por ejemplo, en Oberon-2 o Component Pascal. La nueva versión del lenguaje fue nombrada Oberon-07 [17] . Hay una traducción de "El lenguaje de programación Oberon, revisión 1.11.2008" al ruso [18] . Pero en términos de soporte para la programación orientada a objetos , el lenguaje Oberon-07 no sigue al Oberon-2, sino que continúa la línea minimalista del clásico Oberon, incluyendo la falta de soporte para procedimientos asociados a tipos de registros.
Oberon-07 tiene las siguientes diferencias principales con el Oberon clásico:
La empresa australiana CFB Software (Brisbane) de la Universidad de Queensland ha desarrollado el Astrobe IDE [21] para el lenguaje Oberon-07 para microcontroladores NXP (Philips) ARM7 y los diagramas de sintaxis del lenguaje Oberon-07 [22] , así como como pautas para el estilo de los programas en Oberon-07 [23] .
Inmediatamente después de su publicación en 1992, Oberon-2 fue considerado como candidato para el papel de estándar de lenguaje (Oakwood Conference, Croydon, 1993), pero la experiencia práctica adquirida en la creación de grandes sistemas de software reveló algunas debilidades de las innovaciones y la conveniencia de mayores refinamientos (lo que una vez más enfatiza la sabiduría del conservadurismo mostrado por Wirth al definir el Oberón clásico). Estos refinamientos se llevaron a cabo en una variante de Oberon-2 llamada Component Pascal y publicados en 1999 por Oberon microsystems [24] , formado en 1992 por los estudiantes de Wirth (el propio Wirth se convirtió en miembro de la junta directiva). Al igual que en la transición de Oberon a Oberon-2, estos refinamientos se realizan con moderación [25] . En particular, el lenguaje ahora es totalmente compatible con la metodología de programación orientada a componentes . Gracias a esta última circunstancia, el Componente Pascal es actualmente, aparentemente, el más perfecto entre los descendientes directos del Oberón clásico. Sin embargo, se puede reducir no solo a un subconjunto equivalente al Oberon original, sino también a otro subconjunto minimalista completo, en el que la herencia y la anulación de métodos solo se permiten para tipos y métodos puramente de interfaz (definidos con el atributo ABSTRACT). Esta circunstancia revela la naturaleza algo intermedia de Oberon-2.
El componente Pascal agrega funciones que permiten al desarrollador tener un control total sobre la extensión del tipo de exportación y la anulación del método (atributos EXTENSIBLE, ABSTRACTO, NUEVO, VACÍO, así como la posibilidad de exportar métodos de "solo implementación"). Se agregó el bloque de finalización del cuerpo del módulo (palabra clave CLOSE) y el método FINALIZE vacío predefinido. El sistema de tipos básicos (elementales) está alineado con los tipos de Java. Se ha introducido un tipo de cadena implícito. Oberon Microsystems, que definió Component Pascal , también lanzó BlackBox Component Framework y el entorno de programación visual BlackBox Component Builder [26] , de tamaño pequeño y poco exigente en recursos, construido completamente en Component Pascal.
Posteriormente, el compilador BlackBox se incorporó al entorno de programación multiplataforma Denia , en particular para el sistema operativo en tiempo real JBed , escrito íntegramente en Component Pascal.
Estos idiomas ya con razón pueden llamarse no extensiones o versiones de Oberon, sino idiomas independientes. Ampliaron significativamente la sintaxis, introdujeron construcciones para describir las "propiedades" clásicas (propiedad) con control de lectura / escritura, tipos numéricos con un tamaño específico en bits. Se introdujo soporte para objetos activos que intercambian mensajes en el formato definido por la descripción RBNF, manejo de excepciones [27] .