Características de la implementación del lenguaje MSH

Continúo la serie de artículos sobre el lenguaje de programación MSH. En un artículo anterior, ya describí la especificación de este lenguaje. Pero la especificación no revela todas las características del lenguaje. Quiero eliminar esa brecha con este artículo. Por supuesto, no logré describir todo, pero básicamente describí las características principales. Pospondremos el resto hasta tiempos mejores.

Cuando la imagen del lenguaje simplemente toma forma, parece que todo es lógico y consistente. Pero a la entrada de la implementación del lenguaje hay problemas que deben resolverse eligiendo una u otra estrategia. Ahora que el intérprete de idiomas está listo, puede ver cómo resolvió uno u otro problema de implementación.

Conceptos del lenguaje de programación MSH


Tabla de contenido


Introducción 2
Organización del programa. 2
tiempo de ejecución. 2
Gestión de datos. 3
Localización de datos. 3
Sintaxis abreviada. 5
constantes. 5
Características de algunos equipos. 5
comando CONSTANTE. 5
Comando XECUTE. 6
Comandos COPY y MOVE. 6
Sincronización de recursos. 7
Forma abreviada del comando SET. 8
equipos de bloque. 8
comando IF. 8
comando CASE. 9
MIENTRAS comando. 10
iteradores de bucle de bloque. 10
NEXT Team. 11
comando ATRÁS. 12
CONSULTA equipo. 12
Comandos sin bloque Comandos transversales del árbol de datos. 12
NEXT1 Team. 12
Comando BACK1. trece
CONSULTA Equipo 1. 13
Gestión de ejecución del programa. 13
Parámetros de paso. 14
Manejo de eventos. 15
comando EVENTTRAP. 15
Equipo EVENTDELETE. 16
Equipo de EVENTCALL. 16
Equipo EVENTWAIT. 16
vectores. 16
vectores de 64 bits. 16
vectores de 32 bits. 17
vectores de 16 bits. 17
vectores de 8 bits. 17
operaciones. 17
objetos. 18
Herencia de objetos. 19
Intercambio con el archivo. 20
Conclusión. veinte

Introducción


El lenguaje MSH se basa en los conceptos del lenguaje MUMPS. MUMPS es un lenguaje poco conocido desarrollado en el siglo pasado. Pero todavía se usa en aplicaciones de información. La información sobre este idioma está presente en Internet. Existen implementaciones funcionales de este lenguaje y una comunidad de programadores que lo respaldan. MUMPS se están desarrollando en los Estados Unidos y Rusia. Además, se usa, hasta donde yo sé, en América Latina, Alemania, Australia y China. En general, este concepto vivo del lenguaje. Al reunirse con MUMPS, su naturaleza arcaica es sorprendente. Este desarrollo está diseñado para eliminar sus defectos, al tiempo que conserva sus ventajas, simplicidad, consistencia y organización de datos.
Organización del programa.

La unidad de traducción es el módulo de lenguaje MSH. El módulo comienza desde los 8 bytes estándar que identifican la versión del idioma. Al principio de la línea puede haber una etiqueta o espacios. La etiqueta termina con un ":" y está separada del resto de los comandos por cualquier número de espacios. Las líneas consisten en comandos. Una señal del final del equipo es el símbolo ";". El comando está separado de los argumentos por espacios. El caso de los personajes no importa. El comando se puede escribir con caracteres de cualquier registro. Además, muchos equipos tienen una forma abreviada. Un comando puede tener una condición para su ejecución. Si hay uno, entonces el símbolo "?" Sigue el comando sin espacios y la condición para la ejecución de este comando. Si la condición para ejecutar el comando no es igual a 0, entonces se ejecuta el comando. Dentro de la condición, no se permiten espacios, de lo contrario se procesarán como separadores entre el comando y los argumentos.

Por ejemplo:

SET? [5]> 5 Val [1] = 25; // ¿es correcto
SET? [2]> 5 Val [1] = 25; // error de sintaxis
SET? ([1,2]> 5) Val [1] = 25; // correctamente, los espacios interiores () están permitidos. Los

argumentos están separados por el símbolo ",". Dentro de los argumentos, un espacio no es un carácter especial y puede estar contenido en cualquier lugar. Típicamente, un comando puede tener cualquier número de argumentos. Las etiquetas en el módulo están localizadas dentro del módulo y deben ser únicas, con la excepción de las etiquetas dentro del comando CASE . Las etiquetas del comando CASE localizadas dentro de este comando deben ser únicas solo dentro de este comando y pueden duplicar etiquetas tanto fuera de este comando como en los comandos CASE anidados .

Tiempo de ejecución


En tiempo de ejecución, la aplicación tiene una o más tareas. Todas las tareas se realizan en paralelo. En cada tarea, los programas se ejecutan secuencialmente. En cada momento, solo se ejecuta un código de programa en la tarea. La tarea finaliza con el final del último programa en ejecución. La tarea principal es iniciada por el lenguaje en tiempo de ejecución. Los trabajos restantes son generados por el comando Trabajo .

Gestión de datos


Para aquellos que están familiarizados con el lenguaje MUMPS, la organización de los datos en MSH es bastante clara. No hay una descripción de los datos en MSH. No hay declaración de datos. Los datos se pueden almacenar como un árbol, luego el acceso a los nodos del árbol se realiza mediante un nombre e índice opcionales. El índice está entre corchetes []. El nombre está delante de ellos.

Por ejemplo:

SET Pr [4,5, "rt"] = Is ["ty ^ 578"];
Aquí Pr y Is son los nombres de los árboles. 4,5, “rt” y “ty ^ 578” son índices de nodo.

El árbol puede tener un número arbitrario de niveles y, en consecuencia, el índice tiene el número correspondiente de campos separados por comas. El campo de índice puede tener un valor arbitrario del tipo base. Los tipos básicos en MSH son números y cadenas. Solo los nodos en los que se realizó la grabación se almacenan directamente en el árbol. El nombre, el campo de índice y el índice en sí pueden ser una expresión. Después del cálculo, el nombre solo puede ser un identificador.

Además, los datos pueden almacenarse como una matriz continua, luego un acceso al elemento de la matriz consiste en un nombre de símbolo opcional "$" y un índice. Un índice es un entero. El índice de matriz más pequeño es 1. El nombre y el índice pueden ser expresiones. Después del cálculo, el nombre solo puede ser un identificador y el índice un número entero. Una matriz se puede completar en cualquier orden, pero si escribiómc $ 1000 , luego se creará una matriz mc de 1000 elementos. Los elementos no definidos no contendrán valores, pero estarán presentes. El número de elementos en la matriz se puede encontrar haciendo referencia al elemento cero de esta matriz.

Por ejemplo: mc $ 0 El
tamaño de la matriz se puede cambiar escribiendo una nueva longitud de matriz en este elemento. Pero en el caso general, esto no es necesario, ya que la matriz se expande automáticamente.

Los nodos de árbol y los elementos de la matriz contienen datos de tipos básicos. Estas son cadenas o números. No se trata de cómo se almacenan los datos del programador. El almacenamiento de los tipos de datos y su manipulación es responsabilidad de la implementación del lenguaje MSH.

Localización de datos


Los datos de MSH se dividen en globales y locales. Se diferencian en el tipo de nombre. Los datos globales se almacenan en la memoria a largo plazo y no dependen de la vida útil de la aplicación. Una vez que se crean, la aplicación puede cambiarlos y existirán hasta que la aplicación los destruya con el comando KILL . Todos los datos globales tienen el prefijo "^" en el nombre.

El acceso a datos globales es simultáneamente posible desde varias tareas. Por lo tanto, al acceder a los globales, la sincronización es necesaria. Tal sincronización es siempre automática. Siempre hay una primitiva de sincronización en el descriptor global y controla el acceso a la global. Además, cuando la lectura de lo global se bloquea al leer, mientras se escribe a lo global, se bloquea al escribir. No se requiere sincronización global adicional. Los accesos a las matrices globales también están sincronizados.

Por ejemplo:

^ gl [87,9] : acceso al nodo del árbol global.

^ glar $ 45 - acceso al elemento de la matriz global.

Los datos locales solo existen mientras la aplicación se está ejecutando. El próximo lanzamiento de la aplicación no puede acceder a los datos locales del lanzamiento anterior.

El alcance de los datos locales depende de su tipo. Hay tres tipos de localización de datos.

1. Datos del programa local. Están localizados dentro del programa y existen desde el momento en que se inicia el programa hasta su finalización. Si el programa llama a la subrutina, se crean nuevos datos de subrutina y los datos del programa local dentro de la subrutina no están visibles. Cuando regresa al programa, los datos del programa local vuelven a estar disponibles. Los datos del programa local no tienen nombre.

Por ejemplo:

[7,9] - acceso al nodo del árbol localizado dentro del programa.

$ 5 : acceso a un elemento de matriz localizado dentro del programa.

Hay una excepción Una serie de parámetros pasados ​​al programa A $ también se localiza dentro del programa.

2. Datos de la aplicación local. Son visibles en todas las tareas de la aplicación. Puede contactarlos desde cualquier tarea. Existen desde el momento en que se crean en la aplicación mediante cualquier tarea hasta que se completa la aplicación o hasta que el equipo KILL los destruye . Los nombres de dichos datos tienen el prefijo " % ". Estas variables están disponibles simultáneamente en diferentes tareas, por lo que están tan sincronizadas como globales.

Por ejemplo:

% dapp [87.9] : acceso al nodo del árbol localizado dentro de la aplicación.

% dapp $ 45 : acceso a un elemento de matriz localizado dentro de la aplicación.

3. Datos locales del trabajo. Se localizan dentro de la tarea y existen desde el momento en que se crean en cualquier programa de tareas hasta que el equipo KILL completa o destruye la tarea . Dichos datos deben tener un nombre y no contener los prefijos " ^ " y " % ". La excepción es la matriz de parámetros de programa A $ , se localiza dentro del programa,

por ejemplo:

djob [87,9] : acceso al nodo del árbol localizado dentro de la tarea.
djob $ 45 : acceso al elemento de la matriz localizado dentro del trabajo.
El acceso a las variables solo puede tener los tipos enumerados aquí.

Sintaxis de taquigrafía


Este término no tiene relación con un término similar en el lenguaje MUMPS. La sintaxis abreviada en MSH se utiliza para referirse al árbol completo o al conjunto completo. Se usa solo en equipos separados y donde está permitido siempre se negocia. Una apelación a todo el árbol consiste en un nombre y corchetes obligatorios. No se especifica ningún nombre para el árbol del programa local.
Por ejemplo:
us [] : acceso a todo el árbol de us.
[] - acceso al árbol del programa local.

El acceso a toda la matriz consiste en el nombre y el carácter requerido " $ ". No se proporciona ningún nombre para la matriz local del programa.

Por ejemplo:
us $ : acceso a toda la matriz de us.
PS - acceso a la matriz del programa local.

Constantes


Los tipos de datos básicos pueden ser numéricos o de cadena. La forma numérica es un número entero o un número real en presencia de un punto decimal. La base de los números es 10. Pueden ser positivos o negativos.
Por ejemplo:
25, -4, 789.56, -9.3

Las constantes de cadena son cualquier secuencia de caracteres. Si una constante consta solo de letras y números, no puede encerrarla entre comillas, ya que no puede confundirse con una variable. Si la constante contiene otros caracteres, debe estar entre comillas simples o dobles.
Por ejemplo:
"rt @ tty # 123"
'14 "5 * 7" 89 \? '
125Dsv

Características de algunos equipos.


Equipo CONSTANTE


Puede asignar un nombre a una constante con el comando CONSTANTE . Estos son nombres de hora de transmisión. La transmisión los reemplaza por sus valores. En tiempo de ejecución, estos nombres ya no existen. Por lo tanto, no puede asignar una expresión a un nombre en el comando CONSTANTE . El valor debe ser exactamente una constante. Los nombres de las constantes deben elegirse de modo que no coincidan con los valores de las constantes especificadas en el programa sin comillas.

Por ejemplo:
Constant ioInOut = "/ini/par.ini>,maxIs=25;
Las constantes ioInOut y maxIs son valores asignados. Además en el programa, estos nombres se pueden usar en lugar de estos valores.
Equipo constanteTiene 2 formas. En este caso, el lado derecho de la igualdad está ausente. El significado de este equipo es diferente. El nombre es el nombre del módulo que contiene las constantes. Las constantes de este módulo se exportan al módulo actual. El módulo de importación de constantes no contiene ninguna descripción adicional sobre la importación. El módulo de importación solo puede contener constantes y programas.
Por ejemplo:
CONSTANT sysCnsNet, usrCnsByx;
sysCnsNet y usrCnsByx son nombres de módulos que contienen constantes.
Ambas formas pueden aparecer como argumentos para un solo comando CONSTANTE .

Equipo XECUTE


El comando XECUTE es un equipo bastante exótico, pero está presente en el lenguaje MUMPS. En otros lenguajes de programación, me conoció solo en JavaScript. Ahí se llama eval. Cuando se ejecuta este comando, se calcula la expresión del argumento de este comando, y luego esta expresión se convierte en una cadena, se interpreta como un comando MSH y se ejecuta.

Por ejemplo:
XECUTE "SET $ 1 = 89;";
Como resultado, la variable $ 1 recibirá el valor 89 .
Este es un ejemplo primitivo, en esta forma apenas tiene sentido usar este comando. Los argumentos del comando XECUTE son expresiones que le permiten generar varios comandos MSH en el momento en que se ejecuta el programa.
El comando se ejecuta en el contexto del programa donde se encuentra el comando. Todos los recursos del programa están disponibles para ella, incluidos los datos del programa local.

Comandos COPY y MOVE


El comando COPY es similar al comando MERGE MUMPS. Estos comandos copian el nodo fuente junto con todos los descendientes al nodo receptor. El argumento de estos comandos consta de dos campos separados por el símbolo " = ". A la derecha de este signo se encuentra el nodo fuente, el receptor a la izquierda. Se permite un enlace abreviado como nodos, en este caso se utiliza todo el árbol. Estos comandos copian no solo los descendientes, sino también los propios nodos. El comando COPY realiza la fusión de datos. Los datos de origen se copian en el receptor sin limpiarlo. La fuente no cambia. MOVER equipoRealiza datos en movimiento. Previamente, el receptor se limpia, luego todos los descendientes del nodo fuente se copian y el nodo fuente se elimina con todos sus descendientes.

Por ejemplo:
// el nodo us [5,6] se copia al nodo [1]
COPY [1] = us [5,6];
// todo el árbol de EE. UU. se mueve al nodo [8]
MOVE [8] = us [];
Estos comandos también se pueden usar para copiar matrices. En este caso, solo se pueden usar enlaces acortados como fuente y receptor.
Los argumentos del programa se copian en la matriz arg.
COPY arg $ = A $;
Puede usar el comando MOVER para moverse.
MOVER a1 $ = b1 $;
Puede copiar y mover cualquier matriz.

Sincronización de recursos


Al realizar varias tareas, se hace necesario acceder a los recursos compartidos. La sincronización se realiza mediante comandos de bloqueo. Los comandos de sincronización por sí mismos no bloquean nada, este es un conjunto de acuerdos que permiten diferenciar el acceso a los recursos compartidos. Si los comandos de bloqueo no se comparten entre los trabajos, no se realizará la sincronización de acceso. La sincronización se basa en nombres de bloqueo. Estos nombres se localizan dentro de la aplicación y son los mismos en todas las tareas. En las cerraduras, se acepta el concepto de muchos lectores y solo un escritor a la vez. En consecuencia, hay bloqueos de lectura y bloqueos de escritura. LockR

Teambloquea la lectura de nombres. Los nombres que figuran en sus argumentos serán bloqueados por la lectura. Cualquier cantidad de tareas puede bloquear los mismos nombres, pero una tarea que intentó bloquear cualquiera de estos nombres esperará a que se desbloqueen todos estos nombres. Después de capturar el nombre de escritura, no se puede ejecutar ningún comando de bloqueo de lectura hasta que el bloqueo de escritura libere el nombre. Si no es posible bloquear, el comando esperará a que se libere el nombre.

Por ejemplo:
LockR name1, name2, name3;
Estos nombres serán bloqueados al leer. Otra tarea también puede bloquear estos nombres sin tener que esperar para que puedan ser desbloqueados. LockW

comandobloquea nombres por registro. Los nombres enumerados en sus argumentos serán bloqueados por registro. Si los nombres enumerados en los argumentos ya están bloqueados por algún comando de bloqueo, entonces este comando esperará la publicación de estos nombres.
Por ejemplo:
LockW name1, name2, name3;
Estos nombres serán bloqueados por registro.
El comando LockUn desbloquea este nombre. Si el nombre se bloquea leyendo varias veces, debe desbloquear el mismo número de veces.
Por ejemplo:
LockUn name1, name2, name3;
Las funciones estándar tienen análogos de estos comandos con un tiempo de espera.

Forma abreviada del comando SET


El comando SET tiene una forma abreviada. En este caso, el lado izquierdo de la igualdad está ausente; su papel lo desempeña la última variable mencionada en la expresión.
Por ejemplo:
SET $ 1 = 2, $ 1 + 3;
La variable $ 1 será igual a 5 .
Si hay varias variables en la expresión, el resultado se asignará a la última variable.

Por ejemplo:
SET $ 1 = 1, $ 2 = 2, $ 3 = 3, $ 1 + $ 2 + $ 3;
La variable $ 3 se convertirá en 1 + 2 + 3 = 6. Aunque este formulario es más apropiado para usar solo en casos muy simples, similar al primer ejemplo. El segundo ejemplo se proporciona solo para ilustrar las capacidades de esta forma del comando SET .

Bloquear equipos


Los comandos de bloque forman un bloque de comandos y sirven simultáneamente como encabezado de bloque. Cada comando de bloque debe tener su propio comando END , incluso si solo hay un comando en el bloque.

Equipo IF


El comando IF forma un bloque que se ejecuta si las condiciones para la ejecución de este comando son verdaderas. Este comando no tiene argumentos. Este bloque puede contener comandos ELSE . El comando ELSE no tiene argumentos. Fuera del bloque IF , estos comandos no tienen significado y no pueden aplicarse. Si hay comandos ELSE en el bloque IF cuando se cumple la condición del comando IF , solo los comandos detrás del comando IF se ejecutan hasta el siguiente comando ELSE . ELSE Teampuede contener una condición de ejecución, entonces en caso de verdad en esta condición, solo los comandos ubicados después de este comando se ejecutarán hasta el siguiente comando ELSE o el comando END . Un bloque IF puede contener solo un comando ELSE sin una condición para su ejecución, y debe ser el último entre los comandos ELSE .

Por ejemplo:
IF? [6] <0;
SET y [1] = 1;
ELSE? [6] <5;
SET y [1] = 2;
ELSE? [6] <10;
SET y [1] = 3;
MÁS SET y [1] = 4;
FINAL
La condición para la ejecución de este comando puede estar ausente, entonces este bloque se ejecutará en cualquier caso. Aunque es difícil imaginar por qué esto podría ser útil.

Equipo CASO


La semántica de este comando es algo diferente de la semántica de los otros equipos de MSH. La condición para ejecutar un comando en él no es tal. En la condición de ejecución del comando CASE , la expresión debe calcular la etiqueta a la que se transferirá el control. Este comando no tiene argumentos.

Cada etiqueta en este comando forma un bloque desde esta etiqueta a la siguiente. Cuando se transfiere el control a la etiqueta, solo se ejecutan los comandos hasta la siguiente etiqueta, y luego se sale del bloque CASE actual . Este comando está más cerca de la notación Pascal que del comando del interruptor C. Si como resultado del cálculo del nombre de la etiqueta se encuentra un nombre ausente en el bloque CASE actual, los comandos ubicados después del comando CASE antes de que se ejecute la primera etiqueta.

CASO? L_ $ J; // se evalúa la etiqueta
SET x [1] = 1; // si no se encuentra ninguna etiqueta, los comandos de este bloque se ejecutan
SET a [2] = x [1] +1;
L1: SET x [1] = 2; // etiqueta del bloque de comandos L1
SET a [2] = x [1] +2;
L2: SET x [1] = 3; // etiqueta del bloque de comandos L2
SET a [2] = x [1] +3;
FINAL
Las etiquetas en este comando forman implícitamente un bloque interno de comandos. Después de la ejecución de dicho bloque de comandos, el control se transfiere fuera del bloque del comando CASE .

MIENTRAS comando


El comando WHILE se usa para organizar el bucle. La condición para la ejecución de este comando establece la condición para la continuación del ciclo. Mientras la condición para ejecutar el comando no sea 0, se ejecutará el bloque formado por este comando. El bloque termina con el comando FIN . El comando END de dicho bloque tiene una característica. Puede tener una condición de terminación de bloque. Si la condición no es 0, el bloque se completará. Además, estas condiciones pueden estar presentes tanto en el comando WHILE como en el comando END .
Por ejemplo:
WHILE? X [7]> 0; // condición para continuar el ciclo
SET y [2] = x [7] +2;
BREAK? Y [2] <0;
SET x [7] = x [7] +1;
END? X [7]> 20; // condición para finalizar el ciclo
Dentro del bloque, el comando BREAK puede interrumpir el bucle.

Iteradores de bucle de bloque


Los iteradores de bucle de bloque están optimizados para acceder a los nodos del árbol. Utilizan un enlace interno para optimizar el acceso a los nodos de derivación. Esto impone una restricción en el uso de iteradores de bloque. Dentro del bloque no puedes cambiar la estructura del árbol. No puedes escribir a este árbol. En los comandos de iterador de bucle de bloque, 2 argumentos.
Se requiere el primer argumento, el enlace al nodo cuyos descendientes se omitirán. Índice de referencia Se pueden usar enlaces acortados. El segundo argumento es el enlace al nodo donde se almacenará el índice descendiente. Este argumento es opcional. Dentro del bloque, el segundo argumento no debería cambiar. No se permiten expresiones como $$ 2 o [[3]] si $ 2 o [3]cambiar dentro del bloque iterador. Los cambios en estas variables no se tendrán en cuenta. El acceso al índice secundario se puede obtener a través de la variable de sistema % queryKey y los datos del nodo de la propiedad% queryData . Si los descendientes no se deben omitir desde el principio, entonces se requiere el segundo argumento y se debe colocar el índice de nodo después de lo cual comienza la omisión de los descendientes. Si hay un segundo argumento, pero necesita dar la vuelta desde el principio, entonces antes del ciclo debe eliminar esta variable con el comando KILL .
Los comandos pueden tener una condición de ejecución de bloque. Esta condición se verifica solo una vez al ingresar al bloque.

Puede recorrer no solo los nodos del árbol, sino también las matrices. En este caso, el número de serie del campo de matriz entra en el segundo argumento. Solo el enlace acortado puede usarse como primer argumento.

PRÓXIMO equipo


El comando NEXT itera sobre los descendientes inmediatos de un nodo de árbol. Los descendientes cuestan en la dirección hacia adelante desde el índice mínimo al máximo.
Por ejemplo:
NEXT us [4,5]; // No se especifica el argumento 2, el índice se toma
// de la variable del sistema% queryKey
SET $ 1 =% queryKey, $ 2 =% queryData;
FINALIZAR $ 1> 1000; // condición para finalizar el ciclo
El índice de nodo se coloca inmediatamente en los datos
KILL $ 1;
SIGUIENTE us [4,5], $ 1;
SET $ 2 =% queryData;
FINAL

Un enlace abreviado se utiliza como nodo de referencia. En este caso, se omite el primer nivel del árbol.
MATAR $ 1;
SIGUIENTE nosotros [], $ 1;
SET $ 2 =% queryData;
FINAL
Rastrear después del índice 3.
SET $ 1 = 3;
SIGUIENTE us [4,5], $ 1;
SET $ 2 =% queryData;
FINAL
Al atravesar una matriz, todos los campos están en orden, incluso aquellos en los que los datos no están definidos.
Por ejemplo:
KILL $ 1;
SIGUIENTE us $, $ 1;
SET $ 2 =% queryData;
FINAL
Dentro del bloque, el comando BREAK puede interrumpir el bucle .

Equipo de vuelta


El comando BACK difiere del comando NEXT solo en la dirección transversal desde el último vértice hasta el primero.
Por ejemplo:
KILL $ 1;
RESPALDENOS [4,5], $ 1;
SET $ 2 =% queryData;
FINAL

QUERY Team


El comando CONSULTA atraviesa todos los descendientes de un nodo de izquierda a derecha y de arriba a abajo a profundidad total en la dirección hacia adelante. El segundo argumento contiene el índice opcional completo. Si este índice tiene más de 1 campo, la lista se coloca en el segundo argumento.

De lo contrario, este comando es similar al comando NEXT .

El comando QUERY atraviesa la matriz solo en la dirección hacia adelante de izquierda a derecha.

Por ejemplo:
KILL $ 1;
CONSULTE nosotros [4,5], $ 1;
SET $ 2 =% queryData;
FINAL
Pero este comando omite solo los vértices que importan.

Comandos sin bloque Comandos de recorrido del árbol de datos


En estos comandos, se requieren ambos argumentos de comando. El segundo argumento almacena el índice, después de lo cual el siguiente comando encontrará el siguiente vértice. En estos comandos, los enlaces internos no se guardan y, por lo tanto, no hay restricciones para ajustar el árbol que se omite. Por la misma razón, el tiempo de acceso a los picos puede ser mucho más largo. Las matrices también se pueden omitir con estos comandos.

Comando NEXT1 El

comando NEXT1 proporciona el siguiente nodo en el mismo nivel debajo del nodo de referencia.
Los datos de nodo están disponibles en la variable de sistema % queryData .
Por ejemplo:
SET $ 1 = 2;
NEXT1 us [1,4], $ 1;
// dará el nodo ubicado en el nivel 3 después del nodo us [1,4,2]

Equipo BACK1


El comando BACK1 proporciona el vértice anterior en el mismo nivel debajo del nodo de referencia.
De lo contrario, es similar al comando NEXT1 .

QUERY1 Team


El comando QUERY1 proporciona el siguiente vértice de una rama de árbol mientras atraviesa todo el nodo de arriba a abajo y de izquierda a derecha. El segundo argumento contiene el índice opcional completo. Si este índice tiene más de 1 campo, la lista se coloca en el segundo argumento.

De lo contrario, es similar al comando NEXT1 .

Gestión de programas


Una etiqueta de módulo puede ser un punto de llamada para un programa, una función, un punto de llamada para una nueva tarea, una propiedad de objeto, un método de objeto y una etiqueta, dependiendo del acceso a esta etiqueta.
Además, se puede acceder a la etiqueta de manera diferente en diferentes partes del módulo.
Por ejemplo:
LB: Establecer Val [25] = 7 + A $ 1; Val de retorno [25];
Do LB (78); // Acceso al programa. El valor de retorno es ignorado.
Establecer Val [7] = 8 * LB (6); // Llamando como una función se usa el valor de retorno.
TRABAJO LB (17, "yt"); // llamar a un nuevo trabajo
Set Val [9] = Mod.LB (3); // Acceso al método de clase Mod: el nombre del módulo en este contexto se trata como el nombre de la clase del objeto.
Establecer Val [15] = Obj [1,2, A] .LB; // Acceso a la propiedad LB del objeto Obj [1,2, A],
Ir LB; // Ir a la etiqueta LB
En los ejemplos anteriores, además de 4 y 5, la llamada se realiza dentro del módulo actual. La llamada de programas y funciones se puede realizar a cualquier módulo disponible. Por cierto, esto también se aplica al equipo Go.
Por ejemplo, en el módulo Mod hay una etiqueta Lb. Entonces la apelación se verá así:
Do Mod.Lb (78);
Establecer Val [7] = 8 * Mod.Lb (6);
Establecer Val [9] = Mod.Lb (3);
TRABAJO Mod.Lb (78);
Ir Mod.Lb;

En general, una etiqueta se utiliza como programa o función. Si el módulo se usa como una clase de objeto, la etiqueta es una propiedad o método de este objeto. Las propiedades de un objeto y sus métodos son otra forma de acceso a programas y funciones; por lo tanto, todo lo que se dice sobre programas y funciones se aplica igualmente a propiedades y métodos. No hay matices significativos al acceder a objetos solo en términos de herencia. Este problema se describirá con más detalle a continuación.

En el caso general, el nombre del módulo y el nombre de la etiqueta en la llamada al mismo son expresiones y pueden calcularse en el momento de la llamada.
Por ejemplo:
Establecer $ 1 = "Md", $ 2 = "Lb";
Hacer $ 1. $ 2;

El programa termina con el comando Volver; Se puede completar un módulo con el comando Finalizar;

Pasando parámetros


En MSH, los parámetros se pasan solo por valor. En general, no existen referencias e indicadores en términos del lenguaje C ++ y los enormes problemas asociados con su uso aquí, en principio, no existen. Un programa siempre toma un número arbitrario de parámetros en una matriz A $ . Su número se puede encontrar haciendo referencia al elemento 0 de la matriz A $ 0 . El programador es responsable del significado de estos parámetros.

Por ejemplo:
tenemos un programa Lb. Se puede abordar:
DO Lb (1,2,7);
DO Lb (25,7);
DO Lb ();
TRABAJO Lb (8);
SET [7.8] = Lb (187, "Privet");

Los parámetros no aprobados en el programa no están definidos.
Pasar parámetros por valor no interfiere con pasar nombres de variables al programa y usarlos allí.
Por ejemplo:
Set us [5] = 48;
Do Lb ("nosotros");
...
Regreso;
Lb: Establecer par [8,1,9] = A $ 1 [5];
// A $ 1: el primer parámetro pasado contiene el nombre de la
variable //: us .
// [5] - acceso a la parte superior de este árbol
// como resultado, el valor del nodo us [5] = 48
Return;
Como resultado, el nombre de las variables se puede manipular arbitrariamente.
Al ir a la etiqueta con el comando GO, la matriz de parámetros no cambia. MSH tiene un formulario de comando GO con la transferencia de nuevos parámetros. En este caso, la matriz de parámetros actual se reemplaza por la nueva especificada en la llamada al comando GO .
Por ejemplo:
GO Lb (1,2,7);

Manejo de eventos


El procesamiento de eventos es un poderoso mecanismo de programación que generalmente no se incluye en lenguajes de alto nivel. Pero su presencia en las bibliotecas y el sistema operativo habla de su importancia. Las bibliotecas de componentes visuales se basan en este mecanismo. La presencia de procesamiento de eventos en el lenguaje expande las capacidades del lenguaje. Abre un nuevo estilo de programación basado en eventos. Por cierto, un lenguaje de programación tan respetado como Assembler tiene tales características. Por lo tanto, este mecanismo se ha agregado al lenguaje MSH.

La base de este mecanismo es el evento. Está localizado dentro de la aplicación. En consecuencia, estos nombres deben ser únicos en toda la aplicación. Si el nombre del evento coincide con el nombre declarado en el comando CONSTANTE, el nombre del evento se reemplazará por el valor de la constante declarada. Ten cuidado. Al nombrar eventos, es aconsejable adherirse a algún tipo de estrategia de nombres. Por ejemplo, asigne nombres de eventos que comiencen con evu . La longitud del nombre, teniendo en cuenta la codificación UTF8, no debe exceder los 18 bytes. Esta limitación está asociada solo con la implementación del lenguaje actual.

Un evento creado en una tarea es visible y puede procesarse en cualquier tarea. Los controladores de eventos pueden ser cualquier número y pueden estar en diferentes tareas.
Después de que ocurra el evento, se verifica si hay controladores para este evento; si no hay controladores, el evento se elimina. Si hay controladores, se ejecutan secuencialmente. Los eventos pueden ser sistema y usuario. Los eventos del sistema son generados por el sistema. Un evento personalizado es generado por el comando EVENTTRAP . El evento pasa parámetros a los controladores, como cuando se invoca un programa. Después del procesamiento, los controladores de eventos no se eliminan. Para eliminar un evento, use el comando EVENTDELETE . Un evento puede ser manejado por los comandos EVENTCALL y EVENTWAIT .

Equipo EVENTTRAP


El comando crea un evento personalizado. El formato del comando se asemeja a una llamada de programa, solo se usa el nombre del evento en lugar del nombre del programa. Los nombres de los eventos son locales dentro de la aplicación, por lo que son visibles en todas las tareas.

El equipo creará un evento con el nombre especificado. Los controladores de eventos recibirán los parámetros enumerados en los argumentos del comando.

Por ejemplo:
EVENTTRAP? X [1]> 8 evuBoxData (us [7], $ 4), evuKorXY (us [X], us [Y], sh);
2 eventos evuBoxData y evuKorXY se generan como variables us [7], $ 4, us [X], us [Y] y la cadena constante sh .
Si actualmente no hay controladores para este evento, entonces el evento no se genera.

Equipo de EVENTDELETE


El comando elimina los controladores de eventos enumerados en los argumentos del programa.
Por ejemplo:
EVENTDELETE? X [1]> 7 evuKorXY, evuBoxData;
Los eventos se eliminarán en el orden en el equipo.

Equipo de EVENTCALL


El comando asigna un controlador de eventos. Un controlador es un programa. Este programa se llamará asincrónicamente desde la tarea en ejecución y se le pasarán parámetros. Después de la ejecución del programa controlador, el control volverá a la tarea principal en el sitio de interrupción.

Por ejemplo:
EVENTCALL evuBoxData = Mod1.intEvuBoxData, evuKorXY = Mod2.intevuKorXY;

Equipo de EVENTWAIT


El equipo está esperando que ocurran los eventos. Este comando bloqueará la tarea actual hasta que ocurran los eventos enumerados en sus argumentos. Todos los eventos enumerados en sus argumentos deberían ocurrir para continuar la ejecución del hilo actual. Los parámetros actuales del programa se reemplazan por los transferidos en el comando de creación de eventos.
Por ejemplo:
EVENTWAIT evuBoxData, evuKorXY;
Estos comandos le permiten organizar la ejecución asincrónica de programas.

Vectores


Los vectores están estrechamente relacionados con la implementación actual. Almacenan enteros con y sin signo. La dimensión de estos vectores depende de la profundidad de bits del componente del vector y es fija.

Vectores de 64 bits


La dimensión de dicho vector es 2. Los componentes pueden ser enteros de 64 bits o enteros sin signo. Dichos números se almacenan en cualquier variable.
El atractivo para los componentes enteros icónicos del vector.
SET us [5].% V64 (0) = ss $ 1.% v64 (1);
% v64 : acceso al componente entero con signo del vector.
Una apelación a los componentes enteros sin signo de un vector.
SET us [5].% Vu64 (0) = ss $ 1.% vu64 (1);
% vu64 : acceso a todo el componente sin signo del vector.

Vectores de 32 bits


La dimensión de dicho vector es 5. Los componentes pueden ser enteros de 32 bits o enteros sin signo. Dichos números se almacenan en cualquier variable.
El atractivo para los componentes enteros icónicos del vector.
SET us [5].% V32 (0) = ss $ 1.% v32 (4);
% v32 : acceso al componente entero con signo del vector.
Una apelación a los componentes enteros sin signo de un vector.
SET us [5].% Vu32 (0) = ss $ 1.% vu32 (4);
% vu32 : acceso a todo el componente sin signo del vector.

Vectores de 16 bits


La dimensión de dicho vector es 11. Los componentes pueden ser números enteros o sin signo de 16 bits. Dichos números se almacenan en cualquier variable.
El atractivo para los componentes enteros icónicos del vector.
SET us [5].% V16 (0) = ss $ 1.% v16 (10);
% v16 : acceso al componente entero con signo del vector.
Una apelación a los componentes enteros sin signo de un vector.
SET us [5].% Vu16 (0) = ss $ 1.% vu16 (4);
% vu16 : acceso al componente entero sin signo del vector.

Vectores de 8 bits


La dimensión de dicho vector es 22. Los componentes pueden ser números enteros o sin signo de 8 bits. Dichos números se almacenan en cualquier variable.
El atractivo para los componentes enteros icónicos del vector.
SET us [5].% V8 (0) = ss $ 1.% v8 (21);
% v8 : acceso al componente entero con signo del vector.
Una apelación a los componentes enteros sin signo de un vector.
SET us [5].% Vu8 (0) = ss $ 1.% vu8 (21);
% vu8 : acceso a todo el componente sin signo del vector.

Operaciones


Las operaciones en MSH juegan un papel especial. Son ellos quienes controlan la conversión de los tipos de datos. Dependiendo de la operación, los operandos se convierten al tipo de datos deseado. El tipo de resultado de datos corresponde únicamente al tipo de operación. Las operaciones de cadena y numéricas no se superponen, como la operación + en lenguajes tipo C. El tipo de operación en MSH no depende del tipo de operandos, todo es exactamente lo contrario. No hay prioridades de operaciones en MSH; es un legado histórico de MUMPS.

Por ejemplo:
SET $ 1 = 2 + 3 * 4;
$ 1 será 20, no 14.
Para que el resultado sea 14, se utilizan paréntesis.
SET $ 1 = 2 + (3 * 4);
Como operación de unir cadenas, se usa el símbolo " _ ".
La falta de prioridad de las operaciones es inusual, pero bastante conveniente. La referencia a la naturalidad de las prioridades, cuando las operaciones son más que sumar, se vuelve muy dudosa. Habiendo aprendido una vez que no hay prioridades, no hay necesidad de recordar dolorosamente sus prioridades, y si algo entra en la documentación. En general, esto es una cuestión de costumbre.

Los objetos


La presencia de objetos en lenguajes de programación modernos es una buena forma. En el caso general, los objetos constan de 2 partes. Partes de la descripción declarativa y partes de la implementación. En los sistemas MUMPS, las variables no tienen la parte declarativa de una declaración de tipo. Las clases son básicamente tipos de datos de usuario. Para no violar los principios de MUMPS, MSH carece de la parte declarativa de la descripción de la clase. Y resultó que puedes prescindir de él perfectamente. Solo queda parte de la implementación de la clase. La implementación de la clase puede estar perfectamente representada por el módulo estándar. Solo para la clase tuvo que introducir convenciones adicionales asociadas con la descripción de las propiedades del objeto. Los objetos solo pueden estar en el árbol. La colocación de un objeto en una matriz fallará porque no hay ningún lugar para almacenar las propiedades del objeto. Aunque si el objeto no tiene propiedades, puede intentarlo.Aunque qué tipo de objeto es.
Para describir una propiedad pública, necesita una función que devuelva el valor de esta propiedad. Debe tener un nombre de propiedad y estar en el módulo con el nombre de la clase. Un programa para escribir en una propiedad. Este programa tiene un nombre de propiedad con el prefijo "." Y un programa de eliminación de propiedades. Este programa tiene un nombre de propiedad con el prefijo "..".

El nombre de la función en el módulo puede corresponder a la propiedad pública para la lectura. En este caso, el valor de retorno de dicha función se pasa al programa de llamada como el valor de la propiedad pública.

El constructor de la clase es el método de datos% objNew. Si, al crear un objeto, es necesario determinar cualquier propiedad u obtener recursos, entonces se puede usar cualquier programa de módulo de clase (método de clase). Pero es aconsejable adherirse a cualquier estrategia para nombrar constructores de clases. Por ejemplo, el nombre del constructor debe coincidir con el nombre de la clase.

El acceso a las propiedades protegidas de la clase se realiza a través de la propiedad del sistema % this .

El destructor es la eliminación del objeto utilizando el comando KILLD. Si necesita liberar recursos o realizar manipulaciones adicionales, esto puede hacerlo cualquier programa de esta clase (método de clase). Como en el caso del constructor, es aconsejable adherirse a alguna estrategia de denominación al nombrar el destructor.

Por ejemplo:
// Persona de clase
// Edad de propiedad
// lea la propiedad pública Edad
Edad: RETORNO [% this, Age];
// registro de la propiedad pública Age
.Age: SET [% this, Age] = A $ 1;
Regreso
FINAL

La apelación de los programas a un objeto y sus propiedades públicas tendrá el siguiente aspecto.
// crea un objeto Persona
SET us [1,2].% objNew = Person;
// escribe el valor 50 en la propiedad Age
SET us [1,2] .Age = 50;
// lee la propiedad
SET de edad u $ 1 = us [1,2] .Age + 5;
// eliminar propiedad Age
KILL us [1,2] .Age;

Herencia de objetos


Las clases MSH admiten herencia múltiple. El comando PADRE establece todos los antepasados ​​de esta clase. Además, el orden de los nombres de los antepasados ​​de esta clase en los argumentos del comando determina la prioridad de la herencia. Cuanto más tarde se menciona la clase, menor es su prioridad.

Por ejemplo:
USUARIO PADRE, CAJA;
La clase hereda de los antepasados ​​de USER y BOX . La prioridad del ancestro del USUARIO es mayor. ¿Cuál es la prioridad? Al acceder a un objeto, la propiedad pública o el método de la clase se buscarán en la clase misma, si no se encuentran allí, se buscarán en el ancestro con la prioridad más alta, y luego en los ancestros de esta clase, etc., por prioridad.

Compartición de archivos

En MSH, el intercambio de archivos se organiza al nivel más primitivo. Los archivos en MSH juegan un papel de apoyo. El intercambio se organiza solo con archivos de texto. La estructura del archivo son campos de texto separados por un separador especificado. El delimitador está en la variable de sistema % dlmIO . Por defecto, esta variable es " , ". Está disponible para leer y escribir. Al escribir en un archivo, las variables se convierten a un tipo de cadena y se escriben en el archivo a través del delimitador. Al leer desde un archivo, las variables se seleccionan a través de un separador y se llevan a una forma normalizada. Si el campo es un registro de un número, entonces se coloca un número en la variable. El intercambio con el archivo ocurre a través de la matriz B $ . Al escribir, la matriz es B $escrito en el archivo a través del delimitador. Al leer desde un archivo, los campos se seleccionan en la matriz B $ .

Los comandos de intercambio de archivos toman la ruta del archivo como argumentos.
Por ejemplo:
desde una matriz de datos B $ se escribe en un archivo. El archivo se abre por escrito. Los datos en el archivo se reemplazan.
Escribe "txt / tt1.txt";
Los datos se leen en la matriz B $ . La matriz se borra previamente.
Lea "txt / tt1.txt";
txt / tt1.txt: ruta al archivo.

Conclusión


Este documento no sustituye la descripción del lenguaje MSH, sino que solo lo complementa. Aquí, no se consideran todas las características del lenguaje MSH, sino solo aquellas a las que me gustaría llamar su atención.

Autor: Sharymov Mikhail Alekseevich. Correo electrónico: misha_shar53@mail.ru

Al usar este material, se requiere un enlace a la fuente y al autor.

All Articles