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