Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ zo-benches = { path = "crates/compiler/zo-benches", version = "0.4.0" }
zo-binder = { path = "crates/compiler/zo-binder", version = "0.4.0" }
zo-buffer = { path = "crates/compiler/zo-buffer", version = "0.4.0" }
zo-bundler = { path = "crates/compiler/zo-bundler", version = "0.4.0" }
zo-checker = { path = "crates/compiler/zo-checker", version = "0.4.0" }
zo-c-abi = { path = "crates/compiler/zo-c-abi", version = "0.4.0" }
zo-codegen = { path = "crates/compiler/zo-codegen", version = "0.4.0" }
zo-codegen-arm = { path = "crates/compiler/zo-codegen-arm", version = "0.4.0" }
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ zo RESOLVES THESE COMPROMiSES BY COMPiLiNG A SiNGLE, DECLARATiVE CODEBASE TO NAT

> *« Rust makes you wait. C makes you think. zo just lets you build. » — i10e*

## status.

zo iS iN EARLY DEVELOPMENT AND NOT READY FOR PRODUCTiON YET.

REGARDiNG Ai USAGE — WE ARE USiNG Ai TO BUiLD zo BASED ON OUR ARCHiTECTURE (MADE BY HUMANS). THE COMPiLER CURRENTLY COVERS OVER 1500 UNiT AND iNTEGRATiON TESTS.

### benchmark.

| Compiler | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 | Average |
Expand All @@ -163,7 +169,7 @@ zo RESOLVES THESE COMPROMiSES BY COMPiLiNG A SiNGLE, DECLARATiVE CODEBASE TO NAT

*Workload: 503 tasks in a ring (`threadring`). A token hops node-to-node `N` times compiled to native ARM64 binary (including Hindley-Milner type inference, monomorphization, type checking, constant folding, propagation, dead code elimination and link passes).*

[@methodology-and-full-numbers](./crates/compiler/zo-benches)
- @SEE — [@methodology-and-full-numbers](./crates/compiler/zo-benches)

### our pipeline.

Expand Down Expand Up @@ -216,6 +222,8 @@ ANY iSSUES? CHECK THE iNSTALLATiON GUiDE:

THiS MONO-REPO POWERS AN ECOSYSTEM OF CRATES:

> *More crates are coming. The architecture is modular and composable. Be gentle.*

**-sources**

| NAME | DESCRiPTiON |
Expand All @@ -237,8 +245,6 @@ THiS MONO-REPO POWERS AN ECOSYSTEM OF CRATES:

...

> *More crates are coming. The architecture is modular and composable. Be gentle.*

## the manifesto.

zo iS A COMPiLER OF A COMPiLER iNSiDE ANOTHER GiANT COMPiLER THAT iS iTSELF iNSiDE A GiGANTiC COMPiLER.
Expand All @@ -255,7 +261,7 @@ WE ARE AGAiNST ABUNDANT SOFTWARE UNiFORMiTY. zo UNiFiES DESKTOP, MOBiLE AND THE

WE LOVE CONTRiBUTORS. THiS iS A PLAYGROUND FOR COMPiLER __NERDS__, FRONTEND __HACKERS__, AND __CREATIVES__.

OPEN AN iSSUE, OR COME SAY HELLO ON [discord](https://discord.gg/JaNc4Nk5xw). YOU CAN ALSO CONTACT US AT `echo -n 'dGhlQGNvbXBpbG9yZHMuaG91c2U=' | base64 --decode`.
OPEN AN [iSSUE](https://github.com/invisageable/zo/issues), A [DiSCUSSiON](https://github.com/invisageable/zo/discussions), OR COME SAY HELLO ON [discord](https://discord.gg/JaNc4Nk5xw). YOU CAN ALSO CONTACT US AT `echo -n 'dGhlQGNvbXBpbG9yZHMuaG91c2U=' | base64 --decode`.

OPEN A [DiSCUSSiON](https://github.com/invisageable/zo/discussions), iF YOU NEED MORE iNFO.

Expand Down
23 changes: 23 additions & 0 deletions apps/site/src/content/initiation/en/098-error-messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ By default the compiler renders a human snippet to stderr — the offending line
│ ╰── incompatible type `int` here
```

## warnings

Not every diagnostic stops the build. Warnings point at code that compiles but breaks a convention — an unused variable, unreachable code, or a name that does not follow zo's naming rules:

- `struct`, `enum`, `type`, and generic names are PascalCase.
- `val` constants are SCREAMING_SNAKE_CASE.
- everything else — `imu`/`mut` bindings, `fun` names and arguments, struct fields, `abstract` functions — is snake_case.

Each naming warning carries the convention-correct rename as its help, so the fix is always one copy-paste away:

```text
[E0355] Warning • Name is not snake_case
╭─[ counter.zo:2:7 ]
2 │ imu MyCount := 1;
│ ───┬───
│ ╰───── expected a snake_case name
│ Help • rename it to `my_count`
```

A leading underscore opts a binding out (`_unused`), and digits never need a separator (`r0`, `grid2`, `MAX2` are all fine). The program builds and runs regardless — warnings inform, errors stop.

## machine formats

An agent reads text differently than you do — it never skims and it is never overwhelmed by length. So zo offers two machine formats that carry the *full* diagnostic, not a terse summary. Both stream to stdout, leaving stderr for you.
Expand Down
18 changes: 18 additions & 0 deletions apps/site/src/content/initiation/en/100-faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## basics

**How do I pronounce the name?**

zo is pronounced `/zuː/` just like "zoo".

**I'm new to zo. Where should I start?**

The quick way is to do the [initiation](https://zo.compilords.house/initiation) to get a full understanding of specific concept.
Expand All @@ -20,6 +24,12 @@ There are some extensions available depending of your preferences:

- [VS Code](https://github.com/invisageable/zo/tree/main/crates/compiler/zo-vscode)

## usage

**Can I use zo for servers?**

...

## comptime

**How does zo achieve sub-second compilation speeds?**
Expand Down Expand Up @@ -76,3 +86,11 @@ zo run app.zo --target watchos --device "Apple Watch Ultra 3 (49mm)"
```

If a device name does not match the target platform, the error lists every device on your machine able to run the app.

## comparison

**How does zo compare to Rust?**

zo is faster at build time, if you care about your feedback loop, zo can definetely be the best choice. Also regarding the friction, zo adopt simplicity — no borrow checker, no lifetime. You'll get a better concurrent system à la Go, Swift or Erlang (in a sense).

In terms of runtime, Rust is faster than zo. We planned to improve our runtime performance to be at least close to C (clang).
16 changes: 16 additions & 0 deletions crates/compiler/zo-checker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "zo-checker"
version.workspace = true
edition.workspace = true

[lib]
doctest = false

[dependencies]
# internal:sources.
swisskit-core = { workspace = true }

# internal:crates.
zo-error = { workspace = true }
zo-reporter = { workspace = true }
zo-span = { workspace = true }
44 changes: 44 additions & 0 deletions crates/compiler/zo-checker/src/checker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! The checker pilot — the one entry point the executor drives.

pub mod name_checker;

use name_checker::NameChecker;

use zo_span::Span;

/// Pilots every individual checker.
///
/// @note — the executor owns one and forwards declaration events
/// through these methods; each sub-checker decides whether a warning
/// is due and reports it through `zo-reporter`'s warning channel.
#[derive(Debug, Default)]
pub struct Checker {
/// Naming-convention checks — PascalCase types,
/// SCREAMING_SNAKE_CASE constants, snake_case bindings.
name_checker: NameChecker,
}

impl Checker {
/// Creates a new [`Checker`] instance.
pub fn new() -> Self {
Self::default()
}

/// Checks a `struct`/`enum`/`type`/generic name — PascalCase.
pub fn check_type_name(&self, name: &str, span: Span, file_id: u16) {
self.name_checker.check_type_name(name, span, file_id);
}

/// Checks a `val` constant name — SCREAMING_SNAKE_CASE.
pub fn check_constant_name(&self, name: &str, span: Span, file_id: u16) {
self.name_checker.check_constant_name(name, span, file_id);
}

/// Checks a binding-position name — snake_case.
///
/// @note — binding positions: `imu`/`mut`, `fun` names and
/// arguments, struct fields, `abstract` functions.
pub fn check_binding_name(&self, name: &str, span: Span, file_id: u16) {
self.name_checker.check_binding_name(name, span, file_id);
}
}
64 changes: 64 additions & 0 deletions crates/compiler/zo-checker/src/checker/name_checker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! Naming-convention checks.

use zo_error::{Error, ErrorKind};
use zo_reporter::report_error_with_rename;
use zo_span::Span;

use swisskit_core::{is, to};

/// Warns when a declared name breaks its site's naming convention.
///
/// @note — types are PascalCase, `val` constants are
/// SCREAMING_SNAKE_CASE, every other binding is snake_case. Each
/// warning carries the convention-correct rename as its fix.
#[derive(Debug, Default)]
pub struct NameChecker;

impl NameChecker {
/// Checks a type-position name against PascalCase.
pub fn check_type_name(&self, name: &str, span: Span, file_id: u16) {
let Some(name) = checkable(name) else { return };

if !is!(pascal name) {
report_error_with_rename(
Error::with_file(ErrorKind::NonPascalCaseName, span, file_id),
&to!(pascal name),
);
}
}

/// Checks a `val` constant name against SCREAMING_SNAKE_CASE.
pub fn check_constant_name(&self, name: &str, span: Span, file_id: u16) {
let Some(name) = checkable(name) else { return };

if !is!(snake_screaming name) {
report_error_with_rename(
Error::with_file(ErrorKind::NonScreamingCaseName, span, file_id),
&to!(snake_screaming name),
);
}
}

/// Checks a binding-position name against snake_case.
pub fn check_binding_name(&self, name: &str, span: Span, file_id: u16) {
let Some(name) = checkable(name) else { return };

if !is!(snake name) {
report_error_with_rename(
Error::with_file(ErrorKind::NonSnakeCaseName, span, file_id),
&to!(snake name),
);
}
}
}

/// The convention-relevant part of a name; `None` opts it out.
///
/// @note — leading underscores (the deliberate-unused marker) and
/// the generic `$` sigil carry no case, so they are stripped; a name
/// of only those characters (`_`, `__`) is exempt.
fn checkable(name: &str) -> Option<&str> {
let name = name.trim_start_matches(['_', '$']);

if name.is_empty() { None } else { Some(name) }
}
14 changes: 14 additions & 0 deletions crates/compiler/zo-checker/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Convention checks the executor runs as declarations execute.
//!
//! [`Checker`] pilots every individual checker (naming today). The
//! executor owns one and forwards each declared name to it, so check
//! implementations live here instead of inside the execution loop.
//! Checks report through `zo-reporter`'s warning channel — they never
//! stop compilation.

pub mod checker;

pub use checker::Checker;

#[cfg(test)]
mod tests;
6 changes: 6 additions & 0 deletions crates/compiler/zo-checker/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! ```sh
//! cargo test -p zo-checker
//! ```

pub(crate) mod common;
pub(crate) mod naming;
24 changes: 24 additions & 0 deletions crates/compiler/zo-checker/src/tests/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Shared helpers for checker tests.
//!
//! The reporter is thread-local, so every test drains it after
//! driving the checker — no cross-test interference as long as each
//! test clears what it reports.

use zo_error::ErrorKind;
use zo_reporter::{Detail, collect_diagnostics};

/// The rename detail attached to the single buffered warning —
/// `None` when nothing was reported.
pub(crate) fn drained_rename() -> Option<(ErrorKind, String)> {
let (errors, details) = collect_diagnostics();

let rename = details.iter().find_map(|(error, detail)| match detail {
Detail::Rename(name) => Some((error.kind(), name.to_string())),
_ => None,
});

match rename {
Some(rename) => Some(rename),
None => errors.first().map(|error| (error.kind(), String::new())),
}
}
Loading
Loading