Representação dos termos
Tipos admitidos: átomos/booleanos, inteiros arbitrários, floats binary64 finitos, tuplos, listas próprias/impróprias e strings, maps, bitstrings e records comuns em tuplo; native records (native records); funs (valores de função); pids, ports e referências locais (abaixo, ports). As codificações em palavras estão em abi.md.
Propriedade
- Os valores compostos vivem no heap do seu processo (runtime). Os ponteiros de processo designam sempre o início de objetos. A admissão verifica a propriedade: a palavra aponta, alinhada à palavra, abaixo do topo do bloco de heap do processo ou de um dos seus fragmentos, e o cabeçalho (ou célula cons) aí presente corresponde à sua etiqueta (admissão). Palavras alheias e obsoletas são rejeitadas sem qualquer leitura. O tipo e a extensão são descodificados do cabeçalho.
- Um
Termdo anfitrião para um valor no heap é uma palavra etiquetada em bruto, válida até à próxima recolha do seu heap (raízes); não fixa o armazenamento do heap. Após a destruição do contexto, o acesso devolveexpired_context; após uma recolha posterior,stale_term. Destruir termos é sempre seguro. - A construção valida os filhos, reserva, inicializa e depois publica num só passo; uma falha reverte o armazenamento e os contadores.
Term::from_word(word)admite apenas imediatos independentes do dono;Term::from_word(word, context)admite também átomos e termos de heap desse contexto. A passagem dentro do mesmo heap mantém a identidade.copy_to/ProcessHeap::addcopiam um grafo de outro processo do mesmo runtime com a sua partilha; as fábricas de termos recusam entradas alheias comwrong_owner(cópia entre heaps).- As células vivem até que uma recolha explícita as encontre inalcançáveis, ou até à destruição do heap.
Átomos
AtomStoragepor runtime, internado de forma preguiçosa pela grafia UTF-8 exata, sem normalização nem remoção.RuntimeOptions::max_atoms1..2^26, por omissão 2^20; os nomes de módulos/exportações contam. Grafias existentes continuam a ser aceites quando a capacidade está esgotada.- Até 255 escalares Unicode; vazio, NUL e caracteres suplementares permitidos; UTF-8 malformado/sobrelongo/com surrogates é rejeitado.
- Os conteúdos vêm de um contador global do processo, para que a palavra de átomo de um runtime alheio seja detetável. Os processos de 32 bits têm 2^26 identidades no total durante a sua vida.
- Os
Termde átomo do anfitrião fixam a grafia e sobrevivem à destruição do runtime. Mover um átomo entre runtimes significa internaratom_utf8()no destino. - Os booleanos são os átomos
true/false. - Internar e procurar são seguros a partir de workers concorrentes do escalonador (threads); uma grafia mantém uma só palavra.
Inteiros
- Os valores que cabem no conteúdo de 28/60 bits do alvo são imediatos; os maiores são células imutáveis de sinal/magnitude. O zero e os valores pequenos são sempre normalizados para imediatos. Os literais não são estreitados à largura do anfitrião.
- Os
+,-,*gerados tentam um caminho rápido inline sobre dois imediatos (cálculo em largura dupla com limites explícitos); caso contrário, chamam o serviço do runtime. divtrunca em direção a zero;remtoma o sinal do dividendo; as operações bit a bit usam complemento para dois infinito; contagens de deslocamento negativas invertem a direção; deslocamentos enormes para a direita saturam em 0 ou -1.- Erros: operandos errados e divisor zero →
badarith;abs/1→badarg. - Limites: como no ERTS, uma magnitude de no máximo
BIG_ARITY_MAXpalavras: 4 194 240 bits em alvos de 64 bits (65 535 palavras), 4 194 272 em 32 bits (131 071 palavras); o texto decimal acompanha (1 262 593 e 1 262 602 dígitos). Um resultado aritmético maior lançaerror:system_limitnum corpo (ValueOutcome::system_limit, ABI) e faz falhar uma guard; um segmento inteiro que extraia um valor maior não corresponde. O compilador rejeita um literal acima de 4 194 240 bits (illegal integer, como o scanner do OTP) e um padrão constante acima disso (illegal pattern).
Floats
- Os bits IEEE binary64 passam para o runtime como oito bytes em ordem de rede; NaN e infinito são rejeitados. Sem fast-math.
+ - *mantêm-se exatos sobre dois inteiros; qualquer operando float usa binary64./converte sempre ambos. Resultados não finitos e divisores zero →badarith.float/1arredonda para o par mais próximo;round/1desempata afastando-se de zero;trunc,floor,ceildevolvem inteiros arbitrários. Operandos inválidos →badarg.- A igualdade exata distingue
1de1.0e0.0de-0.0; a comparação numérica compara a parte inteira exata e a fração do float, sem nunca arredondar o inteiro.min/maxdevolvem o primeiro operando em caso de empate.
Tuplos, listas, strings
- Tuplo: cabeçalho de aridade + campos. Cons: palavras de cabeça + cauda.
{}e[]são imediatos. As strings são listas de pontos de código. As listas não têm limite de comprimento além da memória (incluindo um orçamento de heap opcional). Os tuplos contêm até 16 777 215 elementos (MAX_TUPLE_ARITY, oMAX_ARITYVALdo OTP); os construtores comunicam um maior comoresource_limit, os builtins lançarãobadarg. - Serviços:
hd,tl,length,tuple_size,size,elementcom base 1.
Maps
- Tabelas imutáveis ordenadas pela ordem exata das chaves. Chaves duplicadas na
construção mantêm o último valor. A construção ordena as chaves (O(n log n)
comparações; chaves já ascendentes são apenas verificadas); as atualizações
inserem por pesquisa binária. Sem limite de tamanho ou de trabalho além da
memória, como no OTP; em alvos de 32 bits, a contagem de palavras do
cabeçalho limita um map a 2^24 - 1 entradas (
resource_limit). Chaves inteiras e float diferem (também0.0vs-0.0, também aninhadas). - As atualizações
K := Vexigem a chave;K => Vinsere ou substitui. As atualizações preparam uma nova tabela e publicam uma única vez. - Erros no corpo:
{badmap, M},{badkey, K}; as guards rejeitam em vez disso. - Serviços:
is_map,map_size,map_get,is_map_key, construção, atualização.
Bitstrings
- Empacotados com o bit mais significativo primeiro, com comprimento exato em bits e preenchimento a zeros. Até 64 bytes vivem inline num binary de heap dimensionado aos dados; os valores maiores usam um buffer imutável partilhado fora do heap, visto através de células binary fora do heap que as caudas extraídas partilham. O buffer é contabilizado uma única vez ao processo que o cria. Não há limite de tamanho além de um orçamento opcional de heap do processo; os segmentos inteiros são escritos sem construir um inteiro tão largo como o segmento.
- A construção prepara todos os segmentos antes de publicar. Os segmentos inteiros truncam; a ordem de bytes nativa vem da disposição de dados do alvo.
- Segmentos float: larguras 16/32/64; a construção pode codificar infinito, mas
a correspondência rejeita campos infinitos/NaN. Correspondências float de
largura zero extraem
0.0. - Os segmentos UTF-8/16/32 validam escalares, surrogates e truncagem.
- A correspondência avança um cursor de bits explícito apenas em caso de
sucesso;
:allcomo tamanho explícito é inválido. - Serviços:
is_binary,is_bitstring,bit_size,byte_size(arredonda para cima),size(arredonda para baixo),binary_part/2,3. Erros →badarg.
Records
- Os records comuns expandem-se em tuplos
{Tag, Fields...}. As declarações devem preceder o uso; duplicados, campos desconhecidos, referências antecipadas/a si próprio e campos curinga inválidos são erros. - A construção avalia os campos pela ordem da declaração: valor explícito,
senão o valor por omissão curinga
_ = V, senão o valor por omissão declarado, senãoundefined. Cada valor por omissão é avaliado separadamente em cada uso. - Os padrões verificam a aridade e a etiqueta e depois apenas os campos
listados.
#r.fé o índice com base 1 (a etiqueta na posição 1). - O acesso a campos verifica a aridade e a etiqueta; a falha é
{badrecord, V}nos corpos e rejeição nas guards. is_record(V, r)usa a aridade declarada.is_record/3precisa de uma etiqueta átomo e de uma aridade inteira (não positiva → falso; tipos errados →badarg); um terceiro argumento átomo é a consulta de native record e devolve falso. As guards exigem argumentos literais.- A atualização
Expr#r{f = V, ...}avalia os novos valores pela ordem do código-fonte, depoisExpr, depois verifica a aridade e a etiqueta ({badrecord, Value}em caso de discordância, também paraExpr#r{}) e constrói um novo tuplo; os restantes campos são copiados._ = Vé rejeitado em atualizações; as atualizações são ilegais em padrões e guards. record_info(fields | size, r)expande-se em tempo de compilação para a lista de nomes de campos ou para o tamanho do tuplo. Ambos os argumentos devem ser átomos literais erum record em tuplo declarado anteriormente; é ilegal em guards, e umarecord_info/2local é rejeitada como já definida.- Native records: native records (formas locais, qualificadas, importadas e anónimas).
Pids e referências
Plano 11, step 42. self/0 devolve o pid do processo chamador, make_ref/0
uma nova referência; pid_to_list/1 e ref_to_list/1 devolvem o seu texto.
- Um pid é uma palavra imediata (quatro bits inferiores
0x3) que contém o número do processo. Os números vêm de uma única sequência global do processo e nunca são reutilizados, pelo que um runtime só admite uma palavra de pid se tiver emitido esse número: uma palavra forjada (nunca emitida) ou o pid de outro runtime éwrong_owner. O pid de um processo terminado continua a ser um termo válido, como no OTP. Os alvos de 32 bits têm 2^28 números por execução do programa, os de 64 bits 2^60; criar um processo para além deles falha comresource_limit. - Um port (step 57B do plano) é uma palavra imediata (quatro bits inferiores
0x7) que contém o seu número, proveniente da sua própria sequência nunca reutilizada, e é admitido como um pid; é impresso como#Port<0.N>e ordena-se entre funs e pids (ports). - Uma referência é uma célula de heap (
reference: cabeçalho mais um número de 64 bits não rastreado) admitida como qualquer termo de heap: apenas no seu próprio processo, obsoleta após uma recolha para umTermdo anfitrião, copiada por valor entre processos. Os números vêm de um único contador global do processo, pelo que cada referência de uma execução do programa é única. - A impressão segue as identidades locais do OTP: um pid como
<0.N.S>(N os 28 bits inferiores do seu número, S o resto), uma referência como#Ref<0.A.B.C>(C os 18 bits inferiores do seu número, B os 32 seguintes, A o resto), tanto no estilo~wcomo no de display. Os pids ordenam-se por número, as referências por número, pelo que as referências posteriores de um programa ficam depois das anteriores.
Comparação e ordem
Iterativa e sem limite de trabalho, como no OTP: apenas a memória para os pares pendentes limita uma comparação, incluindo as pesquisas de chaves de map, e palavras idênticas são iguais sem percurso. Os bitstrings alinhados ao byte comparam bytes inteiros de uma vez. Ordem: números < átomos < referências < funs < pids < tuplos < native records < maps < nil < listas < bitstrings (as funs ordenam-se entre si). Os átomos comparam-se pela grafia UTF-8 (ordem dos pontos de código); os tuplos pela aridade e depois pelos campos; os maps pelo tamanho, depois pelas chaves e depois pelos valores; os bitstrings pelos bits lógicos.
Impressão
format_term (output.hpp)
apresenta qualquer termo admitido num de dois estilos do OTP. Os inteiros, os
tuplos (os records são tuplos) e o aninhamento têm o mesmo aspeto em ambos.
~w (TermStyle::write) | erlang:display/1 (TermStyle::display) | |
|---|---|---|
| Átomos | Entre aspas, exceto se começar por uma letra minúscula Latin-1 seguida de caracteres de nome (com @); as palavras reservadas e maybe/else levam aspas; fora do Latin-1 escapa como \x{H} | Entre aspas, exceto se começar por uma letra minúscula Latin-1 seguida de alfanuméricos ou _; as palavras reservadas e @ não têm regra especial; o UTF-8 é mantido |
| Floats | A representação mais curta que preserva o valor, na disposição do OTP: 0.1, 100.0, 1.0e16, 1.5e-7 | C %.6e: 1.500000e+00 |
| Listas | Elementos: [104,105], [1,2|3] | Uma lista plana de bytes Latin-1 imprimíveis é impressa como "hi" (bytes em bruto; apenas \n e " são escapados) |
| Bitstrings | <<1,2,5:3>> | Um binary ASCII imprimível é impresso como <<"hi">>, os restantes como ~w |
| Maps | #{k => v,k2 => v2} | #{k=>v,k2=>v2} |
- Os maps são impressos pela ordem das chaves (
maps:iterator(M, ordered), como~kwdo OTP). O~wpor omissão e oerlang:display/1do OTP seguem antes a sua disposição interna: ordem da tabela de átomos para chaves átomo de maps pequenos (varia entre execuções da VM) e ordem de hash acima de 32 chaves. O Clause não reproduz essa ordem. - A apresentação é iterativa, pelo que a profundidade só é limitada pelo
termo. O texto está limitado a 64 MiB por omissão; excedê-lo (por exemplo,
um subtermo muito partilhado) falha com
resource_limite não devolve texto parcial. - Goldens:
runtime_printingcompara ambos os estilos com o OTP para 9542 valores (todos os resultados do corpus mais casos-limite escritos à mão); as linhas de display cuja ordem de map no OTP é interna são ignoradas (fixtures).
Clause