Saltar para o conteúdo
Ridgun
Português

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 ini escrita 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:

  1. Declare import ini (ou import ini as alias) no início do ficheiro: cada lib define o seu espaço de nomes module :: ini e em modo --strict as funções multi-palavra são sempre invocadas com espaços naturais.
  2. Encadeie os seus pipelines da esquerda para a direita com ->: escreva entrada passo um -> passo dois -> resultado, use máscaras (+ _ _ +) para desestruturar sequências e omita _ = em instruções sequenciais (ou descarte com -> ()).
  3. Recursão de cauda com TCO (Tail-Call Optimization): todas as travessias internas da ini são compiladas para ciclos nativos em espaço de pilha $O(1)$.
  4. 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:

ArtefactoCaminhoFinalidade
Bundle Ridguntarget/bundles/ini.rglibDistribuição e importação direta com import ini
Ficheiro compiladotarget/ini.rgcArtefacto intermédio verificado pelo ridgunc
Objeto partilhado ELF64target/libini.soBiblioteca 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 escrever import ini (ou import 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 / TipoO que faz e quando usar
ini parse / ini stringifyAnalisa texto .ini por secções em IniDoc e serializa de volta para texto canónico
ini get / ini get int / ini get bool / ini setConsultas tipadas por secção+chave ("" para secção global) e atualização imutável
ini sections / ini section mapLista todos os nomes de secção ou extrai o mapa [String String] de uma secção
dotenv parseAnalisa 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
1fn :: ini is hspace IntBool
2fn :: ini trim left loop String Int IntString
3fn :: ini trim right loop String IntString
4fn :: ini trim StringString
5fn :: ini split lines loop String Int Int Int [String][String]
6fn :: ini skip quoted String Int Int IntInt
7fn :: ini strip comment loop String Int IntString
8fn :: ini unquote unescape loop String Int Int StringString
9fn :: ini clean val StringString
10fn :: ini clean val len String IntString
11fn :: ini p key IniEntryString
12fn :: ini p val IniEntryString
13fn :: ini kv get [IniEntry] String StringString
14fn :: ini kv put [IniEntry] String String[IniEntry]
15fn :: ini find rbrace String Int IntInt
16fn :: ini expand brace String Int Int [IniEntry] String IntString
17fn :: ini interpolate loop String Int Int [IniEntry] StringString
18fn :: ini interpolate String [IniEntry]String
19fn :: ini sec put [IniSection] String String String[IniSection]
20fn :: ini sec ensure [IniSection] String[IniSection]
21fn :: ini sec get [IniSection] String String StringString
22fn :: ini find eq String Int IntInt
23fn :: ini strip export StringString
24fn :: ini parse kv at String [IniSection] String Int[IniSection]
25fn :: ini sec entries [IniSection] String[IniEntry]
26fn :: ini parse lines [String] [IniSection] String[IniSection]
27fn :: ini parse sec hdr String [String] [IniSection][IniSection]
28fn :: ini parse kv line String [String] [IniSection] String[IniSection]
29fn :: ini parse clean line String [String] [IniSection] String[IniSection]
30fn :: ini parse StringIniDoc
31fn :: dotenv parse String[IniEntry]
32fn :: ini get IniDoc String String StringString
33fn :: ini get int IniDoc String String IntInt
34fn :: ini parse int or String IntInt
35fn :: ini get bool IniDoc String String BoolBool
36fn :: ini parse bool or String BoolBool
37fn :: ini set IniDoc String String StringIniDoc
38fn :: ini collect sec names [IniSection][String]
39fn :: ini sections IniDoc[String]
40fn :: dotenv get [IniEntry] String StringString
41fn :: ini format entries [IniEntry]String
42fn :: ini format section IniSectionString
43fn :: ini format sections [IniSection]String
44fn :: ini stringify IniDocString

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/on vs false/0/no/off).
  • Complexidade de pilha $O(1)$ com TCO: Todos os ciclos recursivos em src/main/ridgun/lib/ini.rg estão em posição de cauda, pelo que o ridgunc os 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 check e pela suite automatizada src/test/ridgun/test ini.rg.

As 5 Regras de Ouro ao programar com a ini

  1. Declare import ini no início do ficheiro e aproveite a equivalência entre espaços e underscores para que o seu código se leia como prosa.
  2. Alinhe os seus pipelines de dados com =: encadeie as chamadas à ini em passos nomeados e deixe o stable format alinhar as colunas.
  3. Aproveite as funções totais da ini nas 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.
  4. Evite copiar grandes estruturas se só precisa de consultar um campo: lembre-se de que no RMM2 os argumentos de entrada não incrementam nem decrementam contadores (+0 / -0).
  5. Combine a ini com test (import test): escreva sempre asserções com assert equals ou assert true para 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)$).