init
This commit is contained in:
commit
34e45e702f
16 changed files with 1109 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
use flake
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.direnv/
|
||||
370
Cargo.lock
generated
Normal file
370
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "ar_archive_writer"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
|
||||
dependencies = [
|
||||
"object",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ba4a05c9ce83b07de31b31c874e87c069881ac4355db9e752e3a55c11ec75a6"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"regex-automata 0.3.9",
|
||||
"serde",
|
||||
"stacker",
|
||||
"unicode-ident",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jackal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chumsky",
|
||||
"logos",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.185"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
|
||||
|
||||
[[package]]
|
||||
name = "logos"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2c55a318a87600ea870ff8c2012148b44bf18b74fad48d0f835c38c7d07c5f"
|
||||
dependencies = [
|
||||
"logos-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logos-codegen"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58b3ffaa284e1350d017a57d04ada118c4583cf260c8fb01e0fe28a2e9cf8970"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex-automata 0.4.14",
|
||||
"regex-syntax 0.8.10",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "logos-derive"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52d3a9855747c17eaf4383823f135220716ab49bea5fbea7dd42cc9a92f8aa31"
|
||||
dependencies = [
|
||||
"logos-codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8"
|
||||
dependencies = [
|
||||
"ar_archive_writer",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "jackal"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
chumsky = "0.12.0"
|
||||
logos = "0.16.1"
|
||||
winnow = "1.0.1"
|
||||
42
README.md
Normal file
42
README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
## Basic hello world
|
||||
|
||||
```c
|
||||
open std::io;
|
||||
|
||||
// const { io } = import!("std");
|
||||
pub main := fn() -> () {
|
||||
io.println("Hello, world");
|
||||
io.Writer
|
||||
iterator.map(color::Color::to_bytes)
|
||||
};
|
||||
```
|
||||
|
||||
## Structured data
|
||||
|
||||
```c
|
||||
Color ::= Color { red: u8, green: u8, blue: u8 };
|
||||
```
|
||||
|
||||
## Union types
|
||||
|
||||
```c
|
||||
Result<T, E> ::= Ok (T) | Err (E);
|
||||
|
||||
Option<T> ::= Some (T) | None;
|
||||
|
||||
MaybeColor ::= SomeColor { red: u8, green: u8, blue: u8 } | None;
|
||||
```
|
||||
|
||||
# Symbol Semantics
|
||||
|
||||
- `=` => Equal by assignment (or reassignment)
|
||||
- `:=` => Equal by definition
|
||||
- `::=` => Equal in structure or type
|
||||
- `//` => in-line comment
|
||||
- `///` => in-line doc comment
|
||||
- `//!` => in-line module doc comment
|
||||
- `|` => type union
|
||||
|
||||
## Classes
|
||||
|
||||
Same thing as a Haskell typeclass or Rust trait.
|
||||
17
examples/main.jkl
Normal file
17
examples/main.jkl
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//! Module-level documentation
|
||||
|
||||
open std::io;
|
||||
|
||||
Result<T, E> ::= Ok (T) | Err (E);
|
||||
|
||||
/// This just logs a thing
|
||||
log := fn(data ::= Display) {
|
||||
io::println("[info] {}", data);
|
||||
};
|
||||
|
||||
// we need a main function
|
||||
pub main := fn() -> () {
|
||||
io::println("Hello, world");
|
||||
data := [ 1, 2, 3 ];
|
||||
strings := data.map(Display::fmt).collect::<List<...>();
|
||||
};
|
||||
77
flake.lock
generated
Normal file
77
flake.lock
generated
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775087534,
|
||||
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"import-tree": {
|
||||
"locked": {
|
||||
"lastModified": 1773693634,
|
||||
"narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=",
|
||||
"owner": "vic",
|
||||
"repo": "import-tree",
|
||||
"rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "vic",
|
||||
"repo": "import-tree",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1776169885,
|
||||
"narHash": "sha256-l/iNYDZ4bGOAFQY2q8y5OAfBBtrDAaPuRQqWaFHVRXM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1774748309,
|
||||
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"import-tree": "import-tree",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
11
flake.nix
Normal file
11
flake.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
import-tree.url = "github:vic/import-tree";
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./nix-modules);
|
||||
}
|
||||
245
nix-modules/packages/boilerplate/boilerplate.py
Normal file
245
nix-modules/packages/boilerplate/boilerplate.py
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
KIND_LAYOUTS = {
|
||||
"layer": Path("nix-modules/features/layers"),
|
||||
"feature": Path("nix-modules/features"),
|
||||
"host": Path("nix-modules/hosts"),
|
||||
"package": Path("nix-modules/packages"),
|
||||
}
|
||||
|
||||
|
||||
NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$")
|
||||
|
||||
|
||||
def nix_string(value: str) -> str:
|
||||
return json.dumps(value)
|
||||
|
||||
|
||||
def git_root() -> Path | None:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
return Path(result.stdout.strip())
|
||||
|
||||
|
||||
def host_templates(name: str) -> dict[Path, str]:
|
||||
return {
|
||||
Path("default.nix"):
|
||||
textwrap.dedent(
|
||||
f"""\
|
||||
{{ self, inputs, ... }}:
|
||||
{{
|
||||
flake.nixosConfigurations.{name} = inputs.nixpkgs.lib.nixosSystem {{
|
||||
system = builtins.currentSystem;
|
||||
modules = [
|
||||
self.nixosModules.{name}Configuration
|
||||
];
|
||||
}};
|
||||
}}
|
||||
"""
|
||||
),
|
||||
Path("configuration.nix"):
|
||||
textwrap.dedent(
|
||||
f"""\
|
||||
{{ self, inputs, ... }}:
|
||||
{{
|
||||
flake.nixosModules.{name}Configuration =
|
||||
{{ pkgs, lib, ... }}:
|
||||
{{
|
||||
imports = [
|
||||
self.nixosModules.{name}Hardware
|
||||
];
|
||||
system.stateVersion = "25.11";
|
||||
}};
|
||||
}}
|
||||
"""
|
||||
),
|
||||
Path("hardware-configuration.nix"):
|
||||
textwrap.dedent(
|
||||
f"""\
|
||||
{{ self, inputs, ... }}:
|
||||
{{
|
||||
flake.nixosModules.{name}Hardware =
|
||||
{{ lib, pkgs, ... }}:
|
||||
{{
|
||||
config = {{
|
||||
# If you're building a full system here, you'd likely want to just copy the hardware
|
||||
# configuration generated by the NixOS installer. I've added some stuff here just so
|
||||
# `nix flake check` passes on initialization of the flake and to show how it _could_
|
||||
# look. You can find it here: `/etc/nixos/hardware-configuration.nix`
|
||||
|
||||
boot.loader.grub.devices = [ "/dev/sda" ];
|
||||
|
||||
fileSystems."/" = {{
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
}};
|
||||
}};
|
||||
}};
|
||||
}}
|
||||
"""
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def template(kind: str, name: str) -> str:
|
||||
if kind in {"layer", "feature"}:
|
||||
return textwrap.dedent(
|
||||
f"""\
|
||||
{{ self, inputs, ... }}:
|
||||
{{
|
||||
flake.nixosModules.{name} = {{ pkgs, lib, ... }}:
|
||||
{{
|
||||
config = {{
|
||||
}};
|
||||
}};
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
if kind == "host":
|
||||
quoted_name = nix_string(name)
|
||||
return textwrap.dedent(
|
||||
f"""\
|
||||
{{ self, inputs, ... }}:
|
||||
{{
|
||||
flake.nixosModules.{name} = {{ pkgs, lib, ... }}:
|
||||
{{
|
||||
config = {{
|
||||
networking.hostName = {quoted_name};
|
||||
# system.stateVersion = "25.11";
|
||||
}};
|
||||
}};
|
||||
|
||||
flake.nixosConfigurations.{name} = inputs.nixpkgs.lib.nixosSystem {{
|
||||
modules = [
|
||||
self.nixosModules.{name}
|
||||
];
|
||||
}};
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
if kind == "package":
|
||||
quoted_name = nix_string(name)
|
||||
return textwrap.dedent(
|
||||
f"""\
|
||||
{{ self, inputs, ... }}:
|
||||
{{
|
||||
perSystem =
|
||||
{{ pkgs, ... }}:
|
||||
{{
|
||||
packages.{name} = pkgs.writeShellApplication {{
|
||||
name = {quoted_name};
|
||||
runtimeInputs = [ ];
|
||||
text = ''
|
||||
echo "TODO: implement {name}"
|
||||
'';
|
||||
}};
|
||||
}};
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
raise ValueError(f"Unsupported kind: {kind}")
|
||||
|
||||
|
||||
def planned_files(kind: str, name: str, multifile: bool) -> dict[Path, str]:
|
||||
if kind == "host":
|
||||
return host_templates(name)
|
||||
|
||||
target = Path("default.nix") if multifile else Path(f"{name}.nix")
|
||||
return {target: template(kind, name)}
|
||||
|
||||
|
||||
def write_file(path: Path, content: str) -> None:
|
||||
if path.exists():
|
||||
raise FileExistsError(f"Refusing to overwrite existing path: {path}")
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(content.rstrip() + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def stage(paths: list[Path], root: Path) -> None:
|
||||
subprocess.run(
|
||||
["git", "add", "--", *[str(path.relative_to(root)) for path in paths]],
|
||||
check=True,
|
||||
cwd=root,
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Generate Nix boilerplate modules")
|
||||
parser.add_argument("kind", choices=sorted(KIND_LAYOUTS))
|
||||
parser.add_argument("name")
|
||||
parser.add_argument("--no-git-stage", action="store_true", help="Do not stage created files")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print planned files without creating them")
|
||||
parser.add_argument(
|
||||
"--multifile",
|
||||
action="store_true",
|
||||
help="Create <name>/default.nix instead of <name>.nix",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
|
||||
if not NAME_RE.fullmatch(args.name):
|
||||
print(
|
||||
"name must match ^[A-Za-z0-9][A-Za-z0-9_.-]*$",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
root = git_root()
|
||||
if root is None:
|
||||
if args.dry_run or args.no_git_stage:
|
||||
root = Path.cwd()
|
||||
else:
|
||||
print("not inside a git repository; use --no-git-stage", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
files = planned_files(args.kind, args.name, args.multifile)
|
||||
base = root / KIND_LAYOUTS[args.kind]
|
||||
target_base = base / args.name if args.kind == "host" else base
|
||||
targets = [target_base / rel for rel in files]
|
||||
|
||||
if args.dry_run:
|
||||
print("dry run: no files will be created")
|
||||
for target in targets:
|
||||
print(target.relative_to(root))
|
||||
return 0
|
||||
|
||||
try:
|
||||
for rel_path, content in files.items():
|
||||
write_file(target_base / rel_path, content)
|
||||
except FileExistsError as err:
|
||||
print(err, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if not args.no_git_stage:
|
||||
stage(targets, root)
|
||||
|
||||
for target in targets:
|
||||
print(target.relative_to(root))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
32
nix-modules/packages/boilerplate/default.nix
Normal file
32
nix-modules/packages/boilerplate/default.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{ self, inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
packages.boilerplate = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "boilerplate";
|
||||
version = "0.1.0";
|
||||
|
||||
src = ./.;
|
||||
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
substituteInPlace boilerplate.py \
|
||||
--replace-fail '#!/usr/bin/env python3' '#!${pkgs.python3}/bin/python3'
|
||||
install -Dm755 boilerplate.py "$out/bin/boilerplate"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Generate boilerplate Nix modules";
|
||||
license = lib.licenses.mit;
|
||||
mainProgram = "boilerplate";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
10
nix-modules/parts.nix
Normal file
10
nix-modules/parts.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
config = {
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-linux"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
||||
26
nix-modules/shell.nix
Normal file
26
nix-modules/shell.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{ ... }:
|
||||
{
|
||||
perSystem =
|
||||
{ pkgs, self', ... }:
|
||||
{
|
||||
# An example devshell with some Rust and Nix tools
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
bacon
|
||||
cargo
|
||||
rust-analyzer
|
||||
rustc
|
||||
rustfmt
|
||||
clippy
|
||||
glibc
|
||||
sea-orm-cli
|
||||
nixfmt
|
||||
nil
|
||||
alejandra
|
||||
self'.packages.boilerplate
|
||||
];
|
||||
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||
env.RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||
};
|
||||
};
|
||||
}
|
||||
6
src/main.rs
Normal file
6
src/main.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
mod parser;
|
||||
mod token;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
46
src/parser/ast.rs
Normal file
46
src/parser/ast.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub struct Ast {
|
||||
program: Vec<Statement>,
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Open(String), // TODO: parse into `OpenTree`
|
||||
Definition {
|
||||
is_public: bool,
|
||||
ident: String,
|
||||
generics: Vec<Expression>, // TODO: refine
|
||||
definition_kind: crate::token::Kind,
|
||||
definition: Expression,
|
||||
},
|
||||
Module {
|
||||
is_public: bool,
|
||||
ident: String,
|
||||
definition: Vec<Statement>,
|
||||
},
|
||||
Comment(String), // TODO: differentiate kinds
|
||||
}
|
||||
|
||||
pub enum Expression {
|
||||
Function {
|
||||
args: Vec<Expression>, // TODO: get more granular
|
||||
body: Vec<Statement>,
|
||||
},
|
||||
Application {
|
||||
function: Box<Expression>,
|
||||
args: Vec<Expression>,
|
||||
},
|
||||
BinOp {
|
||||
operands: Box<(Expression, Expression)>,
|
||||
operator: Box<Expression>, // TODO: refine? (maybe we _want_ arbitrary binary operators)
|
||||
},
|
||||
Type(Vec<Type>),
|
||||
GenericSpecification(Vec<Expression>), // TODO: refine
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
Record(HashMap<String, String>), // { red: u8, green: u8, blue: u8 }
|
||||
Tuple(Vec<String>), // `Some (T) | None`
|
||||
Simple(String), // `Option::Some (T)`
|
||||
Empty, // `Option::None`
|
||||
}
|
||||
31
src/parser/mod.rs
Normal file
31
src/parser/mod.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use chumsky::prelude::*;
|
||||
|
||||
use crate::token::{Kind, Token};
|
||||
|
||||
pub mod ast;
|
||||
|
||||
type Span = SimpleSpan;
|
||||
type Spanned<T> = (T, Span);
|
||||
|
||||
fn tok<'a>(kind: Kind) -> impl Parser<'a, &'a [Token], (), extra::Err<Rich<'a, Token>>> {
|
||||
any().filter(move |t: &Token| t.kind == kind).ignored()
|
||||
}
|
||||
|
||||
fn parser<'a>() -> impl Parser<'a, &'a [Token], i64, extra::Err<Rich<'a, Token>>> {
|
||||
recursive(|expr| {
|
||||
// let ident = select! { Kind::Ident(n) => n };
|
||||
|
||||
let args = expr
|
||||
.recover_with(skip_then_retry_until(
|
||||
any().ignored(),
|
||||
any()
|
||||
.filter(|t: &Token| matches!(t.kind, Kind::Comma | Kind::RParen))
|
||||
.ignored(),
|
||||
))
|
||||
.separated_by(tok(Kind::Comma))
|
||||
.collect::<Vec<_>>()
|
||||
.delimited_by(tok(Kind::LParen), tok(Kind::RParen));
|
||||
|
||||
args
|
||||
})
|
||||
}
|
||||
184
src/token.rs
Normal file
184
src/token.rs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
use logos::{Lexer, Logos};
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq)]
|
||||
pub struct TokenExtras {
|
||||
line: usize,
|
||||
line_start: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Logos, Debug, PartialEq)]
|
||||
#[logos(skip r"[ \t\r\n\f]+", extras = TokenExtras)]
|
||||
pub enum Kind {
|
||||
// special, idents, literals
|
||||
#[regex(r#"([_a-zA-Z][a-zA-Z0-9]*)"#, |lex| lex.slice().to_owned())]
|
||||
Ident(String),
|
||||
#[regex(r#"([0-9]+)"#, |lex| lex.slice().to_owned())]
|
||||
Integer(String),
|
||||
// Float(f64),
|
||||
#[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| lex.slice().get(1..lex.slice().len()-1).unwrap().to_owned())]
|
||||
StringLiteral(String), // TODO: string interpolation will likely require this be changed
|
||||
|
||||
#[regex(r#"//![\s]*(.*)"#, |lex| lex.slice().to_owned(), allow_greedy=true)]
|
||||
ModDocComment(String),
|
||||
#[regex(r#"///[\s]*(.*)"#, |lex| lex.slice().to_owned(), allow_greedy=true)]
|
||||
DocComment(String),
|
||||
#[regex(r#"//[\s]*(.*)"#, |lex| lex.slice().to_owned(), allow_greedy=true)]
|
||||
InLineComment(String),
|
||||
|
||||
// keywords
|
||||
#[token("fn")]
|
||||
Fn,
|
||||
#[token("pub")]
|
||||
Pub,
|
||||
#[token("open")]
|
||||
Open,
|
||||
#[token("module")]
|
||||
Module,
|
||||
|
||||
// equality
|
||||
#[token("=")]
|
||||
EqualByAssign,
|
||||
#[token(":=")]
|
||||
EqualByDefinition,
|
||||
#[token("::=")]
|
||||
EqualByStructure,
|
||||
|
||||
// syntax
|
||||
#[token("(")]
|
||||
LParen,
|
||||
#[token(")")]
|
||||
RParen,
|
||||
#[token("{")]
|
||||
LBrace,
|
||||
#[token("}")]
|
||||
RBrace,
|
||||
#[token("[")]
|
||||
LBracket,
|
||||
#[token("]")]
|
||||
RBracket,
|
||||
#[token(",")]
|
||||
Comma,
|
||||
#[token(".")]
|
||||
Dot,
|
||||
#[token(";")]
|
||||
Semicolon,
|
||||
#[token("::")]
|
||||
DoubleColon,
|
||||
#[token("...")]
|
||||
Ellipsis,
|
||||
#[token("<")]
|
||||
LAngle,
|
||||
#[token(">")]
|
||||
RAngle,
|
||||
#[token("|")]
|
||||
Bar,
|
||||
#[token("->")]
|
||||
Arrow,
|
||||
}
|
||||
|
||||
pub struct SpannedToken {
|
||||
pub kind: Result<Kind, ()>,
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Token {
|
||||
pub kind: Kind,
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
impl TryFrom<SpannedToken> for Token {
|
||||
type Error = ();
|
||||
fn try_from(SpannedToken { kind, line, column }: SpannedToken) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
kind: kind?,
|
||||
line,
|
||||
column,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpannedLexer<'src> {
|
||||
inner: Lexer<'src, Kind>,
|
||||
}
|
||||
|
||||
impl<'src> SpannedLexer<'src> {
|
||||
pub fn new(source: &'src str) -> Self {
|
||||
Self {
|
||||
inner: Kind::lexer(source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SpannedLexer<'_> {
|
||||
type Item = SpannedToken;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let kind = self.inner.next()?;
|
||||
let span = self.inner.span();
|
||||
let line = self.inner.extras.line;
|
||||
let column = span.start - self.inner.extras.line_start;
|
||||
|
||||
Some(SpannedToken { kind, line, column })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn spans() -> Vec<Token> {
|
||||
vec![
|
||||
Token {
|
||||
kind: Kind::ModDocComment("//! Module-level documentation".into()),
|
||||
line: 0,
|
||||
column: 0,
|
||||
},
|
||||
Token {
|
||||
kind: Kind::Open,
|
||||
line: 0,
|
||||
column: 0,
|
||||
},
|
||||
Token {
|
||||
kind: Kind::Ident("std".into()),
|
||||
line: 0,
|
||||
column: 0,
|
||||
},
|
||||
Token {
|
||||
kind: Kind::DoubleColon,
|
||||
line: 0,
|
||||
column: 0,
|
||||
},
|
||||
Token {
|
||||
kind: Kind::Ident("io".into()),
|
||||
line: 0,
|
||||
column: 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_lex_example_main() {
|
||||
let mut lexer = SpannedLexer::new(include_str!("../examples/main.jkl"));
|
||||
for span in spans() {
|
||||
let next: Token = lexer
|
||||
.next()
|
||||
.expect("Should be Some")
|
||||
.try_into()
|
||||
.expect("Should be Ok");
|
||||
assert_eq!(next.kind, span.kind);
|
||||
}
|
||||
|
||||
while let Some(span) = lexer.next() {
|
||||
dbg!(&span.kind);
|
||||
if span.kind.is_err() {
|
||||
println!("Err token found at line={} col={}", span.line, span.column);
|
||||
}
|
||||
span.kind.expect("Should be valid token");
|
||||
}
|
||||
|
||||
assert!(lexer.next().is_none());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue