In questa sezione

lungo si configura in due posti che descrivono le stesse impostazioni:

  • lungo.toml, letto dal comando lungo, per ogni linguaggio di output;
  • un lungo_build::Builder in un build.rs di Cargo, per l’output Rust (compile_lean(project) equivale a configure().compile_lean(project)).

Ogni impostazione ha un valore predefinito. Le chiavi sconosciute sono errori (LNG0107).

# lungo.toml
project = "lean"

[lean]
root-modules = ["Formal.Session"]
assurance-modules = ["Formal.Laws"]

[assurance]
require-claims = ["Formal.Session"]

[rust]
out-dir = "src/generated"
type-attributes = [
  { path = "Formal", attribute = "#[derive(serde::Serialize, serde::Deserialize)]" },
]
rust-externs = { host_log = "crate::host::log" }

[go]
out = "gen/formal"
options = { package = "formal" }

[plugins.kotlin]
out = "gen/kotlin"
// build.rs
fn main() -> lungo_build::Result<()> {
    lungo_build::configure()
        .root_module("Formal.Session")
        .assurance_module("Formal.Laws")
        .require_claims("Formal.Session")
        .type_attribute("Formal", "#[derive(serde::Serialize, serde::Deserialize)]")
        .rust_extern("host_log", "crate::host::log")
        .compile_lean("lean")
}

Il file

Chiave Significato
project La directory del progetto Lake. Obbligatoria.
[lean] Le impostazioni condivise da tutti i linguaggi: che cosa viene compilato ed esportato, i moduli letti per i loro record di garanzia, la politica di fiducia, il nome del programma, il worker, la toolchain.
[assurance] La politica di garanzia: gli export che devono avere un claim dimostrato, e le assunzioni su cui nulla può poggiare.
[rust] Le impostazioni del generatore Rust.
[c], [go], [python], [swift], [ts] La directory di output di un generatore integrato, le sue opzioni e i suoi tipi extern.
[plugins.<name>] La directory di output e le opzioni di un plugin. <name> è composto da lettere minuscole, cifre e -, e non è il nome di un generatore integrato.

I percorsi nel file (project, out, out-dir, toolchain-dir) sono relativi alla directory del file. lungo tiene i file di lavoro in .lungo/ accanto al file; aggiungila a .gitignore.

Programma

Si impostano in [lean], oppure con i metodi del Builder:

Builder Chiave TOML Tipo Predefinito Significato
root_module(m) root-modules lista di nomi di modulo le radici dei target predefiniti Moduli del progetto il cui codice viene compilato, con tutto ciò che importano. Ciascuno deve appartenere al pacchetto radice del progetto.
export(d) exports lista di nomi di dichiarazione [] Dichiarazioni che ricevono funzioni pubbliche.
export_module(m) export-modules lista di nomi di modulo [] Moduli (con i loro sottomoduli) le cui definizioni pubbliche ricevono funzioni pubbliche.
assurance_module(m) assurance-modules lista di nomi di modulo [] Moduli caricati soltanto per i record di garanzia che dichiarano, come leggi dimostrate a parte rispetto al codice di cui parlano. Vengono importati nell’ambiente che lungo legge ma mai compilati nel programma, quindi non cambiano nulla di ciò che il programma fa. Ciascuno deve appartenere a un pacchetto del workspace Lake e non essere un modulo radice.
name(n) name stringa il nome del pacchetto Lake Il nome del programma: del modulo generato, del pacchetto e del prefisso dei simboli. Lettere, cifre, _ e -, senza iniziare con una cifra o con -.

Quali extern implementa l’host non è un’impostazione: li dichiara il codice Lean del programma, come operazioni di facility (@[lungo_facility] e @[lungo_operation]; vedi come chiamare codice dell’host da Lean). Ogni pacchetto generato chiede all’host ogni operazione di ogni facility dichiarata dai moduli collegati del programma. La chiave extern di un’operazione è il simbolo del suo @[extern "symbol"], oppure il nome della dichiarazione per le altre forme di extern.

Senza root_module, le radici sono ciò che compila lake build: i moduli radice di ogni target predefinito del pacchetto radice (defaultTargets in lakefile.toml, @[default_target] in lakefile.lean), che sono librerie Lean (le loro roots) o eseguibili Lean (il loro root). Un pacchetto senza target predefiniti richiede root_module (LNG0301).

Senza export ed export_module, vengono esportati i moduli radice e i loro sottomoduli. Da un modulo esportato si esportano le definizioni, tranne quelle private, interne (componenti con prefisso _), le istanze e i loro metodi, quelle generate dal compilatore (ricorsori, matcher, noConfusion, proiezioni, costruttori, ausiliari strutturali), i tipi di nodo sintattico (syntax, notation, macro) e le definizioni meta.

Politica di fiducia

Si impostano in [lean]:

Builder Chiave TOML Predefinito Rifiuta un export che…
deny_sorry(b) deny-sorry true dipende da sorry
deny_axioms(b) deny-axioms false dipende da un assioma diverso da propext, Classical.choice, Quot.sound
deny_unsafe(b) deny-unsafe false ha una definizione unsafe nella sua chiusura eseguibile (fuori dalla toolchain di Lean)

deny_sorry e deny_axioms si applicano anche all’evidenza di ogni claim (il teorema che nomina): un claim dimostrato con sorry, o con un assioma che la politica nega, viene rifiutato come lo sarebbe un export. Con deny-sorry = false, un tale claim viene riportato con stato incomplete, e non conta come claim dimostrato per require-claims.

Le violazioni fanno fallire la build con LNG0601.

Politica di garanzia

Si impostano in [assurance], oppure con i metodi del Builder. lungo controlla prima i record di garanzia del programma (la loro forma, le dichiarazioni che nominano, la forma di ogni claim); un problema in uno di essi fa fallire la build qualunque sia la politica, con un codice proprio (LNG0701–LNG0710). Poi:

Builder Chiave TOML Predefinito Significato
require_claims(path) require-claims [] Ogni export selezionato dal percorso deve essere soggetto di almeno un claim dimostrato (LNG0602 altrimenti). . seleziona ogni export; ogni altro percorso seleziona l’export che nomina e ogni export al suo interno come namespace (Formal seleziona Formal.apply, Formal.Se non seleziona nulla).
forbid_assumption(name) forbid-assumptions [] Nessun claim dimostrato può assumere l’assunzione name; se name è una facility, nessun claim può assumere una delle sue assunzioni e nessun export può chiamare una delle sue operazioni (LNG0603 altrimenti).

Un percorso che non seleziona alcun export, o un nome che non è né un’assunzione né una facility del programma, è un errore (LNG0107): non potrebbe avere alcun effetto.

La politica fa parte di ciò da cui un output viene generato: renderla più stringente fa sì che lungo generate controlli di nuovo un output invece di considerarlo aggiornato.

Worker

Si impostano in [lean]:

Builder Chiave TOML Predefinito Significato
worker_timeout(d) worker-timeout nessuno Secondi di tempo reale dopo i quali i processi del worker vengono terminati (LNG0304).
worker_cpu_limit(d) worker-cpu-limit nessuno Secondi di processore che il worker può usare (LNG0305).
worker_memory_limit(bytes) worker-memory-limit nessuno Memoria che il worker può usare. Applicato su Linux e Windows; un errore (LNG0104) altrove.
hermetic(b) hermetic false Esegue il worker con soltanto PATH, HOME, USERPROFILE, SystemRoot, SYSTEMROOT, TEMP, TMP, TMPDIR, LANG, LC_ALL, ELAN_HOME, LOCALAPPDATA, APPDATA dell’ambiente, più l’ambiente di Lake per il progetto.
hermetic_worker_cache(b) hermetic-worker-cache false Tiene il worker compilato accanto all’output della build invece che nella cache condivisa.
lean_option(name, value) lean-options {} Opzioni di Lean in vigore mentre il worker carica il progetto ed esegue metaprogrammi Lean.
max_errors(n) max-errors 0 Riporta al massimo n errori del worker; 0 li riporta tutti.

Toolchain

Si impostano in [lean]:

Builder Chiave TOML Predefinito Significato
toolchain_policy(p) install-toolchain Strict / false Install (true) installa con elan una toolchain fissata mancante. Strict (false) fallisce con LNG0103.
toolchain_dir(dir) toolchain-dir l’installazione di elan Usa la toolchain installata in dir.

Rust

Si impostano in [rust], oppure con i metodi del Builder:

Builder Chiave TOML Predefinito Significato
mode(m) mode pure-rust pure-rust genera Rust sul runtime di lungo. lean-oracle esegue il backend nativo di Lean dietro la stessa API (vedi piattaforme).
rust_extern(key, path) rust-externs {} La funzione Rust che implementa ciascuna operazione delle facility del programma: chiave extern → percorso Rust. Ogni operazione dichiarata dal programma ne ha una (LNG0401 altrimenti). Una chiave che è un extern ma non un’operazione viene rifiutata (LNG0706), e una chiave che nessun extern usa è inutilizzata (LNG0405).
out_dir(d) out-dir $OUT_DIR/lungo La directory che riceve <name>/. In build.rs è relativa al pacchetto Cargo, e include_lean! trova solo quella predefinita (usa include! per un’altra). Per lungo generate, l’output Rust (--rust_out la sostituisce).
facade_namespace(n) facade-namespace il primo componente del primo modulo radice Il namespace Lean posto alla radice del modulo generato. I nomi al di fuori vengono posti sotto _root_.
embed_sources(b) embed-sources false Incorpora i sorgenti Lean locali, disponibili tramite __meta::source.
emit_rerun_if_changed(b) emit-rerun-if-changed se la build gira sotto Cargo Stampa cargo::rerun-if-changed per ogni input, e cargo::rerun-if-env-changed per le variabili d’ambiente.

Uno script di build può compilare più progetti Lean; ciascuno genera il proprio modulo, e due che genererebbero lo stesso modulo sono un errore (LNG0107). Dai a uno dei due un altro name.

Modellare il codice generato

Queste impostazioni selezionano elementi generati tramite un percorso. Il percorso . seleziona ogni elemento; qualsiasi altro percorso seleziona il nome Lean che compone e ogni nome al suo interno inteso come namespace: Formal seleziona Formal.Sess e Formal.Sess.isOpen, ma Formal.Se non seleziona nessuno dei due. I campi si indicano come in names.json: <Structure>.<field>, <Constructor>.<binder> oppure <Constructor>#<index>. Un percorso diverso da . che non seleziona nulla è un errore (LNG0107): non può avere alcun effetto.

Builder Chiave TOML Predefinito Significato
type_attribute(path, attr) type-attributes [] Aggiunge l’attributo Rust attr a ogni struct ed enum generato selezionato da path.
struct_attribute(path, attr) struct-attributes [] Lo stesso, solo per le struct.
enum_attribute(path, attr) enum-attributes [] Lo stesso, solo per gli enum.
field_attribute(path, attr) field-attributes [] Aggiunge attr a ogni campo di un tipo generato selezionato da path.
skip_debug(paths) skip-debug [] Genera i tipi selezionati senza #[derive(Debug)], perché l’applicazione implementi Debug.
disable_comments(paths) disable-comments [] Genera i tipi e le funzioni selezionati senza commenti di documentazione.
extern_type(lean, path) extern-types {} Usa il tipo Rust esistente in path per il tipo Lean lean invece di generarne uno.

In TOML, le impostazioni degli attributi sono liste di tabelle { path, attribute }, skip-debug e disable-comments liste di percorsi, ed extern-types una tabella da tipo Lean a percorso Rust.

Gli attributi si scrivono come nel sorgente Rust, #[…], e vengono posti dopo le derivazioni generate da lungo (Clone, Debug, e PartialEq, Eq, Hash dove ogni campo le ha), quindi possono essere #[derive(…)] di altri trait o attributi di quelle derivazioni, come #[serde(…)]. La feature serde del crate lungo implementa serde per i tipi della facciata.

Un tipo extern implementa lungo::LeanType per il backend in uso, ha i parametri del tipo Lean e rappresenta i suoi valori come fa il tipo Lean; in pratica è il tipo che un’altra build di lungo ha generato per lo stesso tipo Lean. Non viene generato, e i tipi che lo contengono derivano solo Clone e Debug. Implementa anche lungo::LeanLayout con l’impronta del layout del tipo Lean, che il modulo generato controlla quando viene compilato (vedi tipi extern); un tipo generato da lungo la implementa già. Vedi Come condividere tipi tra progetti Lean.

Opzioni dei generatori

La tabella di un linguaggio contiene la sua directory di output, out, e le sue opzioni, options, una tabella di stringhe. --<language>_opt=KEY=VALUE dalla riga di comando le integra (e le sostituisce). Un generatore rifiuta le opzioni che non conosce.

Linguaggio Opzione Predefinito Significato
c embed assente true: una directory del progetto CMake dell’host invece di un progetto a sé (incorporato).
go package l’identificatore C del programma, in minuscolo Il nome del pacchetto Go.
python package l’identificatore C del programma, in minuscolo Il nome di import.
python distribution package con _ al posto di - Il nome della distribuzione (PyPI).
python version 0.1.0 La versione della distribuzione.
python embed assente Un modulo puntato del pacchetto dell’host (host.formal, oppure host._formal per uno privato): l’output è quel modulo, incorporato, senza una distribuzione propria. Esclude package, distribution e version.
swift module il nome del programma in UpperCamelCase Il modulo Swift; il target C è <module>Program.
swift embed assente true: i due target che il pacchetto dell’host deve dichiarare, senza Package.swift (incorporato).
ts package il nome del programma, in minuscolo Il nome del pacchetto npm.
ts version 0.1.0 La versione del pacchetto.
ts embed assente true: un modulo del pacchetto dell’host, senza package.json (incorporato). Esclude package e version.

Go è sempre un pacchetto del modulo dell’host; non ha embed. Vedi pacchetti generati per ciò che scrive ciascun generatore.

Tipi extern

Un programma costruito sul modello di un altro programma (un pacchetto Lake che ne richiede un altro) usa i tipi di quel programma. Generati separatamente, ogni pacchetto avrebbe il proprio tipo per essi, e un valore dell’uno non potrebbe essere passato all’altro. La tabella extern-types di un linguaggio indica, per un tipo Lean, il tipo dell’altro pacchetto generato da usare al suo posto:

[go.extern-types."TaggedUrn.Exec.Wf"]
package = "github.com/machinefabric/tagged-urn-go/formal"   # ciò che un import indica
name = "Wf"                                                 # il tipo, come lo ha chiamato quel generatore
Linguaggio package name
c l’header del pacchetto che lo fornisce (provider.h) il prefisso dei suoi elementi per il tipo (provider_pos)
go il percorso di import il tipo Go
python il modulo la classe
swift il modulo il tipo
ts lo specificatore di modulo il tipo

Il tipo deve essere usato dalle funzioni o dalle operazioni delle facility del programma (LNG0107 altrimenti). I valori passano allora tra i due pacchetti così come sono: i dati per valore, e i valori tenuti tramite handle come lo stesso handle, poiché ogni pacchetto di un processo gira sull’unico runtime.

In TypeScript ogni programma gira in una propria istanza WebAssembly, con una propria tabella di handle, quindi possono essere tipi extern solo i tipi i cui valori sono dati; un tipo i cui valori contengono un handle (un tipo opaco, una funzione) viene rifiutato indicandone il motivo.

Entrambi i pacchetti devono essere stati generati dalla stessa definizione del tipo, altrimenti un programma leggerebbe i valori dell’altro con il layout sbagliato. Ogni tipo generato porta l’impronta del suo layout (un digest dei suoi costruttori, dei loro campi e di ogni tipo che raggiungono, insieme alla versione di Lean), e un pacchetto che usa un tipo extern controlla quella del pacchetto che lo fornisce prima che qualcosa venga eseguito: in fase di compilazione in Rust, al caricamento del pacchetto in Go (init), Python (import), Swift e C (il primo uso del programma), e in TypeScript (la valutazione del modulo). Una discrepanza indica il tipo e il pacchetto; rigenerali entrambi dalla stessa definizione Lean.

Variabili d’ambiente

Variabile Significato
ELAN_HOME Dove elan tiene le toolchain (predefinito ~/.elan).
LUNGO_CACHE_DIR Radice della cache di lungo: i worker compilati in workers/, i runtime scaricati in runtime/, il wasi-sdk in wasi-sdk/. Predefiniti: $XDG_CACHE_HOME/lungo, ~/.cache/lungo oppure %LOCALAPPDATA%\lungo.
WASI_SDK_PATH Un wasi-sdk con cui collegare il WebAssembly del binding TypeScript, al posto della release fissata che lungo scarica.

Punti di ingresso

Funzione Uso
compile_lean(project) In build.rs, con la configurazione predefinita.
configure() Un Builder con la configurazione predefinita.
Builder::compile_lean(self, project) In build.rs. Genera in <out_dir>/<name> e stampa le direttive di Cargo: rerun-if-changed (vedi emit_rerun_if_changed) e, in modalità LeanOracle, le direttive di collegamento. project è relativo al pacchetto Cargo.
Builder::run(&self, project, &Environment) La stessa pipeline per un dato Environment; restituisce un BuildOutcome.
Builder::analyze / Builder::generate Le due metà di run: l’analisi del worker, poi la generazione del codice, senza pubblicare.
Builder::from_options(lean, rust, assurance) Un Builder a partire da LeanOptions ([lean]), RustOptions ([rust]) e AssurancePolicy ([assurance]).