Pular para o conteúdo

Plugins nativos e stdlib

Nesta página

Plugins nativos do Zolo (escritos em Rust e compilados como cdylib) têm sintaxe dedicada de importação. Prefira uma lista nomeada como use plugin binary::{BinaryReader, parse_csv_text} e chame funções livres importadas diretamente.

O wildcard registra todos os símbolos exportados como globais. Ele é útil para um prelude deliberado, mas uma lista nomeada deixa as dependências mais claras:

use plugin binary::* expõe BinaryReader e BinaryWriter como globais. Útil quando você quer toda a API do plugin sem prefixo.

07-wildcard-prelude.zolo
Playground
// Feature: wildcard `use ...::*` — imports all public names

// Syntax: `use plugin foo::*` (native plugins) or `use foo::*` (modules)

// When to use: the canonical case is importing every symbol from a

// native Zolo plugin (`binary`, `winit`, `wgpu`, etc.) when you want

// the entire API available without prefix. For typical user-land

// `.zolo` modules, prefer a named list `{a, b, c}` to avoid polluting

// the scope.


use std::string

// The `binary` plugin registers BinaryReader, BinaryWriter and helpers

// as classes. `use plugin binary::*` opts in to "register everything

// as globals" — the same shape the plugin's runtime publishes.

use plugin binary::*

fn main() {
  // 4 little-endian bytes = u32 = 7

  let raw = string::char(0x07, 0x00, 0x00, 0x00)

  // BinaryReader came from the prelude — no need to qualify.

  let r = BinaryReader.from_bytes(raw)
  print(r.read_u32_le())
  // expected: 7

}

A forma avançada use plugin foo cria um namespace (foo.Window, foo.EventLoop, ...). Adicionar {self, member} a uma lista nomeada vincula o namespace e um membro direto ao mesmo tempo:

use plugin winit cria winit.Window; use plugin crypto::{self, hash} cria tanto crypto.hash(...) quanto o atalho direto hash(...).

10-use-plugin.zolo
Playground
// Feature: `use plugin <name>` — explicit native-plugin loader

//

// Five supported forms:

//

//   use plugin foo                  → namespaced: load + move every

//                                     symbol the plugin registers

//                                     into a `foo` namespace table.

//                                     Globals are kept clean.

//   use plugin foo::*               → opt-in glob: every symbol the

//                                     plugin registers becomes a global.

//   use plugin foo::Bar             → load + `local Bar = foo.Bar`

//   use plugin foo::{a, b}          → load + per-member locals

//   use plugin foo::{self, a}       → also bind the plugin namespace itself

//

// Bare `use plugin foo` works the same way regardless of whether the

// plugin's runtime registers `<name>` as a unified namespace or as

// individual classes (Window, EventLoop, …) — the loader bridges both

// into the namespace table.


use std::crypto

use plugin winit                          // namespace — winit.Window, winit.EventLoop

use plugin wgpu                           // namespace — wgpu.*

use plugin crypto::{self, hash}           // namespace + direct hash() binding


fn main() {
  // Bare `use plugin foo` keeps the namespace; nothing leaks to

  // global scope. Members live under the plugin's name.

  print("winit.Window in scope:    {winit.Window != nil}")    // true

  print("winit.EventLoop in scope: {winit.EventLoop != nil}")  // true

  print("wgpu in scope:            {wgpu != nil}")            // true


  // `crypto` is bound by the `self` entry; `hash` by the explicit

  // member entry. Both forms reach the same underlying function.

  let digest_a = hash("sha256", "zolo")          // direct binding

  let digest_b = crypto.hash("sha256", "zolo")   // via namespace

  print("hash(\"sha256\", \"zolo\") direct = {digest_a}")
  print("crypto.hash(...)            = {digest_b}")
  print("digests match:               {digest_a == digest_b}")
}

Carrega o plugin binary como namespace e acessa funções livres, classes, métodos estáticos e métodos de instância por binary.

11-use-plugin-namespace.zolo
Playground
// Feature: bare `use plugin <name>` — the namespaced loader form.

//

// Bare `use plugin binary` loads the native `binary` cdylib AND builds a

// `binary` namespace table whose fields are every symbol the plugin

// registers (the `BinaryReader` class, the `parse_csv_text` free fn). The

// members live under `binary.*`; nothing leaks to the global scope.

//

// This is the light, deterministic sibling of `10-use-plugin.zolo` (which

// exercises the same namespace mechanism against the heavier winit/wgpu

// plugins). Backend parity target: VM = native = LLVM.

//

// Byte source is a plain ASCII string literal so the case exercises ONLY the

// plugin namespace path — no dependency on `std::string::char`, which is not

// yet bridged on the native/LLVM backends.


use plugin binary                 // namespace — binary.BinaryReader, binary.parse_csv_text


fn main() {
  // The plugin's name resolves to the namespace table, not nil.

  print("binary in scope:            {binary != nil}")
  print("binary.BinaryReader:        {binary.BinaryReader != nil}")
  print("binary.parse_csv_text:      {binary.parse_csv_text != nil}")

  // Invoke a free function THROUGH the namespace and index the nested tables

  // returned by the plugin through Zolo's 0-based array syntax.

  let rows = binary.parse_csv_text(
    "kind, value\nplanet, 42\n# ignored",
    ",",
    "#"
  )
  print("csv rows:                   {rows.len()}")
  print("csv header:                 {rows[0][0]}={rows[0][1]}")
  print("csv data:                   {rows[1][0]}={rows[1][1]}")

  // Static-method + instance-method dispatch reached THROUGH the namespace:

  // read the first byte of "A" (0x41 = 65).

  let r = binary.BinaryReader.from_bytes("ABC")
  print(r.read_u8())
  // expected: 65

}

Falhas de plugins usam o canal normal de panic do Zolo. Elas interrompem callbacks de ordem superior, executam a limpeza adiada e podem ser capturadas:

Captura um erro de plugin dentro de map e preserva a falha original quando a limpeza adiada também produz panic.

12-plugin-error-propagation.zolo
Playground
// Regression: native/LLVM plugin errors use the ordinary Zolo panic channel.

// No placeholder nil may leak into the rest of the expression, HOF callbacks

// stop immediately, and defer cleanup runs completely before catch_panic sees

// the original error.


use plugin binary

fn cleanup() {
  print("cleanup A")
  let inner = try {
    print("cleanup try")
    7
  } catch e {
    print("unexpected cleanup catch")
    -1
  }
  print("cleanup B {inner}")
}

fn failing_callback(value) {
  print("callback before {value}")
  binary.parse_csv_text("x", "", "")
  print("UNREACHABLE callback after")
  return value
}

fn fail_inside_hof() {
  defer cleanup()
  let values = [1, 2]
  let mapped = values.map(failing_callback)
  print("UNREACHABLE hof after {mapped.len()}")
}

fn fail_with_secondary_cleanup() {
  defer print("remaining cleanup")
  defer {
    print("secondary cleanup before")
    panic("secondary cleanup failure")
    print("UNREACHABLE secondary cleanup after")
  }
  binary.parse_csv_text("x", "", "")
}

fn main() {
  let caught = catch_panic(fail_inside_hof)
  print("caught={caught.ok}")
  print("after catch")

  let secondary = catch_panic(fail_with_secondary_cleanup)
  print("secondary caught={secondary.ok}")
  let kept_original = secondary.error.contains("binary: parse_csv_text empty separator")
  let replaced_by_secondary = secondary.error.contains("secondary cleanup failure")
  print("secondary kept original={kept_original}")
  print("secondary replaced original={replaced_by_secondary}")
  print("after secondary catch")
}

// expected, identically on VM/native/LLVM:

// callback before 1

// cleanup A

// cleanup try

// cleanup B 7

// caught=false

// after catch

// secondary cleanup before

// remaining cleanup

// secondary caught=false

// secondary kept original=true

// secondary replaced original=false

// after secondary catch

Para a biblioteca padrão, você pode usar o caminho totalmente qualificado std::modulo::funcao() sem nenhum use — ideal para chamadas avulsas onde importar o módulo inteiro seria ruído:

std::os::clock() funciona sem use std::os. Se você chamar os.clock() com frequência, aí vale a pena fazer use std::os e usar a forma curta.

11-qualified-std-no-use.zolo
Playground
// Feature: fully-qualified `std::` paths without `use`

// Syntax: `std::os::clock()`, `std::math::floor(x)` — no `use` needed

// When to use: a one-off stdlib call where importing the whole module

//   would be noise. Reach for `use std::M` when you call `M.foo` a lot.


// The fully-qualified form resolves on its own — note: NO `use std::os`.

let start = std::os::clock()

var count = 0
for _ in 0..100_000 {
  count += 1
}

print("count   =", count)
print("elapsed =", std::os::clock() - start)

// Contrast — the SHORT form still requires the import:

//

//   use std::os          // <- required for the line below

//   let t = os.clock()   // TE105 without the `use`

//

// So both of these reach the same function:

//   std::os::clock()   // qualified  — works with or without `use`

//   os.clock()         // short      — needs `use std::os`

//

// How it works: a compiler pass (std_path_desugar) rewrites every

// `std::M::...` value path into the same member-access the parser emits

// for `M....`, and injects a synthetic `use std::M`. So typeck (TE105),

// effect-checking, the unused-import lint, and lowering all treat the

// qualified form exactly like the imported short form. The formatter

// keeps `std::os::clock()` verbatim — it round-trips unchanged.

//

// expected:

//   count   =	100000

//   elapsed =	<small float>

Buscar no Zolo

9 resultados

enespt-br