Características da implementação da linguagem MSH

Continuo a série de artigos sobre a linguagem de programação MSH. Em um artigo anterior, eu já descrevi a especificação dessa linguagem. Mas a especificação não revela todos os recursos da linguagem. Eu quero eliminar essa lacuna com este artigo. Obviamente, não consegui descrever tudo, mas basicamente descrevi os principais recursos. Adiaremos o resto para tempos melhores.

Quando a imagem da linguagem apenas toma forma, parece que tudo é lógico e consistente. Mas, na entrada para a implementação da linguagem, há problemas que precisam ser resolvidos escolhendo uma ou outra estratégia. Agora que o intérprete de idiomas está pronto, você pode ver como ele resolveu um ou outro problema de implementação.

Conceitos da linguagem de programação MSH


Índice


Introdução 2
Organização do programa. 2
tempo de execução. 2
Gerenciamento de dados. 3
Localização de Dados. 3
Sintaxe abreviada. 5
constantes. 5
Recursos de algumas equipes. 5
Comando CONSTANTE. 5
Comando XECUTE. 6
Comandos COPIAR e MOVER. 6
Sincronização de Recursos. 7
Forma abreviada do comando SET. 8
equipes de blocos. 8
comando IF. 8
comando CASE. 9
comando WHILE. 10
iteradores de loop de bloco. 10
PRÓXIMA Equipe. 11
comando BACK. 12
equipe QUERY. 12
Comandos não-bloco Comandos transversais da árvore de dados. 12
Equipe NEXT1. 12
Comando BACK1. treze
QUERY Team1. 13
Gerenciamento de Execução do Programa. 13
Passando parâmetros. 14
Manipulação de Eventos. 15
Comando EVENTTRAP. 15
Equipe EVENTDELETE. 16
Equipe EVENTCALL. 16
Equipe EVENTWAIT. 16
vetores. 16
vetores de 64 bits. 16
vetores de 32 bits. 17
vetores de 16 bits. 17
vetores de 8 bits. 17
Operações. 17
objetos. 18
Herança de objetos. 19
Troque com o arquivo. 20
Conclusão. vinte

Introdução


A linguagem MSH baseia-se nos conceitos da linguagem MUMPS. MUMPS é uma linguagem pouco conhecida desenvolvida no século passado. Mas ainda é usado em aplicativos de informação. Informações sobre esse idioma estão presentes na Internet. Existem implementações de trabalho dessa linguagem e uma comunidade de programadores que a suportam. MUMPS estão sendo desenvolvidos nos EUA e na Rússia. Além disso, é usado, até onde eu sei, na América Latina, Alemanha, Austrália e China. Em geral, esse conceito vivo de linguagem. Ao se encontrar com o MUMPS, sua natureza arcaica é impressionante. Esse desenvolvimento foi projetado para eliminar suas deficiências, preservando suas vantagens, simplicidade, consistência e organização dos dados.
Organização do programa.

A unidade de tradução é o módulo de idioma MSH. O módulo inicia a partir dos 8 bytes padrão que identificam a versão do idioma. No início da linha pode haver um rótulo ou espaços. O rótulo termina com um ":" e é separado do restante dos comandos por qualquer número de espaços. Linhas consistem em comandos. Um sinal do final da equipe é o símbolo ";". O comando é separado dos argumentos por espaços. O caso dos caracteres não importa. O comando pode ser escrito com caracteres de qualquer registro. Além disso, muitas equipes têm uma forma abreviada. Um comando pode ter uma condição para sua execução. Se houver um, o símbolo "?" Segue o comando sem espaços e a condição para a execução deste comando. Se a condição para executar o comando não for igual a 0, o comando será executado. Dentro da condição, os espaços não são permitidos; caso contrário, eles serão processados ​​como separadores entre o comando e os argumentos.

Por exemplo:

SET? [5]> 5 Val [1] = 25; // está correto
SET? [2]> 5 Val [1] = 25; // erro de sintaxe
SET? ([1,2]> 5) Val [1] = 25; // corretamente, os espaços inside () são permitidos.Os

argumentos são separados pelo símbolo ",". Argumentos internos, um espaço não é um caractere especial e pode estar contido em qualquer lugar. Normalmente, um comando pode ter qualquer número de argumentos. Os rótulos no módulo estão localizados dentro do módulo e devem ser exclusivos, com exceção dos rótulos dentro do comando CASE . Os rótulos do comando CASE localizados dentro deste comando devem ser exclusivos apenas dentro deste comando e podem duplicar rótulos fora deste comando e nos comandos CASE aninhados .

Tempo de execução


Em tempo de execução, o aplicativo possui uma ou mais tarefas. Todas as tarefas são executadas em paralelo. Em cada tarefa, os programas são executados sequencialmente. A cada momento, apenas um código de programa é executado na tarefa. A tarefa termina com o final do último programa em execução. A tarefa principal é iniciada pelo idioma de execução. Os trabalhos restantes são gerados pelo comando Trabalho .

Gestão de dados


Para aqueles que estão familiarizados com a linguagem MUMPS, a organização dos dados no MSH é bastante clara. Não há descrição dos dados no MSH. Não há declaração de dados. Os dados podem ser armazenados como uma árvore, e o acesso aos nós da árvore é realizado por um nome e índice opcionais. O índice está entre colchetes []. O nome está na frente deles.

Por exemplo:

SET Pr [4,5, "rt"] = Is ["ty ^ 578"];
Aqui Pr e Is são os nomes das árvores. 4,5, “rt” e “ty ^ 578” são índices de nós.

A árvore pode ter um número arbitrário de níveis e, consequentemente, o índice possui o número correspondente de campos separados por vírgulas. O campo de índice pode ter um valor arbitrário do tipo base. Os tipos básicos no MSH são números e seqüências de caracteres. Somente nós nos quais a gravação foi feita são armazenados diretamente na árvore. O nome, o campo de índice e o próprio índice podem ser uma expressão. Após o cálculo, o nome pode ser apenas um identificador.

Além disso, os dados podem ser armazenados como uma matriz contínua e, em seguida, um acesso ao elemento da matriz consiste em um nome opcional de símbolo "$" e um índice. Um índice é um número inteiro. O menor índice de matriz é 1. O nome e o índice podem ser expressões. Após o cálculo, o nome pode ser apenas um identificador e o índice um número inteiro. Uma matriz pode ser preenchida em qualquer ordem, mas se você escreveu emmc $ 1000 , será criada uma matriz mc de 1000 elementos. Elementos indefinidos não conterão valores, mas estarão presentes. O número de elementos na matriz pode ser encontrado consultando o elemento zero dessa matriz.

Por exemplo: mc $ 0 O
tamanho da matriz pode ser alterado escrevendo um novo comprimento de matriz para esse elemento. Mas, no caso geral, isso não é necessário, pois a matriz se expande automaticamente.

Os nós da árvore e os elementos da matriz contêm dados de tipos básicos. Estes são strings ou números. Como os dados do programador são armazenados não está em causa. O armazenamento dos tipos de dados e sua manipulação é de responsabilidade da implementação da linguagem MSH.

Localização de dados


Os dados do MSH são divididos em global e local. Eles diferem no tipo de nome. Os dados globais são armazenados na memória de longo prazo e não dependem da vida útil do aplicativo. Uma vez criados, eles podem ser alterados pelo aplicativo e existirão até que o aplicativo os destrua com o comando KILL . Todos os dados globais têm o prefixo "^" no nome.

O acesso a dados globais é simultaneamente possível a partir de várias tarefas. Portanto, ao acessar globais, a sincronização é necessária. Essa sincronização é sempre automática. Sempre existe uma primitiva de sincronização no descritor global e controla o acesso ao global. Além disso, ao ler o global é bloqueado pela leitura, ao escrever para o global, ele é bloqueado pela escrita. Nenhuma sincronização global adicional é necessária. Os acessos a matrizes globais também são sincronizados.

Por exemplo:

^ gl [87,9] - acesso ao nó da árvore global.

^ glar $ 45 - acesso ao elemento da matriz global.

Os dados locais existem apenas enquanto o aplicativo está em execução. O próximo lançamento do aplicativo não pode acessar os dados locais do lançamento anterior.

O escopo dos dados locais depende do seu tipo. Existem três tipos de localização de dados.

1. Dados do programa local. Eles estão localizados dentro do programa e existem desde o momento em que o programa é iniciado até sua conclusão. Se o programa chamar a sub-rotina, novos dados de sub-rotina serão criados e os dados do programa local dentro da sub-rotina não serão visíveis. Quando você retorna ao programa, os dados do programa local ficam disponíveis novamente. Os dados do programa local não têm um nome.

Por exemplo:

[7,9] - acesso ao nó da árvore localizado dentro do programa.

$ 5 - acesso a um elemento de matriz localizado dentro do programa.

Há uma exceção. Uma matriz de parâmetros passados ​​para o programa A $ também está localizada dentro do programa.

2. Dados de aplicativos locais. Eles são visíveis em todas as tarefas do aplicativo. Você pode contatá-los de qualquer tarefa. Eles existem desde o momento em que são criados no aplicativo por qualquer tarefa até que o aplicativo seja concluído ou até que sejam destruídos pela equipe do KILL . Os nomes desses dados são prefixados com " % ". Essas variáveis ​​estão disponíveis simultaneamente em tarefas diferentes, portanto são tão sincronizadas quanto globais.

Por exemplo:

% dapp [87.9] - acesso ao nó da árvore localizado dentro do aplicativo.

% dapp $ 45 - acesso a um elemento da matriz localizado dentro do aplicativo.

3. Dados locais do trabalho. Eles estão localizados dentro da tarefa e existem desde o momento em que são criados em qualquer programa de tarefas até que a tarefa seja concluída ou destruída pela equipe KILL . Esses dados devem ter um nome e não conter os prefixos " ^ " e " % ". A exceção é a matriz dos parâmetros do programa A $ , localizada dentro do programa.Por

exemplo:

djob [87,9] - acesso ao nó da árvore localizado dentro da tarefa.
djob $ 45 - acesso ao elemento da matriz localizado dentro do trabalho.
O acesso a variáveis ​​pode ter apenas os tipos listados aqui.

Sintaxe abreviada


Este termo não tem relação com um termo semelhante na linguagem MUMPS. A sintaxe abreviada no MSH é usada para se referir à árvore inteira ou à matriz inteira. É usado apenas em equipes separadas e, onde é permitido, é sempre negociado. Um apelo à árvore inteira consiste em um nome e colchetes obrigatórios. Nenhum nome é especificado para a árvore do programa local.
Por exemplo:
us [] - acesso a toda a árvore dos eua.
[] - acesso à árvore do programa local.

O acesso a toda a matriz consiste no nome e no caractere necessário " $ ". Nenhum nome é fornecido para a matriz local do programa.

Por exemplo:
us $ - acesso a toda a matriz us.
$ - acesso à matriz de programas local.

Constantes


Os tipos de dados básicos podem ser numéricos ou de sequência. A forma numérica é um número inteiro ou um número real na presença de um ponto decimal. A base dos números é 10. Eles podem ser positivos ou negativos.
Por exemplo:
25, -4, 789,56, -9,3

As constantes de sequência são qualquer sequência de caracteres. Se uma constante consistir apenas em letras e números, não será possível colocá-la entre aspas, pois não pode ser confundida com uma variável. Se a constante contiver outros caracteres, ela deverá ser colocada entre aspas simples ou duplas.
Por exemplo:
"rt @ tty # 123"
'14 "5 * 7" 89 \? '
125Dsv

Características de algumas equipes


Equipe CONSTANT


Você pode atribuir um nome a uma constante usando o comando CONSTANT . Estes são nomes de horário de transmissão. A transmissão os substitui por seus valores. No tempo de execução, esses nomes não existem mais. Portanto, você não pode atribuir uma expressão a um nome no comando CONSTANT . O valor deve ser exatamente uma constante. Os nomes das constantes devem ser escolhidos para que não coincidam com os valores das constantes especificadas no programa sem aspas.

Por exemplo:
Constant ioInOut = "/ini/par.ini>,maxIs=25;
As constantes ioInOut e maxIs recebem valores atribuídos. Além disso, no programa, esses nomes podem ser usados ​​em vez desses valores.
Equipe constanteTem 2 formas. Nesse caso, o lado direito da igualdade está ausente. O significado dessa equipe é diferente. O nome é o nome do módulo que contém as constantes. As constantes deste módulo são exportadas para o módulo atual. As constantes de importação do módulo não contêm descrições adicionais sobre importação. O módulo de importação pode conter apenas constantes e programas.
Por exemplo:
CONSTANT sysCnsNet, usrCnsByx;
sysCnsNet e usrCnsByx são nomes de módulos que contêm constantes.
Ambos os formulários podem ocorrer como argumentos para um único comando CONSTANT .

Equipe XECUTE


O comando XECUTE é uma equipe bastante exótica, mas está presente na linguagem MUMPS. Em outras linguagens de programação, ela me conheceu apenas em JavaScript. Lá é chamado eval. Quando esse comando é executado, a expressão do argumento deste comando é calculada e, em seguida, essa expressão é convertida em uma seqüência de caracteres, interpretada como um comando MSH e executada.

Por exemplo:
XECUTE "SET $ 1 = 89;";
Como resultado, a variável $ 1 receberá o valor 89 .
Este é um exemplo primitivo; dessa forma, dificilmente faz sentido usar esse comando. Os argumentos para o comando XECUTE são expressões, o que permite gerar vários comandos MSH no momento em que o programa é executado .
O comando é executado no contexto do programa em que o comando está localizado. Todos os recursos do programa estão disponíveis para ela, incluindo dados do programa local.

Comandos COPY e MOVE


O comando COPY é semelhante ao comando MERGE MUMPS. Esses comandos copiam o nó de origem junto com todos os descendentes para o nó receptor. O argumento desses comandos consiste em dois campos separados pelo símbolo " = ". À direita deste sinal está o nó de origem, o receptor à esquerda. Um link abreviado é permitido como nós; nesse caso, a árvore inteira é usada. Esses comandos copiam não apenas os descendentes, mas também os próprios nós. O comando COPY executa a mesclagem de dados. Os dados de origem são copiados para o receptor sem limpá-los. A fonte não muda. Equipe MOVEefetua a movimentação de dados. Anteriormente, o receptor é limpo, todos os descendentes do nó de origem são copiados e o nó de origem é excluído com todos os seus descendentes.

Por exemplo:
// o nó us [5,6] é copiado para o nó [1]
COPY [1] = us [5,6];
// toda a árvore us é movida para o nó [8]
MOVE [8] = us [];
Esses comandos também podem ser usados ​​para copiar matrizes. Nesse caso, apenas links encurtados podem ser usados ​​como fonte e receptor.
Os argumentos do programa são copiados para a matriz arg.
COPY arg $ = A $;
Você pode usar o comando MOVE para mover.
MOVER a1 $ = b1 $;
Você pode copiar e mover qualquer matriz.

Sincronização de Recursos


Ao executar várias tarefas, torna-se necessário acessar recursos compartilhados. A sincronização é realizada por comandos de bloqueio. Os comandos de sincronização, por si só, não bloqueiam nada; este é um conjunto de acordos que permitem diferenciar o acesso a recursos compartilhados. Se os comandos de bloqueio não forem compartilhados entre os trabalhos, nenhuma sincronização de acesso ocorrerá. A sincronização é baseada nos nomes dos bloqueios Esses nomes estão localizados no aplicativo e são os mesmos em todas as tarefas. Nas fechaduras, o conceito de muitos leitores e apenas um escritor de cada vez é aceito. Assim, existem bloqueios de leitura e gravação.

Equipe LockRbloqueia a leitura de nomes. Os nomes listados em seus argumentos serão bloqueados pela leitura. Qualquer número de tarefas pode bloquear os mesmos nomes, mas uma tarefa que tentou bloquear qualquer um desses nomes aguardará o desbloqueio de todos esses nomes. Após capturar o nome de gravação, nenhum comando de bloqueio de leitura pode ser executado até que o bloqueio de gravação libere o nome. Se não for possível bloquear, o comando aguardará o lançamento do nome.

Por exemplo:
LockR nome1, nome2, nome3;
Esses nomes serão bloqueados pela leitura. Outra tarefa também pode bloquear esses nomes sem esperar por eles para ser desbloqueado. LockW

comandobloqueia nomes por registro. Os nomes listados em seus argumentos serão bloqueados por registro. Se os nomes listados nos argumentos já estiverem bloqueados por qualquer comando de bloqueio, esse comando aguardará o lançamento desses nomes.
Por exemplo:
LockW nome1, nome2, nome3;
Esses nomes serão bloqueados por registro.
O comando LockUn desbloqueia esse nome. Se o nome for bloqueado pela leitura várias vezes, você precisará desbloquear o mesmo número de vezes.
Por exemplo:
LockUn nome1, nome2, nome3;
As funções padrão têm análogos desses comandos com um tempo limite.

Forma abreviada do comando SET


O comando SET possui um formulário abreviado. Nesse caso, o lado esquerdo da igualdade está ausente; seu papel é desempenhado pela última variável mencionada na expressão.
Por exemplo:
SET $ 1 = 2, $ 1 + 3;
A variável $ 1 se tornará igual a 5 .
Se houver várias variáveis ​​na expressão, o resultado será atribuído à última variável.

Por exemplo:
SET $ 1 = 1, $ 2 = 2, $ 3 = 3, $ 1 + $ 2 + $ 3;
A variável $ 3 se tornará 1 + 2 + 3 = 6. Embora este formulário seja mais apropriado para uso apenas em casos muito simples, semelhante ao primeiro exemplo. O segundo exemplo é fornecido apenas como uma ilustração dos recursos deste formulário do comando SET .

Bloquear equipes


Os comandos de bloco formam um bloco de comandos e servem simultaneamente como cabeçalho do bloco. Cada comando do bloco deve ter seu próprio comando END , mesmo se houver apenas um comando no bloco.

Equipe IF


O comando IF forma um bloco que é executado se as condições para a execução deste comando forem verdadeiras. Este comando não tem argumentos. Este bloco pode conter comandos ELSE . O comando ELSE não possui argumentos. Fora do bloco IF , esses comandos não têm significado e não podem ser aplicados. Se houver comandos ELSE no bloco IF quando a condição do comando IF for atendida, somente os comandos atrás do comando IF serão executados até o próximo comando ELSE . Equipe ELSEpode conter uma condição de execução; em caso de verdade, nessa condição, apenas os comandos localizados após esse comando serão executados até o próximo comando ELSE ou END . Um bloco IF pode conter apenas um comando ELSE sem uma condição para sua execução e deve ser o último entre os comandos ELSE .

Por exemplo:
IF? [6] <0;
SET y [1] = 1;
ELSE? [6] <5;
SET y [1] = 2;
ELSE? [6] <10;
SET y [1] = 3;
OUTRO SET y [1] = 4;
FIM
A condição para a execução deste comando pode estar ausente, então este bloco será executado em qualquer caso. Embora seja difícil imaginar por que isso poderia ser útil.

Equipe CASE


A semântica deste comando é um pouco diferente da semântica das outras equipes do MSH. A condição para executar um comando nele não é essa. Na condição de execução do comando CASE , a expressão deve calcular o rótulo para o qual o controle será transferido. Este comando não tem argumentos.

Cada etiqueta neste comando forma um bloco dessa etiqueta para a próxima. Quando o controle é transferido para o rótulo, apenas os comandos são executados até o próximo rótulo e o bloco CASE atual é encerrado . Este comando está mais próximo da notação Pascal do que do comando C switch. Se, como resultado do cálculo do nome do rótulo, for encontrado um nome ausente no bloco CASE atual, os comandos localizados após o comando CASE antes do primeiro rótulo serão executados .

CASO? L_ $ J; // etiqueta é avaliada
SET x [1] = 1; // se nenhum rótulo for encontrado, os comandos deste bloco serão executados
SET a [2] = x [1] +1;
L1: SET x [1] = 2; // etiqueta o bloco de comando L1
SET a [2] = x [1] +2;
L2: SET x [1] = 3; // etiqueta o bloco de comando L2
SET a [2] = x [1] +3;
FIM
Os rótulos nesse comando formam implicitamente um bloco interno de comandos. Após a execução desse bloco de comandos, o controle é transferido para fora do bloco do comando CASE .

Comando WHILE


O comando WHILE é usado para organizar o loop. A condição para a execução deste comando define a condição para a continuação do loop. Enquanto a condição para executar o comando não for 0, o bloco formado por este comando será executado. O bloco termina com o comando END . O comando END de um bloco desse tipo possui um recurso. Pode ter uma condição de terminação de bloco. Se a condição não for 0, o bloco será concluído. Além disso, essas condições podem estar presentes no comando WHILE e no comando END .
Por exemplo:
WHILE? X [7]> 0; // condição para continuar o ciclo
SET y [2] = x [7] +2;
BREAK? S [2] <0;
SET x [7] = x [7] +1;
FIM X [7]> 20; // condição para finalizar o loop
Dentro do bloco, o loop pode ser interrompido pelo comando BREAK.

Iteradores de loop de bloco


Os iteradores de loop de bloco são otimizados para acessar os nós da árvore. Eles usam um link interno para otimizar o acesso aos nós de desvio. Isso impõe uma restrição ao uso de iteradores de bloco. Dentro do bloco, você não pode alterar a estrutura da árvore. Você não pode escrever nesta árvore. Nos comandos do iterador de loop de bloco, 2 argumentos.
O primeiro argumento é obrigatório, o link para o nó cujos descendentes serão ignorados. Índice de referência. Links encurtados podem ser usados. O segundo argumento é o link para o nó em que o índice descendente será armazenado. Este argumento é opcional. Dentro do bloco, o segundo argumento não deve mudar. Expressões como $$ 2 ou [[3]] não são permitidas se $ 2 ou [3]mudar dentro do bloco iterador. Alterações nessas variáveis ​​não serão levadas em consideração. O acesso ao índice filho pode ser obtido através da variável de sistema % queryKey e dos dados do nó da propriedade% queryData . Se os descendentes não devem ser ignorados desde o início, o segundo argumento é necessário e o índice do nó deve ser colocado nele, após o qual o desvio dos descendentes começa. Se houver um segundo argumento, mas você precisar dar a volta desde o início, antes do loop, você precisará excluir esta variável com o comando KILL .
Os comandos podem ter uma condição de execução de bloco. Esta condição é verificada apenas uma vez ao entrar no bloco.

Você pode percorrer não apenas os nós da árvore, mas as matrizes. Nesse caso, o número de série do campo da matriz entra no segundo argumento. Somente o link reduzido pode ser usado como o primeiro argumento.

NEXT Team


O comando NEXT itera sobre os descendentes imediatos de um nó da árvore. Os descendentes custam na direção direta do índice mínimo para o máximo.
Por exemplo:
NEXT us [4,5]; // argumento 2 não está especificado, o índice é obtido
// da variável do sistema% queryKey
SET $ 1 =% queryKey, $ 2 =% queryData;
FIM? $ 1> 1000; // condição para finalizar o loop
O índice do nó é imediatamente colocado nos dados
KILL $ 1;
Nos próximos [4,5], US $ 1;
SET $ 2 =% queryData;
FIM

Um link abreviado é usado como um nó de referência. Nesse caso, o primeiro nível da árvore é ignorado.
MATE $ 1;
Nos próximos [], $ 1;
SET $ 2 =% queryData;
FIM
Rastrear após o índice 3.
SET $ 1 = 3;
Nos próximos [4,5], US $ 1;
SET $ 2 =% queryData;
FIM
Ao atravessar uma matriz, todos os campos estão em ordem, mesmo aqueles em que os dados não estão definidos.
Por exemplo:
KILL $ 1;
Nos próximos $, $ 1;
SET $ 2 =% queryData;
FIM
Dentro do bloco, o loop pode ser interrompido pelo comando BREAK .

Equipe BACK


O comando BACK difere do comando NEXT apenas na direção transversal do último vértice ao primeiro.
Por exemplo:
KILL $ 1;
BACK nós [4,5], US $ 1;
SET $ 2 =% queryData;
FIM

Equipe QUERY


O comando QUERY percorre todos os descendentes de um nó da esquerda para a direita e de cima para baixo até a profundidade total na direção direta. O segundo argumento contém o índice opcional inteiro. Se esse índice tiver mais de um campo, a lista será colocada no segundo argumento.

Caso contrário, este comando é semelhante ao comando NEXT .

O comando QUERY percorre a matriz apenas na direção para a frente da esquerda para a direita.

Por exemplo:
KILL $ 1;
QUERY us [4,5], US $ 1;
SET $ 2 =% queryData;
FIM
Mas esse comando ignora apenas os vértices que importam.

Comandos não bloqueados Comandos transversais da árvore de dados


Nesses comandos, os dois argumentos de comando são necessários. O segundo argumento armazena o índice, após o qual o próximo comando encontrará o próximo vértice. Nesses comandos, os links internos não são salvos e, portanto, não há restrição no ajuste da árvore que é ignorada. Pelo mesmo motivo, o tempo de acesso aos picos pode ser muito maior. As matrizes também podem ser ignoradas com esses comandos.

Comando NEXT1 O

comando NEXT1 fornece o próximo nó no mesmo nível no nó de referência.
Os dados do nó estão disponíveis na variável de sistema % queryData .
Por exemplo:
SET $ 1 = 2;
NEXT1 nos [1,4], US $ 1;
// dará o nó localizado no nível 3 após o nó us [1,4,2]

Equipe BACK1


O comando BACK1 fornece o vértice anterior no mesmo nível abaixo do nó de referência.
Caso contrário, é semelhante ao comando NEXT1 .

Equipe QUERY1


O comando QUERY1 fornece o próximo vértice de um galho de árvore enquanto percorre todo o nó de cima para baixo e da esquerda para a direita. O segundo argumento contém o índice opcional inteiro. Se esse índice tiver mais de um campo, a lista será colocada no segundo argumento.

Caso contrário, é semelhante ao comando NEXT1 .

Gerenciamento de Programas


Um rótulo de módulo pode ser um ponto de chamada para um programa, função, um ponto de chamada para uma nova tarefa, uma propriedade de objeto, um método de objeto e um rótulo, dependendo do acesso a esse rótulo.
Além disso, a etiqueta pode ser acessada de maneira diferente em diferentes partes do módulo.
Por exemplo:
LB: Defina Val [25] = 7 + A $ 1; Return Val [25];
Faça LB (78); // Acesso ao programa. O valor de retorno é ignorado.
Defina Val [7] = 8 * LB (6); // Chamando como uma função, o valor de retorno é usado.
TRABALHO LB (17, "yt"); // chama um novo trabalho
Set Val [9] = Mod.LB (3); // Acesso ao método de classe Mod - o nome do módulo nesse contexto é tratado como o nome da classe do objeto.
Conjunto Val [15] = Obj [1,2, A] .LB; // Acesso à propriedade LB do objeto Obj [1,2, A],
Go LB; // Vá para a etiqueta LB
Nos exemplos acima, além de 4 e 5, a chamada é realizada dentro do módulo atual. A chamada de programas e funções pode ser realizada em qualquer módulo disponível. A propósito, isso também se aplica à equipe Go.
Por exemplo, no módulo Mod, há um rótulo Lb. Em seguida, o apelo será:
Do Mod.Lb (78);
Defina Val [7] = 8 * Mod.Lb (6);
Set Val [9] = Mod.Lb (3);
JOB Mod.Lb (78);
Vá Mod.Lb;

Em geral, um rótulo é usado como um programa ou função. Se o módulo for usado como uma classe de objeto, o rótulo será uma propriedade ou método desse objeto. As propriedades de um objeto e seus métodos são outra forma de acesso a programas e funções; portanto, tudo o que é dito sobre programas e funções se aplica igualmente a propriedades e métodos. Não há nuances significativas ao acessar objetos apenas em termos de herança. Este problema será descrito em mais detalhes abaixo.

Geralmente, o nome do módulo e o nome do rótulo na chamada para ele são expressões e podem ser calculados no momento da chamada.
Por exemplo:
Defina $ 1 = "Md", $ 2 = "Lb";
Faça $ 1. $ 2;

O programa termina com o comando Return; Um módulo pode ser concluído com o comando End;

Passando parâmetros


No MSH, os parâmetros são passados ​​apenas por valor. Em geral, não existem referências e ponteiros em termos da linguagem C ++ e os enormes problemas associados ao seu uso aqui, em princípio, não existem. Um programa sempre aceita um número arbitrário de parâmetros em uma matriz A $ . Seu número pode ser encontrado consultando o elemento 0 da matriz A $ 0 . O programador é responsável pelo significado desses parâmetros.

Por exemplo:
Temos um programa Lb. Pode ser abordado:
DO Lb (1,2,7);
DO Lb (25,7);
DO Lb ();
TRABALHO Lb (8);
SET [7.8] = Lb (187, "Privet");

Os parâmetros não passados ​​no programa não estão definidos.
A passagem de parâmetros por valor não interfere na passagem de nomes de variáveis ​​para o programa e na sua utilização.
Por exemplo:
Defina-nos [5] = 48;
Do Lb ("nos");
...
retorno;
Lb: Defina o par [8,1,9] = A $ 1 [5];
// A $ 1 - o primeiro parâmetro passado contém o nome da
variável //: us .
// [5] - acesso ao topo desta árvore
// como resultado, o valor do nó us [5] = 48
Return;
Como resultado, o nome das variáveis ​​pode ser manipulado arbitrariamente.
Ao ir para o rótulo com o comando GO, a matriz de parâmetros não muda. O MSH possui um formulário de comando GO com a transferência de novos parâmetros. Nesse caso, a matriz de parâmetros atual é substituída pela nova especificada na chamada de comando GO .
Por exemplo:
GO Lb (1,2,7);

Manipulação de eventos


O processamento de eventos é um poderoso mecanismo de programação que geralmente não é incluído em linguagens de alto nível. Mas sua presença nas bibliotecas e no sistema operacional fala de sua importância. Bibliotecas de componentes visuais são construídas sobre esse mecanismo. Ter o processamento de eventos no idioma expande o idioma. Abre um novo estilo de programação orientado a eventos. A propósito, uma linguagem de programação respeitada como o Assembler possui esses recursos. Portanto, esse mecanismo foi adicionado à linguagem MSH.

A base desse mecanismo é o evento. Está localizado dentro do aplicativo. Portanto, esses nomes devem ser exclusivos em todo o aplicativo. Se o nome do evento corresponder ao nome declarado no comando CONSTANT, o nome do evento será substituído pelo valor da constante declarada. Seja cuidadoso. Ao nomear eventos, é aconselhável aderir a algum tipo de estratégia de nomeação. Por exemplo, atribua nomes de eventos começando com evu . O comprimento do nome, levando em consideração a codificação UTF8, não deve exceder 18 bytes. Essa limitação está associada apenas à implementação do idioma atual.

Um evento criado em uma tarefa é visível e pode ser processado em qualquer tarefa. Os manipuladores de eventos podem ter qualquer número e podem estar em tarefas diferentes.
Depois que o evento ocorre, é verificado se há manipuladores para esse evento; se não houver manipuladores, o evento será excluído. Se houver manipuladores, eles serão executados sequencialmente. Eventos podem ser sistema e usuário. Eventos do sistema são gerados pelo sistema. Um evento customizado é gerado pelo comando EVENTTRAP . O evento passa parâmetros para os manipuladores, como ao invocar um programa. Após o processamento, os manipuladores de eventos não são excluídos. Para excluir um evento, use o comando EVENTDELETE . Um evento pode ser manuseado pelos EVENTCALL e comandos EVENTWAIT .

Equipe EVENTTRAP


O comando cria um evento personalizado. O formato do comando é semelhante a uma chamada de programa, apenas o nome do evento é usado em vez do nome do programa. Os nomes de eventos são locais no aplicativo e, portanto, são visíveis em todas as tarefas.

A equipe criará um evento com o nome especificado. Os manipuladores de eventos receberão os parâmetros listados nos argumentos do comando.

Por exemplo:
EVENTTRAP? X [1]> 8 evuBoxData (us [7], US $ 4), evuKorXY (us [X], us [Y], sh);
2 eventos evuBoxData e evuKorXY são gerados como variáveis us [7], $ 4, us [X], us [Y] e a constante de string sh .
Se atualmente não houver manipuladores para este evento, o evento não será gerado.

Equipe EVENTDELETE


O comando remove os manipuladores de eventos listados nos argumentos do programa.
Por exemplo:
EVENTDELETE? X [1]> 7 evuKorXY, evuBoxData;
Os eventos serão excluídos na ordem da equipe.

Equipe EVENTCALL


O comando atribui um manipulador de eventos. Um manipulador é um programa. Este programa será chamado de forma assíncrona a partir da tarefa em execução e os parâmetros serão passados ​​para ele. Após a execução do programa manipulador, o controle retornará à tarefa principal no site de interrupção.

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

Equipe EVENTWAIT


A equipe está aguardando a ocorrência de eventos. Este comando bloqueará a tarefa atual até que os eventos listados em seus argumentos ocorram. Todos os eventos listados em seus argumentos devem ocorrer para continuar a execução do encadeamento atual. Os parâmetros atuais do programa são substituídos pelos transferidos no comando de criação de eventos.
Por exemplo:
EVENTWAIT evuBoxData, evuKorXY;
Esses comandos permitem organizar a execução assíncrona de programas.

Vetores


Os vetores estão intimamente relacionados à implementação atual. Eles armazenam números inteiros assinados e não assinados. A dimensão desses vetores depende da profundidade de bits do componente vetorial e é fixa.

Vetores de 64 bits


A dimensão desse vetor é 2. Os componentes podem ser números inteiros de 64 bits ou números não assinados. Esses números são armazenados em quaisquer variáveis.
O apelo aos componentes inteiros icônicos do vetor.
SET us [5].% V64 (0) = ss $ 1.% v64 (1);
% v64 - acesso ao componente inteiro assinado do vetor.
Uma apelação para componentes inteiros não assinados de um vetor.
SET us [5].% Vu64 (0) = ss $ 1.% vu64 (1);
% vu64 - acesso a todo o componente não assinado do vetor.

Vetores de 32 bits


A dimensão desse vetor é 5. Os componentes podem ser números inteiros de 32 bits ou números não assinados. Esses números são armazenados em quaisquer variáveis.
O apelo aos componentes inteiros icônicos do vetor.
SET us [5].% V32 (0) = ss $ 1.% v32 (4);
% v32 - acesso ao componente inteiro assinado do vetor.
Uma apelação para componentes inteiros não assinados de um vetor.
SET us [5].% Vu32 (0) = ss $ 1.% vu32 (4);
% vu32 - acesso a todo o componente não assinado do vetor.

Vetores de 16 bits


A dimensão desse vetor é 11. Os componentes podem ser números inteiros ou não assinados de 16 bits. Esses números são armazenados em quaisquer variáveis.
O apelo aos componentes inteiros icônicos do vetor.
SET us [5].% V16 (0) = ss $ 1.% v16 (10);
% v16 - acesso ao componente inteiro assinado do vetor.
Uma apelação para componentes inteiros não assinados de um vetor.
SET us [5].% Vu16 (0) = ss $ 1.% vu16 (4);
% vu16 - acesso ao componente inteiro não assinado do vetor.

Vetores de 8 bits


A dimensão desse vetor é 22. Os componentes podem ser números inteiros ou sem sinal de 8 bits. Esses números são armazenados em quaisquer variáveis.
O apelo aos componentes inteiros icônicos do vetor.
SET us [5].% V8 (0) = ss $ 1.% v8 (21);
% v8 - acesso ao componente inteiro assinado do vetor.
Uma apelação para componentes inteiros não assinados de um vetor.
SET us [5].% Vu8 (0) = ss $ 1.% vu8 (21);
% vu8 - acesso a todo o componente não assinado do vetor.

Operações


As operações no MSH desempenham um papel especial. São eles que controlam a conversão dos tipos de dados. Dependendo da operação, os operandos são convertidos para o tipo de dados desejado. O tipo de resultado dos dados corresponde exclusivamente ao tipo de operação. As operações de seqüência de caracteres e numéricas não se sobrepõem, como a operação + em idiomas do tipo C. O tipo de operação no MSH não depende do tipo de operandos, tudo é exatamente o oposto. Não há prioridades de operações no MSH, é um legado histórico do MUMPS.

Por exemplo:
SET $ 1 = 2 + 3 * 4;
$ 1 será 20, não 14.
Para que o resultado seja 14, parênteses são usados.
SET $ 1 = 2 + (3 * 4);
Como operação de união de cadeias, o símbolo " _ " é usado.
A falta de prioridade das operações é incomum, mas bastante conveniente. A referência à naturalidade das prioridades, quando as operações são mais do que somadas, se torna muito duvidosa. Depois de aprender que não há prioridades, não há necessidade de recordar dolorosamente suas prioridades e se algo entra na documentação. Em geral, isso é uma questão de hábito.

Os objetos


A presença de objetos nas linguagens de programação modernas é uma boa forma. No caso geral, os objetos consistem em 2 partes. Partes da descrição declarativa e partes da implementação. Nos sistemas MUMPS, as variáveis ​​não possuem a parte declarativa de uma declaração de tipo. Classes são basicamente tipos de dados do usuário. Para não violar os princípios do MUMPS, o MSH não possui a parte declarativa da descrição da classe. E, como se viu, você pode ficar sem ele perfeitamente. Somente parte da implementação resta da classe. A implementação da classe pode ser perfeitamente representada pelo módulo padrão. Somente a classe teve que introduzir convenções adicionais associadas à descrição das propriedades do objeto. Os objetos podem estar apenas na árvore. A colocação de um objeto em uma matriz falhará porque não há onde armazenar as propriedades do objeto. Embora se o objeto não tiver propriedades, você poderá tentar.Embora que tipo de objeto seja.
Para descrever uma propriedade pública, você precisa de uma função que retorne o valor dessa propriedade. Ele deve ter um nome de propriedade e estar no módulo com o nome da classe. Um programa para gravar em uma propriedade. Este programa tem um nome de propriedade prefixado com ".". E um programa de remoção de propriedades. Este programa possui um nome de propriedade prefixado com "..".

O nome da função no módulo pode corresponder à propriedade pública para leitura. Nesse caso, o valor de retorno de uma função é passado para o programa de chamada como o valor da propriedade pública.

O construtor da classe é o método de dados% objNew. Se, ao criar um objeto, for necessário determinar quaisquer propriedades ou obter recursos, qualquer programa de módulo de classe (método de classe) poderá ser usado. Mas é aconselhável aderir a qualquer estratégia para nomear construtores de classes. Por exemplo, o nome do construtor deve corresponder ao nome da classe.

O acesso às propriedades protegidas da classe é realizado através da propriedade do sistema % this .

O destruidor é a remoção do objeto usando o comando KILLD. Se você precisar liberar recursos ou executar manipulações adicionais, isso poderá ser feito por qualquer programa dessa classe (método de classe). Como no caso do construtor, é aconselhável aderir a alguma estratégia de nomeação ao nomear o destruidor.

Por exemplo:
// Pessoa da classe
// Idade da propriedade
// leia a propriedade pública Age
Age: RETURN [% this, Age];
// registro da propriedade pública Age
.Age: SET [% this, Age] = A $ 1;
Retorna
FIM

Um apelo dos programas a um objeto e suas propriedades públicas será parecido com o seguinte.
// cria um objeto Person
SET us [1,2].% objNew = Person;
// escreve o valor 50 na propriedade Age
SET us [1,2] .Age = 50;
// lê a propriedade Age
SET u $ 1 = us [1,2]. Age + 5;
// excluir propriedade Age
KILL us [1,2] .Age;

Herança de Objeto


As classes MSH oferecem suporte a herança múltipla. O comando PARENT define todos os ancestrais dessa classe. Além disso, a ordem dos nomes dos ancestrais dessa classe nos argumentos de comando determina a prioridade da herança. Quanto mais tarde a classe é mencionada, menor é sua prioridade.

Por exemplo:
USUÁRIO PAI, CAIXA;
A classe herda dos ancestrais de USER e BOX . A prioridade do ancestral USER é mais alta. Qual é a prioridade. Ao acessar um objeto, a propriedade pública ou o método da classe será pesquisado na própria classe, se não for encontrado lá, será pesquisado no ancestral com a maior prioridade e, em seguida, nos ancestrais dessa classe e assim por diante.

Compartilhamento de arquivos

No MSH, o compartilhamento de arquivos é organizado no nível mais primitivo. Os arquivos no MSH desempenham um papel de suporte. A troca é organizada apenas com arquivos de texto. A estrutura do arquivo são campos de texto separados por um separador especificado. O delimitador está na variável de sistema % dlmIO . Por padrão, essa variável é " , ". Está disponível para leitura e escrita. Ao gravar em um arquivo, as variáveis ​​são convertidas em um tipo de sequência e gravadas no arquivo por meio do delimitador. Ao ler de um arquivo, as variáveis ​​são selecionadas através de um separador e trazidas para um formato normalizado. Se o campo for um registro de um número, um número será colocado na variável. A troca com o arquivo ocorre através da matriz B $ . Ao escrever, a matriz é B $gravado no arquivo através do delimitador. Ao ler de um arquivo, os campos são selecionados na matriz B $ .

Os comandos de troca de arquivos usam o caminho do arquivo como argumentos.
Por exemplo:
De uma matriz de dados B $ é gravada em um arquivo. O arquivo é aberto escrevendo. Os dados no arquivo são substituídos.
Escreva "txt / tt1.txt";
Os dados são lidos na matriz B $ . A matriz é pré-limpa.
Leia "txt / tt1.txt";
txt / tt1.txt - caminho para o arquivo.

Conclusão


Este documento não substitui a descrição da linguagem MSH, mas apenas a complementa. Aqui, nem todos os recursos da linguagem MSH são considerados, mas apenas aqueles aos quais gostaria de chamar sua atenção.

Autor: Sharymov Mikhail Alekseevich. E-mail: misha_shar53@mail.ru

Ao usar este material, é necessário um link para a fonte e o autor.

All Articles