En programación informática , una macro (abreviatura de " instrucción macro "; del griego μακρο - 'largo, grande' [1] ) es una regla o patrón que especifica cómo se debe asignar una determinada entrada a una salida de reemplazo. La aplicación de una macro a una entrada se conoce como expansión de macro . La entrada y la salida pueden ser una secuencia de tokens léxicos o caracteres , o un árbol sintáctico . Las macros de caracteres se admiten en aplicaciones de software para facilitar la invocación de secuencias de comandos comunes . Las macros de token y árbol se admiten en algunos lenguajes de programación para permitir la reutilización de código o para extender el lenguaje, a veces para lenguajes específicos del dominio .
Las macros se utilizan para poner a disposición del programador una secuencia de instrucciones informáticas como una única declaración de programa , lo que hace que la tarea de programación sea menos tediosa y menos propensa a errores. [2] [3] Por ello, se denominan "macros" porque un bloque "grande" de código se puede expandir a partir de una secuencia "pequeña" de caracteres. Las macros suelen permitir parámetros posicionales o de palabras clave que dictan lo que genera el programa ensamblador condicional y se han utilizado para crear programas completos o conjuntos de programas de acuerdo con variables como el sistema operativo , la plataforma u otros factores. El término deriva de "instrucción macro", y dichas expansiones se utilizaron originalmente para generar código en lenguaje ensamblador .
Las macros de teclado y de ratón permiten transformar secuencias cortas de pulsaciones de teclas y acciones del ratón en otras secuencias de pulsaciones de teclas y acciones del ratón que suelen requerir más tiempo. De esta forma, se pueden automatizar secuencias de pulsaciones de teclas y movimientos del ratón que se utilizan con frecuencia o que se repiten . Los programas independientes para crear estas macros se denominan grabadores de macros .
Durante la década de 1980, los programas de macros (originalmente SmartKey , luego SuperKey, KeyWorks, Prokey) fueron muy populares, primero como un medio para formatear automáticamente los guiones y luego para una variedad de tareas de entrada de usuario. Estos programas se basaban en el modo de operación de terminación y permanencia residente y se aplicaban a todas las entradas de teclado, sin importar en qué contexto ocurrieran. Hasta cierto punto, han caído en desuso tras la llegada de las interfaces de usuario controladas por mouse y la disponibilidad de macros de teclado y mouse en aplicaciones, como procesadores de texto y hojas de cálculo , lo que hace posible crear macros de teclado sensibles a la aplicación.
Las macros del teclado se pueden utilizar en los juegos de rol multijugador masivos en línea (MMORPG) para realizar tareas repetitivas, pero lucrativas, acumulando así recursos. Como esto se hace sin esfuerzo humano, puede distorsionar la economía del juego. Por este motivo, el uso de macros es una violación de los términos de servicio o del acuerdo de licencia de usuario final de la mayoría de los MMORPG, y sus administradores dedican un esfuerzo considerable para eliminarlas. [4]
Las macros de teclado y ratón que se crean utilizando las funciones de macro integradas de una aplicación a veces se denominan macros de aplicación . Se crean ejecutando la secuencia una vez y dejando que la aplicación registre las acciones. También puede existir un lenguaje de programación de macros subyacente, más comúnmente un lenguaje de script , con acceso directo a las funciones de la aplicación.
El editor de texto para programadores Emacs (abreviatura de "macros de edición") sigue esta idea hasta el final. En efecto, la mayor parte del editor está compuesto por macros. Emacs fue concebido originalmente como un conjunto de macros en el lenguaje de edición TECO ; más tarde fue adaptado a dialectos de Lisp .
Otro editor de texto para programadores, Vim (un descendiente de vi ), también tiene una implementación de macros de teclado. Puede registrar en un registro (macro) lo que una persona escribe en el teclado y puede reproducirse o editarse como las macros de VBA para Microsoft Office. Vim también tiene un lenguaje de script llamado Vimscript [5] para crear macros.
Visual Basic para Aplicaciones (VBA) es un lenguaje de programación incluido en Microsoft Office desde Office 97 hasta Office 2019 (aunque estaba disponible en algunos componentes de Office antes de Office 97). Sin embargo, su función ha evolucionado y reemplazado a los lenguajes de macros que originalmente se incluían en algunas de estas aplicaciones.
XEDIT , que se ejecuta en el componente Conversational Monitor System (CMS) de VM , admite macros escritas en EXEC , EXEC2 y REXX , y algunos comandos CMS eran en realidad envoltorios de macros XEDIT. Hessling Editor (THE), un clon parcial de XEDIT, admite macros Rexx mediante Regina y Open Object REXX (oorexx). Muchas aplicaciones comunes, y algunas en PC, utilizan Rexx como lenguaje de script.
VBA tiene acceso a la mayoría de las llamadas del sistema de Microsoft Windows y se ejecuta cuando se abren los documentos. Esto hace que sea relativamente fácil escribir virus informáticos en VBA, comúnmente conocidos como virus de macro . A mediados y finales de la década de 1990, este se convirtió en uno de los tipos de virus informáticos más comunes. Sin embargo, durante finales de la década de 1990 y hasta la fecha, Microsoft ha estado parcheando y actualizando sus programas. [ cita requerida ] Además, los programas antivirus actuales contrarrestan inmediatamente este tipo de ataques.
Una macro parametrizada es una macro que puede insertar determinados objetos en su expansión. Esto le otorga a la macro parte del poder de una función .
Como ejemplo simple, en el lenguaje de programación C , esta es una macro típica que no es una macro parametrizada, es decir, una macro sin parámetros :
#define PI 3.14159
Esto hace PI
que siempre se reemplace 3.14159
por donde aparece. Un ejemplo de una macro parametrizada, por otro lado, es el siguiente:
#define pred(x) ((x)-1)
La expansión de esta macro depende del argumento x que se le pase. A continuación se muestran algunas expansiones posibles:
pred(2) → ((2) -1) pred(y+2) → ((y+2) -1) pred(f(5)) → ((f(5))-1)
Las macros parametrizadas son un mecanismo útil a nivel de fuente para realizar expansión en línea , pero en lenguajes como C donde utilizan sustitución textual simple, tienen una serie de desventajas graves sobre otros mecanismos para realizar expansión en línea, como las funciones en línea .
Las macros parametrizadas utilizadas en lenguajes como Lisp , PL/I y Scheme , por otro lado, son mucho más potentes, capaces de tomar decisiones sobre qué código producir en función de sus argumentos; por lo tanto, pueden utilizarse de manera efectiva para realizar la generación de código en tiempo de ejecución .
Los lenguajes como C y algunos lenguajes ensambladores tienen sistemas de macros rudimentarios, implementados como preprocesadores para el compilador o ensamblador. Las macros de preprocesador de C funcionan mediante una simple sustitución textual en el token , en lugar de en el nivel de caracteres. Sin embargo, las funciones de macros de ensambladores más sofisticados, por ejemplo, IBM High Level Assembler (HLASM), no se pueden implementar con un preprocesador; el código para ensamblar instrucciones y datos se intercala con el código para ensamblar invocaciones de macros.
Un uso clásico de las macros es en el sistema de composición tipográfica TeX y sus derivados, donde la mayor parte de la funcionalidad se basa en macros.
MacroML es un sistema experimental que busca reconciliar la tipificación estática y los sistemas de macros. Nemerle ha tipificado macros de sintaxis y una forma productiva de pensar en estas macros de sintaxis es como un cálculo de múltiples etapas .
Otros ejemplos:
Algunas aplicaciones importantes se han escrito como macros de texto invocadas por otras aplicaciones, por ejemplo, por XEDIT en CMS.
Algunos lenguajes, como PHP , pueden incrustarse en texto de formato libre o en el código fuente de otros lenguajes. El mecanismo por el cual se reconocen los fragmentos de código (por ejemplo, al estar entre corchetes <?php
y ?>
) es similar al de un lenguaje de macros textual, pero son lenguajes mucho más potentes y con más funciones.
Las macros en el lenguaje PL/I se escriben en un subconjunto del propio PL/I: el compilador ejecuta " sentencias de preprocesador " en el momento de la compilación, y la salida de esta ejecución forma parte del código que se compila. La capacidad de utilizar un lenguaje procedimental conocido como lenguaje de macros proporciona una potencia mucho mayor que la de las macros de sustitución de texto, a expensas de un compilador más grande y más lento. Las macros en PL/I, así como en muchos ensambladores, pueden tener efectos secundarios , por ejemplo, configurar variables a las que otras macros pueden acceder.
Las macros de marcos de la tecnología de marcos tienen su propia sintaxis de comandos, pero también pueden contener texto en cualquier idioma. Cada marco es a la vez un componente genérico en una jerarquía de subconjuntos anidados y un procedimiento para integrarse con sus marcos de subconjuntos (un proceso recursivo que resuelve los conflictos de integración a favor de los subconjuntos de nivel superior). Los resultados son documentos personalizados, normalmente módulos fuente compilables. La tecnología de marcos puede evitar la proliferación de componentes similares pero sutilmente diferentes, un problema que ha afectado al desarrollo de software desde la invención de las macros y las subrutinas .
La mayoría de los lenguajes ensambladores tienen funciones de macroprocedimiento menos potentes, por ejemplo, permiten que un bloque de código se repita N veces para desenrollar el bucle ; pero estas tienen una sintaxis completamente diferente del lenguaje ensamblador real.
Los sistemas de macros (como el preprocesador de C descrito anteriormente) que funcionan a nivel de tokens léxicos no pueden preservar la estructura léxica de manera confiable. Los sistemas de macros sintácticos funcionan en cambio a nivel de árboles de sintaxis abstracta y preservan la estructura léxica del programa original. Las implementaciones más utilizadas de los sistemas de macros sintácticos se encuentran en lenguajes similares a Lisp . Estos lenguajes son especialmente adecuados para este estilo de macro debido a su sintaxis uniforme entre paréntesis (conocida como S-expresiones ). En particular, la sintaxis uniforme facilita la determinación de las invocaciones de macros. Las macros de Lisp transforman la estructura del programa en sí, con todo el lenguaje disponible para expresar tales transformaciones. Si bien las macros sintácticas se encuentran a menudo en lenguajes similares a Lisp, también están disponibles en otros lenguajes como Prolog , [6] Erlang , [7] Dylan , [8] Scala , [9] Nemerle , [10] Rust , [11] Elixir , [12] Nim , [13] Haxe , [14] y Julia . [15] También están disponibles como extensiones de terceros para JavaScript [16] y C# . [17]
Antes de que Lisp tuviera macros, tenía los llamados FEXPRs , operadores similares a funciones cuyas entradas no eran los valores calculados por los argumentos sino más bien las formas sintácticas de los argumentos, y cuya salida eran valores que se usarían en el cálculo. En otras palabras, los FEXPRs se implementaron al mismo nivel que EVAL, y proporcionaron una ventana a la capa de metaevaluación. En general, se consideró que era un modelo difícil sobre el que razonar de manera efectiva. [18]
En 1963, Timothy Hart propuso agregar macros a Lisp 1.5 en AI Memo 57: Definiciones de MACRO para LISP. [19]
Una macro anafórica es un tipo de macro de programación que captura deliberadamente alguna forma suministrada a la macro y a la que se puede hacer referencia mediante una anáfora (una expresión que hace referencia a otra). Las macros anafóricas aparecieron por primera vez en On Lisp de Paul Graham y su nombre es una referencia a la anáfora lingüística, el uso de palabras como sustituto de las palabras anteriores.
A mediados de los años ochenta, una serie de artículos [20] [21] introdujeron la noción de expansión de macro higiénicasyntax-rules
( ), un sistema basado en patrones donde los entornos sintácticos de la definición de macro y el uso de la macro son distintos, lo que permite a los definidores de macro y a los usuarios no preocuparse por la captura inadvertida de variables (cf. transparencia referencial ). Las macros higiénicas se han estandarizado para Scheme en los estándares R5RS , R6RS y R7RS . Existe una serie de implementaciones competitivas de macros higiénicas, como syntax-rules
, syntax-case
, cambio de nombre explícito y cierres sintácticos. Tanto syntax-rules
y syntax-case
se han estandarizado en los estándares de Scheme.
Recientemente, Racket ha combinado las nociones de macros higiénicas con una "torre de evaluadores", de modo que el tiempo de expansión sintáctica de un sistema de macros es el tiempo de ejecución ordinario de otro bloque de código, [22] y mostró cómo aplicar la expansión y el análisis intercalados en un lenguaje sin paréntesis. [23]
Además de Scheme, existen otros lenguajes que implementan macros higiénicas o sistemas parcialmente higiénicos. Algunos ejemplos son Scala , Rust , Elixir , Julia , Dylan , Nim y Nemerle .
cond
pero carece de if
, es posible definir este último en términos de los primeros usando macros. Por ejemplo, Scheme tiene tanto macros continuaciones como macros higiénicas, lo que permite a un programador diseñar sus propias abstracciones de control, como construcciones de bucles y salidas tempranas, sin la necesidad de incorporarlas al lenguaje.let
en la aplicación de una función a un conjunto de argumentos.Felleisen conjetura [25] que estas tres categorías constituyen los principales usos legítimos de las macros en un sistema de este tipo. Otros han propuesto usos alternativos de las macros, como las macros anafóricas en sistemas macro que son antihigiénicos o permiten una transformación antihigiénica selectiva.
La interacción de las macros y otras características del lenguaje ha sido un área productiva de investigación. Por ejemplo, los componentes y módulos son útiles para la programación a gran escala, pero la interacción de las macros y estos otros constructos debe definirse para su uso conjunto. Se han propuesto sistemas de módulos y componentes que pueden interactuar con macros para Scheme y otros lenguajes con macros. Por ejemplo, el lenguaje Racket extiende la noción de un sistema de macros a una torre sintáctica, donde las macros se pueden escribir en lenguajes que incluyen macros, utilizando la higiene para garantizar que las capas sintácticas sean distintas y permitiendo que los módulos exporten macros a otros módulos.
Las macros se utilizan normalmente para mapear una cadena corta (invocación de macro) a una secuencia más larga de instrucciones. Otro uso menos común de las macros es hacer lo contrario: mapear una secuencia de instrucciones a una cadena de macros. Este fue el enfoque adoptado por el Sistema de Programación Móvil STAGE2 , que utilizó un compilador de macros rudimentario (llamado SIMCMP) para mapear el conjunto de instrucciones específico de una computadora dada en macros independientes de la máquina . Las aplicaciones (notablemente compiladores) escritas en estas macros independientes de la máquina pueden luego ejecutarse sin cambios en cualquier computadora equipada con el compilador de macros rudimentario. La primera aplicación ejecutada en tal contexto es un compilador de macros más sofisticado y poderoso, escrito en el lenguaje de macros independiente de la máquina. Este compilador de macros se aplica a sí mismo, en un estilo de arranque , para producir una versión compilada y mucho más eficiente de sí mismo. La ventaja de este enfoque es que las aplicaciones complejas pueden ser trasladadas de una computadora a una computadora muy diferente con muy poco esfuerzo (para cada arquitectura de máquina de destino, solo la escritura del compilador de macros rudimentario). [26] [27] La llegada de los lenguajes de programación modernos, en particular C , para el que hay compiladores disponibles en prácticamente todas las computadoras, ha hecho que este enfoque sea superfluo. Sin embargo, este fue uno de los primeros casos (si no el primero) de arranque del compilador .
Si bien un programador puede definir instrucciones macro para cualquier conjunto de instrucciones de programas ensambladores nativos, normalmente las macros están asociadas con bibliotecas de macros entregadas con el sistema operativo, lo que permite el acceso a funciones del sistema operativo como
En sistemas operativos más antiguos, como los utilizados en los mainframes de IBM, la funcionalidad completa del sistema operativo solo estaba disponible para programas en lenguaje ensamblador, no para programas en lenguaje de alto nivel (a menos que se utilizaran subrutinas en lenguaje ensamblador, por supuesto), ya que las macroinstrucciones estándar no siempre tenían contrapartes en rutinas disponibles para lenguajes de alto nivel.
A mediados de la década de 1950, cuando la programación en lenguaje ensamblador se usaba comúnmente para escribir programas para computadoras digitales , se inició el uso de macroinstrucciones con dos propósitos principales: reducir la cantidad de codificación de programas que se debía escribir generando varias instrucciones en lenguaje ensamblador a partir de una macroinstrucción y hacer cumplir los estándares de escritura de programas, por ejemplo, especificando comandos de entrada/salida de formas estándar. [30] Las macroinstrucciones eran efectivamente un paso intermedio entre la programación en lenguaje ensamblador y los lenguajes de programación de alto nivel que le siguieron, como FORTRAN y COBOL . Dos de las primeras instalaciones de programación que desarrollaron "lenguajes macro" para la computadora IBM 705 estaban en Dow Chemical Corp. en Delaware y en Air Material Command, Ballistics Missile Logistics Office en California. Una macroinstrucción escrita en el formato del lenguaje ensamblador de destino sería procesada por un compilador de macros, que era un preprocesador del ensamblador, para generar una o más instrucciones en lenguaje ensamblador que serían procesadas a continuación por el programa ensamblador que traduciría las instrucciones en lenguaje ensamblador en instrucciones en lenguaje máquina . [31]
A finales de los años 1950, el lenguaje de macros fue reemplazado por los ensambladores de macros , una combinación de ambos, en la que un programa cumplía ambas funciones, la de preprocesador de macros y la de ensamblador, todo en un mismo paquete. [31] [ verificación fallida ]
En 1959, Douglas E. Eastwood y Douglas McIlroy de Bell Labs introdujeron macros condicionales y recursivas en el popular ensamblador SAP , [32] creando lo que se conoce como Macro SAP. [33] El artículo de McIlroy de 1960 fue fundamental en el área de extensión de cualquier lenguaje de programación (incluidos los de alto nivel ) a través de procesadores de macros . [34] [32]
Los ensambladores de macros permitían a los programadores de lenguaje ensamblador implementar su propio lenguaje de macros y permitían una portabilidad limitada del código entre dos máquinas que ejecutaban la misma CPU pero diferentes sistemas operativos, por ejemplo, las primeras versiones de MS-DOS y CP/M-86 . La biblioteca de macros debía escribirse para cada máquina de destino, pero no para el programa de lenguaje ensamblador en general. Nótese que los ensambladores de macros más potentes permitían el uso de construcciones de ensamblaje condicional en las instrucciones de macros que podían generar código diferente en diferentes máquinas o diferentes sistemas operativos, lo que reducía la necesidad de múltiples bibliotecas. [ cita requerida ]
En los años 1980 y principios de los 1990, las computadoras de escritorio solo funcionaban a unos pocos MHz y las rutinas en lenguaje ensamblador se usaban comúnmente para acelerar los programas escritos en C, Fortran, Pascal y otros. Estos lenguajes, en ese momento, usaban diferentes convenciones de llamada. Las macros se podían usar para interconectar rutinas escritas en lenguaje ensamblador con el front-end de aplicaciones escritas en casi cualquier lenguaje. Nuevamente, el código básico en lenguaje ensamblador seguía siendo el mismo, solo era necesario escribir las bibliotecas de macros para cada lenguaje de destino. [ cita requerida ]
En los sistemas operativos modernos, como Unix y sus derivados, el acceso al sistema operativo se proporciona a través de subrutinas, generalmente proporcionadas por bibliotecas dinámicas. Los lenguajes de alto nivel, como C, ofrecen un acceso integral a las funciones del sistema operativo, lo que evita la necesidad de programas en lenguaje ensamblador para dicha funcionalidad. [ cita requerida ]
Además, las bibliotecas estándar de varios lenguajes de programación más nuevos, como Go , desalientan activamente el uso de llamadas al sistema en favor de bibliotecas independientes de la plataforma también, si no es necesario, para mejorar la portabilidad y la seguridad. [35]
Uno de los usos importantes de las macros de programación es ahorrar tiempo y evitar errores de tipo administrativo al escribir secuencias de instrucciones que a menudo se repiten en el transcurso de un programa.