mirror of
https://github.com/Noratrieb/config-language.git
synced 2026-01-14 16:45:06 +01:00
stuff
This commit is contained in:
commit
bce268cceb
13 changed files with 672 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
150
Cargo.lock
generated
Normal file
150
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codespan"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e"
|
||||||
|
dependencies = [
|
||||||
|
"codespan-reporting",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codespan-reporting"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||||
|
dependencies = [
|
||||||
|
"termcolor",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "config-language"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"codespan",
|
||||||
|
"codespan-reporting",
|
||||||
|
"indexmap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
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"
|
||||||
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "config-language"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codespan = "0.11.1"
|
||||||
|
codespan-reporting = "0.11.1"
|
||||||
|
indexmap = "2.2.6"
|
||||||
45
example-configs/kubernetes/deployment-dry.nix
Normal file
45
example-configs/kubernetes/deployment-dry.nix
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
let
|
||||||
|
trivialWebService = { name, selectorLabel, port, extraPodLabels ? { } }:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
apiVersion = "apps/v1";
|
||||||
|
kind = "Deployment";
|
||||||
|
metadata = {
|
||||||
|
inherit name;
|
||||||
|
};
|
||||||
|
spec = {
|
||||||
|
selector.matchLabels = selectorLabel;
|
||||||
|
replicas = 2;
|
||||||
|
template = {
|
||||||
|
metadata.labels = selectorLabel // extraPodLabels;
|
||||||
|
spec.containters = [
|
||||||
|
{
|
||||||
|
inherit name;
|
||||||
|
image = "nginx";
|
||||||
|
ports = [{ containerPort = port; }];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
apiVersion = "apps/v1";
|
||||||
|
kind = "Service";
|
||||||
|
metadata = {
|
||||||
|
inherit name;
|
||||||
|
labels = selectorLabel;
|
||||||
|
};
|
||||||
|
spec = {
|
||||||
|
ports = { port = 80; protocol = "TCP"; };
|
||||||
|
selector = selectorLabel;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
||||||
|
;
|
||||||
|
in
|
||||||
|
trivialWebService {
|
||||||
|
name = "my-nginx";
|
||||||
|
selectorLabel = { run = "my-nginx"; };
|
||||||
|
port = 80;
|
||||||
|
extraPodLabels = { someOther = "label"; };
|
||||||
|
}
|
||||||
33
example-configs/kubernetes/deployment-raw.nix
Normal file
33
example-configs/kubernetes/deployment-raw.nix
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
apiVersion = "apps/v1";
|
||||||
|
kind = "Deployment";
|
||||||
|
metadata.name = "my-nginx";
|
||||||
|
spec = {
|
||||||
|
selector.matchLabels.run = "my-nginx";
|
||||||
|
replicas = 2;
|
||||||
|
template = {
|
||||||
|
metadata.labels.run = "my-nginx";
|
||||||
|
spec.containters = [
|
||||||
|
{
|
||||||
|
name = "my-nginx";
|
||||||
|
image = "nginx";
|
||||||
|
ports = [{ containerPort = 80; }];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
apiVersion = "apps/v1";
|
||||||
|
kind = "Service";
|
||||||
|
metadata = {
|
||||||
|
name = "my-nginx";
|
||||||
|
labels.run = "my-nginx";
|
||||||
|
};
|
||||||
|
spec = {
|
||||||
|
ports = { port = 80; protocol = "TCP"; };
|
||||||
|
selector = { run = "my-nginx"; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
||||||
33
example-configs/kubernetes/deployment.yml
Normal file
33
example-configs/kubernetes/deployment.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# real k8s configs use --- and not a top-level array
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
run: my-nginx
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
run: my-nginx
|
||||||
|
someOther: label
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: my-nginx
|
||||||
|
image: nginx
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: my-nginx
|
||||||
|
labels:
|
||||||
|
run: my-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
protocol: TCP
|
||||||
|
selector:
|
||||||
|
run: my-nginx
|
||||||
34
example-configs/rust-cargo/Cargo.json
Normal file
34
example-configs/rust-cargo/Cargo.json
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"cargo-features": ["public-dependency"],
|
||||||
|
"package": {
|
||||||
|
"name": "std",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"metadata": { "fortanix-sgx": { "threads": 125, "heap_size": 134217728 } }
|
||||||
|
},
|
||||||
|
"lib": { "crate-type": ["dylib", "rlib"] },
|
||||||
|
"dependencies": {
|
||||||
|
"alloc": { "path": "../alloc", "public": true },
|
||||||
|
"something": "1.2.0",
|
||||||
|
"std_detect": {
|
||||||
|
"path": "../stdarch/crates/std_detect",
|
||||||
|
"default-features": false,
|
||||||
|
"features": ["rustc-dep-of-std"]
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"cfg(not(all(windows, target_env = \"msvc\", not(target_vendor = \"uwp\"))))": {
|
||||||
|
"dependencies": {
|
||||||
|
"miniz_oxide": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"optional": true,
|
||||||
|
"default-features": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"backtrace": ["gimli-symbolize", "miniz_oxide/rustc-dep-of-std"],
|
||||||
|
"gimli-symbolize": []
|
||||||
|
},
|
||||||
|
"bench": [{ "name": "stdbenches", "path": "benches/lib.rs", "test": true }]
|
||||||
|
}
|
||||||
37
example-configs/rust-cargo/Cargo.toml
Normal file
37
example-configs/rust-cargo/Cargo.toml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# github.com/rust-lang/rust:library/std/Cargo.toml
|
||||||
|
|
||||||
|
cargo-features = ["public-dependency"]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "std"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["dylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
alloc = { path = "../alloc", public = true }
|
||||||
|
something = "1.2.0"
|
||||||
|
std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] }
|
||||||
|
|
||||||
|
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
|
||||||
|
miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
|
||||||
|
|
||||||
|
|
||||||
|
[features]
|
||||||
|
backtrace = [
|
||||||
|
"gimli-symbolize",
|
||||||
|
'miniz_oxide/rustc-dep-of-std',
|
||||||
|
]
|
||||||
|
gimli-symbolize = []
|
||||||
|
|
||||||
|
[package.metadata.fortanix-sgx]
|
||||||
|
# Maximum possible number of threads when testing
|
||||||
|
threads = 125
|
||||||
|
# Maximum heap size
|
||||||
|
heap_size = 0x8000000
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "stdbenches"
|
||||||
|
path = "benches/lib.rs"
|
||||||
|
test = true
|
||||||
38
example-configs/rust-cargo/Cargo.yml
Normal file
38
example-configs/rust-cargo/Cargo.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
cargo-features: [public-dependency]
|
||||||
|
|
||||||
|
package:
|
||||||
|
name: std
|
||||||
|
version: "0.0.0"
|
||||||
|
metadata:
|
||||||
|
fortanix-sgx:
|
||||||
|
threads: 125
|
||||||
|
heap_size: 0x8000000
|
||||||
|
lib:
|
||||||
|
crate-type:
|
||||||
|
- dylib
|
||||||
|
- rlib
|
||||||
|
dependencies:
|
||||||
|
alloc:
|
||||||
|
path: ../alloc
|
||||||
|
public: true
|
||||||
|
something: "1.2.0"
|
||||||
|
std_detect:
|
||||||
|
path: ../stdarch/crates/std_detect
|
||||||
|
default-features: false
|
||||||
|
features: [rustc-dep-of-std]
|
||||||
|
target:
|
||||||
|
'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))':
|
||||||
|
dependencies:
|
||||||
|
miniz_oxide:
|
||||||
|
version: "0.7.0"
|
||||||
|
optional: true
|
||||||
|
default-features: false
|
||||||
|
features:
|
||||||
|
backtrace:
|
||||||
|
- gimli-symbolize
|
||||||
|
- miniz_oxide/rustc-dep-of-std
|
||||||
|
gimli-symbolize: []
|
||||||
|
bench:
|
||||||
|
- name: stdbenches
|
||||||
|
path: benches/lib.rs
|
||||||
|
test: true
|
||||||
12
example.nc
Normal file
12
example.nc
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#type X = jsonSchema("https://kubernetesjsonschema.dev/v1.14.0/deployment-apps-v1.json")
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
apiVersion = "apps/v1",
|
||||||
|
kind = "Deployment",
|
||||||
|
metadata = {
|
||||||
|
name = "my-nginx",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x",
|
||||||
|
]
|
||||||
45
src/lib.rs
Normal file
45
src/lib.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use codespan::Span;
|
||||||
|
use codespan_reporting::{
|
||||||
|
diagnostic::{Diagnostic, Label},
|
||||||
|
files::SimpleFiles,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod parser;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Parse(parser::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
fn into_msg_and_span(self) -> (String, Span) {
|
||||||
|
match self {
|
||||||
|
Error::Parse(e) => e.into_msg_and_span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(input: &str) -> Result<(), Error> {
|
||||||
|
let expr = parser::parse(input).map_err(Error::Parse)?;
|
||||||
|
dbg!(expr);
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_error(input: &str, error: Error) {
|
||||||
|
let mut files = SimpleFiles::new();
|
||||||
|
let file_id = files.add("example.nc", input);
|
||||||
|
let (msg, span) = error.into_msg_and_span();
|
||||||
|
let diagnostic = Diagnostic::error()
|
||||||
|
.with_message(msg)
|
||||||
|
.with_labels(vec![Label::primary(
|
||||||
|
file_id,
|
||||||
|
(span.start().to_usize())..span.end().to_usize(),
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let writer = codespan_reporting::term::termcolor::StandardStream::stderr(
|
||||||
|
codespan_reporting::term::termcolor::ColorChoice::Always,
|
||||||
|
);
|
||||||
|
let config = codespan_reporting::term::Config::default();
|
||||||
|
|
||||||
|
codespan_reporting::term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
|
||||||
|
}
|
||||||
7
src/main.rs
Normal file
7
src/main.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn main() {
|
||||||
|
let input = std::fs::read_to_string("example.nc").unwrap();
|
||||||
|
match config_language::evaluate(&input) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => config_language::render_error(&input, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
228
src/parser.rs
Normal file
228
src/parser.rs
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
use std::{iter::Peekable, vec};
|
||||||
|
|
||||||
|
use codespan::Span;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
SourceFileTooBig(usize),
|
||||||
|
InvalidCharacter(char, Span),
|
||||||
|
UnterminatedStringLiteral(Span),
|
||||||
|
UnexpectedToken(String, Token),
|
||||||
|
TooNested(Span),
|
||||||
|
UnexpectedEndOfFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Expr {
|
||||||
|
Array(Vec<Expr>, Span),
|
||||||
|
Object(IndexMap<String, Expr>, Span),
|
||||||
|
String(String, Span),
|
||||||
|
Ident(String, Span),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TokenStream {
|
||||||
|
tokens: Peekable<vec::IntoIter<Token>>,
|
||||||
|
nesting: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenStream {
|
||||||
|
fn peek(&mut self) -> Result<&Token> {
|
||||||
|
self.tokens.peek().ok_or_else(|| Error::UnexpectedEndOfFile)
|
||||||
|
}
|
||||||
|
fn next(&mut self) -> Result<Token> {
|
||||||
|
self.tokens.next().ok_or_else(|| Error::UnexpectedEndOfFile)
|
||||||
|
}
|
||||||
|
fn eat(&mut self, kind: TokenKind) -> Option<Token> {
|
||||||
|
let next = self.peek().ok()?;
|
||||||
|
if next.kind != kind {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(self.next().unwrap())
|
||||||
|
}
|
||||||
|
fn expect(&mut self, kind: TokenKind) -> Result<Token> {
|
||||||
|
let next = self.next()?;
|
||||||
|
if next.kind != kind {
|
||||||
|
return Err(Error::UnexpectedToken(format!("expected {kind:?}"), next));
|
||||||
|
}
|
||||||
|
Ok(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(input: &str) -> Result<Expr> {
|
||||||
|
let tokens = lexer(input)?;
|
||||||
|
dbg!(&tokens);
|
||||||
|
let expr = parse_expr(&mut TokenStream {
|
||||||
|
tokens: tokens.into_iter().peekable(),
|
||||||
|
nesting: 0,
|
||||||
|
})?;
|
||||||
|
dbg!(&expr);
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr(t: &mut TokenStream) -> Result<Expr> {
|
||||||
|
let token = t.next()?;
|
||||||
|
if t.nesting > 50 {
|
||||||
|
return Err(Error::TooNested(token.span));
|
||||||
|
}
|
||||||
|
let expr = match token.kind {
|
||||||
|
TokenKind::BracketOpen => {
|
||||||
|
let mut elems = vec![];
|
||||||
|
let mut had_comma = true;
|
||||||
|
loop {
|
||||||
|
if let Ok(Token {
|
||||||
|
kind: TokenKind::BracketClose,
|
||||||
|
..
|
||||||
|
}) = t.peek()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !had_comma {
|
||||||
|
return Err(Error::UnexpectedToken("comma".to_owned(), t.next()?));
|
||||||
|
}
|
||||||
|
|
||||||
|
t.nesting += 1;
|
||||||
|
elems.push(parse_expr(t)?);
|
||||||
|
t.nesting -= 1;
|
||||||
|
|
||||||
|
had_comma = t.eat(TokenKind::Comma).is_some();
|
||||||
|
}
|
||||||
|
|
||||||
|
t.next()?;
|
||||||
|
Expr::Array(elems, token.span)
|
||||||
|
}
|
||||||
|
TokenKind::BraceOpen => {
|
||||||
|
let mut elems = IndexMap::new();
|
||||||
|
let mut had_comma = true;
|
||||||
|
loop {
|
||||||
|
if let Ok(Token {
|
||||||
|
kind: TokenKind::BraceClose,
|
||||||
|
..
|
||||||
|
}) = t.peek()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !had_comma {
|
||||||
|
return Err(Error::UnexpectedToken("comma".to_owned(), t.next()?));
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_tok = t.next()?;
|
||||||
|
let Token {
|
||||||
|
kind: TokenKind::Ident(name),
|
||||||
|
..
|
||||||
|
} = name_tok
|
||||||
|
else {
|
||||||
|
return Err(Error::UnexpectedToken("expected name".into(), name_tok));
|
||||||
|
};
|
||||||
|
t.expect(TokenKind::Equal)?;
|
||||||
|
t.nesting += 1;
|
||||||
|
elems.insert(name, parse_expr(t)?);
|
||||||
|
t.nesting -= 1;
|
||||||
|
|
||||||
|
had_comma = t.eat(TokenKind::Comma).is_some();
|
||||||
|
}
|
||||||
|
|
||||||
|
t.next()?;
|
||||||
|
Expr::Object(elems, token.span)
|
||||||
|
}
|
||||||
|
TokenKind::String(s) => Expr::String(s, token.span),
|
||||||
|
TokenKind::Ident(s) => Expr::Ident(s, token.span),
|
||||||
|
_ => return Err(Error::UnexpectedToken("expected expression".into(), token)),
|
||||||
|
};
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Token {
|
||||||
|
span: Span,
|
||||||
|
kind: TokenKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
enum TokenKind {
|
||||||
|
BracketOpen,
|
||||||
|
BracketClose,
|
||||||
|
BraceOpen,
|
||||||
|
BraceClose,
|
||||||
|
Equal,
|
||||||
|
Comma,
|
||||||
|
Ident(String),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lexer(input_str: &str) -> Result<Vec<Token>> {
|
||||||
|
if input_str.len() >= (u32::MAX as usize) {
|
||||||
|
return Err(Error::SourceFileTooBig(input_str.len()));
|
||||||
|
}
|
||||||
|
let mut input = input_str.char_indices().peekable();
|
||||||
|
|
||||||
|
let mut tokens = vec![];
|
||||||
|
|
||||||
|
while let Some((idx, c)) = input.next() {
|
||||||
|
let span = Span::new(idx as u32, (idx as u32) + 1);
|
||||||
|
let mut simple = |kind| tokens.push(Token { span, kind });
|
||||||
|
match c {
|
||||||
|
c if c.is_whitespace() => {}
|
||||||
|
'#' => loop {
|
||||||
|
if let Some((_, '\n')) = input.next() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'[' => simple(TokenKind::BracketOpen),
|
||||||
|
']' => simple(TokenKind::BracketClose),
|
||||||
|
'{' => simple(TokenKind::BraceOpen),
|
||||||
|
'}' => simple(TokenKind::BraceClose),
|
||||||
|
'=' => simple(TokenKind::Equal),
|
||||||
|
',' => simple(TokenKind::Comma),
|
||||||
|
'"' => loop {
|
||||||
|
match input.next() {
|
||||||
|
None => return Err(Error::UnterminatedStringLiteral(span)),
|
||||||
|
Some((next_idx, '"')) => {
|
||||||
|
let s = &input_str[(idx + 1)..next_idx];
|
||||||
|
tokens.push(Token {
|
||||||
|
span: Span::new(idx as u32, next_idx as u32),
|
||||||
|
kind: TokenKind::String(s.to_owned()),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
c if c.is_alphabetic() => {
|
||||||
|
while let Some((next_idx, c)) = input.peek() {
|
||||||
|
if !c.is_alphanumeric() {
|
||||||
|
let s = &input_str[idx..*next_idx];
|
||||||
|
tokens.push(Token {
|
||||||
|
span: Span::new(idx as u32, *next_idx as u32),
|
||||||
|
kind: TokenKind::Ident(s.to_owned()),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c => return Err(Error::InvalidCharacter(c, span)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub(crate) fn into_msg_and_span(self) -> (String, Span) {
|
||||||
|
match self {
|
||||||
|
Error::SourceFileTooBig(_) => ("source file bigger than 4GB".into(), Span::default()),
|
||||||
|
Error::InvalidCharacter(c, sp) => (format!("invalid character: {c}"), sp),
|
||||||
|
Error::UnterminatedStringLiteral(sp) => (format!("unterminated string literal"), sp),
|
||||||
|
Error::UnexpectedToken(expected, tok) => {
|
||||||
|
(format!("unexpected token, expected {expected}"), tok.span)
|
||||||
|
}
|
||||||
|
Error::TooNested(sp) => ("too nested".to_owned(), sp),
|
||||||
|
Error::UnexpectedEndOfFile => ("unexpected end of file".to_owned(), Span::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue