Caractéristiques de l'implémentation du langage MSH

Je continue la série d'articles sur le langage de programmation MSH. Dans un article précédent, j'ai déjà décrit les spécifications de ce langage. Mais la spécification ne révèle pas toutes les fonctionnalités du langage. Je veux éliminer cet écart avec cet article. Bien sûr, je n'ai pas réussi à tout décrire, mais en gros j'ai décrit les principales fonctionnalités. Nous reporterons le reste jusqu'à des temps meilleurs.

Lorsque l'image de la langue prend forme, il semble que tout soit logique et cohérent. Mais à l'entrée dans la mise en œuvre de la langue, il y a des problèmes à résoudre en choisissant l'une ou l'autre stratégie. Maintenant que l'interpréteur de langue est prêt, vous pouvez voir comment il a résolu l'un ou l'autre problème d'implémentation.

Concepts du langage de programmation MSH


Table des matières


introduction 2
Organisation du programme. 2
temps d'exécution. 2
Gestion des données. 3
Localisation des données. 3
Syntaxe abrégée. 5
constantes. 5
Caractéristiques de certaines équipes. 5
Commande CONSTANT. 5
Commande XECUTE. 6
Commandes COPY et MOVE. 6
Synchronisation des ressources. 7
Forme abrégée de la commande SET. 8
équipes de blocs. 8
Commande IF. 8
Commande CASE. 9
PENDANT la commande. 10
Itérateurs de boucle de bloc. 10
NEXT Team. 11
Commande BACK. 12
équipe QUERY. 12
Commandes non bloquantes Commandes de traversée de l'arborescence des données. 12
Équipe NEXT1. 12
Commande BACK1. treize
QUERY Team1. 13
Gestion de l'exécution du programme. 13
Passage des paramètres. 14
Gestion des événements. 15
Commande EVENTTRAP. 15
Équipe EVENTDELETE. 16
Équipe EVENTCALL. 16
Équipe EVENTWAIT. 16
vecteurs. 16
vecteurs 64 bits. 16
vecteurs 32 bits. 17
vecteurs 16 bits. 17
vecteurs 8 bits. 17
Opérations. 17
objets. 18
Héritage d'objets. 19
Échange avec le fichier. 20
Conclusion. vingt

introduction


Le langage MSH est construit sur les concepts du langage MUMPS. MUMPS est un langage peu connu développé au siècle dernier. Mais il est toujours utilisé dans les applications d'information. Des informations sur cette langue sont présentes sur Internet. Il existe des implémentations de travail de ce langage et une communauté de programmeurs le supportant. MUMPS est en cours de développement aux États-Unis et en Russie. De plus, il est utilisé, à ma connaissance, en Amérique latine, en Allemagne, en Australie et en Chine. En général, ce concept vivant de la langue. Lors de sa rencontre avec MUMPS, sa nature archaïque est saisissante. Ce développement vise à éliminer ses lacunes, tout en préservant ses avantages, simplicité, cohérence, organisation des données.
Organisation du programme.

L'unité de traduction est le module linguistique MSH. Le module démarre à partir des 8 octets standard identifiant la version linguistique. Au début de la ligne peut être une étiquette ou des espaces. L'étiquette se termine par un «:» et est séparée du reste des commandes par un nombre quelconque d'espaces. Les lignes se composent de commandes. Un signe de la fin de l'équipe est le symbole «;». La commande est séparée des arguments par des espaces. La casse des personnages n'a pas d'importance. La commande peut être écrite avec des caractères de n'importe quel registre. De plus, de nombreuses équipes ont une forme abrégée. Une commande peut avoir une condition pour son exécution. S'il y en a un, alors le symbole "?" Suit la commande sans espaces et les conditions d'exécution de cette commande. Si la condition d'exécution de la commande n'est pas égale à 0, alors la commande est exécutée. À l'intérieur de la condition, les espaces ne sont pas autorisés, sinon ils seront traités comme des séparateurs entre la commande et les arguments.

Par exemple:

SET? [5]> 5 Val [1] = 25; // est-ce correct
SET? [2]> 5 Val [1] = 25; // erreur de syntaxe
SET? ([1,2]> 5) Val [1] = 25; // correctement, les espaces inside () sont autorisés.

Les arguments sont séparés par le symbole ",". À l'intérieur des arguments, un espace n'est pas un caractère spécial et peut être contenu n'importe où. En règle générale, une commande peut avoir un nombre illimité d'arguments. Les étiquettes du module sont localisées à l'intérieur du module et doivent y être uniques, à l'exception des étiquettes à l'intérieur de la commande CASE . Les étiquettes de la commande CASE localisées à l'intérieur de cette commande doivent être uniques uniquement à l'intérieur de cette commande et peuvent dupliquer des étiquettes à la fois en dehors de cette commande et dans des commandes CASE imbriquées .

Durée


Au moment de l'exécution, l'application a une ou plusieurs tâches. Toutes les tâches sont effectuées en parallèle. Dans chaque tâche, les programmes sont exécutés séquentiellement. À chaque instant, un seul code de programme est exécuté dans la tâche. La tâche se termine à la fin du dernier programme en cours d'exécution. La tâche principale est lancée par le runtime de langue. Les travaux restants sont générés par la commande Job .

Gestion de données


Pour ceux qui connaissent le langage MUMPS, l'organisation des données dans MSH est assez claire. Il n'y a pas de description des données dans MSH. Il n'y a pas de déclaration de données. Les données peuvent être stockées sous forme d'arbre, puis l'accès aux nœuds d'arbre est effectué par un nom et un index facultatifs. L'index est placé entre crochets []. Le nom est devant eux.

Par exemple:

SET Pr [4,5, "rt"] = Is ["ty ^ 578"];
Ici Pr et Is sont les noms des arbres. 4,5, "rt" et "ty ^ 578" sont des indices de nœuds.

L'arbre peut avoir un nombre arbitraire de niveaux et, par conséquent, l'index a le nombre correspondant de champs séparés par des virgules. Le champ d'index peut avoir une valeur arbitraire du type de base. Les types de base dans MSH sont les nombres et les chaînes. Seuls les nœuds sur lesquels l'enregistrement a été effectué sont directement stockés dans l'arborescence. Le nom, le champ d'index et l'index lui-même peuvent être une expression. Après calcul, le nom ne peut être qu'un identifiant.

De plus, les données peuvent être stockées sous forme de tableau continu, puis un accès à l'élément de tableau se compose d'un nom de symbole facultatif "$" et d'un index. Un index est un entier. Le plus petit index du tableau est 1. Le nom et l'index peuvent être des expressions. Après le calcul, le nom ne peut être qu'un identifiant et l'index un entier. Un tableau peut être rempli dans n'importe quel ordre, mais si vous avez écritmc 1000 $ , puis un tableau mc de 1000 éléments sera créé . Les éléments non définis ne contiendront pas de valeurs, mais ils seront présents. Le nombre d'éléments dans le tableau peut être trouvé en se référant à l'élément zéro de ce tableau.

Par exemple: mc $ 0 La
taille du tableau peut être modifiée en écrivant une nouvelle longueur de tableau dans cet élément. Mais dans le cas général, cela n'est pas nécessaire, car le tableau se développe automatiquement.

Les nœuds d'arbre et les éléments de tableau contiennent des données de types basiques. Ce sont des chaînes ou des nombres. La façon dont les données du programmeur sont stockées n'est pas concernée. Le stockage des types de données et leur manipulation est la responsabilité de l'implémentation du langage MSH.

Localisation des données


Les données MSH sont divisées en globales et locales. Ils diffèrent par le type de nom. Les données globales sont stockées dans la mémoire à long terme et ne dépendent pas de la durée de vie de l'application. Une fois qu'ils sont créés, ils peuvent être modifiés par l'application et existeront jusqu'à ce que l'application les détruise avec la commande KILL . Toutes les données globales ont le préfixe «^» dans le nom.

L'accès aux données globales est possible simultanément à partir de différentes tâches. Par conséquent, lors de l'accès aux globaux, la synchronisation est nécessaire. Cette synchronisation est toujours automatique. Il y a toujours une primitive de synchronisation dans le descripteur global et elle contrôle l'accès au global. De plus, lorsque la lecture du global est bloquée par la lecture, lors de l'écriture au global, elle est bloquée par l'écriture. Aucune synchronisation globale supplémentaire n'est requise. Les accès aux tableaux globaux sont également synchronisés.

Par exemple:

^ gl [87,9] - accès au nœud d'arbre global.

^ glar $ 45 - accès à l'élément du tableau global.

Les données locales existent uniquement lorsque l'application est en cours d'exécution. Le prochain lancement d'application ne peut pas accéder aux données locales du lancement précédent.

L'étendue des données locales dépend de leur type. Il existe trois types de localisation de données.

1. Données de programme locales. Ils sont localisés dans le programme et existent depuis le lancement du programme jusqu'à son achèvement. Si le programme appelle le sous-programme, de nouvelles données de sous-programme sont créées et les données du programme local à l'intérieur du sous-programme ne sont pas visibles. Lorsque vous revenez au programme, les données du programme local redeviennent disponibles. Les données de programme locales n'ont pas de nom.

Par exemple:

[7,9] - accès au nœud de l'arborescence localisé à l'intérieur du programme.

$ 5 - accès à un élément de tableau localisé dans le programme.

Il y a une exception. Un tableau de paramètres transmis au programme A $ est également localisé à l'intérieur du programme.

2. Données d'application locales. Ils sont visibles dans toutes les tâches de l'application. Vous pouvez les contacter à partir de n'importe quelle tâche. Ils existent à partir du moment où ils sont créés dans l'application par n'importe quelle tâche jusqu'à ce que l'application soit terminée ou jusqu'à ce qu'ils soient détruits par l'équipe KILL . Les noms de ces données sont préfixés de " % ". Ces variables sont simultanément disponibles dans différentes tâches, elles sont donc aussi synchronisées que les globales.

Par exemple:

% dapp [87.9] - accès au nœud de l'arborescence localisé à l'intérieur de l'application.

% dapp $ 45 - accès à un élément de tableau localisé à l'intérieur de l'application.

3. Données de travail locales. Ils sont localisés à l'intérieur de la tâche et existent à partir du moment où ils sont créés dans n'importe quel programme de tâche jusqu'à ce que la tâche soit terminée ou détruite par l'équipe KILL . Ces données doivent avoir un nom et ne doivent pas contenir les préfixes « ^ » et « % ». L'exception est le tableau des paramètres du programme A $ , il est localisé à l'intérieur du programme

Par exemple:

djob [87,9] - accès au nœud de l'arborescence localisé à l'intérieur de la tâche.
djob $ 45 - accès à l'élément du tableau localisé à l'intérieur du travail.
L'accès aux variables ne peut avoir que les types répertoriés ici.

Syntaxe abrégée


Ce terme n'a aucun rapport avec un terme similaire dans le langage MUMPS. La syntaxe abrégée de MSH est utilisée pour faire référence à l’arborescence entière ou à l’ensemble du tableau. Il n'est utilisé que dans des équipes distinctes et lorsqu'il est autorisé, il est toujours négocié. Un appel à l'arbre entier se compose d'un nom et des crochets requis. Aucun nom n'est spécifié pour l'arborescence du programme local.
Par exemple:
us [] - accès à la totalité de l'arborescence us.
[] - accès à l'arborescence du programme local.

L'accès à l'ensemble du tableau se compose du nom et du caractère requis " $ ". Aucun nom n'est fourni pour le tableau local du programme.

Par exemple:
us $ - accès à l'ensemble du tableau us.
$ - accès au tableau de programmes local.

Constantes


Les types de données de base peuvent être numériques ou sous forme de chaîne. La forme numérique est soit un nombre entier soit un nombre réel en présence d'un point décimal. La base des nombres est 10. Ils peuvent être positifs ou négatifs.
Par exemple:
25, -4, 789.56, -9.3

Les constantes de chaîne sont n'importe quelle séquence de caractères. Si une constante se compose uniquement de lettres et de chiffres, vous ne pouvez pas la mettre entre guillemets, car elle ne peut pas être confondue avec une variable. Si la constante contient d'autres caractères, elle doit être placée entre guillemets simples ou doubles.
Par exemple:
"rt @ tty # 123"
'14 "5 * 7" 89 \? '
125Dsv

Caractéristiques de certaines équipes


Équipe CONSTANT


Vous pouvez donner un nom à une constante à l'aide de la commande CONSTANT . Ce sont des noms d'heures de diffusion. La diffusion les remplace par leurs valeurs. Au moment de l'exécution, ces noms n'existent plus. Par conséquent, vous ne pouvez pas affecter une expression à un nom dans la commande CONSTANT . La valeur doit être exactement une constante. Les noms des constantes doivent être choisis de façon à ce qu'ils ne coïncident pas avec les valeurs des constantes spécifiées dans le programme sans guillemets.

Par exemple:
Constant ioInOut = "/ini/par.ini>,maxIs=25;
Les constantes ioInOut et maxIs reçoivent des valeurs. Plus loin dans le programme, ces noms peuvent être utilisés à la place de ces valeurs.
Équipe constanteIl a 2 formes. Dans ce cas, le côté droit de l'égalité est absent. Le sens de cette équipe est différent. Le nom est le nom du module contenant les constantes. Les constantes de ce module sont exportées vers le module actuel. Le module important des constantes ne contient aucune description supplémentaire sur l'importation. Le module d'importation ne peut contenir que des constantes et des programmes.
Par exemple:
CONSTANT sysCnsNet, usrCnsByx;
sysCnsNet et usrCnsByx sont des noms de module contenant des constantes.
Les deux formes peuvent apparaître comme arguments d'une seule commande CONSTANT .

L'équipe XECUTE


La commande XECUTE est une équipe plutôt exotique, mais elle est présente dans le langage MUMPS. Dans d'autres langages de programmation, elle ne m'a rencontré qu'en JavaScript. Là, on l'appelle eval. Lorsque cette commande est exécutée, l'expression de l'argument de cette commande est calculée, puis cette expression est convertie en chaîne, interprétée comme une commande MSH et exécutée.

Par exemple:
XECUTE "SET $ 1 = 89;";
Par conséquent, la variable $ 1 recevra la valeur 89 .
Il s'agit d'un exemple primitif, sous cette forme, il n'a guère de sens d'utiliser cette commande. Les arguments de la commande XECUTE sont des expressions, qui vous permettent de générer diverses commandes MSH au moment de l' exécution du programme.
La commande est exécutée dans le contexte du programme où se trouve la commande. Toutes les ressources du programme sont à sa disposition, y compris les données locales du programme.

Commandes COPY et MOVE


La commande COPY est similaire à la commande MERGE MUMPS. Ces commandes copient le nœud source ainsi que tous les descendants sur le nœud récepteur. L'argument de ces commandes se compose de deux champs séparés par le symbole " = ". À droite de ce signe se trouve le nœud source, le récepteur à gauche. Un lien abrégé est autorisé en tant que nœuds, dans ce cas, tout l'arbre est utilisé. Ces commandes copient non seulement les descendants, mais aussi les nœuds eux-mêmes. La commande COPY effectue la fusion des données. Les données source sont copiées sur le récepteur sans les nettoyer. La source ne change pas. MOVE Teameffectue des données réellement en mouvement. Auparavant, le récepteur était nettoyé, puis tous les descendants du nœud source étaient copiés et le nœud source était supprimé avec tous ses descendants.

Par exemple:
// le noeud us [5,6] est copié dans le noeud [1]
COPY [1] = us [5,6];
// tout l'arbre us est déplacé vers le noeud [8]
MOVE [8] = us [];
Ces commandes peuvent également être utilisées pour copier des tableaux. Dans ce cas, seules les liaisons raccourcies peuvent être utilisées comme source et récepteur.
Les arguments du programme sont copiés dans le tableau arg.
COPY arg $ = A $;
Vous pouvez utiliser la commande MOVE pour vous déplacer.
DÉPLACER a1 $ = b1 $;
Vous pouvez copier et déplacer n'importe quel tableau.

Synchronisation des ressources


Lors de l'exécution de plusieurs tâches, il devient nécessaire d'accéder aux ressources partagées. La synchronisation est effectuée par des commandes de verrouillage. Les commandes de synchronisation en elles-mêmes ne bloquent rien, il s'agit d'un ensemble d'accords qui permettent de différencier l'accès aux ressources partagées. Si les commandes de verrouillage ne sont pas partagées entre les travaux, aucune synchronisation d'accès n'aura lieu. La synchronisation est basée sur les noms de verrouillage. Ces noms sont localisés dans l'application et sont les mêmes dans toutes les tâches. Dans les serrures, le concept de plusieurs lecteurs et d'un seul écrivain à la fois est accepté. En conséquence, il existe des verrous de lecture et des verrous d'écriture.

Équipe LockRbloque la lecture des noms. Les noms répertoriés dans ses arguments seront bloqués en lisant. N'importe quel nombre de tâches peut bloquer les mêmes noms, mais une tâche qui a tenté de bloquer l'un de ces noms attendra que tous ces noms soient déverrouillés. Après avoir capturé le nom d'écriture, aucune commande de verrouillage de lecture ne peut être exécutée tant que le verrouillage d'écriture ne libère pas le nom. S'il n'est pas possible de verrouiller, la commande attendra la libération du nom.

Par exemple:
LockR nom1, nom2, nom3;
Ces noms seront bloqués en lisant. Une autre tâche peut également verrouiller ces noms sans attendre qu'ils soient déverrouillées. LockW

commandebloque les noms par enregistrement. Les noms répertoriés dans ses arguments seront bloqués par enregistrement. Si les noms répertoriés dans les arguments sont déjà bloqués par une commande de verrouillage, cette commande attendra la libération de ces noms.
Par exemple:
LockW nom1, nom2, nom3;
Ces noms seront verrouillés par enregistrement.
La commande LockUn déverrouille ce nom. Si le nom est bloqué en lisant plusieurs fois, vous devez déverrouiller le même nombre de fois.
Par exemple:
LockUn nom1, nom2, nom3;
Les fonctions standard ont des analogues de ces commandes avec un timeout.

Forme abrégée de la commande SET


La commande SET a une forme abrégée. Dans ce cas, le côté gauche de l'égalité est absent; son rôle est joué par la dernière variable mentionnée dans l'expression.
Par exemple:
SET $ 1 = 2, $ 1 + 3;
La variable $ 1 deviendra égale à 5 .
S'il y a plusieurs variables dans l'expression, le résultat sera affecté à la dernière variable.

Par exemple:
SET 1 $ = 1, 2 $ = 2, 3 $ = 3, 1 $ + 2 $ + 3 $;
La variable $ 3 deviendra 1 + 2 + 3 = 6. Bien que ce formulaire soit plus approprié à utiliser uniquement dans des cas très simples, semblable au premier exemple. Le deuxième exemple est fourni uniquement à titre d'illustration des capacités de cette forme de la commande SET .

Bloquer les équipes


Les commandes de bloc forment un bloc de commandes et servent simultanément d'en-tête du bloc. Chaque commande de bloc doit avoir sa propre commande END , même s'il n'y a qu'une seule commande dans le bloc.

Équipe IF


La commande IF forme un bloc qui est exécuté si les conditions d'exécution de cette commande sont vraies. Cette commande n'a aucun argument. Ce bloc peut contenir des commandes ELSE . La commande ELSE n'a aucun argument. En dehors du bloc IF , ces commandes n'ont aucune signification et ne peuvent pas être appliquées. S'il y a des commandes ELSE dans le bloc IF lorsque la condition de la commande IF est remplie, seules les commandes derrière la commande IF sont exécutées jusqu'à la prochaine commande ELSE . ELSE Teampeut contenir une condition d'exécution, alors en cas de vérité dans cette condition, seules les commandes situées après cette commande seront exécutées jusqu'à la prochaine commande ELSE ou END . Un bloc IF ne peut contenir qu'une seule commande ELSE sans condition pour son exécution, et il doit être le dernier parmi les commandes ELSE .

Par exemple:
SI? [6] <0;
SET y [1] = 1;
AUTRE? [6] <5;
SET y [1] = 2;
AUTRE? [6] <10;
SET y [1] = 3;
AUTRE SET y [1] = 4;
FIN
La condition pour l'exécution de cette commande peut être absente, alors ce bloc sera exécuté dans tous les cas. Bien qu'il soit difficile d'imaginer pourquoi cela pourrait être utile.

Équipe CASE


La sémantique de cette commande est quelque peu différente de la sémantique des autres équipes MSH. La condition pour y exécuter une commande n'est pas telle. Dans la condition d'exécution de la commande CASE , l' expression doit calculer l'étiquette vers laquelle le contrôle sera transféré. Cette commande n'a aucun argument.

Chaque étiquette de cette commande forme un bloc de cette étiquette à la suivante. Lorsque le contrôle est transféré à l'étiquette, seules les commandes sont exécutées jusqu'à l'étiquette suivante, puis le bloc CASE actuel est quitté . Cette commande est plus proche de la notation Pascal que de la commande de commutateur C. Si, suite au calcul du nom de l'étiquette, un nom est trouvé qui manque dans le bloc CASE actuel, les commandes situées après la commande CASE avant la première étiquette seront exécutées .

CASE? L_ $ J; // l'étiquette est évaluée
SET x [1] = 1; // si aucune étiquette n'est trouvée, les commandes de ce bloc sont exécutées
SET a [2] = x [1] +1;
L1: SET x [1] = 2; // étiquette du bloc de commande L1
SET a [2] = x [1] +2;
L2: SET x [1] = 3; // étiquette de bloc de commande L2
SET a [2] = x [1] +3;
FIN
Les étiquettes de cette commande forment implicitement un bloc interne de commandes. Après l'exécution d'un tel bloc de commandes, le contrôle est transféré en dehors du bloc de la commande CASE .

WHILE Command


La commande WHILE est utilisée pour organiser la boucle. La condition d'exécution de cette commande fixe la condition de poursuite de la boucle. Tant que la condition d'exécution de la commande n'est pas 0, le bloc formé par cette commande sera exécuté. Le bloc se termine avec la commande END . La commande END d' un tel bloc a une fonctionnalité. Il peut avoir une condition de terminaison de bloc. Si la condition n'est pas 0, le bloc sera terminé. De plus, ces conditions peuvent être présentes à la fois dans la commande WHILE et dans la commande END .
Par exemple:
WHILE? X [7]> 0; // condition pour continuer le cycle
SET y [2] = x [7] +2;
PAUSE? Y [2] <0;
SET x [7] = x [7] +1;
FIN? X [7]> 20; // condition pour terminer la boucle
A l'intérieur du bloc, la boucle peut être interrompue par la commande BREAK.

Itérateurs de boucle de bloc


Les itérateurs de boucle de bloc sont optimisés pour l'accès aux nœuds d'arbre. Ils utilisent un lien interne pour optimiser l'accès aux nœuds de contournement. Cela impose une restriction sur l'utilisation des itérateurs de bloc. À l'intérieur du bloc, vous ne pouvez pas modifier la structure de l'arbre. Vous ne pouvez pas écrire dans cet arbre. Dans les commandes d'itérateur de boucle de bloc, 2 arguments.
Le premier argument est obligatoire, le lien vers le nœud dont les descendants seront contournés. Index de référence. Des liens raccourcis peuvent être utilisés. Le deuxième argument est le lien vers le nœud où sera stocké l'index descendant. Cet argument est facultatif. A l'intérieur du bloc, le 2ème argument ne doit pas changer. Les expressions comme $$ 2 ou [[3]] ne sont pas autorisées si 2 $ ou [3]changer à l'intérieur du bloc itérateur. Les modifications de ces variables ne seront pas prises en compte. L'accès à l'index enfant peut être obtenu via la variable système % queryKey et les données de nœud à partir de la propriété% queryData . Si les descendants ne doivent pas être contournés depuis le début, le deuxième argument est requis et l'index de noeud doit être placé dedans, après quoi le contournement des descendants commence. S'il y a un deuxième argument, mais que vous devez faire le tour depuis le début, alors avant la boucle, vous devez supprimer cette variable avec la commande KILL .
Les commandes peuvent avoir une condition d'exécution de bloc. Cette condition n'est vérifiée qu'une seule fois lors de l'entrée dans le bloc.

Vous pouvez contourner non seulement les nœuds de l'arbre, mais les tableaux. Dans ce cas, le numéro de série du champ du tableau entre dans le deuxième argument. Seul le lien raccourci peut être utilisé comme premier argument.

NEXT Team


La commande NEXT parcourt les descendants immédiats d'un nœud d'arbre. Les descendants coûtent dans le sens direct du minimum au maximum.
Par exemple:
NEXT us [4,5]; // 2 argument n'est pas spécifié, l'index est tiré
// de la variable système% queryKey
SET $ 1 =% queryKey, $ 2 =% queryData;
FIN? 1 $> 1000; // condition pour terminer la boucle
L'index du nœud est immédiatement placé dans les données
KILL $ 1;
SUIVANT [4,5], 1 $;
SET $ 2 =% queryData;
FIN

Un lien abrégé est utilisé comme nœud de référence. Dans ce cas, le premier niveau de l'arborescence est contourné.
TUER 1 $;
SUIVANT [], 1 $;
SET $ 2 =% queryData;
FIN
Explorer après l'index 3.
SET 1 $ = 3;
SUIVANT [4,5], 1 $;
SET $ 2 =% queryData;
FIN
Lors de la traversée d'un tableau, tous les champs sont même dans l'ordre, même ceux dans lesquels les données ne sont pas définies.
Par exemple:
KILL $ 1;
SUIVANT us $, $ 1;
SET $ 2 =% queryData;
FIN
A l'intérieur du bloc, la boucle peut être interrompue par la commande BREAK .

L'équipe BACK


La commande BACK ne diffère de la commande NEXT que dans le sens de la traversée du dernier sommet au premier.
Par exemple:
KILL $ 1;
BACK us [4,5], 1 $;
SET $ 2 =% queryData;
FIN

Équipe QUERY


La commande QUERY parcourt tous les descendants d'un nœud de gauche à droite et de haut en bas jusqu'à toute la profondeur dans la direction avant. Le 2ème argument contient l'intégralité de l'index facultatif. Si cet index a plus d'un champ, la liste est placée dans le 2e argument.

Sinon, cette commande est similaire à la commande NEXT .

La commande QUERY parcourt le tableau uniquement dans le sens direct de gauche à droite.

Par exemple:
KILL $ 1;
DEMANDEZ-NOUS [4,5], 1 $;
SET $ 2 =% queryData;
FIN
Mais cette commande contourne uniquement les sommets qui comptent.

Commandes de non-blocage Commandes d'arborescence de données


Dans ces commandes, les deux arguments de commande sont requis. Le deuxième argument stocke l'index, après quoi la commande suivante trouvera le sommet suivant. Dans ces commandes, les liens internes ne sont pas enregistrés et il n'y a donc aucune restriction quant au réglage de l'arborescence contournée. Pour la même raison, le temps d'accès aux pics peut être beaucoup plus long. Les tableaux peuvent également être contournés avec ces commandes.

Commande NEXT1 La

commande NEXT1 donne le nœud suivant au même niveau sous le nœud de référence.
Les données de nœud sont disponibles dans la variable système % queryData .
Par exemple:
SET $ 1 = 2;
NEXT1 us [1,4], 1 $;
// donnera le nœud situé au niveau 3 après le nœud us [1,4,2]

L'équipe BACK1


La commande BACK1 donne le sommet précédent au même niveau sous le nœud de référence.
Sinon, elle est similaire à la commande NEXT1 .

QUERY1 Team


La commande QUERY1 donne le sommet suivant d'une branche d'arbre tout en parcourant le nœud entier de haut en bas et de gauche à droite. Le 2ème argument contient l'intégralité de l'index facultatif. Si cet index a plus d'un champ, la liste est placée dans le 2e argument.

Sinon, elle est similaire à la commande NEXT1 .

Gestion de programme


Une étiquette de module peut être un point d'appel pour un programme, une fonction, un point d'appel pour une nouvelle tâche, une propriété d'objet, une méthode d'objet et une étiquette, selon l'accès à cette étiquette.
De plus, l'étiquette est accessible différemment dans différentes parties du module.
Par exemple:
LB: Set Val [25] = 7 + A $ 1; Retour Val [25];
Faire LB (78); // Accès au programme. La valeur de retour est ignorée.
Réglez Val [7] = 8 * LB (6); // Appel en tant que fonction, la valeur de retour est utilisée.
JOB LB (17, «yt»); // appeler un nouveau travail
Set Val [9] = Mod.LB (3); // Accès à la méthode de classe Mod - le nom du module dans ce contexte est traité comme le nom de la classe de l'objet.
Set Val [15] = Obj [1,2, A] .LB; // Accès à la propriété LB de l'objet Obj [1,2, A],
Allez LB; // Aller à l'étiquette LB
Dans les exemples ci-dessus, en plus de 4 et 5, l'appel est effectué à l'intérieur du module actuel. L'appel de programmes et de fonctions peut être effectué vers n'importe quel module disponible. Soit dit en passant, cela s'applique également à l'équipe Go.
Par exemple, dans le module Mod, il y a une étiquette Lb. Ensuite, l'appel ressemblera à ceci :
Do Mod.Lb (78);
Set Val [7] = 8 * Mod.Lb (6);
Set Val [9] = Mod.Lb (3);
JOB Mod.Lb (78);
Go Mod.Lb;

En général, une étiquette est utilisée comme programme ou fonction. Si le module est utilisé comme classe d'objets, l'étiquette est une propriété ou une méthode de cet objet. Les propriétés d'un objet et ses méthodes sont une autre forme d'accès aux programmes et aux fonctions, donc tout ce qui est dit sur les programmes et les fonctions s'applique également aux propriétés et aux méthodes. Il n'y a pas de nuances significatives lors de l'accès aux objets uniquement en termes d'héritage. Ce problème sera décrit plus en détail ci-dessous.

Dans le cas général, le nom du module et le nom de l'étiquette dans l'appel à celui-ci sont des expressions et peuvent être calculés au moment de l'appel.
Par exemple:
Set $ ​​1 = "Md", $ 2 = "Lb";
Faites $ 1. $ 2;

Le programme se termine avec la commande Return; Un module peut être complété avec la commande End;

Passer des paramètres


Dans MSH, les paramètres ne sont transmis que par valeur. En général, aucune référence et pointeurs en termes de langage C ++ et les énormes problèmes associés à leur utilisation ici, en principe, n'existent. Un programme prend toujours un nombre arbitraire de paramètres dans un tableau A $ . Leur nombre peut être trouvé en se référant à l'élément 0 du tableau A $ 0 . Le programmeur est responsable de la signification de ces paramètres.

Par exemple:
Nous avons un programme Lb. Il peut être adressé:
DO Lb (1,2,7);
DO Lb (25,7);
DO Lb ();
EMPLOI Lb (8);
SET [7.8] = Lb (187, «Privet»);

Les paramètres non transmis dans le programme ne sont pas définis.
La transmission de paramètres par valeur n'interfère pas avec la transmission de noms de variables dans le programme et leur utilisation à cet endroit.
Par exemple:
définissez-nous [5] = 48;
Do Lb ("nous");
...
Retour;
Lb: Set par [8,1,9] = A $ 1 [5];
// A $ 1 - le premier paramètre transmis contient le nom de
variable //: us .
// [5] - accès au sommet de cet arbre
// en conséquence, la valeur du nœud us [5] = 48
Return;
Par conséquent, le nom des variables peut être manipulé arbitrairement.
Lorsque vous accédez à l'étiquette avec la commande GO, le tableau de paramètres ne change pas. MSH a un formulaire de commande GO avec le transfert de nouveaux paramètres. Dans ce cas, le tableau de paramètres actuel est remplacé par le nouveau spécifié dans l'appel de commande GO .
Par exemple:
GO Lb (1,2,7);

Gestion des événements


Le traitement des événements est un mécanisme de programmation puissant qui n'est généralement pas inclus dans les langages de haut niveau. Mais sa présence dans les bibliothèques et le système d'exploitation témoigne de son importance. Les bibliothèques de composants visuels sont construites sur ce mécanisme. La présence d'un traitement d'événements dans la langue étend les capacités de la langue. Ouvre un nouveau style de programmation piloté par les événements. Soit dit en passant, un langage de programmation aussi respecté qu'Assembler possède de telles fonctionnalités. Par conséquent, ce mécanisme a été ajouté au langage MSH.

La base de ce mécanisme est l'événement. Il est localisé dans l'application. Par conséquent, ces noms doivent être uniques dans toute l'application. Si le nom de l'événement correspond au nom déclaré dans la commande CONSTANT, le nom de l'événement sera remplacé par la valeur de la constante déclarée. Faites attention. Lorsque vous nommez des événements, il est conseillé de s'en tenir à une sorte de stratégie de dénomination. Par exemple, affectez des noms d'événement commençant par evu . La longueur du nom, compte tenu du codage UTF8, ne doit pas dépasser 18 octets. Cette limitation n'est associée qu'à l'implémentation actuelle du langage.

Un événement créé dans une tâche est visible et peut être traité dans toutes les tâches. Les gestionnaires d'événements peuvent être n'importe quel nombre et ils peuvent être dans différentes tâches.
Une fois que l'événement se produit, il est vérifié s'il existe des gestionnaires pour cet événement; s'il n'y a pas de gestionnaires, l'événement est supprimé. S'il existe des gestionnaires, ils sont exécutés de manière séquentielle. Les événements peuvent être système et utilisateur. Les événements système sont générés par le système. Un événement personnalisé est généré par la commande EVENTTRAP . L'événement transmet des paramètres aux gestionnaires, comme lors de l'appel d'un programme. Après le traitement, les gestionnaires d'événements ne sont pas supprimés. Pour supprimer un événement, utilisez la commande EVENTDELETE . Un événement peut être géré par les commandes EVENTCALL et EVENTWAIT .

Équipe EVENTTRAP


La commande crée un événement personnalisé. Le format de la commande ressemble à un appel de programme, seul le nom de l'événement est utilisé à la place du nom du programme. Les noms d'événements sont locaux dans l'application, ils sont donc visibles dans toutes les tâches.

L'équipe créera un événement avec le nom spécifié. Les gestionnaires d'événements recevront les paramètres répertoriés dans les arguments de commande.

Par exemple:
EVENTTRAP? X [1]> 8 evuBoxData (us [7], $ 4), evuKorXY (us [X], us [Y], sh);
2 événements evuBoxData et evuKorXY sont générés sous forme de variables us [7], $ 4, us [X], us [Y] et la constante de chaîne sh .
S'il n'y a actuellement aucun gestionnaire pour cet événement, l'événement n'est pas généré.

Équipe EVENTDELETE


La commande supprime les gestionnaires d'événements répertoriés dans les arguments du programme.
Par exemple:
EVENTDELETE? X [1]> 7 evuKorXY, evuBoxData;
Les événements seront supprimés dans l'ordre dans l'équipe.

Équipe EVENTCALL


La commande affecte un gestionnaire d'événements. Un gestionnaire est un programme. Ce programme sera appelé de manière asynchrone à partir de la tâche en cours d'exécution et des paramètres lui seront transmis. Après l'exécution du programme gestionnaire, le contrôle sera retourné à la tâche principale sur le site d'interruption.

Par exemple:
EVENTCALL evuBoxData = Mod1.intEvuBoxData, evuKorXY = Mod2.intevuKorXY;

Équipe EVENTWAIT


L'équipe attend que des événements se produisent. Cette commande bloquera la tâche en cours jusqu'à ce que les événements répertoriés dans ses arguments se produisent. Tous les événements répertoriés dans ses arguments doivent se produire pour continuer l'exécution du thread actuel. Les paramètres actuels du programme sont remplacés par ceux transférés dans la commande de création d'événement.
Par exemple:
EVENTWAIT evuBoxData, evuKorXY;
Ces commandes vous permettent d'organiser l'exécution asynchrone de programmes.

Vecteurs


Les vecteurs sont étroitement liés à la mise en œuvre actuelle. Ils stockent des entiers signés et non signés. La dimension de ces vecteurs dépend de la profondeur de bits de la composante vectorielle et elle est fixe.

Vecteurs 64 bits


La dimension d'un tel vecteur est 2. Les composants peuvent être des entiers 64 bits ou des entiers non signés. Ces nombres sont stockés dans toutes les variables.
L'appel aux composants entiers iconiques du vecteur.
SET us [5].% V64 (0) = ss $ 1.% v64 (1);
% v64 - accès au composant signé entier du vecteur.
Un appel aux composants entiers non signés d'un vecteur.
SET us [5].% Vu64 (0) = ss $ 1.% vu64 (1);
% vu64 - accès à l'ensemble du composant non signé du vecteur.

Vecteurs 32 bits


La dimension d'un tel vecteur est 5. Les composants peuvent être des entiers 32 bits ou des entiers non signés. Ces nombres sont stockés dans toutes les variables.
L'appel aux composants entiers iconiques du vecteur.
SET us [5].% V32 (0) = ss $ 1.% v32 (4);
% v32 - accès au composant signé entier du vecteur.
Un appel aux composants entiers non signés d'un vecteur.
SET us [5].% Vu32 (0) = ss $ 1.% vu32 (4);
% vu32 - accès à l'ensemble du composant non signé du vecteur.

Vecteurs 16 bits


La dimension d'un tel vecteur est 11. Les composants peuvent être des nombres entiers ou non signés de 16 bits. Ces nombres sont stockés dans toutes les variables.
L'appel aux composants entiers iconiques du vecteur.
SET us [5].% V16 (0) = ss $ 1.% v16 (10);
% v16 - accès au composant entier signé du vecteur.
Un appel aux composants entiers non signés d'un vecteur.
SET us [5].% Vu16 (0) = ss $ 1.% vu16 (4);
% vu16 - accès au composant entier non signé du vecteur.

Vecteurs 8 bits


La dimension d'un tel vecteur est de 22. Les composants peuvent être des nombres entiers ou non signés de 8 bits. Ces nombres sont stockés dans toutes les variables.
L'appel aux composants entiers iconiques du vecteur.
SET us [5].% V8 (0) = ss $ 1.% v8 (21);
% v8 - accès au composant entier signé du vecteur.
Un appel aux composants entiers non signés d'un vecteur.
SET us [5].% Vu8 (0) = ss $ 1.% vu8 (21);
% vu8 - accès à l'ensemble du composant non signé du vecteur.

Les opérations


Les opérations dans MSH jouent un rôle particulier. Ce sont eux qui contrôlent la conversion des types de données. En fonction de l'opération, les opérandes sont convertis dans le type de données souhaité. Le type de résultat des données correspond uniquement au type d'opération. Les opérations de chaîne et numériques ne se chevauchent pas, comme l'opération + dans les langages de type C. Le type d'opération dans MSH ne dépend pas du type d'opérandes, tout est exactement le contraire. Il n'y a pas de priorités opérationnelles dans MSH, c'est un héritage historique de MUMPS.

Par exemple:
SET $ 1 = 2 + 3 * 4;
1 $ sera 20, pas 14.
Pour que le résultat soit 14, des parenthèses sont utilisées.
SET 1 $ = 2 + (3 * 4);
Comme opération de jonction de chaînes, le symbole " _ " est utilisé.
Le manque de priorité des opérations est inhabituel, mais assez pratique. La référence au caractère naturel des priorités, lorsque les opérations sont plus que cumulées, se multiplie devient très douteuse. Ayant appris une fois qu'il n'y a pas de priorités, il n'est pas nécessaire de rappeler douloureusement leurs priorités et si quelque chose entre dans la documentation. En général, c'est une question d'habitude.

Les objets


La présence d'objets dans les langages de programmation modernes est une bonne forme. Dans le cas général, les objets se composent de 2 parties. Parties de la description déclarative et parties de l'implémentation. Dans les systèmes MUMPS, les variables n'ont pas la partie déclarative d'une déclaration de type. Les classes sont essentiellement des types de données utilisateur. Afin de ne pas violer les principes de MUMPS, il manque à MSH la partie déclarative de la description de la classe. Et comme il s'est avéré, vous pouvez vous en passer parfaitement. Il ne reste qu'une partie de l'implémentation de la classe. L'implémentation de la classe peut être parfaitement représentée par le module standard. Seulement pour la classe a dû introduire des conventions supplémentaires associées à la description des propriétés de l'objet. Les objets ne peuvent être que dans l'arborescence. Le placement d'un objet dans un tableau échouera car il n'y a nulle part où stocker les propriétés de l'objet. Bien que si l'objet n'a pas de propriétés, vous pouvez essayer.Mais quel genre d'objet c'est.
Pour décrire une propriété publique, vous avez besoin d'une fonction qui renvoie la valeur de cette propriété. Il doit avoir un nom de propriété et être dans le module avec le nom de classe. Un programme pour écrire dans une propriété. Ce programme a un nom de propriété précédé de ".". Et un programme de retrait de propriété. Ce programme a un nom de propriété précédé de "..".

Le nom de la fonction dans le module peut correspondre à la propriété publique pour la lecture. Dans ce cas, la valeur de retour d'une telle fonction est transmise au programme appelant comme valeur de la propriété publique.

Le constructeur de la classe est la méthode de données% objNew. Si, lors de la création d'un objet, il est nécessaire de déterminer des propriétés ou d'obtenir des ressources, alors n'importe quel programme de module de classe (méthode de classe) peut être utilisé. Mais il est conseillé d'adhérer à toute stratégie pour nommer les constructeurs de classe. Par exemple, le nom du constructeur doit correspondre au nom de la classe.

L'accès aux propriétés protégées de la classe s'effectue via la propriété système % this .

Le destructeur est la suppression de l'objet à l'aide de la commande KILLD. Si vous avez besoin de libérer des ressources ou d'effectuer des manipulations supplémentaires, cela peut être fait par n'importe quel programme de cette classe (méthode de classe). Comme dans le cas du constructeur, il est conseillé de respecter une stratégie de dénomination lors de la dénomination du destructeur.

Par exemple:
// Personne de classe
// Age de la propriété
// lire la propriété publique Age
Age: RETURN [% this, Age];
// enregistrement de la propriété publique Age
.Age: SET [% this, Age] = A $ 1;
Revenir
FIN

Un appel de programmes à un objet et à ses propriétés publiques se présente comme suit.
// crée un objet Person
SET us [1,2].% objNew = Person;
// écrit la valeur 50 dans la propriété Age
SET us [1,2] .Age = 50;
// lire la propriété Age
SET u $ 1 = us [1,2] .Age + 5;
// supprimer la propriété Age
KILL us [1,2] .Age;

Héritage d'objet


Les classes MSH prennent en charge l'héritage multiple. La commande PARENT définit tous les ancêtres de cette classe. De plus, l'ordre des noms des ancêtres de cette classe dans les arguments de commande détermine la priorité d'héritage. Plus la classe est mentionnée tard, plus sa priorité est faible.

Par exemple:
PARENT USER, BOX;
La classe hérite des ancêtres de USER et BOX . La priorité de l'ancêtre USER est plus élevée. Quelle est la priorité. Lors de l'accès à un objet, la propriété publique ou la méthode de la classe sera recherchée dans la classe elle-même, si elles n'y sont pas trouvées, elles seront recherchées dans l'ancêtre avec la priorité la plus élevée, puis dans les ancêtres de cette classe et ainsi de suite par priorité.

Partage de fichiers

Dans MSH, le partage de fichiers est organisé au niveau le plus primitif. Les fichiers dans MSH jouent un rôle de support. L'échange est organisé uniquement avec des fichiers texte. La structure du fichier est constituée de champs de texte séparés par un séparateur spécifié. Le délimiteur se trouve dans la variable système % dlmIO . Par défaut, cette variable est " , ". Il est disponible pour la lecture et l'écriture. Lors de l'écriture dans un fichier, les variables sont converties en type chaîne et écrites dans le fichier via le délimiteur. Lors de la lecture d'un fichier, les variables sont sélectionnées via un séparateur et sont amenées sous une forme normalisée. Si le champ est un enregistrement d'un nombre, alors un nombre est placé dans la variable. L'échange avec le fichier s'effectue via le tableau B $ . Lors de l'écriture, le tableau est B $écrit dans le fichier via le délimiteur. Lors de la lecture d'un fichier, les champs sont sélectionnés dans le tableau B $ .

Les commandes d'échange de fichiers prennent le chemin d'accès au fichier comme arguments.
Par exemple:
À partir d'un tableau de B $, les données sont écrites dans un fichier. Le fichier est ouvert par écrit. Les données du fichier sont remplacées.
Écrivez "txt / tt1.txt";
Les données sont lues dans le tableau B $ . Le tableau est pré-effacé.
Lire "txt / tt1.txt";
txt / tt1.txt - chemin d'accès au fichier.

Conclusion


Ce document ne se substitue pas à la description du langage MSH, mais le complète uniquement. Ici, toutes les fonctionnalités du langage MSH ne sont pas prises en compte, mais uniquement celles sur lesquelles je voudrais attirer votre attention.

Auteur: Sharymov Mikhail Alekseevich. Courriel: misha_shar53@mail.ru

Lors de l'utilisation de ce matériel, un lien vers la source et l'auteur est requis.

All Articles