Tour de sintaxe (a linguagem numa página)
A “parte 2”: um passeio por toda a sintaxe em código comentado, agrupado por tema, como referência rápida. Nada é novo, é o consolidado das seções 1 a 15, escrito como você escreveria. Cada bloco é um exemplo executável; os comentários apontam a decisão por trás.
Declaração e mutabilidade
Seção intitulada “Declaração e mutabilidade”x := 5 // declara imutável, runtime (default): atalho de 'let'let y := 5 // imutável, forma explícita (idêntica a 'x := 5')var z := 5 // mutávelconst N := 64 // constante de compilação (seção 10)nome: string = "ana" // tipo explícito ao estilo Odin; ':=' é ': =' com tipo omitido
z = z + 1 // '=' pelado só atribui, e só a um 'var'; o ':' é o que declara// var w := compute(); return w // ERRO: 'w' é var mas nunca foi mutado. Use a forma imutável
// Um só tipo de ponteiro; 'mut' é write-back. Ambos exigem alvo 'var'.p := &z // &lugar = endereço de um lugartotal := *p // *p = dereffn bump(v: mut int) { v = v + 1 } // parâmetro write-backbump(mut z) // muta z in-place; bump(mut x) seria ERRO (x é imutável)Controle de fluxo: loop e match unificados
Seção intitulada “Controle de fluxo: loop e match unificados”loop { break } // sem cabeçalho → infinitoloop ativo { ... } // expressão booleana → whileloop item in lista { ... } // IDENT in iterável → for-eachloop i in 0..8 { ... } // range exclusivo (..= é inclusivo)loop i := 0; i < 8; i = i+1 { ... } // C-style; ';' delimita as 3 partes (parênteses opcionais)
// match COM sujeito → casa padrões (o "switch")match parse_port(input) { Ok(p) => listen(p) Err(error.Empty) => use_default() Err(error.NotNumber) => abort() Err(error.OutOfRange) => abort() // domínio fechado: todas as variantes, '_' seria erro}
// match SEM sujeito → espera vários channels, reage ao primeiro pronto (o "select")match { msg := -> chan_a => handle_a(msg) msg := -> chan_b => handle_b(msg)} timeout(1s) { give_up()}
defer file.close() // cleanup determinístico; roda na saída do escopo, LIFOFunções, UFCS e dispatch por tipo
Seção intitulada “Funções, UFCS e dispatch por tipo”// Modificadores: pub? comptime? unsafe? fn ('@generator' é decorador: acima ou inline)pub fn add(a: int, b: int) -> int { return a + b }
// UFCS: x.f(y) é açúcar para f(x, y): pipeline com '.', e lambdas são 'fn(x) => expr'errors := lines .filter(fn(l) => l.contains("ERROR")) .map(fn(l) => parse_entry(l))
// Generic com lógica uniforme:fn show[T + Display](x: T) -> string { return x.to_string() }
// Dispatch por tipo, variações pequenas e juntas → 'match T' interno (domínio fechado, sem '_'):fn encode[T: {int, bool}](x: T) -> []byte { match T { int => return x.to_bytes() bool => return x.to_bytes() }}
// Variações grandes ou separadas → set externo (roteador por tipo, resolvido em compilação):fn to_string = { int_to_string, bool_to_string }Tipos: struct, enum, union, interface
Seção intitulada “Tipos: struct, enum, union, interface”decl User { id: int // privado ao módulo por default (encapsulado) name: string pub email: string // campo exposto individualmente a outros módulos}
// enum SEM payload = enumeração simples / state machine (sem iota; cada variante é um valor):decl State { Idle; Running; Done }
// enum COM payload = sum type (payload é opcional por variante, e pode misturar):decl Event { Tick; Click(int, int); Quit }
// match exaustivo sobre enum: cobre todas as variantes e '_' é erro de compilação// (adicionar uma variante quebra a build até você tratá-la, o '_' anularia isso):fn step(s: State) -> State { match s { Idle => return Running Running => return Done Done => return Done }}
// match com payload vincula os campos por posição; domínio fechado → todas as variantes (sem '_'):fn describe(e: Event) -> string { match e { Tick => return "tick" Click(x, y) => return "click em " + x.to_string() Quit => return "quit" }}
// Método = função externa com receiver (estilo Go); a struct só guarda dados:fn (u: User) display() -> string { return u.name + " <" + u.email + ">" }
// Interface = conjunto de métodos; implementar os métodos JÁ satisfaz, sem "implements":decl Writeable { fn write(data: []byte) -> error}
// union = bytes sobrepostos, sempre unsafe (reinterpretação):unsafe union Bits { f: f32; raw: u32 }
flag: Optional[int] // ausência explícita; inteiros i8..usize, byte = u8, floats IEEEStrings
Seção intitulada “Strings”s := "Olá {{nome}}!" // string UTF-8; interpolação nomeada (via Display)n := s.char_at(0) // um grapheme; NÃO há 's[i]' puro (ambíguo → erro)view := s.chars[2..6] // VIEW por graphemes ('..'), mantém s vivo; .bytes/.codepoints idemcp := s.chars_copy(2, 6) // método-irmão; ':' / '_copy' = cópia O(n)raw := s.to[u16]() // conversão de borda, explícita → []byteeq := a == b // '==' é igualdade de bytes; '.equals(_, .semantic)' p/ Unicodevar b := "x"; b.append("y") // mutável só com 'var'; crescimento por doublingErros: Result, catch, match |e|
Seção intitulada “Erros: Result, catch, match |e|”fn parse_port(s: string) -> Result[u16, error{Empty, NotNumber, OutOfRange}] { if s == "" { return Err(error.Empty) } // ...}
// 'catch |e|' trata o erro como unidade e ESCAPA (return/panic): propaga ou devolve um fallback:port := parse_port(input) catch |e| { return default_port() }
// 'match |e|' ramifica por variante (trailer; '|e|' é o erro), e como o catch cada arm escapa.// Variante única → use catch. Recuperar COM valor (dar default ao cfg) é match sobre o Result.cfg := load() match |e| { error.NotFound => return e // propaga (açúcar → Err(e)) error.Corrupted => runtime.panic("config ilegível") // aborta}
// Três formas, sem sobreposição: só-valores → tupla (string, string); só-erro → error-set sozinho;// valor(es) + erro → Result. Não existe retorno posicional com erro (entrou erro, é Result):fn split_pair(s: string) -> (string, string) { ... }Concorrência e channels
Seção intitulada “Concorrência e channels”// Channel tipado pela perspectiva de quem segura o handle: <-T = "envio T", ->T = "recebo T"c: Channel[<-int, ->string]spawn worker(c) // o compilador verifica que os lados são complementares
fn worker(c: Channel[<-string, ->int]) { val := -> c // recebe (bloqueia até chegar) c <- val.to_string() // envia (async, fire-and-forget)}
reply := server <-> request timeout(5s) // sync request-reply; timeout OBRIGATÓRIO (sem ele, erro)
spawn task(data) catch |e| { log(e) } // processo com handler de falhaspawn { // grupo de processos (a supervisão emerge da estrutura) indexer(docs) catch |e| { ... } notifier(subs) catch |e| { ... }}Memória: @mm, @transfer, @promote
Seção intitulada “Memória: @mm, @transfer, @promote”fn build_report() -> Report { @mm(arena) // estratégia de alocação deste escopo: arena | gc | none buf: HugeBuffer @mm(arena) = alloc_buffer() // (forma postfix fixa por-valor) defer free_arena() // @mm(none)/manual libera com defer // ...}
@mm(arena) spawn producer(data @transfer) // @transfer invalida 'data' aqui; chega no destino com seu mm@mm(gc) spawn consumer(chan) // Arena → GC via @transfer é válido
promoted := node @promote(gc, deep) // copia para outro MM (shallow é o default; 'deep' explícito)Comptime e generics
Seção intitulada “Comptime e generics”// 'type' é valor de primeira classe; um generic é uma função que recebe/devolve 'type':decl List[T] { items: *T, len: usize } // açúcar para a comptime fn abaixocomptime fn List(T: type) -> type { return decl { items: *T; len: usize }}
const nums := List[int] // chamada em compilação → tipo concreto (monomorfizado)buffer: [N]u8 // '[...]' aceita valores comptime, não só tipos
comptime { // bloco que roda no compilador assert (N <= 1024)}
const backend := use(if target.arch == x86 { return "backend_x86" } else { return "backend_wasm" })Generators
Seção intitulada “Generators”@generator fn fibonacci() -> int { var a := 0 var b := 1 loop { yield a next := a + b a = b b = next }}
loop n in fibonacci().take(10) { io.print(n) } // consumido como qualquer iterávelDesign by contract e unsafe
Seção intitulada “Design by contract e unsafe”// Contrato de fronteira: pré-condição empilha; pós-condição tem duas formas equivalentes.@requires(i < buf.len)@requires(buf.len > 0)@ensures(u8 == buf.bytes[i]) // forma diretiva (empilha, referencia o retorno por tipo)fn at(buf: Buffer, i: usize) -> u8 { // ou tersa, no tipo de retorno: '-> u8 == buf.bytes[i]' // a pré-condição contratada torna 'at' SEGURA de chamar (o compilador checa na chamada) return unsafe raw_read(buf.ptr + i) assume "ptr válido: invariante de Buffer + bounds do @requires"}
b := at(buffer, 3) // chamada safe: o compilador checa 3 < buf.len
// named return: obrigatório SÓ quando um tipo de retorno se repete e há @ensures sobre ele@ensures(lo > 0)@ensures(hi > 50)fn split(...) -> (lo: u8, hi: u8) { return (a, b) } // nomes = rótulos do slot; return livre
// Abate de operação no corpo: forte (checa, panic) vs fraca (caso-B, razão não-vazia, auditada)y := unsafe x.raw assume "reinterpretação intencional float→bits"result := unsafe { // bloco = escopo-expressão v := raw_read(p) assert (p_valid) // assert posterior enxerga o que veio antes ('p_valid') return v * 2 // valor sai por return (expressão → vai pro 'result')} assert (p_valid) // trailer compartilhado = pré-condição de entradaFFI com C
Seção intitulada “FFI com C”const c := @cimport("stdio.h") // C entra como módulo (comptime) → c.stdio@cimport("gcc") // tipos fundamentais → c.gcc.int, c.gcc.size_t (ABI do compilador)
buf: *c.gcc.char = c.stdio.malloc(n) // tudo qualificado sob 'c.'; tipo do runtime + fn do headerunsafe c.stdio.printf("%d", count) // chamar C é unsafe; variádico é caso-B (tipos por sua conta)
@repr(c) decl Point { x: c.gcc.int } // layout C-fiel na fronteira (structs internas são otimizadas)@pin handle // objeto @mm(gc) cruzando pro C exige @pin (senão não compila)mine := data @promote(gc) // memória do C (@mm(c)) repatriada pro meu GC
@callback fn cmp(a: *c.gcc.void, b: *c.gcc.void) -> c.gcc.int { ... } // fn chamável pelo CInline assembly
Seção intitulada “Inline assembly”@asm(x86, intel) // função-assembly: corpo é asm, retorna valorunsafe fn add10(v: u32 @asm(in, rax)) -> u32 @asm(out, rbx) { mov rbx, rax add rbx, 10}
var lo: u32; var hi: u32 // bloco inline: roda asm no meio de código normal@asm(x86, intel)@asm(out, rax) lo@asm(out, rdx) hi@asm(clobber, "memory") // clobbers EXPLÍCITOS no bloco (a função usa a ABI)unsafe { rdtsc }
@asm(wasm, wasm) // WASM não tem registradores: liga por param/localunsafe fn add(a: u32, b: u32) -> u32 { local.get a local.get b i32.add }Observabilidade
Seção intitulada “Observabilidade”procs := runtime.processes() // snapshot barato: processos, memória, árvore, @mm por processop := runtime.process("db_writer") // por NOME registrado (restart-estável); runtime.group("h") p/ gruposme := runtime.self() // a própria referência, de dentro de qualquer processo
@register("db_writer") // nomeia o processo (string, estável); @register("h", group) p/ gruposspawn db_writer(conn) // 'spawn' solto não registra nada (fire-and-forget, custo zero)
ev := runtime.trace(p, [.send, .receive, .channel_block], sink: .channel, sample: 100)loop e in ev { match e { ... } } // captura = ring buffer (barata); consumo via channel/callback/endpointExtensão de UI (.mkoui)
Seção intitulada “Extensão de UI (.mkoui)”@componentfn Counter(start: int) { @state n := start // estado reativo (decorator, não keyword) @derived label := "cliques: {{n}}" // recomputa quando 'n' muda fn inc() { n = n + 1 } // handler nomeado return { // markup no return block (sem implicit return) <button class="btn" onclick={inc}>{{label}}</button> // handler: { } ; texto: {{ }} <style> .btn { padding: 8px 16px; } // CSS escopado: '.btn' → '.btn_a3f8' (hashing); typo = erro </style> }}
@jsimport("dom", browser) // FFI de JS: tudo sob 'js.'; ambiente propaga @server/@client@css("tailwind.css") // CSS externo: global, nomes preservados, entra no check
@server fn load(id: int) -> Post { return db.get(id) } // @server/@client = restrição; resto atravessa// ilhas: só as partes interativas viram WASM; o resto é HTML estático// fronteira servidor/cliente = channel de rede (net.connect[T], T + Serializable)// onde WASM não roda: transpile do .mkoui pra JS via build --target=js (paridade total)Módulos e visibilidade
Seção intitulada “Módulos e visibilidade”// arquivo dentro de um projeto com mk.project na raizuse myapp/store // local: prefixo = nome do pacote (absoluto, nunca '../')use json // stdlib: bare (vem com o compilador)use github.com/acme/http as web // externo: forma URL + aliasuse json.{parse, decode} // traz nomes específicos pro escopo
priv fn helper() { ... } // visível só neste ARQUIVOfn internal() { ... } // visível no MÓDULO (default)pub fn api() { ... } // visível a outros MÓDULOS
module quickscript // (alternativa) módulo single-file transportável, checksums embutidos