18 O Livro do Ridgun
A lib ini — Ficheiros .ini por Secções, .env e Interpolação de Variáveis
18.1. Por que existe a lib ini? (O problema real e a filosofia Ridgun)
«Por que preciso de uma lib
iniescrita de raiz em Ridgun puro em vez de ligar uma biblioteca C tradicional?»
Essa é a primeira pergunta que qualquer engenheiro de sistemas faz ao chegar à ini. E a resposta resume toda a filosofia do ridgun-libs:
Sejamos sinceros: os ficheiros .env e .ini parecem triviais até alguém adicionar export KEY="valor # com cardinal", comentários ; no fim da linha ou referências cruzadas entre variáveis.
A lib ini analisa configurações .ini com secções ([database]) e ficheiros .env com prefixos export, preservando símbolos # e ; dentro de aspas e resolvendo interpolações de variáveis em dotenv parse.
Num ecossistema tradicional, resolver as tarefas do domínio INI implica arrastar dependências externas em C, lidar com ponteiros brutos, conversões opacas de tipos, exceções não verificadas ou pausas imprevisíveis do coletor de lixo. Em Ridgun, a lib ini (src/main/ridgun/lib/ini.rg) aborda o problema com quatro princípios inegociáveis:
- Declare
import ini(ouimport ini as alias) no início do ficheiro: cada lib define o seu espaço de nomesmodule :: inie em modo--strictas funções multi-palavra são sempre invocadas com espaços naturais. - Encadeie os seus pipelines da esquerda para a direita com
->: escrevaentrada passo um -> passo dois -> resultado, use máscaras(+ _ _ +)para desestruturar sequências e omita_ =em instruções sequenciais (ou descarte com-> ()). - Recursão de cauda com TCO (Tail-Call Optimization): todas as travessias internas da
inisão compiladas para ciclos nativos em espaço de pilha $O(1)$. - Memória determinística (
RMM1/RMM2): cada estrutura intermédia é libertada no instante exato em que termina a sua última leitura, sem Garbage Collector.
18.2. Artefactos nativos, importação e catálogo completo da API de ini
No ridgun-libs, ini é uma lib de primeira classe localizada em src/main/ridgun/lib/ini.rg (domínio INI). Ao compilar com stable bundle, é empacotada em três formatos nativos:
| Artefacto | Caminho | Finalidade |
|---|---|---|
| Bundle Ridgun | target/bundles/ini.rglib | Distribuição e importação direta com import ini |
| Ficheiro compilado | target/ini.rgc | Artefacto intermédio verificado pelo ridgunc |
| Objeto partilhado ELF64 | target/libini.so | Biblioteca dinâmica nativa sem dependências de C nem libc |
Cada lib declara o seu próprio espaço de nomes com
module :: ini: basta escreverimport ini(ouimport ini as alias) no topo e invocar as suas funções com espaços naturais (minha funcao x) e pipelines->(x minha funcao -> resultado).
18.2.1. Resumo das famílias funcionais da ini
| Assinatura / Função / Tipo | O que faz e quando usar |
|---|---|
ini parse / ini stringify | Analisa texto .ini por secções em IniDoc e serializa de volta para texto canónico |
ini get / ini get int / ini get bool / ini set | Consultas tipadas por secção+chave ("" para secção global) e atualização imutável |
ini sections / ini section map | Lista todos os nomes de secção ou extrai o mapa [String String] de uma secção |
dotenv parse | Analisa linhas .env (export K=V, aspas, comentários e expansão de variáveis) |
18.2.2. Catálogo exaustivo de tipos e assinaturas em src/main/ridgun/lib/ini.rg
Abaixo encontra-se o inventário completo de tipos algébricos (type), operadores (op ::) e assinaturas de funções (fn ::) definidos em src/main/ridgun/lib/ini.rg (3 tipos, 0 operadores e 44 funções):
Tipos algébricos e registos (type) da ini:
type IniEntry = IniEntry String String
type IniSection = section_name: String, entries: [IniEntry]
type IniDoc = sections: [IniSection]
| # | Função / Assinatura (fn ::) | Tipo de Retorno |
|---|---|---|
1 | fn :: ini is hspace Int | Bool |
2 | fn :: ini trim left loop String Int Int | String |
3 | fn :: ini trim right loop String Int | String |
4 | fn :: ini trim String | String |
5 | fn :: ini split lines loop String Int Int Int [String] | [String] |
6 | fn :: ini skip quoted String Int Int Int | Int |
7 | fn :: ini strip comment loop String Int Int | String |
8 | fn :: ini unquote unescape loop String Int Int String | String |
9 | fn :: ini clean val String | String |
10 | fn :: ini clean val len String Int | String |
11 | fn :: ini p key IniEntry | String |
12 | fn :: ini p val IniEntry | String |
13 | fn :: ini kv get [IniEntry] String String | String |
14 | fn :: ini kv put [IniEntry] String String | [IniEntry] |
15 | fn :: ini find rbrace String Int Int | Int |
16 | fn :: ini expand brace String Int Int [IniEntry] String Int | String |
17 | fn :: ini interpolate loop String Int Int [IniEntry] String | String |
18 | fn :: ini interpolate String [IniEntry] | String |
19 | fn :: ini sec put [IniSection] String String String | [IniSection] |
20 | fn :: ini sec ensure [IniSection] String | [IniSection] |
21 | fn :: ini sec get [IniSection] String String String | String |
22 | fn :: ini find eq String Int Int | Int |
23 | fn :: ini strip export String | String |
24 | fn :: ini parse kv at String [IniSection] String Int | [IniSection] |
25 | fn :: ini sec entries [IniSection] String | [IniEntry] |
26 | fn :: ini parse lines [String] [IniSection] String | [IniSection] |
27 | fn :: ini parse sec hdr String [String] [IniSection] | [IniSection] |
28 | fn :: ini parse kv line String [String] [IniSection] String | [IniSection] |
29 | fn :: ini parse clean line String [String] [IniSection] String | [IniSection] |
30 | fn :: ini parse String | IniDoc |
31 | fn :: dotenv parse String | [IniEntry] |
32 | fn :: ini get IniDoc String String String | String |
33 | fn :: ini get int IniDoc String String Int | Int |
34 | fn :: ini parse int or String Int | Int |
35 | fn :: ini get bool IniDoc String String Bool | Bool |
36 | fn :: ini parse bool or String Bool | Bool |
37 | fn :: ini set IniDoc String String String | IniDoc |
38 | fn :: ini collect sec names [IniSection] | [String] |
39 | fn :: ini sections IniDoc | [String] |
40 | fn :: dotenv get [IniEntry] String String | String |
41 | fn :: ini format entries [IniEntry] | String |
42 | fn :: ini format section IniSection | String |
43 | fn :: ini format sections [IniSection] | String |
44 | fn :: ini stringify IniDoc | String |
18.3. Explorando a ini a fundo: Exemplos passo a passo de cada funcionalidade
Não basta ver a tabela de assinaturas: vamos explorar cada uma das funcionalidades da lib ini através de exemplos progressivos e executáveis em Ridgun. Começaremos pelas duas receitas tabulares de uso diário e depois percorreremos em detalhe todos os cenários funcionais, transformações e casos de fronteira verificados em src/test/ridgun/test ini.rg.
18.3.1. Exemplo 1: Fluxo principal em estilo tabular com ini
Repare como em Ridgun os sinais = e os passos de transformação com ini ficam alinhados como uma tabela limpa, sem variáveis mutáveis desnecessárias nem efeitos colaterais ocultos:
import ini
fn :: cargar base de datos String => String Int Bool
cargar base de datos texto_ini =
texto_ini ini parse -> doc
ini get doc "database" "host" "localhost" -> host
ini get int doc "database" "port" 5432 -> port
ini get bool doc "database" "pool" True -> pool
(host, port, pool)
18.3.2. Exemplo 2: Composição segura e tratamento de casos de fronteira
Todas as funções da lib ini foram desenhadas para se comporem com guardas booleanas | e com as restantes 32 libs do ridgun-libs:
import ini
fn :: actualizar puerto [String String String] String => String
actualizar puerto doc nuevo =
ini set doc "database" "port" nuevo -> doc2
doc2 ini encode
18.3.3. Exemplo 3: Secções [seccao], comentários (; e #), leituras tipadas (get str, get int, get bool) e atualização imutável (ini set)
Neste exemplo prático exploramos de forma exaustiva como a ini se comporta neste bloco funcional, incluindo entradas padrão, combinações de parâmetros e valores limite:
fn :: test ini sections and typed getters => ()
test ini sections and typed getters =
nl = char from code 10
src = "# Global header comment" + nl + "; Secondary comment" + nl + "global_env = production" + nl + nl + "[database]" + nl + "host = \"127.0.0.1\"" + nl + "port = 5432 ; default pg" + nl + "timeout = -1 # infinite" + nl + "password = \"p@ss#word;123\"" + nl + "ssl = true" + nl + "pool_enabled = yes" + nl + "readonly = 0" + nl + "debug_sql = no" + nl + nl + "[server]" + nl + "workers = 4" + nl + "enabled = 1" + nl
doc = ini parse src
assert str eq (ini get doc "" "global_env" "") "production" "AC1: global sectionless key in root '' section"
assert str eq (ini get doc "database" "host" "") "127.0.0.1" "AC1: ini get quoted string"
assert int equals (ini get int doc "database" "port" 0) 5432 "AC1: ini get int with ';' inline comment"
assert int equals (ini get int doc "database" "timeout" 0) (-1) "AC1: ini get negative int with '#' inline comment"
assert str eq (ini get doc "database" "password" "") "p@ss#word;123" "AC1: quoted '#' and ';' preserved inside string"
assert true (ini get bool doc "database" "ssl" False) "AC1: ini get bool 'true'"
assert true (ini get bool doc "database" "pool_enabled" False) "AC1: ini get bool 'yes'"
assert false (ini get bool doc "database" "readonly" True) "AC1: ini get bool '0'"
assert false (ini get bool doc "database" "debug_sql" True) "AC1: ini get bool 'off'"
assert int equals (ini get int doc "server" "workers" 0) 4 "AC1: ini get second section"
assert true (ini get bool doc "server" "enabled" False) "AC1: ini get bool '1'"
assert str eq (ini get doc "server" "missing" "default_v") "default_v" "AC1: ini get fallback on missing key"
assert str eq (ini get doc "no_such_section" "k" "fallback") "fallback" "AC1: ini get fallback on missing section"
secs = ini sections doc
assert int equals (ini sec count secs) 3 "AC1: 3 sections (root '', database, server)"
()
18.3.4. Exemplo 4: Ficheiros .env com export, valores entre aspas, interpolação de variáveis ${VAR} e serialização canónica (ini stringify)
Neste exemplo prático exploramos de forma exaustiva como a ini se comporta neste bloco funcional, incluindo entradas padrão, combinações de parâmetros e valores limite:
fn :: test dotenv interpolation and roundtrip => ()
test dotenv interpolation and roundtrip =
nl = char from code 10
lb = char from code 123
rb = char from code 125
env_src = "# .env test" + nl + "export BASE_DIR=/srv/app" + nl + "APP_NAME='solstice'" + nl + "LOG_DIR=\"$" + lb + "BASE_DIR" + rb + "/$" + lb + "APP_NAME" + rb + "/logs\"" + nl + "WITH_UNKNOWN=\"prefix_$" + lb + "UNDEFINED_VAR" + rb + "_suffix\"" + nl + "PORT=8080 # inline comment" + nl
env = dotenv parse env_src
assert str eq (dotenv get env "BASE_DIR" "") "/srv/app" "AC2: dotenv export key"
assert str eq (dotenv get env "APP_NAME" "") "solstice" "AC2: dotenv single-quoted value"
assert str eq (dotenv get env "LOG_DIR" "") "/srv/app/solstice/logs" "AC2: dotenv multiple variable interpolations"
assert str eq (dotenv get env "WITH_UNKNOWN" "") "prefix__suffix" "AC2: dotenv undefined variable expands to empty"
assert str eq (dotenv get env "PORT" "") "8080" "AC2: dotenv inline comment stripped"
assert str eq (dotenv get env "MISSING_KEY" "def") "def" "AC2: dotenv missing key fallback"
doc0 = ini parse ("[app]" + nl + "name = demo" + nl + "port = 3000" + nl)
doc1 = ini set doc0 "app" "version" "1.0"
doc2 = ini set doc1 "app" "port" "9090"
doc3 = ini set doc2 "metrics" "enabled" "true"
out_s = ini stringify doc3
doc_rt = ini parse out_s
assert str eq (ini get doc_rt "app" "name" "") "demo" "AC3: ini stringify round-trip name"
assert str eq (ini get doc_rt "app" "version" "") "1.0" "AC3: ini set new key + round-trip"
assert int equals (ini get int doc_rt "app" "port" 0) 9090 "AC3: ini set overwrite existing key + round-trip"
assert true (ini get bool doc_rt "metrics" "enabled" False) "AC3: ini set new section + round-trip"
()
18.3.5. Exemplo 5: Configuração hierárquica de um serviço: fusão de .env com interpolação ${VAR} e secções [database] / [server] em INI
Neste exemplo prático exploramos de forma exaustiva como a ini se comporta neste bloco funcional, incluindo entradas padrão, combinações de parâmetros e valores limite:
import ini
fn :: cargar configuracion servicio (raw_ini : String) (raw_env : String) => (String, Int, Bool, String)
cargar configuracion servicio raw_ini raw_env =
env_entries = dotenv parse raw_env
doc = ini parse raw_ini
db_host = ini get str doc "database" "host" "localhost"
db_port = ini get int doc "database" "port" 5432
tls_activo = ini get bool doc "database" "tls" True
doc_act = ini set doc "server" "dsn" "{db_host}:{db_port}"
(dotenv get env_entries "APP_ENV" "prod", db_port, tls_activo, ini stringify doc_act)
18.4. Debaixo do capô: como a ini é construída em Ridgun puro (src/main/ridgun/lib/ini.rg)
Uma das maiores vantagens do ridgun-libs é que não existem caixas-pretas em C. Pode abrir src/main/ridgun/lib/ini.rg e ler exatamente como cada operação é implementada usando pattern matching, árvores de guardas | / |. e recursão de cauda (TCO). Vejamos as peças-chave da sua implementação real:
18.4.1. Implementação interna: Whitespace, Comment & Line Helpers
Observe como ini.rg estrutura primeiro as assinaturas fn :: ... => ... e resolve cada caso através de equações alinhadas e chamadas de cauda:
module :: ini
fn :: ini is hspace Int => Bool
ini is hspace c = c == 32 or c == 9 or c == 13
fn :: ini trim left loop String Int Int => String
ini trim left loop s pos len =
| pos >= len = ""
| ini is hspace (str char at s pos) = ini trim left loop s (pos + 1) len
| otherwise = str slice s pos len
fn :: ini trim right loop String Int => String
ini trim right loop s end_pos =
| end_pos <= 0 = ""
| ini is hspace (str char at s (end_pos - 1)) = ini trim right loop s (end_pos - 1)
| otherwise = str slice s 0 end_pos
fn :: ini trim String => String
ini trim s =
ini trim right loop s (str len s) -> r
ini trim left loop r 0 (str len r)
fn :: ini split lines loop String Int Int Int [String] => [String]
ini split lines loop s pos len start acc =
| pos >= len and start == len = acc
| pos >= len = acc :: [str slice s start len]
| (str char at s pos) == 10 = ini split lines loop s (pos + 1) len (pos + 1) (acc :: [str slice s start pos])
| otherwise = ini split lines loop s (pos + 1) len start acc
fn :: ini skip quoted String Int Int Int => Int
ini skip quoted s pos len q =
| pos >= len = len
| (str char at s pos) == 92 = ini skip quoted s (pos + 2) len q
| (str char at s pos) == q = pos + 1
| otherwise = ini skip quoted s (pos + 1) len q
fn :: ini strip comment loop String Int Int => String
ini strip comment loop s pos len =
| pos >= len = s
| (str char at s pos) == 34 = ini strip comment loop s (ini skip quoted s (pos + 1) len 34) len
| (str char at s pos) == 39 = ini strip comment loop s (ini skip quoted s (pos + 1) len 39) len
| (str char at s pos) == 35 or (str char at s pos) == 59 = str slice s 0 pos
| (pos + 1) < len and (str char at s pos) == 45 and (str char at s (pos + 1)) == 45 = str slice s 0 pos
| otherwise = ini strip comment loop s (pos + 1) len
fn :: ini unquote unescape loop String Int Int String => String
ini unquote unescape loop s pos len acc =
| pos >= len = acc
| (str char at s pos) == 92 and (pos + 1) < len and (str char at s (pos + 1)) == 110 = ini unquote unescape loop s (pos + 2) len (acc + (char from code 10))
| (str char at s pos) == 92 and (pos + 1) < len and (str char at s (pos + 1)) == 116 = ini unquote unescape loop s (pos + 2) len (acc + (char from code 9))
| (str char at s pos) == 92 and (pos + 1) < len = ini unquote unescape loop s (pos + 1 + 1) len (acc + (str slice s (pos + 1) (pos + 2)))
| otherwise = ini unquote unescape loop s (pos + 1) len (acc + (str slice s pos (pos + 1)))
fn :: ini clean val String => String
ini clean val raw =
raw ini trim -> tr
tr str len -> len
ini clean val len tr len
fn :: ini clean val len String Int => String
ini clean val len tr len =
| len >= 2 and (str char at tr 0) == 34 and (str char at tr (len - 1)) == 34 = ini unquote unescape loop (str slice tr 1 (len - 1)) 0 (len - 2) ""
| len >= 2 and (str char at tr 0) == 39 and (str char at tr (len - 1)) == 39 = str slice tr 1 (len - 1)
| otherwise = tr
18.4.2. Implementação interna: Key-Value Pair Helpers ([IniEntry])
Observe como ini.rg estrutura primeiro as assinaturas fn :: ... => ... e resolve cada caso através de equações alinhadas e chamadas de cauda:
fn :: ini p key IniEntry => String
ini p key (IniEntry k _) = k
fn :: ini p val IniEntry => String
ini p val (IniEntry _ v) = v
fn :: ini kv get [IniEntry] String String => String
ini kv get [] _ fallback = fallback
ini kv get (pair:rest) key fallback =
| (ini p key pair) == key = ini p val pair
| otherwise = ini kv get rest key fallback
fn :: ini kv put [IniEntry] String String => [IniEntry]
ini kv put [] key val = [IniEntry key val]
ini kv put (pair:rest) key val =
| (ini p key pair) == key = [IniEntry key val] :: rest
| otherwise = [pair] :: (ini kv put rest key val)
18.4.3. Implementação interna: Sectioned INI Document Helpers
Observe como ini.rg estrutura primeiro as assinaturas fn :: ... => ... e resolve cada caso através de equações alinhadas e chamadas de cauda:
fn :: ini sec put [IniSection] String String String => [IniSection]
ini sec put [] sec key val = [IniSection sec [IniEntry key val]]
ini sec put (s:rest) sec key val =
| (section_name s) == sec = [IniSection sec (ini kv put (entries s) key val)] :: rest
| otherwise = [s] :: (ini sec put rest sec key val)
fn :: ini sec ensure [IniSection] String => [IniSection]
ini sec ensure [] sec = [IniSection sec []]
ini sec ensure (s:rest) sec =
| (section_name s) == sec = [s] :: rest
| otherwise = [s] :: (ini sec ensure rest sec)
fn :: ini sec get [IniSection] String String String => String
ini sec get [] _ _ fallback = fallback
ini sec get (s:rest) sec key fallback =
| (section_name s) == sec = ini kv get (entries s) key fallback
| otherwise = ini sec get rest sec key fallback
fn :: ini find eq String Int Int => Int
ini find eq s pos len =
| pos >= len = (0 - 1)
| (str char at s pos) == 61 = pos
| otherwise = ini find eq s (pos + 1) len
fn :: ini strip export String => String
ini strip export tr =
| str starts with tr "export " = ini trim (str slice tr 7 (str len tr))
| otherwise = tr
fn :: ini parse kv at String [IniSection] String Int => [IniSection]
ini parse kv at line secs cur_s eq_pos =
| eq_pos == (0 - 1) = secs
| otherwise = ini sec put secs cur_s (ini trim (str slice line 0 eq_pos)) (ini interpolate (ini clean val (str slice line (eq_pos + 1) (str len line))) (ini sec entries secs cur_s))
fn :: ini sec entries [IniSection] String => [IniEntry]
ini sec entries [] _ = []
ini sec entries (s:rest) sec =
| (section_name s) == sec = entries s
| otherwise = ini sec entries rest sec
fn :: ini parse lines [String] [IniSection] String => [IniSection]
ini parse lines [] secs _ = secs
ini parse lines (l:ls) secs cur_s =
ini strip comment loop l 0 (str len l) -> ini trim -> clean
ini parse clean line clean ls secs cur_s
fn :: ini parse sec hdr String [String] [IniSection] => [IniSection]
ini parse sec hdr clean ls secs =
str slice clean 1 ((str len clean) - 1) -> ini trim -> new_sec
ini parse lines ls (ini sec ensure secs new_sec) new_sec
fn :: ini parse kv line String [String] [IniSection] String => [IniSection]
ini parse kv line clean ls secs cur_s =
clean ini strip export -> stripped
ini parse kv at stripped secs cur_s (ini find eq stripped 0 (str len stripped)) -> next_secs
ini parse lines ls next_secs cur_s
fn :: ini parse clean line String [String] [IniSection] String => [IniSection]
ini parse clean line "" ls secs cur_s = ini parse lines ls secs cur_s
ini parse clean line clean ls secs cur_s =
| (str char at clean 0) == 91 and (str char at clean ((str len clean) - 1)) == 93 = ini parse sec hdr clean ls secs
| otherwise = ini parse kv line clean ls secs cur_s
fn :: ini parse String => IniDoc
ini parse src =
ini split lines loop src 0 (str len src) 0 [] -> lines
IniDoc (ini parse lines lines [] "")
fn :: dotenv parse String => [IniEntry]
dotenv parse src =
src ini parse -> doc
ini sec entries (sections doc) ""
18.4.4. Implementação interna: Queries, Updates & Serialization
Observe como ini.rg estrutura primeiro as assinaturas fn :: ... => ... e resolve cada caso através de equações alinhadas e chamadas de cauda:
fn :: ini get IniDoc String String String => String
ini get doc sec key fallback = ini sec get (sections doc) sec key fallback
fn :: ini get int IniDoc String String Int => Int
ini get int doc sec key fallback =
ini get doc sec key "" -> v
ini parse int or v fallback
fn :: ini parse int or String Int => Int
ini parse int or "" fallback = fallback
ini parse int or v _ = str to int v
fn :: ini get bool IniDoc String String Bool => Bool
ini get bool doc sec key fallback =
ini get doc sec key "" -> v
ini parse bool or v fallback
fn :: ini parse bool or String Bool => Bool
ini parse bool or "" fallback = fallback
ini parse bool or "true" _ = True
ini parse bool or "True" _ = True
ini parse bool or "1" _ = True
ini parse bool or "yes" _ = True
ini parse bool or "false" _ = False
ini parse bool or "False" _ = False
ini parse bool or "0" _ = False
ini parse bool or "no" _ = False
ini parse bool or _ fallback = fallback
fn :: ini set IniDoc String String String => IniDoc
ini set doc sec key val = IniDoc (ini sec put (sections doc) sec key val)
fn :: ini collect sec names [IniSection] => [String]
ini collect sec names [] = []
ini collect sec names (s:rest) = [section_name s] :: (ini collect sec names rest)
fn :: ini sections IniDoc => [String]
ini sections doc = ini collect sec names (sections doc)
fn :: dotenv get [IniEntry] String String => String
dotenv get env key fallback = ini kv get env key fallback
fn :: ini format entries [IniEntry] => String
ini format entries [] = ""
ini format entries ((IniEntry k v):rs) = k + " = " + v + (char from code 10) + (ini format entries rs)
fn :: ini format section IniSection => String
ini format section sec =
| (section_name sec) == "" = ini format entries (entries sec)
| otherwise = "[" + (section_name sec) + "]" + (char from code 10) + (ini format entries (entries sec))
fn :: ini format sections [IniSection] => String
ini format sections [] = ""
ini format sections (s:[]) = ini format section s
ini format sections (s:rest) = (ini format section s) + (char from code 10) + (ini format sections rest)
fn :: ini stringify IniDoc => String
ini stringify doc = ini format sections (sections doc)
18.5. Garantias algorítmicas, memória RMM1/RMM2 e as 5 Regras de Ouro da ini
- Garantia algorítmica e de segurança: Respeito total pelos caracteres
#e;quando surgem entre aspas simples ou duplas, e conversão booleana flexível (true/1/yes/onvsfalse/0/no/off). - Complexidade de pilha $O(1)$ com TCO: Todos os ciclos recursivos em
src/main/ridgun/lib/ini.rgestão em posição de cauda, pelo que oridguncos compila para saltos nativos sem crescimento de pilha. - Memória determinística (
RMM1/RMM2): Zero pausas de coletor de lixo; todas as estruturas intermédias são libertadas de forma determinística à saída do escopo. - Verificação contínua: Testada exaustivamente com
stable checke pela suite automatizadasrc/test/ridgun/test ini.rg.
As 5 Regras de Ouro ao programar com a ini
- Declare
import inino início do ficheiro e aproveite a equivalência entre espaços e underscores para que o seu código se leia como prosa. - Alinhe os seus pipelines de dados com
=: encadeie as chamadas àiniem passos nomeados e deixe ostable formatalinhar as colunas. - Aproveite as funções totais da
ininas suas guardas|: verifique os valores de retorno ou desempacote os ADTs com pattern matching em vez de assumir que a entrada externa é sempre válida. - Evite copiar grandes estruturas se só precisa de consultar um campo: lembre-se de que no
RMM2os argumentos de entrada não incrementam nem decrementam contadores (+0/-0). - Combine a
inicomtest(import test): escreva sempre asserções comassert equalsouassert truepara fixar os invariantes do seu domínio.
No Capítulo 19 passaremos a explorar em profundidade a lib regex (A lib regex — Motor de Expressões Regulares Thompson NFA em Tempo Linear $O(N \cdot M)$).